summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstkhan <personal@slickd.xyz>2023-12-26 13:42:41 -0600
committerstkhan <personal@slickd.xyz>2023-12-26 13:42:41 -0600
commit5b5086084b4f8451afb097a471f905c2cb2ad634 (patch)
treed0e6378a453c0896c64e2225b630544117dfe427
Init commitHEADmaster
-rw-r--r--Makefile19
-rw-r--r--dwl-v0.5/CHANGELOG.md98
-rw-r--r--dwl-v0.5/LICENSE692
-rw-r--r--dwl-v0.5/LICENSE.dwm39
-rw-r--r--dwl-v0.5/LICENSE.sway19
-rw-r--r--dwl-v0.5/LICENSE.tinywl127
-rw-r--r--dwl-v0.5/Makefile74
-rw-r--r--dwl-v0.5/Makefile.orig66
-rw-r--r--dwl-v0.5/Makefile.rej15
-rw-r--r--dwl-v0.5/README.md173
-rw-r--r--dwl-v0.5/client.h394
-rw-r--r--dwl-v0.5/config.def.h201
-rw-r--r--dwl-v0.5/config.h201
-rw-r--r--dwl-v0.5/config.mk15
-rw-r--r--dwl-v0.5/dwl.1158
-rw-r--r--dwl-v0.5/dwl.c3306
-rw-r--r--dwl-v0.5/dwl.c.orig3096
-rw-r--r--dwl-v0.5/dwl.c.rej10
-rw-r--r--dwl-v0.5/dwl.desktop5
-rw-r--r--dwl-v0.5/net-tapesoftware-dwl-wm-unstable-v1-protocol.c89
-rw-r--r--dwl-v0.5/patches/autostart.patch140
-rw-r--r--dwl-v0.5/patches/main...krypciak:patch-restartdwl.patch52
-rw-r--r--dwl-v0.5/patches/main...sevz17:vanitygaps.patch345
-rw-r--r--dwl-v0.5/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml164
-rw-r--r--dwl-v0.5/protocols/wlr-layer-shell-unstable-v1.xml390
-rw-r--r--dwl-v0.5/util.c35
-rw-r--r--dwl-v0.5/util.h4
-rw-r--r--dwl-v0.5/wayland-ipc.patch498
-rwxr-xr-xscripts/swaybg_set3
-rw-r--r--somebar/.builds/archlinux.yml19
-rw-r--r--somebar/.builds/freebsd.yml20
-rw-r--r--somebar/.editorconfig5
-rw-r--r--somebar/CHANGELOG.md19
-rw-r--r--somebar/LICENSE20
-rw-r--r--somebar/README.md113
-rw-r--r--somebar/contrib/clickable-tags-using-wtype.patch91
-rw-r--r--somebar/contrib/colorless-status.patch15
-rw-r--r--somebar/contrib/disable-window-title.patch15
-rw-r--r--somebar/contrib/dwm-like-tag-indicator.patch34
-rw-r--r--somebar/contrib/hide-vacant-tags.patch54
-rw-r--r--somebar/contrib/indicator-size-props.patch54
-rw-r--r--somebar/contrib/ipc.patch506
-rw-r--r--somebar/contrib/markup-in-status-messages.patch65
-rw-r--r--somebar/contrib/show-status-on-selected-monitor.patch43
-rw-r--r--somebar/meson.build31
-rw-r--r--somebar/protocols/meson.build22
-rw-r--r--somebar/protocols/wlr-layer-shell-unstable-v1.xml390
-rw-r--r--somebar/screenshot.pngbin0 -> 6715 bytes
-rw-r--r--somebar/somebar.155
-rw-r--r--somebar/src/bar.cpp315
-rw-r--r--somebar/src/bar.hpp74
-rw-r--r--somebar/src/common.hpp76
-rw-r--r--somebar/src/config.def.hpp27
-rw-r--r--somebar/src/config.hpp27
-rw-r--r--somebar/src/line_buffer.hpp71
-rw-r--r--somebar/src/main.cpp613
-rw-r--r--somebar/src/shm_buffer.cpp85
-rw-r--r--somebar/src/shm_buffer.hpp45
58 files changed, 13332 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..45133f6
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,19 @@
+all: scripts dwl scripts somebar
+
+dwl:
+ cp scripts/* ~/.local/bin/
+ make -C dwl-v0.5 all
+
+clean:
+ make -C dwl-v0.5 clean
+
+test:
+ Xephyr :4 -screen 1920x1080 &
+ DISPLAY=:4 ./dwl-v0.5/dwl
+
+somebar:
+ meson -C somebar setup build
+ ninja -C somebar/build
+install:
+ make -C dwl-v0.5 install
+ ninja -C somebar/build install
diff --git a/dwl-v0.5/CHANGELOG.md b/dwl-v0.5/CHANGELOG.md
new file mode 100644
index 0000000..ac0f3f2
--- /dev/null
+++ b/dwl-v0.5/CHANGELOG.md
@@ -0,0 +1,98 @@
+# Changelog
+
+* [0.5](#0.5)
+
+## 0.5
+
+### Added
+
+* Allow configure x and y position of outputs ([#301][301])
+* Implement repeatable keybindings ([#368][368])
+* Print app id in printstatus() output ([#381][381])
+* Display client count in monocle symbol ([#387][387])
+* Export XCURSOR_SIZE to fix apps using an older version of Qt ([#425][425])
+* Support for wp-fractional-scale-v1 (through wlr_scene: [wlroots!3511][wlroots!3511])
+* dwl now sends `wl_surface.preferred_buffer_scale` (through wlr_scene: [wlroots!4269][wlroots!4269])
+* Add support for xdg-shell v6 ([#465][465])
+* Add support for wp-cursor-shape-v1 ([#444][444])
+* Add desktop file ([#484][484])
+* Add macro to easily configure colors ([#466][466])
+* Color of urgent clients are now red ([#494][494])
+* New flag `-d` and option `log_level` to change the wlroots debug level
+* Add CHANGELOG.md ([#501][501])
+
+[301]: https://github.com/djpohly/dwl/pull/301
+[368]: https://github.com/djpohly/dwl/pull/368
+[381]: https://github.com/djpohly/dwl/pull/381
+[387]: https://github.com/djpohly/dwl/issues/387
+[425]: https://github.com/djpohly/dwl/pull/425
+[wlroots!4269]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4269
+[wlroots!3511]: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/3511
+[465]: https://github.com/djpohly/dwl/pull/465
+[444]: https://github.com/djpohly/dwl/pull/444
+[484]: https://github.com/djpohly/dwl/pull/484
+[466]: https://github.com/djpohly/dwl/issues/466
+[494]: https://github.com/djpohly/dwl/pull/494
+[501]: https://github.com/djpohly/dwl/pull/501
+
+
+### Changed
+
+* Replace `tags` with `TAGCOUNT` in config.def.h ([#403][403])
+* Pop ups are now destroyed when focusing another client ([#408][408])
+* dwl does not longer respect size hints, instead clip windows if they are
+ larger than they should be ([#455][455])
+* The version of wlr-layer-shell-unstable-v1 was lowered to 3 (from 4)
+* Use the same border color as dwm ([#494][494])
+
+[403]: https://github.com/djpohly/dwl/pull/403
+[408]: https://github.com/djpohly/dwl/pull/409
+[455]: https://github.com/djpohly/dwl/pull/455
+[494]: https://github.com/djpohly/dwl/pull/494
+
+
+### Removed
+
+* Remove unused `rootcolor` option ([#401][401])
+* Remove support for wlr-input-inhibitor-unstable-v1 ([#430][430])
+* Remove support for KDE idle protocol ([#431][431])
+
+[401]: https://github.com/djpohly/dwl/pull/401
+[430]: https://github.com/djpohly/dwl/pull/430
+[431]: https://github.com/djpohly/dwl/pull/431
+
+
+### Fixed
+
+* Fix crash when creating a layer surface with all outputs disabled
+ ([#421][421])
+* Fix other clients being shown as focused if the focused client have pop ups
+ open ([#408][408])
+* Resize fullscreen clients when updating monitor mode
+* dwl no longer crash at exit like sometimes did
+* Fullscreen background appearing above clients ([#487][487])
+* Fix a segfault when user provides invalid xkb_rules ([#518][518])
+
+[421]: https://github.com/djpohly/dwl/pull/421
+[408]: https://github.com/djpohly/dwl/issues/408
+[487]: https://github.com/djpohly/dwl/issues/487
+[518]: https://github.com/djpohly/dwl/pull/518
+
+
+### Contributors
+
+* A Frederick Christensen
+* Angelo Antony
+* Ben Collerson
+* Devin J. Pohly
+* Forrest Bushstone
+* gan-of-culture
+* godalming123
+* Job79
+* link2xt
+* Micah Gorrell
+* Nikita Ivanov
+* Palanix
+* pino-desktop
+* Weiseguy
+* Yves Zoundi
diff --git a/dwl-v0.5/LICENSE b/dwl-v0.5/LICENSE
new file mode 100644
index 0000000..658085a
--- /dev/null
+++ b/dwl-v0.5/LICENSE
@@ -0,0 +1,692 @@
+dwl - dwm for Wayland
+
+Copyright © 2020 dwl team
+
+See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway.
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+----
+
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ <program> Copyright (C) <year> <name of author>
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/dwl-v0.5/LICENSE.dwm b/dwl-v0.5/LICENSE.dwm
new file mode 100644
index 0000000..507e4dc
--- /dev/null
+++ b/dwl-v0.5/LICENSE.dwm
@@ -0,0 +1,39 @@
+Portions of dwl based on dwm code are used under the following license:
+
+MIT/X Consortium License
+
+© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
+© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
+© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
+© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
+© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
+© 2007-2009 Christof Musik <christof at sendfax dot de>
+© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
+© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
+© 2008 Martin Hurton <martin dot hurton at gmail dot com>
+© 2008 Neale Pickett <neale dot woozle dot org>
+© 2009 Mate Nagy <mnagy at port70 dot net>
+© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
+© 2011 Christoph Lohmann <20h@r-36.net>
+© 2015-2016 Quentin Rameau <quinq@fifth.space>
+© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
+© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+the rights to use, copy, modify, merge, publish, distribute, sublicense,
+and/or sell copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
diff --git a/dwl-v0.5/LICENSE.sway b/dwl-v0.5/LICENSE.sway
new file mode 100644
index 0000000..3e0cacc
--- /dev/null
+++ b/dwl-v0.5/LICENSE.sway
@@ -0,0 +1,19 @@
+Copyright (c) 2016-2017 Drew DeVault
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/dwl-v0.5/LICENSE.tinywl b/dwl-v0.5/LICENSE.tinywl
new file mode 100644
index 0000000..7023690
--- /dev/null
+++ b/dwl-v0.5/LICENSE.tinywl
@@ -0,0 +1,127 @@
+dwl is originally based on TinyWL, which is used under the following license:
+
+This work is licensed under CC0, which effectively puts it in the public domain.
+
+---
+
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/dwl-v0.5/Makefile b/dwl-v0.5/Makefile
new file mode 100644
index 0000000..580d06e
--- /dev/null
+++ b/dwl-v0.5/Makefile
@@ -0,0 +1,74 @@
+.POSIX:
+.SUFFIXES:
+
+include config.mk
+
+# flags for compiling
+DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XWAYLAND)
+DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\
+ -Werror=strict-prototypes -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types
+
+# CFLAGS / LDFLAGS
+PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS)
+DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
+LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS)
+
+all: dwl
+dwl: dwl.o util.o net-tapesoftware-dwl-wm-unstable-v1-protocol.o
+ $(CC) dwl.o util.o net-tapesoftware-dwl-wm-unstable-v1-protocol.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
+dwl.o: dwl.c config.mk config.h client.h cursor-shape-v1-protocol.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h net-tapesoftware-dwl-wm-unstable-v1-protocol.o
+util.o: util.c util.h
+
+# wayland-scanner is a tool which generates C headers and rigging for Wayland
+# protocols, which are specified in XML. wlroots requires you to rig these up
+# to your build system yourself and provide them in the include path.
+WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner`
+WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols`
+
+xdg-shell-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+wlr-layer-shell-unstable-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ protocols/wlr-layer-shell-unstable-v1.xml $@
+cursor-shape-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@
+
+net-tapesoftware-dwl-wm-unstable-v1-protocol.h: protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
+ $(WAYLAND_SCANNER) server-header \
+ protocols/net-tapesoftware-dwl-wm-unstable-v1.xml $@
+net-tapesoftware-dwl-wm-unstable-v1-protocol.c: protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
+ $(WAYLAND_SCANNER) private-code \
+ protocols/net-tapesoftware-dwl-wm-unstable-v1.xml $@
+net-tapesoftware-dwl-wm-unstable-v1-protocol.o: net-tapesoftware-dwl-wm-unstable-v1-protocol.h
+
+config.h:
+ cp config.def.h $@
+clean:
+ rm -f dwl *.o *-protocol.h
+
+dist: clean
+ mkdir -p dwl-$(VERSION)
+ cp -R LICENSE* Makefile CHANGELOG.md README.md client.h config.def.h\
+ config.mk protocols dwl.1 dwl.c util.c util.h dwl.desktop\
+ dwl-$(VERSION)
+ tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
+ rm -rf dwl-$(VERSION)
+
+install: dwl
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp -f dwl $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
+ mkdir -p $(DESTDIR)$(MANDIR)/man1
+ cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1
+ chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1
+ mkdir -p $(DESTDIR)$(DATADIR)/wayland-sessions
+ cp -f dwl.desktop $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+ chmod 644 $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+uninstall:
+ rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+
+.SUFFIXES: .c .o
+.c.o:
+ $(CC) $(CPPFLAGS) $(DWLCFLAGS) -c $<
diff --git a/dwl-v0.5/Makefile.orig b/dwl-v0.5/Makefile.orig
new file mode 100644
index 0000000..f0ff805
--- /dev/null
+++ b/dwl-v0.5/Makefile.orig
@@ -0,0 +1,66 @@
+.POSIX:
+.SUFFIXES:
+
+include config.mk
+
+# flags for compiling
+DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XWAYLAND)
+DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\
+ -Werror=strict-prototypes -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types
+
+# CFLAGS / LDFLAGS
+PKGS = wlroots wayland-server xkbcommon libinput $(XLIBS)
+DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
+LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS)
+
+all: dwl
+dwl: dwl.o util.o
+ $(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
+dwl.o: dwl.c config.mk config.h client.h cursor-shape-v1-protocol.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h
+util.o: util.c util.h
+
+# wayland-scanner is a tool which generates C headers and rigging for Wayland
+# protocols, which are specified in XML. wlroots requires you to rig these up
+# to your build system yourself and provide them in the include path.
+WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner`
+WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols`
+
+xdg-shell-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
+wlr-layer-shell-unstable-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ protocols/wlr-layer-shell-unstable-v1.xml $@
+cursor-shape-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@
+
+config.h:
+ cp config.def.h $@
+clean:
+ rm -f dwl *.o *-protocol.h
+
+dist: clean
+ mkdir -p dwl-$(VERSION)
+ cp -R LICENSE* Makefile CHANGELOG.md README.md client.h config.def.h\
+ config.mk protocols dwl.1 dwl.c util.c util.h dwl.desktop\
+ dwl-$(VERSION)
+ tar -caf dwl-$(VERSION).tar.gz dwl-$(VERSION)
+ rm -rf dwl-$(VERSION)
+
+install: dwl
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp -f dwl $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/dwl
+ mkdir -p $(DESTDIR)$(MANDIR)/man1
+ cp -f dwl.1 $(DESTDIR)$(MANDIR)/man1
+ chmod 644 $(DESTDIR)$(MANDIR)/man1/dwl.1
+ mkdir -p $(DESTDIR)$(DATADIR)/wayland-sessions
+ cp -f dwl.desktop $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+ chmod 644 $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+uninstall:
+ rm -f $(DESTDIR)$(PREFIX)/bin/dwl $(DESTDIR)$(MANDIR)/man1/dwl.1 $(DESTDIR)$(DATADIR)/wayland-sessions/dwl.desktop
+
+.SUFFIXES: .c .o
+.c.o:
+ $(CC) $(CPPFLAGS) $(DWLCFLAGS) -c $<
diff --git a/dwl-v0.5/Makefile.rej b/dwl-v0.5/Makefile.rej
new file mode 100644
index 0000000..1eb88fc
--- /dev/null
+++ b/dwl-v0.5/Makefile.rej
@@ -0,0 +1,15 @@
+--- Makefile
++++ Makefile
+@@ -14,9 +14,9 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CF
+ LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS)
+
+ all: dwl
+-dwl: dwl.o util.o
+- $(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
+-dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h
++dwl: dwl.o util.o net-tapesoftware-dwl-wm-unstable-v1-protocol.o
++ $(CC) dwl.o util.o net-tapesoftware-dwl-wm-unstable-v1-protocol.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
++dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h net-tapesoftware-dwl-wm-unstable-v1-protocol.o
+ util.o: util.c util.h
+
+ # wayland-scanner is a tool which generates C headers and rigging for Wayland
diff --git a/dwl-v0.5/README.md b/dwl-v0.5/README.md
new file mode 100644
index 0000000..62ae872
--- /dev/null
+++ b/dwl-v0.5/README.md
@@ -0,0 +1,173 @@
+# dwl - dwm for Wayland
+
+Join us on our IRC channel: [#dwl on Libera Chat]
+Or on our [Discord server].
+
+dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is
+intended to fill the same space in the Wayland world that dwm does in X11,
+primarily in terms of philosophy, and secondarily in terms of functionality.
+Like dwm, dwl is:
+
+- Easy to understand, hack on, and extend with patches
+- One C source file (or a very small number) configurable via `config.h`
+- Limited to 2200 SLOC to promote hackability
+- Tied to as few external dependencies as possible
+
+dwl is not meant to provide every feature under the sun. Instead, like dwm, it
+sticks to features which are necessary, simple, and straightforward to implement
+given the base on which it is built. Implemented default features are:
+
+- Any features provided by dwm/Xlib: simple window borders, tags, keybindings,
+ client rules, mouse move/resize. Providing a built-in status bar is an
+ exception to this goal, to avoid dependencies on font rendering and/or
+ drawing libraries when an external bar could work well.
+- Configurable multi-monitor layout support, including position and rotation
+- Configurable HiDPI/multi-DPI support
+- Idle-inhibit protocol which lets applications such as mpv disable idle
+ monitoring
+- Provide information to external status bars via stdout/stdin
+- Urgency hints via xdg-activate protocol
+- Support screen lockers via ext-session-lock-v1 protocol
+- Various Wayland protocols
+- XWayland support as provided by wlroots (can be enabled in `config.mk`)
+- Zero flickering - Wayland users naturally expect that "every frame is perfect"
+- Layer shell popups (used by Waybar)
+- Damage tracking provided by scenegraph API
+
+Features under consideration (possibly as patches) are:
+
+- Protocols made trivial by wlroots
+- Implement the text-input and input-method protocols to support IME once ibus
+ implements input-method v2 (see https://github.com/ibus/ibus/pull/2256 and
+ https://github.com/djpohly/dwl/pull/235)
+
+Feature *non-goals* for the main codebase include:
+
+- Client-side decoration (any more than is necessary to tell the clients not to)
+- Client-initiated window management, such as move, resize, and close, which can
+ be done through the compositor
+- Animations and visual effects
+
+## Building dwl
+
+dwl has the following dependencies:
+```
+libinput
+wayland
+wlroots (compiled with the libinput backend)
+xkbcommon
+wayland-protocols (compile-time only)
+pkg-config (compile-time only)
+```
+If you enable X11 support:
+```
+libxcb
+libxcb-wm
+wlroots (compiled with X11 support)
+Xwayland (runtime only)
+```
+
+Simply install these (and their `-devel` versions if your distro has separate
+development packages) and run `make`. If you wish to build against a Git
+version of wlroots, check out the [wlroots-next branch].
+
+To enable XWayland, you should uncomment its flags in `config.mk`.
+
+## Configuration
+
+All configuration is done by editing `config.h` and recompiling, in the same
+manner as dwm. There is no way to separately restart the window manager in
+Wayland without restarting the entire display server, so any changes will take
+effect the next time dwl is executed.
+
+As in the dwm community, we encourage users to share patches they have created.
+Check out the [patches page on our wiki]!
+
+## Running dwl
+
+dwl can be run on any of the backends supported by wlroots. This means you can
+run it as a separate window inside either an X11 or Wayland session, as well
+as directly from a VT console. Depending on your distro's setup, you may need
+to add your user to the `video` and `input` groups before you can run dwl on
+a VT. If you are using `elogind` or `systemd-logind` you need to install
+polkit; otherwise you need to add yourself in the `seat` group and
+enable/start the seatd daemon.
+
+When dwl is run with no arguments, it will launch the server and begin handling
+any shortcuts configured in `config.h`. There is no status bar or other
+decoration initially; these are instead clients that can be run within
+the Wayland session.
+Do note that the background color is black.
+
+If you would like to run a script or command automatically at startup, you can
+specify the command using the `-s` option. This command will be executed as a
+shell command using `/bin/sh -c`. It serves a similar function to `.xinitrc`,
+but differs in that the display server will not shut down when this process
+terminates. Instead, dwl will send this process a SIGTERM at shutdown and wait
+for it to terminate (if it hasn't already). This makes it ideal for execing into
+a user service manager like [s6], [anopa], [runit], or [`systemd --user`].
+
+Note: The `-s` command is run as a *child process* of dwl, which means that it
+does not have the ability to affect the environment of dwl or of any processes
+that it spawns. If you need to set environment variables that affect the entire
+dwl session, these must be set prior to running dwl. For example, Wayland
+requires a valid `XDG_RUNTIME_DIR`, which is usually set up by a session manager
+such as `elogind` or `systemd-logind`. If your system doesn't do this
+automatically, you will need to configure it prior to launching `dwl`, e.g.:
+
+ export XDG_RUNTIME_DIR=/tmp/xdg-runtime-$(id -u)
+ mkdir -p $XDG_RUNTIME_DIR
+ dwl
+
+### Status information
+
+Information about selected layouts, current window title, app-id, and
+selected/occupied/urgent tags is written to the stdin of the `-s` command (see
+the `printstatus()` function for details). This information can be used to
+populate an external status bar with a script that parses the information.
+Failing to read this information will cause dwl to block, so if you do want to
+run a startup command that does not consume the status information, you can
+close standard input with the `<&-` shell redirection, for example:
+
+ dwl -s 'foot --server <&-'
+
+If your startup command is a shell script, you can achieve the same inside the
+script with the line
+
+ exec <&-
+
+To get a list of status bars that work with dwl consult our [wiki].
+
+## Replacements for X applications
+
+You can find a [list of useful resources on our wiki].
+
+## Acknowledgements
+
+dwl began by extending the TinyWL example provided (CC0) by the sway/wlroots
+developers. This was made possible in many cases by looking at how sway
+accomplished something, then trying to do the same in as suckless a way as
+possible.
+
+Many thanks to suckless.org and the dwm developers and community for the
+inspiration, and to the various contributors to the project, including:
+
+- Alexander Courtis for the XWayland implementation
+- Guido Cella for the layer-shell protocol implementation, patch maintenance,
+ and for helping to keep the project running
+- Stivvo for output management and fullscreen support, and patch maintenance
+
+
+[Discord server]: https://discord.gg/jJxZnrGPWN
+[#dwl on Libera Chat]: https://web.libera.chat/?channels=#dwl
+[Wayland]: https://wayland.freedesktop.org/
+[wlroots]: https://gitlab.freedesktop.org/wlroots/wlroots/
+[wlroots-next branch]: https://github.com/djpohly/dwl/tree/wlroots-next
+[patches page on our wiki]: https://github.com/djpohly/dwl/wiki/Patches
+[s6]: https://skarnet.org/software/s6/
+[anopa]: https://jjacky.com/anopa/
+[runit]: http://smarden.org/runit/faq.html#userservices
+[`systemd --user`]: https://wiki.archlinux.org/title/Systemd/User
+[wiki]: https://github.com/djpohly/dwl/wiki#compatible-status-bars
+[list of useful resources on our wiki]:
+ https://github.com/djpohly/dwl/wiki#migrating-from-x
diff --git a/dwl-v0.5/client.h b/dwl-v0.5/client.h
new file mode 100644
index 0000000..71c7d76
--- /dev/null
+++ b/dwl-v0.5/client.h
@@ -0,0 +1,394 @@
+/*
+ * Attempt to consolidate unavoidable suck into one file, away from dwl.c. This
+ * file is not meant to be pretty. We use a .h file with static inline
+ * functions instead of a separate .c module, or function pointers like sway, so
+ * that they will simply compile out if the chosen #defines leave them unused.
+ */
+
+/* Leave these functions first; they're used in the others */
+static inline int
+client_is_x11(Client *c)
+{
+#ifdef XWAYLAND
+ return c->type == X11Managed || c->type == X11Unmanaged;
+#endif
+ return 0;
+}
+
+static inline struct wlr_surface *
+client_surface(Client *c)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return c->surface.xwayland->surface;
+#endif
+ return c->surface.xdg->surface;
+}
+
+static inline int
+toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl)
+{
+ struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface;
+ struct wlr_surface *root_surface;
+ struct wlr_layer_surface_v1 *layer_surface;
+ Client *c = NULL;
+ LayerSurface *l = NULL;
+ int type = -1;
+#ifdef XWAYLAND
+ struct wlr_xwayland_surface *xsurface;
+#endif
+
+ if (!s)
+ return -1;
+ root_surface = wlr_surface_get_root_surface(s);
+
+#ifdef XWAYLAND
+ if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) {
+ c = xsurface->data;
+ type = c->type;
+ goto end;
+ }
+#endif
+
+ if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) {
+ l = layer_surface->data;
+ type = LayerShell;
+ goto end;
+ }
+
+ xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
+ while (xdg_surface) {
+ tmp_xdg_surface = NULL;
+ switch (xdg_surface->role) {
+ case WLR_XDG_SURFACE_ROLE_POPUP:
+ if (!xdg_surface->popup || !xdg_surface->popup->parent)
+ return -1;
+
+ tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
+
+ if (!tmp_xdg_surface)
+ return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl);
+
+ xdg_surface = tmp_xdg_surface;
+ break;
+ case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
+ c = xdg_surface->data;
+ type = c->type;
+ goto end;
+ case WLR_XDG_SURFACE_ROLE_NONE:
+ return -1;
+ }
+ }
+
+end:
+ if (pl)
+ *pl = l;
+ if (pc)
+ *pc = c;
+ return type;
+}
+
+/* The others */
+static inline void
+client_activate_surface(struct wlr_surface *s, int activated)
+{
+ struct wlr_xdg_toplevel *toplevel;
+#ifdef XWAYLAND
+ struct wlr_xwayland_surface *xsurface;
+ if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
+ wlr_xwayland_surface_activate(xsurface, activated);
+ return;
+ }
+#endif
+ if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s)))
+ wlr_xdg_toplevel_set_activated(toplevel, activated);
+}
+
+static inline uint32_t
+client_set_bounds(Client *c, int32_t width, int32_t height)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return 0;
+#endif
+ if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
+ XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0
+ && (c->bounds.width != width || c->bounds.height != height)) {
+ c->bounds.width = width;
+ c->bounds.height = height;
+ return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height);
+ }
+ return 0;
+}
+
+static inline const char *
+client_get_appid(Client *c)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return c->surface.xwayland->class;
+#endif
+ return c->surface.xdg->toplevel->app_id;
+}
+
+static inline void
+client_get_clip(Client *c, struct wlr_box *clip)
+{
+ struct wlr_box xdg_geom = {0};
+ *clip = (struct wlr_box){
+ .x = 0,
+ .y = 0,
+ .width = c->geom.width - c->bw,
+ .height = c->geom.height - c->bw,
+ };
+
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return;
+#endif
+
+ wlr_xdg_surface_get_geometry(c->surface.xdg, &xdg_geom);
+ clip->x = xdg_geom.x;
+ clip->y = xdg_geom.y;
+}
+
+static inline void
+client_get_geometry(Client *c, struct wlr_box *geom)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c)) {
+ geom->x = c->surface.xwayland->x;
+ geom->y = c->surface.xwayland->y;
+ geom->width = c->surface.xwayland->width;
+ geom->height = c->surface.xwayland->height;
+ return;
+ }
+#endif
+ wlr_xdg_surface_get_geometry(c->surface.xdg, geom);
+}
+
+static inline Client *
+client_get_parent(Client *c)
+{
+ Client *p = NULL;
+#ifdef XWAYLAND
+ if (client_is_x11(c) && c->surface.xwayland->parent)
+ toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL);
+#endif
+ if (c->surface.xdg->toplevel->parent)
+ toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL);
+ return p;
+}
+
+static inline const char *
+client_get_title(Client *c)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return c->surface.xwayland->title;
+#endif
+ return c->surface.xdg->toplevel->title;
+}
+
+static inline int
+client_is_float_type(Client *c)
+{
+ struct wlr_xdg_toplevel *toplevel;
+ struct wlr_xdg_toplevel_state state;
+
+#ifdef XWAYLAND
+ if (client_is_x11(c)) {
+ struct wlr_xwayland_surface *surface = c->surface.xwayland;
+ xcb_size_hints_t *size_hints = surface->size_hints;
+ size_t i;
+ if (surface->modal)
+ return 1;
+
+ for (i = 0; i < surface->window_type_len; i++)
+ if (surface->window_type[i] == netatom[NetWMWindowTypeDialog]
+ || surface->window_type[i] == netatom[NetWMWindowTypeSplash]
+ || surface->window_type[i] == netatom[NetWMWindowTypeToolbar]
+ || surface->window_type[i] == netatom[NetWMWindowTypeUtility])
+ return 1;
+
+ return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0
+ && (size_hints->max_width == size_hints->min_width
+ || size_hints->max_height == size_hints->min_height);
+ }
+#endif
+
+ toplevel = c->surface.xdg->toplevel;
+ state = toplevel->current;
+ return toplevel->parent || (state.min_width != 0 && state.min_height != 0
+ && (state.min_width == state.max_width
+ || state.min_height == state.max_height));
+}
+
+static inline int
+client_is_rendered_on_mon(Client *c, Monitor *m)
+{
+ /* This is needed for when you don't want to check formal assignment,
+ * but rather actual displaying of the pixels.
+ * Usually VISIBLEON suffices and is also faster. */
+ struct wlr_surface_output *s;
+ int unused_lx, unused_ly;
+ if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
+ return 0;
+ wl_list_for_each(s, &client_surface(c)->current_outputs, link)
+ if (s->output == m->wlr_output)
+ return 1;
+ return 0;
+}
+
+static inline int
+client_is_stopped(Client *c)
+{
+ int pid;
+ siginfo_t in = {0};
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return 0;
+#endif
+
+ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
+ if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) {
+ /* This process is not our child process, while is very unluckely that
+ * it is stopped, in order to do not skip frames assume that it is. */
+ if (errno == ECHILD)
+ return 1;
+ } else if (in.si_pid) {
+ if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
+ return 1;
+ if (in.si_code == CLD_CONTINUED)
+ return 0;
+ }
+
+ return 0;
+}
+
+static inline int
+client_is_unmanaged(Client *c)
+{
+#ifdef XWAYLAND
+ return c->type == X11Unmanaged;
+#endif
+ return 0;
+}
+
+static inline void
+client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb)
+{
+ if (kb)
+ wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes,
+ kb->num_keycodes, &kb->modifiers);
+ else
+ wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL);
+}
+
+static inline void
+client_restack_surface(Client *c)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ wlr_xwayland_surface_restack(c->surface.xwayland, NULL,
+ XCB_STACK_MODE_ABOVE);
+#endif
+ return;
+}
+
+static inline void
+client_send_close(Client *c)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c)) {
+ wlr_xwayland_surface_close(c->surface.xwayland);
+ return;
+ }
+#endif
+ wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel);
+}
+
+static inline void
+client_set_border_color(Client *c, const float color[static 4])
+{
+ int i;
+ for (i = 0; i < 4; i++)
+ wlr_scene_rect_set_color(c->border[i], color);
+}
+
+static inline void
+client_set_fullscreen(Client *c, int fullscreen)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c)) {
+ wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
+ return;
+ }
+#endif
+ wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen);
+}
+
+static inline uint32_t
+client_set_size(Client *c, uint32_t width, uint32_t height)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c)) {
+ wlr_xwayland_surface_configure(c->surface.xwayland,
+ c->geom.x, c->geom.y, width, height);
+ return 0;
+ }
+#endif
+ if (width == c->surface.xdg->toplevel->current.width
+ && height ==c->surface.xdg->toplevel->current.height)
+ return 0;
+ return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, width, height);
+}
+
+static inline void
+client_set_tiled(Client *c, uint32_t edges)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return;
+#endif
+ if (wl_resource_get_version(c->surface.xdg->resource)
+ >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
+ wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
+ } else {
+ wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE);
+ }
+}
+
+static inline void
+client_set_suspended(Client *c, int suspended)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c)) {
+ wlr_xwayland_surface_set_withdrawn(c->surface.xwayland, suspended);
+ return;
+ }
+#endif
+
+ wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
+}
+
+static inline int
+client_wants_focus(Client *c)
+{
+#ifdef XWAYLAND
+ return client_is_unmanaged(c)
+ && wlr_xwayland_or_surface_wants_focus(c->surface.xwayland)
+ && wlr_xwayland_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE;
+#endif
+ return 0;
+}
+
+static inline int
+client_wants_fullscreen(Client *c)
+{
+#ifdef XWAYLAND
+ if (client_is_x11(c))
+ return c->surface.xwayland->fullscreen;
+#endif
+ return c->surface.xdg->toplevel->requested.fullscreen;
+}
diff --git a/dwl-v0.5/config.def.h b/dwl-v0.5/config.def.h
new file mode 100644
index 0000000..0f29b02
--- /dev/null
+++ b/dwl-v0.5/config.def.h
@@ -0,0 +1,201 @@
+/* Taken from https://github.com/djpohly/dwl/issues/466 */
+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \
+ ((hex >> 16) & 0xFF) / 255.0f, \
+ ((hex >> 8) & 0xFF) / 255.0f, \
+ (hex & 0xFF) / 255.0f }
+/* appearance */
+static const int sloppyfocus = 1; /* focus follows mouse */
+static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
+static const unsigned int borderpx = 1; /* border pixel of windows */
+static const float bordercolor[] = COLOR(0x444444ff);
+static const float focuscolor[] = COLOR(0x005577ff);
+static const float urgentcolor[] = COLOR(0xff0000ff);
+
+static const unsigned int gappih = 20;
+static const unsigned int gappiv = 10;
+static const unsigned int gappoh = 10;
+static const unsigned int gappov = 10;
+static const int monoclegaps = 0;
+static const int smartgaps = 0;
+
+/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
+static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */
+
+/* Autostart */
+static const char *const autostart[] = {
+ "swaybg", "-i", "~/.config/wallpaper.png", NULL,
+ NULL /* terminate */
+};
+
+/* tagging - TAGCOUNT must be no greater than 31 */
+#define TAGCOUNT (9)
+
+/* logging */
+static int log_level = WLR_ERROR;
+
+static const Rule rules[] = {
+ /* app_id title tags mask isfloating monitor */
+ /* examples:
+ { "Gimp", NULL, 0, 1, -1 },
+ */
+ { "firefox", NULL, 1 << 8, 0, -1 },
+};
+
+/* layout(s) */
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile },
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* monitors */
+static const MonitorRule monrules[] = {
+ /* name mfact nmaster scale layout rotate/reflect x y */
+ /* example of a HiDPI laptop monitor:
+ { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
+ */
+ /* defaults */
+ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
+};
+
+/* keyboard */
+static const struct xkb_rule_names xkb_rules = {
+ /* can specify fields: rules, model, layout, variant, options */
+ /* example:
+ .options = "ctrl:nocaps",
+ */
+ .options = NULL,
+};
+
+static const int repeat_rate = 25;
+static const int repeat_delay = 600;
+
+/* Trackpad */
+static const int tap_to_click = 1;
+static const int tap_and_drag = 1;
+static const int drag_lock = 1;
+static const int natural_scrolling = 0;
+static const int disable_while_typing = 1;
+static const int left_handed = 0;
+static const int middle_button_emulation = 0;
+/* You can choose between:
+LIBINPUT_CONFIG_SCROLL_NO_SCROLL
+LIBINPUT_CONFIG_SCROLL_2FG
+LIBINPUT_CONFIG_SCROLL_EDGE
+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
+*/
+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
+
+/* You can choose between:
+LIBINPUT_CONFIG_CLICK_METHOD_NONE
+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
+*/
+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+
+/* You can choose between:
+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
+*/
+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+
+/* You can choose between:
+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
+*/
+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+static const double accel_speed = 0.0;
+/* You can choose between:
+LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
+LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
+*/
+static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+
+/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
+#define MODKEY WLR_MODIFIER_ALT
+
+#define TAGKEYS(KEY,SKEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static const char *termcmd[] = { "alacritty", NULL };
+static const char *menucmd[] = { "bemenu-run", NULL };
+
+static const Key keys[] = {
+ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
+ /* modifier key function argument */
+
+ { MODKEY, XKB_KEY_q, restartdwl, {0} },
+ { MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },
+ { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
+ { MODKEY, XKB_KEY_u, incnmaster, {.i = -1} },
+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
+ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
+ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, zoom, {0} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
+ { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
+
+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
+ /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
+ * do not remove them.
+ */
+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
+};
+
+static const Button buttons[] = {
+ { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
+ { MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
+};
diff --git a/dwl-v0.5/config.h b/dwl-v0.5/config.h
new file mode 100644
index 0000000..d80983f
--- /dev/null
+++ b/dwl-v0.5/config.h
@@ -0,0 +1,201 @@
+/* Taken from https://github.com/djpohly/dwl/issues/466 */
+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \
+ ((hex >> 16) & 0xFF) / 255.0f, \
+ ((hex >> 8) & 0xFF) / 255.0f, \
+ (hex & 0xFF) / 255.0f }
+/* appearance */
+static const int sloppyfocus = 1; /* focus follows mouse */
+static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
+static const unsigned int borderpx = 1; /* border pixel of windows */
+static const float bordercolor[] = COLOR(0x444444ff);
+static const float focuscolor[] = COLOR(0x005577ff);
+static const float urgentcolor[] = COLOR(0xff0000ff);
+
+static const unsigned int gappih = 20;
+static const unsigned int gappiv = 10;
+static const unsigned int gappoh = 10;
+static const unsigned int gappov = 10;
+static const int monoclegaps = 0;
+static const int smartgaps = 0;
+
+/* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
+static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */
+
+/* Autostart */
+static const char *const autostart[] = {
+ "swaybg_set", NULL,
+ NULL /* terminate */
+};
+
+/* tagging - TAGCOUNT must be no greater than 31 */
+#define TAGCOUNT (9)
+
+/* logging */
+static int log_level = WLR_ERROR;
+
+static const Rule rules[] = {
+ /* app_id title tags mask isfloating monitor */
+ /* examples:
+ { "Gimp", NULL, 0, 1, -1 },
+ */
+ { "firefox", NULL, 1 << 8, 0, -1 },
+};
+
+/* layout(s) */
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile },
+ { "><>", NULL }, /* no layout function means floating behavior */
+ { "[M]", monocle },
+};
+
+/* monitors */
+static const MonitorRule monrules[] = {
+ /* name mfact nmaster scale layout rotate/reflect x y */
+ /* example of a HiDPI laptop monitor:
+ { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
+ */
+ /* defaults */
+ { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
+};
+
+/* keyboard */
+static const struct xkb_rule_names xkb_rules = {
+ /* can specify fields: rules, model, layout, variant, options */
+ /* example:
+ .options = "ctrl:nocaps",
+ */
+ .options = NULL,
+};
+
+static const int repeat_rate = 25;
+static const int repeat_delay = 600;
+
+/* Trackpad */
+static const int tap_to_click = 1;
+static const int tap_and_drag = 1;
+static const int drag_lock = 1;
+static const int natural_scrolling = 0;
+static const int disable_while_typing = 1;
+static const int left_handed = 0;
+static const int middle_button_emulation = 0;
+/* You can choose between:
+LIBINPUT_CONFIG_SCROLL_NO_SCROLL
+LIBINPUT_CONFIG_SCROLL_2FG
+LIBINPUT_CONFIG_SCROLL_EDGE
+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
+*/
+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
+
+/* You can choose between:
+LIBINPUT_CONFIG_CLICK_METHOD_NONE
+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
+*/
+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
+
+/* You can choose between:
+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
+*/
+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
+
+/* You can choose between:
+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
+*/
+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
+static const double accel_speed = 0.0;
+/* You can choose between:
+LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
+LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
+*/
+static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
+
+/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
+#define MODKEY WLR_MODIFIER_ALT
+
+#define TAGKEYS(KEY,SKEY,TAG) \
+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
+
+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
+
+/* commands */
+static const char *termcmd[] = { "alacritty", NULL };
+static const char *menucmd[] = { "bemenu-run", NULL };
+
+static const Key keys[] = {
+ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
+ /* modifier key function argument */
+
+ { MODKEY, XKB_KEY_q, restartdwl, {0} },
+ { MODKEY, XKB_KEY_d, spawn, {.v = menucmd} },
+ { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
+ { MODKEY, XKB_KEY_u, incnmaster, {.i = -1} },
+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
+ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
+ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, zoom, {0} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
+ { MODKEY, XKB_KEY_space, setlayout, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
+ { MODKEY, XKB_KEY_e, togglefullscreen, {0} },
+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
+ { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
+
+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
+ /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
+ * do not remove them.
+ */
+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
+};
+
+static const Button buttons[] = {
+ { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
+ { MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
+};
diff --git a/dwl-v0.5/config.mk b/dwl-v0.5/config.mk
new file mode 100644
index 0000000..6ebb218
--- /dev/null
+++ b/dwl-v0.5/config.mk
@@ -0,0 +1,15 @@
+_VERSION = 0.5
+VERSION = `git describe --tags 2>/dev/null || echo $(_VERSION)`
+
+PKG_CONFIG = pkg-config
+
+# paths
+PREFIX = /usr/local
+MANDIR = $(PREFIX)/share/man
+DATADIR = $(PREFIX)/share
+
+XWAYLAND =
+XLIBS =
+# Uncomment to build XWayland support
+XWAYLAND = -DXWAYLAND
+XLIBS = xcb xcb-icccm
diff --git a/dwl-v0.5/dwl.1 b/dwl-v0.5/dwl.1
new file mode 100644
index 0000000..ce1acf9
--- /dev/null
+++ b/dwl-v0.5/dwl.1
@@ -0,0 +1,158 @@
+.Dd January 8, 2021
+.Dt DWL 1
+.Os
+.Sh NAME
+.Nm dwl
+.Nd dwm for Wayland
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl d
+.Op Fl s Ar startup command
+.Sh DESCRIPTION
+.Nm
+is a Wayland compositor based on wlroots.
+It is intended to fill the same space in the Wayland world that
+.Nm dwm
+does for X11.
+.Pp
+When given the
+.Fl v
+option,
+.Nm
+writes its name and version to standard error and exits unsuccessfully.
+.Pp
+When given the
+.Fl d
+option,
+.Nm
+enables full wlroots logging, including debug information.
+.Pp
+When given the
+.Fl s
+option,
+.Nm
+starts a shell process running
+.Ar command
+when starting.
+When stopping, it sends
+.Dv SIGTERM
+to the child process and waits for it to exit.
+.Pp
+Users are encouraged to customize
+.Nm
+by editing the sources, in particular
+.Pa config.h .
+The default key bindings are as follows:
+.Bl -tag -width 20n -offset indent -compact
+.It Mod-[1-9]
+Show only all windows with a tag.
+.It Mod-Ctrl-[1-9]
+Show all windows with a tag.
+.It Mod-Shift-[1-9]
+Move window to a single tag.
+.It Mod-Ctrl-Shift-[1-9]
+Toggle tag for window.
+.It Mod-p
+Spawn
+.Nm bemenu-run .
+.It Mod-Shift-Return
+Spawn
+.Nm foot .
+.It Mod-[jk]
+Move focus down/up the stack.
+.It Mod-[id]
+Increase/decrease number of windows in master area.
+.It Mod-[hl]
+Decrease/increase master area.
+.It Mod-Return
+Move window on top of stack or switch top of stack with second window.
+.It Mod-Tab
+Show only all windows with previous tag.
+.It Mod-Shift-c
+Close window.
+.It Mod-t
+Switch to tabbed layout.
+.It Mod-f
+Switch to floating layout.
+.It Mod-m
+Switch to monocle layout.
+.It Mod-Space
+Switch to previous layout.
+.It Mod-Shift-Space
+Toggle floating state of window.
+.It Mod-e
+Toggle fullscreen state of window.
+.It Mod-0
+Show all windows.
+.It Mod-Shift-0
+Set all tags for window.
+.It Mod-,
+Move focus to previous monitor.
+.It Mod-.
+Move focus to next monitor.
+.It Mod-Shift-,
+Move window to previous monitor.
+.It Mod-Shift-.
+Move window to next monitor.
+.It Mod-Shift-q
+Quit
+.Nm .
+.El
+These might differ depending on your keyboard layout.
+.Sh ENVIRONMENT
+These environment variables are used by
+.Nm :
+.Bl -tag -width XDG_RUNTIME_DIR
+.It Ev XDG_RUNTIME_DIR
+A directory where temporary user files, such as the Wayland socket,
+are stored.
+.It Ev XDG_CONFIG_DIR
+A directory containing configuration of various programs and
+libraries, including libxkbcommon.
+.It Ev DISPLAY , WAYLAND_DISPLAY , WAYLAND_SOCKET
+Tell how to connect to an underlying X11 or Wayland server.
+.It Ev WLR_*
+Various variables specific to wlroots.
+.It Ev XKB_* , XLOCALEDIR , XCOMPOSEFILE
+Various variables specific to libxkbcommon.
+.It Ev XCURSOR_PATH
+List of directories to search for XCursor themes in.
+.It Ev HOME
+A directory where there are always dear files there for you.
+Waiting for you to clean them up.
+.El
+.Pp
+These are set by
+.Nm :
+.Bl -tag -width WAYLAND_DISPLAY
+.It Ev WAYLAND_DISPLAY
+Tell how to connect to
+.Nm .
+.It Ev DISPLAY
+If using
+.Nm Xwayland ,
+tell how to connect to the
+.Nm Xwayland
+server.
+.El
+.Sh EXAMPLES
+Start
+.Nm
+with s6 in the background:
+.Dl dwl -s 's6-svscan <&-'
+.Sh SEE ALSO
+.Xr foot 1 ,
+.Xr bemenu 1 ,
+.Xr dwm 1 ,
+.Xr xkeyboard-config 7
+.Sh CAVEATS
+The child process's standard input is connected with a pipe to
+.Nm .
+If the child process neither reads from the pipe nor closes its
+standard input,
+.Nm
+will freeze after a while due to it blocking when writing to the full
+pipe buffer.
+.Sh BUGS
+All of them.
diff --git a/dwl-v0.5/dwl.c b/dwl-v0.5/dwl.c
new file mode 100644
index 0000000..045d913
--- /dev/null
+++ b/dwl-v0.5/dwl.c
@@ -0,0 +1,3306 @@
+/*
+ * See LICENSE file for copyright and license details.
+ */
+#include <getopt.h>
+#include <libinput.h>
+#include <linux/input-event-codes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <wayland-server-core.h>
+#include <wlr/backend.h>
+#include <wlr/backend/libinput.h>
+#include <wlr/render/allocator.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_cursor_shape_v1.h>
+#include <wlr/types/wlr_data_control_v1.h>
+#include <wlr/types/wlr_data_device.h>
+#include <wlr/types/wlr_drm.h>
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
+#include <wlr/types/wlr_export_dmabuf_v1.h>
+#include <wlr/types/wlr_fractional_scale_v1.h>
+#include <wlr/types/wlr_gamma_control_v1.h>
+#include <wlr/types/wlr_idle_inhibit_v1.h>
+#include <wlr/types/wlr_idle_notify_v1.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output_management_v1.h>
+#include <wlr/types/wlr_pointer.h>
+#include <wlr/types/wlr_presentation_time.h>
+#include <wlr/types/wlr_primary_selection.h>
+#include <wlr/types/wlr_primary_selection_v1.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_screencopy_v1.h>
+#include <wlr/types/wlr_seat.h>
+#include <wlr/types/wlr_server_decoration.h>
+#include <wlr/types/wlr_session_lock_v1.h>
+#include <wlr/types/wlr_single_pixel_buffer_v1.h>
+#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_viewporter.h>
+#include <wlr/types/wlr_virtual_keyboard_v1.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_activation_v1.h>
+#include <wlr/types/wlr_xdg_decoration_v1.h>
+#include <wlr/types/wlr_xdg_output_v1.h>
+#include <wlr/types/wlr_xdg_shell.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#include "net-tapesoftware-dwl-wm-unstable-v1-protocol.h"
+#ifdef XWAYLAND
+#include <wlr/xwayland.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#endif
+
+#include "util.h"
+
+/* macros */
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
+#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define END(A) ((A) + LENGTH(A))
+#define TAGMASK ((1u << TAGCOUNT) - 1)
+#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
+#define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0)
+
+/* enums */
+enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
+enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */
+enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrFS, LyrTop, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
+#ifdef XWAYLAND
+enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
+ NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
+#endif
+
+typedef union {
+ int i;
+ uint32_t ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int mod;
+ unsigned int button;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct {
+ /* Must keep these three elements in this order */
+ unsigned int type; /* XDGShell or X11* */
+ struct wlr_box geom; /* layout-relative, includes border */
+ Monitor *mon;
+ struct wlr_scene_tree *scene;
+ struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
+ struct wlr_scene_tree *scene_surface;
+ struct wl_list link;
+ struct wl_list flink;
+ union {
+ struct wlr_xdg_surface *xdg;
+ struct wlr_xwayland_surface *xwayland;
+ } surface;
+ struct wl_listener commit;
+ struct wl_listener map;
+ struct wl_listener maximize;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+ struct wl_listener set_title;
+ struct wl_listener fullscreen;
+ struct wlr_box prev; /* layout-relative, includes border */
+ struct wlr_box bounds;
+#ifdef XWAYLAND
+ struct wl_listener activate;
+ struct wl_listener associate;
+ struct wl_listener dissociate;
+ struct wl_listener configure;
+ struct wl_listener set_hints;
+#endif
+ unsigned int bw;
+ uint32_t tags;
+ int isfloating, isurgent, isfullscreen;
+ uint32_t resize; /* configure serial of a pending resize */
+} Client;
+
+typedef struct {
+ uint32_t mod;
+ xkb_keysym_t keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ struct wl_list link;
+ struct wlr_keyboard *wlr_keyboard;
+
+ int nsyms;
+ const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
+ uint32_t mods; /* invalid if nsyms == 0 */
+ struct wl_event_source *key_repeat_source;
+
+ struct wl_listener modifiers;
+ struct wl_listener key;
+ struct wl_listener destroy;
+} Keyboard;
+
+typedef struct {
+ /* Must keep these three elements in this order */
+ unsigned int type; /* LayerShell */
+ struct wlr_box geom;
+ Monitor *mon;
+ struct wlr_scene_tree *scene;
+ struct wlr_scene_tree *popups;
+ struct wlr_scene_layer_surface_v1 *scene_layer;
+ struct wl_list link;
+ int mapped;
+ struct wlr_layer_surface_v1 *layer_surface;
+
+ struct wl_listener destroy;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener surface_commit;
+} LayerSurface;
+
+typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+} Layout;
+
+typedef struct {
+ struct wl_list link;
+ struct wl_resource *resource;
+ struct Monitor *monitor;
+} DwlWmMonitor;
+
+struct Monitor {
+ struct wl_list link;
+ struct wlr_output *wlr_output;
+ struct wlr_scene_output *scene_output;
+ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
+ struct wl_listener frame;
+ struct wl_listener destroy;
+ struct wl_listener request_state;
+ struct wl_listener destroy_lock_surface;
+ struct wlr_session_lock_surface_v1 *lock_surface;
+ struct wlr_box m; /* monitor area, layout-relative */
+ struct wlr_box w; /* window area, layout-relative */
+ struct wl_list layers[4]; /* LayerSurface::link */
+ struct wl_list dwl_wm_monitor_link;
+ const Layout *lt[2];
+ int gappih; /* horizontal gap between windows */
+ int gappiv; /* vertical gap between windows */
+ int gappoh; /* horizontal outer gaps */
+ int gappov; /* vertical outer gaps */
+ unsigned int seltags;
+ unsigned int sellt;
+ uint32_t tagset[2];
+ double mfact;
+ int gamma_lut_changed;
+ int nmaster;
+ char ltsymbol[16];
+};
+
+typedef struct {
+ const char *name;
+ float mfact;
+ int nmaster;
+ float scale;
+ const Layout *lt;
+ enum wl_output_transform rr;
+ int x, y;
+} MonitorRule;
+
+typedef struct {
+ const char *id;
+ const char *title;
+ uint32_t tags;
+ int isfloating;
+ int monitor;
+} Rule;
+
+typedef struct {
+ struct wlr_scene_tree *scene;
+
+ struct wlr_session_lock_v1 *lock;
+ struct wl_listener new_surface;
+ struct wl_listener unlock;
+ struct wl_listener destroy;
+} SessionLock;
+
+/* function declarations */
+static void applybounds(Client *c, struct wlr_box *bbox);
+static void autostartexec(void);
+static void applyrules(Client *c);
+static void arrange(Monitor *m);
+static void arrangelayer(Monitor *m, struct wl_list *list,
+ struct wlr_box *usable_area, int exclusive);
+static void arrangelayers(Monitor *m);
+static void axisnotify(struct wl_listener *listener, void *data);
+static void buttonpress(struct wl_listener *listener, void *data);
+static void chvt(const Arg *arg);
+static void checkidleinhibitor(struct wlr_surface *exclude);
+static void cleanup(void);
+static void cleanupkeyboard(struct wl_listener *listener, void *data);
+static void cleanupmon(struct wl_listener *listener, void *data);
+static void closemon(Monitor *m);
+static void commitlayersurfacenotify(struct wl_listener *listener, void *data);
+static void commitnotify(struct wl_listener *listener, void *data);
+static void createdecoration(struct wl_listener *listener, void *data);
+static void createidleinhibitor(struct wl_listener *listener, void *data);
+static void createkeyboard(struct wlr_keyboard *keyboard);
+static void createlayersurface(struct wl_listener *listener, void *data);
+static void createlocksurface(struct wl_listener *listener, void *data);
+static void createmon(struct wl_listener *listener, void *data);
+static void createnotify(struct wl_listener *listener, void *data);
+static void createpointer(struct wlr_pointer *pointer);
+static void cursorframe(struct wl_listener *listener, void *data);
+static void defaultgaps(const Arg *arg);
+static void destroydragicon(struct wl_listener *listener, void *data);
+static void destroyidleinhibitor(struct wl_listener *listener, void *data);
+static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
+static void destroylock(SessionLock *lock, int unlocked);
+static void destroylocksurface(struct wl_listener *listener, void *data);
+static void destroynotify(struct wl_listener *listener, void *data);
+static void destroysessionlock(struct wl_listener *listener, void *data);
+static void destroysessionmgr(struct wl_listener *listener, void *data);
+static Monitor *dirtomon(enum wlr_direction dir);
+static void focusclient(Client *c, int lift);
+static void focusmon(const Arg *arg);
+static void focusstack(const Arg *arg);
+static Client *focustop(Monitor *m);
+static void fullscreennotify(struct wl_listener *listener, void *data);
+static void handlesig(int signo);
+static void incnmaster(const Arg *arg);
+static void incgaps(const Arg *arg);
+static void incigaps(const Arg *arg);
+static void incihgaps(const Arg *arg);
+static void incivgaps(const Arg *arg);
+static void incogaps(const Arg *arg);
+static void incohgaps(const Arg *arg);
+static void incovgaps(const Arg *arg);
+static void inputdevice(struct wl_listener *listener, void *data);
+static int keybinding(uint32_t mods, xkb_keysym_t sym);
+static void keypress(struct wl_listener *listener, void *data);
+static void keypressmod(struct wl_listener *listener, void *data);
+static int keyrepeat(void *data);
+static void killclient(const Arg *arg);
+static void locksession(struct wl_listener *listener, void *data);
+static void maplayersurfacenotify(struct wl_listener *listener, void *data);
+static void mapnotify(struct wl_listener *listener, void *data);
+static void maximizenotify(struct wl_listener *listener, void *data);
+static void monocle(Monitor *m);
+static void motionabsolute(struct wl_listener *listener, void *data);
+static void motionnotify(uint32_t time);
+static void motionrelative(struct wl_listener *listener, void *data);
+static void moveresize(const Arg *arg);
+static void outputmgrapply(struct wl_listener *listener, void *data);
+static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
+static void outputmgrtest(struct wl_listener *listener, void *data);
+static void pointerfocus(Client *c, struct wlr_surface *surface,
+ double sx, double sy, uint32_t time);
+static void printstatus(void);
+static void quit(const Arg *arg);
+static void rendermon(struct wl_listener *listener, void *data);
+static void restartdwl(const Arg *arg);
+static void requeststartdrag(struct wl_listener *listener, void *data);
+static void requestmonstate(struct wl_listener *listener, void *data);
+static void resize(Client *c, struct wlr_box geo, int interact);
+static void run(char *startup_cmd);
+static void setcursor(struct wl_listener *listener, void *data);
+static void setcursorshape(struct wl_listener *listener, void *data);
+static void setfloating(Client *c, int floating);
+static void setfullscreen(Client *c, int fullscreen);
+static void setgamma(struct wl_listener *listener, void *data);
+static void setgaps(int oh, int ov, int ih, int iv);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setmon(Client *c, Monitor *m, uint32_t newtags);
+static int enablegaps = 1;
+static void setpsel(struct wl_listener *listener, void *data);
+static void setsel(struct wl_listener *listener, void *data);
+static void setup(void);
+static void spawn(const Arg *arg);
+static void startdrag(struct wl_listener *listener, void *data);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *m);
+static void togglefloating(const Arg *arg);
+static void togglefullscreen(const Arg *arg);
+static void togglegaps(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void unlocksession(struct wl_listener *listener, void *data);
+static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
+static void unmapnotify(struct wl_listener *listener, void *data);
+static void updatemons(struct wl_listener *listener, void *data);
+static void updatetitle(struct wl_listener *listener, void *data);
+static void urgent(struct wl_listener *listener, void *data);
+static void view(const Arg *arg);
+static void virtualkeyboard(struct wl_listener *listener, void *data);
+static Monitor *xytomon(double x, double y);
+static void xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+static void zoom(const Arg *arg);
+
+static void dwl_wm_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id);
+static void dwl_wm_printstatus(Monitor *monitor);
+
+/* variables */
+static const char broken[] = "broken";
+static pid_t child_pid = -1;
+static int locked;
+static void *exclusive_focus;
+static struct wl_display *dpy;
+static struct wlr_backend *backend;
+static struct wlr_scene *scene;
+static struct wlr_scene_tree *layers[NUM_LAYERS];
+static struct wlr_scene_tree *drag_icon;
+/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */
+static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay };
+static struct wlr_renderer *drw;
+static struct wlr_allocator *alloc;
+static struct wlr_compositor *compositor;
+static struct wlr_session *session;
+
+static struct wlr_xdg_shell *xdg_shell;
+static struct wlr_xdg_activation_v1 *activation;
+static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
+static struct wl_list clients; /* tiling order */
+static struct wl_list fstack; /* focus order */
+static struct wlr_idle_notifier_v1 *idle_notifier;
+static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr;
+static struct wlr_layer_shell_v1 *layer_shell;
+static struct wlr_output_manager_v1 *output_mgr;
+static struct wlr_gamma_control_manager_v1 *gamma_control_mgr;
+static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
+static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
+
+static struct wlr_cursor *cursor;
+static struct wlr_xcursor_manager *cursor_mgr;
+
+static struct wlr_session_lock_manager_v1 *session_lock_mgr;
+static struct wlr_scene_rect *locked_bg;
+static struct wlr_session_lock_v1 *cur_lock;
+static struct wl_listener lock_listener = {.notify = locksession};
+
+static struct wlr_seat *seat;
+static struct wl_list keyboards;
+static unsigned int cursor_mode;
+static Client *grabc;
+static int grabcx, grabcy; /* client-relative */
+
+static struct wlr_output_layout *output_layout;
+static struct wlr_box sgeom;
+static struct wl_list mons;
+static Monitor *selmon;
+
+#ifdef XWAYLAND
+static void activatex11(struct wl_listener *listener, void *data);
+static void associatex11(struct wl_listener *listener, void *data);
+static void configurex11(struct wl_listener *listener, void *data);
+static void createnotifyx11(struct wl_listener *listener, void *data);
+static void dissociatex11(struct wl_listener *listener, void *data);
+static xcb_atom_t getatom(xcb_connection_t *xc, const char *name);
+static void sethints(struct wl_listener *listener, void *data);
+static void xwaylandready(struct wl_listener *listener, void *data);
+static struct wlr_xwayland *xwayland;
+static xcb_atom_t netatom[NetLast];
+#endif
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* attempt to encapsulate suck into one file */
+#include "client.h"
+
+static pid_t *autostart_pids;
+static size_t autostart_len;
+
+/* function implementations */
+void
+applybounds(Client *c, struct wlr_box *bbox)
+{
+ /* set minimum possible */
+ c->geom.width = MAX(1, c->geom.width);
+ c->geom.height = MAX(1, c->geom.height);
+
+ if (c->geom.x >= bbox->x + bbox->width)
+ c->geom.x = bbox->x + bbox->width - c->geom.width;
+ if (c->geom.y >= bbox->y + bbox->height)
+ c->geom.y = bbox->y + bbox->height - c->geom.height;
+ if (c->geom.x + c->geom.width + 2 * c->bw <= bbox->x)
+ c->geom.x = bbox->x;
+ if (c->geom.y + c->geom.height + 2 * c->bw <= bbox->y)
+ c->geom.y = bbox->y;
+}
+
+void
+autostartexec(void) {
+ const char *const *p;
+ size_t i = 0;
+
+ /* count entries */
+ for (p = autostart; *p; autostart_len++, p++)
+ while (*++p);
+
+ autostart_pids = calloc(autostart_len, sizeof(pid_t));
+ for (p = autostart; *p; i++, p++) {
+ if ((autostart_pids[i] = fork()) == 0) {
+ setsid();
+ execvp(*p, (char *const *)p);
+ die("dwl: execvp %s:", *p);
+ }
+ /* skip arguments */
+ while (*++p);
+ }
+}
+
+void
+applyrules(Client *c)
+{
+ /* rule matching */
+ const char *appid, *title;
+ uint32_t i, newtags = 0;
+ const Rule *r;
+ Monitor *mon = selmon, *m;
+
+ c->isfloating = client_is_float_type(c);
+ if (!(appid = client_get_appid(c)))
+ appid = broken;
+ if (!(title = client_get_title(c)))
+ title = broken;
+
+ for (r = rules; r < END(rules); r++) {
+ if ((!r->title || strstr(title, r->title))
+ && (!r->id || strstr(appid, r->id))) {
+ c->isfloating = r->isfloating;
+ newtags |= r->tags;
+ i = 0;
+ wl_list_for_each(m, &mons, link)
+ if (r->monitor == i++)
+ mon = m;
+ }
+ }
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]);
+ setmon(c, mon, newtags);
+}
+
+void
+arrange(Monitor *m)
+{
+ Client *c;
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon == m) {
+ wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m));
+ client_set_suspended(c, !VISIBLEON(c, m));
+ }
+ }
+
+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
+ (c = focustop(m)) && c->isfullscreen);
+
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
+
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+ motionnotify(0);
+ checkidleinhibitor(NULL);
+}
+
+void
+arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive)
+{
+ LayerSurface *layersurface;
+ struct wlr_box full_area = m->m;
+
+ wl_list_for_each(layersurface, list, link) {
+ struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
+ struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current;
+
+ if (exclusive != (state->exclusive_zone > 0))
+ continue;
+
+ wlr_scene_layer_surface_v1_configure(layersurface->scene_layer, &full_area, usable_area);
+ wlr_scene_node_set_position(&layersurface->popups->node,
+ layersurface->scene->node.x, layersurface->scene->node.y);
+ layersurface->geom.x = layersurface->scene->node.x;
+ layersurface->geom.y = layersurface->scene->node.y;
+ }
+}
+
+void
+arrangelayers(Monitor *m)
+{
+ int i;
+ struct wlr_box usable_area = m->m;
+ uint32_t layers_above_shell[] = {
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ LayerSurface *layersurface;
+ if (!m->wlr_output->enabled)
+ return;
+
+ /* Arrange exclusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+ arrangelayer(m, &m->layers[i], &usable_area, 1);
+
+ if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) {
+ m->w = usable_area;
+ arrange(m);
+ }
+
+ /* Arrange non-exlusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+ arrangelayer(m, &m->layers[i], &usable_area, 0);
+
+ /* Find topmost keyboard interactive layer, if such a layer exists */
+ for (i = 0; i < LENGTH(layers_above_shell); i++) {
+ wl_list_for_each_reverse(layersurface,
+ &m->layers[layers_above_shell[i]], link) {
+ if (!locked && layersurface->layer_surface->current.keyboard_interactive
+ && layersurface->mapped) {
+ /* Deactivate the focused client. */
+ focusclient(NULL, 0);
+ exclusive_focus = layersurface;
+ client_notify_enter(layersurface->layer_surface->surface, wlr_seat_get_keyboard(seat));
+ return;
+ }
+ }
+ }
+}
+
+void
+axisnotify(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits an axis event,
+ * for example when you move the scroll wheel. */
+ struct wlr_pointer_axis_event *event = data;
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ /* TODO: allow usage of scroll whell for mousebindings, it can be implemented
+ * checking the event's orientation and the delta of the event */
+ /* Notify the client with pointer focus of the axis event. */
+ wlr_seat_pointer_notify_axis(seat,
+ event->time_msec, event->orientation, event->delta,
+ event->delta_discrete, event->source);
+}
+
+void
+buttonpress(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_button_event *event = data;
+ struct wlr_keyboard *keyboard;
+ uint32_t mods;
+ Client *c;
+ const Button *b;
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ switch (event->state) {
+ case WLR_BUTTON_PRESSED:
+ cursor_mode = CurPressed;
+ if (locked)
+ break;
+
+ /* Change focus if the button was _pressed_ over a client */
+ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
+ if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
+ focusclient(c, 1);
+
+ keyboard = wlr_seat_get_keyboard(seat);
+ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
+ for (b = buttons; b < END(buttons); b++) {
+ if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
+ event->button == b->button && b->func) {
+ b->func(&b->arg);
+ return;
+ }
+ }
+ break;
+ case WLR_BUTTON_RELEASED:
+ /* If you released any buttons, we exit interactive move/resize mode. */
+ /* TODO should reset to the pointer focus's current setcursor */
+ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+ cursor_mode = CurNormal;
+ /* Drop the window off on its new monitor */
+ selmon = xytomon(cursor->x, cursor->y);
+ setmon(grabc, selmon, 0);
+ return;
+ } else {
+ cursor_mode = CurNormal;
+ }
+ break;
+ }
+ /* If the event wasn't handled by the compositor, notify the client with
+ * pointer focus that a button press has occurred */
+ wlr_seat_pointer_notify_button(seat,
+ event->time_msec, event->button, event->state);
+}
+
+void
+chvt(const Arg *arg)
+{
+ wlr_session_change_vt(session, arg->ui);
+}
+
+void
+checkidleinhibitor(struct wlr_surface *exclude)
+{
+ int inhibited = 0, unused_lx, unused_ly;
+ struct wlr_idle_inhibitor_v1 *inhibitor;
+ wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) {
+ struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface);
+ struct wlr_scene_tree *tree = surface->data;
+ if (exclude != surface && (bypass_surface_visibility || (!tree
+ || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) {
+ inhibited = 1;
+ break;
+ }
+ }
+
+ wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
+}
+
+void
+cleanup(void)
+{
+ size_t i;
+#ifdef XWAYLAND
+ wlr_xwayland_destroy(xwayland);
+ xwayland = NULL;
+#endif
+ wl_display_destroy_clients(dpy);
+
+ /* kill child processes */
+ for (i = 0; i < autostart_len; i++) {
+ if (0 < autostart_pids[i]) {
+ kill(autostart_pids[i], SIGTERM);
+ waitpid(autostart_pids[i], NULL, 0);
+ }
+ }
+
+ if (child_pid > 0) {
+ kill(child_pid, SIGTERM);
+ waitpid(child_pid, NULL, 0);
+ }
+ wlr_xcursor_manager_destroy(cursor_mgr);
+ wlr_output_layout_destroy(output_layout);
+ wl_display_destroy(dpy);
+ /* Destroy after the wayland display (when the monitors are already destroyed)
+ to avoid destroying them with an invalid scene output. */
+ wlr_scene_node_destroy(&scene->tree.node);
+}
+
+void
+cleanupkeyboard(struct wl_listener *listener, void *data)
+{
+ Keyboard *kb = wl_container_of(listener, kb, destroy);
+
+ wl_event_source_remove(kb->key_repeat_source);
+ wl_list_remove(&kb->link);
+ wl_list_remove(&kb->modifiers.link);
+ wl_list_remove(&kb->key.link);
+ wl_list_remove(&kb->destroy.link);
+ free(kb);
+}
+
+void
+cleanupmon(struct wl_listener *listener, void *data)
+{
+ Monitor *m = wl_container_of(listener, m, destroy);
+ DwlWmMonitor *mon, *montmp;
+ LayerSurface *l, *tmp;
+ int i;
+
+ for (i = 0; i <= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; i++)
+ wl_list_for_each_safe(l, tmp, &m->layers[i], link)
+ wlr_layer_surface_v1_destroy(l->layer_surface);
+
+ wl_list_remove(&m->destroy.link);
+ wl_list_remove(&m->frame.link);
+ wl_list_remove(&m->link);
+ m->wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ wl_list_for_each_safe(mon, montmp, &m->dwl_wm_monitor_link, link) {
+ wl_resource_set_user_data(mon->resource, NULL);
+ free(mon);
+ }
+ wlr_scene_output_destroy(m->scene_output);
+ wlr_scene_node_destroy(&m->fullscreen_bg->node);
+
+ closemon(m);
+ free(m);
+}
+
+void
+closemon(Monitor *m)
+{
+ /* update selmon if needed and
+ * move closed monitor's clients to the focused one */
+ Client *c;
+ if (wl_list_empty(&mons)) {
+ selmon = NULL;
+ } else if (m == selmon) {
+ int nmons = wl_list_length(&mons), i = 0;
+ do /* don't switch to disabled mons */
+ selmon = wl_container_of(mons.next, selmon, link);
+ while (!selmon->wlr_output->enabled && i++ < nmons);
+ }
+
+ wl_list_for_each(c, &clients, link) {
+ if (c->isfloating && c->geom.x > m->m.width)
+ resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
+ .width = c->geom.width, .height = c->geom.height}, 0);
+ if (c->mon == m)
+ setmon(c, selmon, c->tags);
+ }
+ focusclient(focustop(selmon), 1);
+ printstatus();
+}
+
+void
+commitlayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit);
+ struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
+ struct wlr_output *wlr_output = wlr_layer_surface->output;
+ struct wlr_scene_tree *layer = layers[layermap[wlr_layer_surface->current.layer]];
+
+ /* For some reason this layersurface have no monitor, this can be because
+ * its monitor has just been destroyed */
+ if (!wlr_output || !(layersurface->mon = wlr_output->data))
+ return;
+
+ if (layer != layersurface->scene->node.parent) {
+ wlr_scene_node_reparent(&layersurface->scene->node, layer);
+ wlr_scene_node_reparent(&layersurface->popups->node, layer);
+ wl_list_remove(&layersurface->link);
+ wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->current.layer],
+ &layersurface->link);
+ }
+ if (wlr_layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
+ wlr_scene_node_reparent(&layersurface->popups->node, layers[LyrTop]);
+
+ if (wlr_layer_surface->current.committed == 0
+ && layersurface->mapped == wlr_layer_surface->surface->mapped)
+ return;
+ layersurface->mapped = wlr_layer_surface->surface->mapped;
+
+ arrangelayers(layersurface->mon);
+}
+
+void
+commitnotify(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, commit);
+
+ if (client_surface(c)->mapped)
+ resize(c, c->geom, (c->isfloating && !c->isfullscreen));
+
+ /* mark a pending resize as completed */
+ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
+ c->resize = 0;
+}
+
+void
+createdecoration(struct wl_listener *listener, void *data)
+{
+ struct wlr_xdg_toplevel_decoration_v1 *dec = data;
+ wlr_xdg_toplevel_decoration_v1_set_mode(dec, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+}
+
+void
+createidleinhibitor(struct wl_listener *listener, void *data)
+{
+ struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
+ LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor);
+
+ checkidleinhibitor(NULL);
+}
+
+void
+createkeyboard(struct wlr_keyboard *keyboard)
+{
+ struct xkb_context *context;
+ struct xkb_keymap *keymap;
+ Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb));
+ kb->wlr_keyboard = keyboard;
+
+ /* Prepare an XKB keymap and assign it to the keyboard. */
+ context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ keymap = xkb_keymap_new_from_names(context, &xkb_rules,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+ if (!keymap)
+ die("createkeyboard: failed to compile keymap");
+
+ wlr_keyboard_set_keymap(keyboard, keymap);
+ xkb_keymap_unref(keymap);
+ xkb_context_unref(context);
+ wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay);
+
+ /* Here we set up listeners for keyboard events. */
+ LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod);
+ LISTEN(&keyboard->events.key, &kb->key, keypress);
+ LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard);
+
+ wlr_seat_set_keyboard(seat, keyboard);
+
+ kb->key_repeat_source = wl_event_loop_add_timer(
+ wl_display_get_event_loop(dpy), keyrepeat, kb);
+
+ /* And add the keyboard to our list of keyboards */
+ wl_list_insert(&keyboards, &kb->link);
+}
+
+void
+createlayersurface(struct wl_listener *listener, void *data)
+{
+ struct wlr_layer_surface_v1 *wlr_layer_surface = data;
+ LayerSurface *layersurface;
+ struct wlr_layer_surface_v1_state old_state;
+ struct wlr_scene_tree *l = layers[layermap[wlr_layer_surface->pending.layer]];
+
+ if (!wlr_layer_surface->output)
+ wlr_layer_surface->output = selmon ? selmon->wlr_output : NULL;
+
+ if (!wlr_layer_surface->output) {
+ wlr_layer_surface_v1_destroy(wlr_layer_surface);
+ return;
+ }
+
+ layersurface = wlr_layer_surface->data = ecalloc(1, sizeof(LayerSurface));
+ layersurface->type = LayerShell;
+ LISTEN(&wlr_layer_surface->surface->events.commit,
+ &layersurface->surface_commit, commitlayersurfacenotify);
+ LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy,
+ destroylayersurfacenotify);
+ LISTEN(&wlr_layer_surface->surface->events.map, &layersurface->map,
+ maplayersurfacenotify);
+ LISTEN(&wlr_layer_surface->surface->events.unmap, &layersurface->unmap,
+ unmaplayersurfacenotify);
+
+ layersurface->layer_surface = wlr_layer_surface;
+ layersurface->mon = wlr_layer_surface->output->data;
+ layersurface->scene_layer = wlr_scene_layer_surface_v1_create(l, wlr_layer_surface);
+ layersurface->scene = layersurface->scene_layer->tree;
+ layersurface->popups = wlr_layer_surface->surface->data = wlr_scene_tree_create(l);
+
+ layersurface->scene->node.data = layersurface;
+
+ wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->pending.layer],
+ &layersurface->link);
+
+ /* Temporarily set the layer's current state to pending
+ * so that we can easily arrange it
+ */
+ old_state = wlr_layer_surface->current;
+ wlr_layer_surface->current = wlr_layer_surface->pending;
+ layersurface->mapped = 1;
+ arrangelayers(layersurface->mon);
+ wlr_layer_surface->current = old_state;
+}
+
+void
+createlocksurface(struct wl_listener *listener, void *data)
+{
+ SessionLock *lock = wl_container_of(listener, lock, new_surface);
+ struct wlr_session_lock_surface_v1 *lock_surface = data;
+ Monitor *m = lock_surface->output->data;
+ struct wlr_scene_tree *scene_tree = lock_surface->surface->data =
+ wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface);
+ m->lock_surface = lock_surface;
+
+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
+ wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height);
+
+ LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface);
+
+ if (m == selmon)
+ client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
+}
+
+void
+createmon(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the backend when a new output (aka a display or
+ * monitor) becomes available. */
+ struct wlr_output *wlr_output = data;
+ const MonitorRule *r;
+ size_t i;
+ Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m));
+ wl_list_init(&m->dwl_wm_monitor_link);
+ m->wlr_output = wlr_output;
+
+ wlr_output_init_render(wlr_output, alloc, drw);
+
+ /* Initialize monitor state using configured rules */
+ for (i = 0; i < LENGTH(m->layers); i++)
+ wl_list_init(&m->layers[i]);
+
+ m->gappih = gappih;
+ m->gappiv = gappiv;
+ m->gappoh = gappoh;
+ m->gappov = gappov;
+ m->tagset[0] = m->tagset[1] = 1;
+ for (r = monrules; r < END(monrules); r++) {
+ if (!r->name || strstr(wlr_output->name, r->name)) {
+ m->mfact = r->mfact;
+ m->nmaster = r->nmaster;
+ wlr_output_set_scale(wlr_output, r->scale);
+ m->lt[0] = m->lt[1] = r->lt;
+ wlr_output_set_transform(wlr_output, r->rr);
+ m->m.x = r->x;
+ m->m.y = r->y;
+ break;
+ }
+ }
+
+ /* The mode is a tuple of (width, height, refresh rate), and each
+ * monitor supports only a specific set of modes. We just pick the
+ * monitor's preferred mode; a more sophisticated compositor would let
+ * the user configure it. */
+ wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output));
+
+ /* Set up event listeners */
+ LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
+ LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon);
+ LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate);
+
+ wlr_output_enable(wlr_output, 1);
+ if (!wlr_output_commit(wlr_output))
+ return;
+
+ wl_list_insert(&mons, &m->link);
+ printstatus();
+
+ /* The xdg-protocol specifies:
+ *
+ * If the fullscreened surface is not opaque, the compositor must make
+ * sure that other screen content not part of the same surface tree (made
+ * up of subsurfaces, popups or similarly coupled surfaces) are not
+ * visible below the fullscreened surface.
+ *
+ */
+ /* updatemons() will resize and set correct position */
+ m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg);
+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0);
+
+ /* Adds this to the output layout in the order it was configured in.
+ *
+ * The output layout utility automatically adds a wl_output global to the
+ * display, which Wayland clients can see to find out information about the
+ * output (such as DPI, scale factor, manufacturer, etc).
+ */
+ m->scene_output = wlr_scene_output_create(scene, wlr_output);
+ if (m->m.x < 0 || m->m.y < 0)
+ wlr_output_layout_add_auto(output_layout, wlr_output);
+ else
+ wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
+}
+
+void
+createnotify(struct wl_listener *listener, void *data)
+{
+ /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
+ * client, either a toplevel (application window) or popup,
+ * or when wlr_layer_shell receives a new popup from a layer.
+ * If you want to do something tricky with popups you should check if
+ * its parent is wlr_xdg_shell or wlr_layer_shell */
+ struct wlr_xdg_surface *xdg_surface = data;
+ Client *c = NULL;
+ LayerSurface *l = NULL;
+
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
+ struct wlr_box box;
+ int type = toplevel_from_wlr_surface(xdg_surface->surface, &c, &l);
+ if (!xdg_surface->popup->parent || type < 0)
+ return;
+ xdg_surface->surface->data = wlr_scene_xdg_surface_create(
+ xdg_surface->popup->parent->data, xdg_surface);
+ if ((l && !l->mon) || (c && !c->mon))
+ return;
+ box = type == LayerShell ? l->mon->m : c->mon->w;
+ box.x -= (type == LayerShell ? l->geom.x : c->geom.x);
+ box.y -= (type == LayerShell ? l->geom.y : c->geom.y);
+ wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box);
+ return;
+ } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE)
+ return;
+
+ /* Allocate a Client for this surface */
+ c = xdg_surface->data = ecalloc(1, sizeof(*c));
+ c->surface.xdg = xdg_surface;
+ c->bw = borderpx;
+
+ wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel,
+ WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
+
+ LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify);
+ LISTEN(&xdg_surface->surface->events.map, &c->map, mapnotify);
+ LISTEN(&xdg_surface->surface->events.unmap, &c->unmap, unmapnotify);
+ LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify);
+ LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle);
+ LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen,
+ fullscreennotify);
+ LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize,
+ maximizenotify);
+}
+
+void
+createpointer(struct wlr_pointer *pointer)
+{
+ if (wlr_input_device_is_libinput(&pointer->base)) {
+ struct libinput_device *libinput_device = (struct libinput_device*)
+ wlr_libinput_get_device_handle(&pointer->base);
+
+ if (libinput_device_config_tap_get_finger_count(libinput_device)) {
+ libinput_device_config_tap_set_enabled(libinput_device, tap_to_click);
+ libinput_device_config_tap_set_drag_enabled(libinput_device, tap_and_drag);
+ libinput_device_config_tap_set_drag_lock_enabled(libinput_device, drag_lock);
+ libinput_device_config_tap_set_button_map(libinput_device, button_map);
+ }
+
+ if (libinput_device_config_scroll_has_natural_scroll(libinput_device))
+ libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling);
+
+ if (libinput_device_config_dwt_is_available(libinput_device))
+ libinput_device_config_dwt_set_enabled(libinput_device, disable_while_typing);
+
+ if (libinput_device_config_left_handed_is_available(libinput_device))
+ libinput_device_config_left_handed_set(libinput_device, left_handed);
+
+ if (libinput_device_config_middle_emulation_is_available(libinput_device))
+ libinput_device_config_middle_emulation_set_enabled(libinput_device, middle_button_emulation);
+
+ if (libinput_device_config_scroll_get_methods(libinput_device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
+ libinput_device_config_scroll_set_method (libinput_device, scroll_method);
+
+ if (libinput_device_config_click_get_methods(libinput_device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
+ libinput_device_config_click_set_method (libinput_device, click_method);
+
+ if (libinput_device_config_send_events_get_modes(libinput_device))
+ libinput_device_config_send_events_set_mode(libinput_device, send_events_mode);
+
+ if (libinput_device_config_accel_is_available(libinput_device)) {
+ libinput_device_config_accel_set_profile(libinput_device, accel_profile);
+ libinput_device_config_accel_set_speed(libinput_device, accel_speed);
+ }
+ }
+
+ wlr_cursor_attach_input_device(cursor, &pointer->base);
+}
+
+void
+cursorframe(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits an frame
+ * event. Frame events are sent after regular pointer events to group
+ * multiple events together. For instance, two axis events may happen at the
+ * same time, in which case a frame event won't be sent in between. */
+ /* Notify the client with pointer focus of the frame event. */
+ wlr_seat_pointer_notify_frame(seat);
+}
+
+void
+defaultgaps(const Arg *arg)
+{
+ setgaps(gappoh, gappov, gappih, gappiv);
+}
+
+void
+destroydragicon(struct wl_listener *listener, void *data)
+{
+ /* Focus enter isn't sent during drag, so refocus the focused node. */
+ focusclient(focustop(selmon), 1);
+ motionnotify(0);
+}
+
+void
+destroyidleinhibitor(struct wl_listener *listener, void *data)
+{
+ /* `data` is the wlr_surface of the idle inhibitor being destroyed,
+ * at this point the idle inhibitor is still in the list of the manager */
+ checkidleinhibitor(wlr_surface_get_root_surface(data));
+}
+
+void
+destroylayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy);
+
+ wl_list_remove(&layersurface->link);
+ wl_list_remove(&layersurface->destroy.link);
+ wl_list_remove(&layersurface->map.link);
+ wl_list_remove(&layersurface->unmap.link);
+ wl_list_remove(&layersurface->surface_commit.link);
+ wlr_scene_node_destroy(&layersurface->scene->node);
+ free(layersurface);
+}
+
+void
+destroylock(SessionLock *lock, int unlock)
+{
+ wlr_seat_keyboard_notify_clear_focus(seat);
+ if ((locked = !unlock))
+ goto destroy;
+
+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
+
+ focusclient(focustop(selmon), 0);
+ motionnotify(0);
+
+destroy:
+ wl_list_remove(&lock->new_surface.link);
+ wl_list_remove(&lock->unlock.link);
+ wl_list_remove(&lock->destroy.link);
+
+ wlr_scene_node_destroy(&lock->scene->node);
+ cur_lock = NULL;
+ free(lock);
+}
+
+void
+destroylocksurface(struct wl_listener *listener, void *data)
+{
+ Monitor *m = wl_container_of(listener, m, destroy_lock_surface);
+ struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
+
+ m->lock_surface = NULL;
+ wl_list_remove(&m->destroy_lock_surface.link);
+
+ if (lock_surface->surface != seat->keyboard_state.focused_surface)
+ return;
+
+ if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {
+ surface = wl_container_of(cur_lock->surfaces.next, surface, link);
+ client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
+ } else if (!locked) {
+ focusclient(focustop(selmon), 1);
+ } else {
+ wlr_seat_keyboard_clear_focus(seat);
+ }
+}
+
+void
+destroynotify(struct wl_listener *listener, void *data)
+{
+ /* Called when the xdg_toplevel is destroyed. */
+ Client *c = wl_container_of(listener, c, destroy);
+ wl_list_remove(&c->destroy.link);
+ wl_list_remove(&c->set_title.link);
+ wl_list_remove(&c->fullscreen.link);
+#ifdef XWAYLAND
+ if (c->type != XDGShell) {
+ wl_list_remove(&c->activate.link);
+ wl_list_remove(&c->associate.link);
+ wl_list_remove(&c->configure.link);
+ wl_list_remove(&c->dissociate.link);
+ wl_list_remove(&c->set_hints.link);
+ } else
+#endif
+ {
+ wl_list_remove(&c->commit.link);
+ wl_list_remove(&c->map.link);
+ wl_list_remove(&c->unmap.link);
+ }
+ free(c);
+}
+
+void
+destroysessionlock(struct wl_listener *listener, void *data)
+{
+ SessionLock *lock = wl_container_of(listener, lock, destroy);
+ destroylock(lock, 0);
+}
+
+void
+destroysessionmgr(struct wl_listener *listener, void *data)
+{
+ wl_list_remove(&lock_listener.link);
+ wl_list_remove(&listener->link);
+}
+
+Monitor *
+dirtomon(enum wlr_direction dir)
+{
+ struct wlr_output *next;
+ if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
+ return selmon;
+ if ((next = wlr_output_layout_adjacent_output(output_layout,
+ dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
+ return next->data;
+ if ((next = wlr_output_layout_farthest_output(output_layout,
+ dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT),
+ selmon->wlr_output, selmon->m.x, selmon->m.y)))
+ return next->data;
+ return selmon;
+}
+
+void
+focusclient(Client *c, int lift)
+{
+ struct wlr_surface *old = seat->keyboard_state.focused_surface;
+ int unused_lx, unused_ly, old_client_type;
+ Client *old_c = NULL;
+ LayerSurface *old_l = NULL;
+
+ if (locked)
+ return;
+
+ /* Raise client in stacking order if requested */
+ if (c && lift)
+ wlr_scene_node_raise_to_top(&c->scene->node);
+
+ if (c && client_surface(c) == old)
+ return;
+
+ if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) {
+ struct wlr_xdg_popup *popup, *tmp;
+ wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link)
+ wlr_xdg_popup_destroy(popup);
+ }
+
+ /* Put the new client atop the focus stack and select its monitor */
+ if (c && !client_is_unmanaged(c)) {
+ wl_list_remove(&c->flink);
+ wl_list_insert(&fstack, &c->flink);
+ selmon = c->mon;
+ c->isurgent = 0;
+ client_restack_surface(c);
+
+ /* Don't change border color if there is an exclusive focus or we are
+ * handling a drag operation */
+ if (!exclusive_focus && !seat->drag)
+ client_set_border_color(c, focuscolor);
+ }
+
+ /* Deactivate old client if focus is changing */
+ if (old && (!c || client_surface(c) != old)) {
+ /* If an overlay is focused, don't focus or activate the client,
+ * but only update its position in fstack to render its border with focuscolor
+ * and focus it after the overlay is closed. */
+ if (old_client_type == LayerShell && wlr_scene_node_coords(
+ &old_l->scene->node, &unused_lx, &unused_ly)
+ && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+ return;
+ } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
+ return;
+ /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
+ * and probably other clients */
+ } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
+ client_set_border_color(old_c, bordercolor);
+
+ client_activate_surface(old, 0);
+ }
+ }
+ printstatus();
+
+ if (!c) {
+ /* With no client, all we have left is to clear focus */
+ wlr_seat_keyboard_notify_clear_focus(seat);
+ return;
+ }
+
+ /* Change cursor surface */
+ motionnotify(0);
+
+ /* Have a client, so focus its top-level wlr_surface */
+ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
+
+ /* Activate the new client */
+ client_activate_surface(client_surface(c), 1);
+}
+
+void
+focusmon(const Arg *arg)
+{
+ int i = 0, nmons = wl_list_length(&mons);
+ if (nmons)
+ do /* don't switch to disabled mons */
+ selmon = dirtomon(arg->i);
+ while (!selmon->wlr_output->enabled && i++ < nmons);
+ focusclient(focustop(selmon), 1);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ /* Focus the next or previous client (in tiling order) on selmon */
+ Client *c, *sel = focustop(selmon);
+ if (!sel || sel->isfullscreen)
+ return;
+ if (arg->i > 0) {
+ wl_list_for_each(c, &sel->link, link) {
+ if (&c->link == &clients)
+ continue; /* wrap past the sentinel node */
+ if (VISIBLEON(c, selmon))
+ break; /* found it */
+ }
+ } else {
+ wl_list_for_each_reverse(c, &sel->link, link) {
+ if (&c->link == &clients)
+ continue; /* wrap past the sentinel node */
+ if (VISIBLEON(c, selmon))
+ break; /* found it */
+ }
+ }
+ /* If only one client is visible on selmon, then c == sel */
+ focusclient(c, 1);
+}
+
+/* We probably should change the name of this, it sounds like
+ * will focus the topmost client of this mon, when actually will
+ * only return that client */
+Client *
+focustop(Monitor *m)
+{
+ Client *c;
+ wl_list_for_each(c, &fstack, flink)
+ if (VISIBLEON(c, m))
+ return c;
+ return NULL;
+}
+
+void
+fullscreennotify(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, fullscreen);
+ setfullscreen(c, client_wants_fullscreen(c));
+}
+
+void
+handlesig(int signo)
+{
+ if (signo == SIGCHLD) {
+ siginfo_t in;
+ /* wlroots expects to reap the XWayland process itself, so we
+ * use WNOWAIT to keep the child waitable until we know it's not
+ * XWayland.
+ */
+ while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid
+ #ifdef XWAYLAND
+ && (!xwayland || in.si_pid != xwayland->server->pid)
+#endif
+ ) {
+ pid_t *p, *lim;
+ waitpid(in.si_pid, NULL, 0);
+ if (in.si_pid == child_pid)
+ child_pid = -1;
+ if (!(p = autostart_pids))
+ continue;
+ lim = &p[autostart_len];
+
+ for (; p < lim; p++) {
+ if (*p == in.si_pid) {
+ *p = -1;
+ break;
+ }
+ }
+ }
+ } else if (signo == SIGINT || signo == SIGTERM) {
+ quit(NULL);
+ }
+}
+
+void
+incnmaster(const Arg *arg)
+{
+ if (!arg || !selmon)
+ return;
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
+void
+incgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incigaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incihgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv
+ );
+}
+
+void
+incivgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incogaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incohgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incovgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+inputdevice(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the backend when a new input device becomes
+ * available. */
+ struct wlr_input_device *device = data;
+ uint32_t caps;
+
+ switch (device->type) {
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ createkeyboard(wlr_keyboard_from_input_device(device));
+ break;
+ case WLR_INPUT_DEVICE_POINTER:
+ createpointer(wlr_pointer_from_input_device(device));
+ break;
+ default:
+ /* TODO handle other input device types */
+ break;
+ }
+
+ /* We need to let the wlr_seat know what our capabilities are, which is
+ * communiciated to the client. In dwl we always have a cursor, even if
+ * there are no pointer devices, so we always include that capability. */
+ /* TODO do we actually require a cursor? */
+ caps = WL_SEAT_CAPABILITY_POINTER;
+ if (!wl_list_empty(&keyboards))
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ wlr_seat_set_capabilities(seat, caps);
+}
+
+int
+keybinding(uint32_t mods, xkb_keysym_t sym)
+{
+ /*
+ * Here we handle compositor keybindings. This is when the compositor is
+ * processing keys, rather than passing them on to the client for its own
+ * processing.
+ */
+ int handled = 0;
+ const Key *k;
+ for (k = keys; k < END(keys); k++) {
+ if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
+ sym == k->keysym && k->func) {
+ k->func(&k->arg);
+ handled = 1;
+ }
+ }
+ return handled;
+}
+
+void
+keypress(struct wl_listener *listener, void *data)
+{
+ int i;
+ /* This event is raised when a key is pressed or released. */
+ Keyboard *kb = wl_container_of(listener, kb, key);
+ struct wlr_keyboard_key_event *event = data;
+
+ /* Translate libinput keycode -> xkbcommon */
+ uint32_t keycode = event->keycode + 8;
+ /* Get a list of keysyms based on the keymap for this keyboard */
+ const xkb_keysym_t *syms;
+ int nsyms = xkb_state_key_get_syms(
+ kb->wlr_keyboard->xkb_state, keycode, &syms);
+
+ int handled = 0;
+ uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard);
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ /* On _press_ if there is no active screen locker,
+ * attempt to process a compositor keybinding. */
+ if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ for (i = 0; i < nsyms; i++)
+ handled = keybinding(mods, syms[i]) || handled;
+
+ if (handled && kb->wlr_keyboard->repeat_info.delay > 0) {
+ kb->mods = mods;
+ kb->keysyms = syms;
+ kb->nsyms = nsyms;
+ wl_event_source_timer_update(kb->key_repeat_source,
+ kb->wlr_keyboard->repeat_info.delay);
+ } else {
+ kb->nsyms = 0;
+ wl_event_source_timer_update(kb->key_repeat_source, 0);
+ }
+
+ if (handled)
+ return;
+
+ /* Pass unhandled keycodes along to the client. */
+ wlr_seat_set_keyboard(seat, kb->wlr_keyboard);
+ wlr_seat_keyboard_notify_key(seat, event->time_msec,
+ event->keycode, event->state);
+}
+
+void
+keypressmod(struct wl_listener *listener, void *data)
+{
+ /* This event is raised when a modifier key, such as shift or alt, is
+ * pressed. We simply communicate this to the client. */
+ Keyboard *kb = wl_container_of(listener, kb, modifiers);
+ /*
+ * A seat can only have one keyboard, but this is a limitation of the
+ * Wayland protocol - not wlroots. We assign all connected keyboards to the
+ * same seat. You can swap out the underlying wlr_keyboard like this and
+ * wlr_seat handles this transparently.
+ */
+ wlr_seat_set_keyboard(seat, kb->wlr_keyboard);
+ /* Send modifiers to the client. */
+ wlr_seat_keyboard_notify_modifiers(seat,
+ &kb->wlr_keyboard->modifiers);
+}
+
+int
+keyrepeat(void *data)
+{
+ Keyboard *kb = data;
+ int i;
+ if (!kb->nsyms || kb->wlr_keyboard->repeat_info.rate <= 0)
+ return 0;
+
+ wl_event_source_timer_update(kb->key_repeat_source,
+ 1000 / kb->wlr_keyboard->repeat_info.rate);
+
+ for (i = 0; i < kb->nsyms; i++)
+ keybinding(kb->mods, kb->keysyms[i]);
+
+ return 0;
+}
+
+void
+killclient(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ client_send_close(sel);
+}
+
+void
+locksession(struct wl_listener *listener, void *data)
+{
+ struct wlr_session_lock_v1 *session_lock = data;
+ SessionLock *lock;
+ wlr_scene_node_set_enabled(&locked_bg->node, 1);
+ if (cur_lock) {
+ wlr_session_lock_v1_destroy(session_lock);
+ return;
+ }
+ lock = session_lock->data = ecalloc(1, sizeof(*lock));
+ focusclient(NULL, 0);
+
+ lock->scene = wlr_scene_tree_create(layers[LyrBlock]);
+ cur_lock = lock->lock = session_lock;
+ locked = 1;
+
+ LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface);
+ LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock);
+ LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession);
+
+ wlr_session_lock_v1_send_locked(session_lock);
+}
+
+void
+maplayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *l = wl_container_of(listener, l, map);
+ motionnotify(0);
+}
+
+void
+mapnotify(struct wl_listener *listener, void *data)
+{
+ /* Called when the surface is mapped, or ready to display on-screen. */
+ Client *p, *w, *c = wl_container_of(listener, c, map);
+ Monitor *m;
+ int i;
+
+ /* Create scene tree for this client and its border */
+ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
+ wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell);
+ c->scene_surface = c->type == XDGShell
+ ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
+ : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
+ c->scene->node.data = c->scene_surface->node.data = c;
+
+ /* Handle unmanaged clients first so we can return prior create borders */
+ if (client_is_unmanaged(c)) {
+ client_get_geometry(c, &c->geom);
+ /* Unmanaged clients always are floating */
+ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
+ wlr_scene_node_set_position(&c->scene->node, c->geom.x + borderpx,
+ c->geom.y + borderpx);
+ if (client_wants_focus(c)) {
+ focusclient(c, 1);
+ exclusive_focus = c;
+ }
+ goto unset_fullscreen;
+ }
+
+ for (i = 0; i < 4; i++) {
+ c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor);
+ c->border[i]->node.data = c;
+ }
+
+ /* Initialize client geometry with room for border */
+ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
+ client_get_geometry(c, &c->geom);
+ c->geom.width += 2 * c->bw;
+ c->geom.height += 2 * c->bw;
+
+ /* Insert this client into client lists. */
+ wl_list_insert(&clients, &c->link);
+ wl_list_insert(&fstack, &c->flink);
+
+ /* Set initial monitor, tags, floating status, and focus:
+ * we always consider floating, clients that have parent and thus
+ * we set the same tags and monitor than its parent, if not
+ * try to apply rules for them */
+ /* TODO: https://github.com/djpohly/dwl/pull/334#issuecomment-1330166324 */
+ if (c->type == XDGShell && (p = client_get_parent(c))) {
+ c->isfloating = 1;
+ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
+ setmon(c, p->mon, p->tags);
+ } else {
+ applyrules(c);
+ }
+ printstatus();
+
+unset_fullscreen:
+ m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
+ wl_list_for_each(w, &clients, link)
+ if (w != c && w->isfullscreen && m == w->mon && (w->tags & c->tags))
+ setfullscreen(w, 0);
+}
+
+void
+maximizenotify(struct wl_listener *listener, void *data)
+{
+ /* This event is raised when a client would like to maximize itself,
+ * typically because the user clicked on the maximize button on
+ * client-side decorations. dwl doesn't support maximization, but
+ * to conform to xdg-shell protocol we still must send a configure.
+ * Since xdg-shell protocol v5 we should ignore request of unsupported
+ * capabilities, just schedule a empty configure when the client uses <5
+ * protocol version
+ * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
+ Client *c = wl_container_of(listener, c, maximize);
+ if (wl_resource_get_version(c->surface.xdg->resource)
+ < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
+ wlr_xdg_surface_schedule_configure(c->surface.xdg);
+}
+
+void
+monocle(Monitor *m)
+{
+ Client *c;
+ int n = 0;
+
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+ n++;
+ if (!monoclegaps)
+ resize(c, m->w, 0);
+ else
+ resize(c, (struct wlr_box){.x = m->w.x + gappoh, .y = m->w.y + gappov,
+ .width = m->w.width - 2 * gappoh, .height = m->w.height - 2 * gappov}, 0);
+ }
+ if (n)
+ snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n);
+ if ((c = focustop(m)))
+ wlr_scene_node_raise_to_top(&c->scene->node);
+}
+
+void
+motionabsolute(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits an _absolute_
+ * motion event, from 0..1 on each axis. This happens, for example, when
+ * wlroots is running under a Wayland window rather than KMS+DRM, and you
+ * move the mouse over the window. You could enter the window from any edge,
+ * so we have to warp the mouse there. There is also some hardware which
+ * emits these events. */
+ struct wlr_pointer_motion_absolute_event *event = data;
+ wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
+ motionnotify(event->time_msec);
+}
+
+void
+motionnotify(uint32_t time)
+{
+ double sx = 0, sy = 0;
+ Client *c = NULL, *w = NULL;
+ LayerSurface *l = NULL;
+ int type;
+ struct wlr_surface *surface = NULL;
+
+ /* time is 0 in internal calls meant to restore pointer focus. */
+ if (time) {
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ /* Update selmon (even while dragging a window) */
+ if (sloppyfocus)
+ selmon = xytomon(cursor->x, cursor->y);
+ }
+
+ /* Update drag icon's position */
+ wlr_scene_node_set_position(&drag_icon->node, cursor->x, cursor->y);
+
+ /* If we are currently grabbing the mouse, handle and return */
+ if (cursor_mode == CurMove) {
+ /* Move the grabbed client to the new position. */
+ resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy,
+ .width = grabc->geom.width, .height = grabc->geom.height}, 1);
+ return;
+ } else if (cursor_mode == CurResize) {
+ resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
+ .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1);
+ return;
+ }
+
+ /* Find the client under the pointer and send the event along. */
+ xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy);
+
+ if (cursor_mode == CurPressed && !seat->drag) {
+ if ((type = toplevel_from_wlr_surface(
+ seat->pointer_state.focused_surface, &w, &l)) >= 0) {
+ c = w;
+ surface = seat->pointer_state.focused_surface;
+ sx = cursor->x - (type == LayerShell ? l->geom.x : w->geom.x);
+ sy = cursor->y - (type == LayerShell ? l->geom.y : w->geom.y);
+ }
+ }
+
+ /* If there's no client surface under the cursor, set the cursor image to a
+ * default. This is what makes the cursor image appear when you move it
+ * off of a client or over its border. */
+ if (!surface && !seat->drag)
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+
+ pointerfocus(c, surface, sx, sy, time);
+}
+
+void
+motionrelative(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits a _relative_
+ * pointer motion event (i.e. a delta) */
+ struct wlr_pointer_motion_event *event = data;
+ /* The cursor doesn't move unless we tell it to. The cursor automatically
+ * handles constraining the motion to the output layout, as well as any
+ * special configuration applied for the specific input device which
+ * generated the event. You can pass NULL for the device if you want to move
+ * the cursor around without any input. */
+ wlr_cursor_move(cursor, &event->pointer->base, event->delta_x, event->delta_y);
+ motionnotify(event->time_msec);
+}
+
+void
+moveresize(const Arg *arg)
+{
+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
+ return;
+ xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
+ if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
+ pnireturn;
+
+ /* Float the window and tell motionnotify to grab it */
+ setfloating(grabc, 1);
+ switch (cursor_mode = arg->ui) {
+ case CurMove:
+ grabcx = cursor->x - grabc->geom.x;
+ grabcy = cursor->y - grabc->geom.y;
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
+ break;
+ case CurResize:
+ /* Doesn't work for X11 output - the next absolute motion event
+ * returns the cursor to where it started */
+ wlr_cursor_warp_closest(cursor, NULL,
+ grabc->geom.x + grabc->geom.width,
+ grabc->geom.y + grabc->geom.height);
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ break;
+ }
+}
+
+void
+outputmgrapply(struct wl_listener *listener, void *data)
+{
+ struct wlr_output_configuration_v1 *config = data;
+ outputmgrapplyortest(config, 0);
+}
+
+void
+outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
+{
+ /*
+ * Called when a client such as wlr-randr requests a change in output
+ * configuration. This is only one way that the layout can be changed,
+ * so any Monitor information should be updated by updatemons() after an
+ * output_layout.change event, not here.
+ */
+ struct wlr_output_configuration_head_v1 *config_head;
+ int ok = 1;
+
+ wl_list_for_each(config_head, &config->heads, link) {
+ struct wlr_output *wlr_output = config_head->state.output;
+ Monitor *m = wlr_output->data;
+
+ wlr_output_enable(wlr_output, config_head->state.enabled);
+ if (!config_head->state.enabled)
+ goto apply_or_test;
+ if (config_head->state.mode)
+ wlr_output_set_mode(wlr_output, config_head->state.mode);
+ else
+ wlr_output_set_custom_mode(wlr_output,
+ config_head->state.custom_mode.width,
+ config_head->state.custom_mode.height,
+ config_head->state.custom_mode.refresh);
+
+ /* Don't move monitors if position wouldn't change, this to avoid
+ * wlroots marking the output as manually configured */
+ if (m->m.x != config_head->state.x || m->m.y != config_head->state.y)
+ wlr_output_layout_add(output_layout, wlr_output,
+ config_head->state.x, config_head->state.y);
+ wlr_output_set_transform(wlr_output, config_head->state.transform);
+ wlr_output_set_scale(wlr_output, config_head->state.scale);
+ wlr_output_enable_adaptive_sync(wlr_output,
+ config_head->state.adaptive_sync_enabled);
+
+apply_or_test:
+ if (test) {
+ ok &= wlr_output_test(wlr_output);
+ wlr_output_rollback(wlr_output);
+ } else {
+ ok &= wlr_output_commit(wlr_output);
+ }
+ }
+
+ if (ok)
+ wlr_output_configuration_v1_send_succeeded(config);
+ else
+ wlr_output_configuration_v1_send_failed(config);
+ wlr_output_configuration_v1_destroy(config);
+
+ /* TODO: use a wrapper function? */
+ updatemons(NULL, NULL);
+}
+
+void
+outputmgrtest(struct wl_listener *listener, void *data)
+{
+ struct wlr_output_configuration_v1 *config = data;
+ outputmgrapplyortest(config, 1);
+}
+
+void
+pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
+ uint32_t time)
+{
+ struct timespec now;
+ int internal_call = !time;
+
+ if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c))
+ focusclient(c, 0);
+
+ /* If surface is NULL, clear pointer focus */
+ if (!surface) {
+ wlr_seat_pointer_notify_clear_focus(seat);
+ return;
+ }
+
+ if (internal_call) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
+ }
+
+ /* Let the client know that the mouse cursor has entered one
+ * of its surfaces, and make keyboard focus follow if desired.
+ * wlroots makes this a no-op if surface is already focused */
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+
+}
+
+void
+printstatus(void)
+{
+ Monitor *m = NULL;
+ Client *c;
+ uint32_t occ, urg, sel;
+ const char *appid, *title;
+
+ wl_list_for_each(m, &mons, link) {
+ occ = urg = 0;
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon != m)
+ continue;
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ if ((c = focustop(m))) {
+ title = client_get_title(c);
+ appid = client_get_appid(c);
+ printf("%s title %s\n", m->wlr_output->name, title ? title : broken);
+ printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken);
+ printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen);
+ printf("%s floating %u\n", m->wlr_output->name, c->isfloating);
+ sel = c->tags;
+ } else {
+ printf("%s title \n", m->wlr_output->name);
+ printf("%s appid \n", m->wlr_output->name);
+ printf("%s fullscreen \n", m->wlr_output->name);
+ printf("%s floating \n", m->wlr_output->name);
+ sel = 0;
+ }
+
+ printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
+ printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags],
+ sel, urg);
+ dwl_wm_printstatus(m);
+ printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
+ }
+ fflush(stdout);
+}
+
+void
+quit(const Arg *arg)
+{
+ wl_display_terminate(dpy);
+}
+
+void
+restartdwl(const Arg *arg)
+{
+ FILE *fp;
+ fp = fopen ("/tmp/restart_dwl", "w");
+ fclose(fp);
+ quit(0);
+}
+
+void
+rendermon(struct wl_listener *listener, void *data)
+{
+ /* This function is called every time an output is ready to display a frame,
+ * generally at the output's refresh rate (e.g. 60Hz). */
+ Monitor *m = wl_container_of(listener, m, frame);
+ Client *c;
+ struct wlr_output_state pending = {0};
+ struct wlr_gamma_control_v1 *gamma_control;
+ struct timespec now;
+
+ /* Render if no XDG clients have an outstanding resize and are visible on
+ * this monitor. */
+ wl_list_for_each(c, &clients, link)
+ if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
+ goto skip;
+
+ /*
+ * HACK: The "correct" way to set the gamma is to commit it together with
+ * the rest of the state in one go, but to do that we would need to rewrite
+ * wlr_scene_output_commit() in order to add the gamma to the pending
+ * state before committing, instead try to commit the gamma in one frame,
+ * and commit the rest of the state in the next one (or in the same frame if
+ * the gamma can not be committed).
+ */
+ if (m->gamma_lut_changed) {
+ gamma_control = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output);
+ m->gamma_lut_changed = 0;
+
+ if (!wlr_gamma_control_v1_apply(gamma_control, &pending))
+ goto commit;
+
+ if (!wlr_output_test_state(m->wlr_output, &pending)) {
+ wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
+ goto commit;
+ }
+ wlr_output_commit_state(m->wlr_output, &pending);
+ wlr_output_schedule_frame(m->wlr_output);
+ } else {
+commit:
+ wlr_scene_output_commit(m->scene_output, NULL);
+ }
+
+skip:
+ /* Let clients know a frame has been rendered */
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ wlr_scene_output_send_frame_done(m->scene_output, &now);
+ wlr_output_state_finish(&pending);
+}
+
+void
+requeststartdrag(struct wl_listener *listener, void *data)
+{
+ struct wlr_seat_request_start_drag_event *event = data;
+
+ if (wlr_seat_validate_pointer_grab_serial(seat, event->origin,
+ event->serial))
+ wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
+ else
+ wlr_data_source_destroy(event->drag->source);
+}
+
+void
+requestmonstate(struct wl_listener *listener, void *data)
+{
+ struct wlr_output_event_request_state *event = data;
+ wlr_output_commit_state(event->output, event->state);
+ updatemons(NULL, NULL);
+}
+
+void
+resize(Client *c, struct wlr_box geo, int interact)
+{
+ struct wlr_box *bbox = interact ? &sgeom : &c->mon->w;
+ struct wlr_box clip;
+ client_set_bounds(c, geo.width, geo.height);
+ c->geom = geo;
+ applybounds(c, bbox);
+
+ /* Update scene-graph, including borders */
+ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
+ wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
+ wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
+ wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
+ wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
+ wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
+ wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
+ wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
+ wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
+
+ /* this is a no-op if size hasn't changed */
+ c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
+ c->geom.height - 2 * c->bw);
+ client_get_clip(c, &clip);
+ wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
+}
+
+void
+run(char *startup_cmd)
+{
+ /* Add a Unix socket to the Wayland display. */
+ const char *socket = wl_display_add_socket_auto(dpy);
+ if (!socket)
+ die("startup: display_add_socket_auto");
+ setenv("WAYLAND_DISPLAY", socket, 1);
+
+ /* Start the backend. This will enumerate outputs and inputs, become the DRM
+ * master, etc */
+ if (!wlr_backend_start(backend))
+ die("startup: backend_start");
+
+ /* Now that the socket exists and the backend is started, run the startup command */
+ autostartexec();
+ if (startup_cmd) {
+ int piperw[2];
+ if (pipe(piperw) < 0)
+ die("startup: pipe:");
+ if ((child_pid = fork()) < 0)
+ die("startup: fork:");
+ if (child_pid == 0) {
+ dup2(piperw[0], STDIN_FILENO);
+ close(piperw[0]);
+ close(piperw[1]);
+ execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
+ die("startup: execl:");
+ }
+ dup2(piperw[1], STDOUT_FILENO);
+ close(piperw[1]);
+ close(piperw[0]);
+ }
+ printstatus();
+
+ /* At this point the outputs are initialized, choose initial selmon based on
+ * cursor position, and set default cursor image */
+ selmon = xytomon(cursor->x, cursor->y);
+
+ /* TODO hack to get cursor to display in its initial location (100, 100)
+ * instead of (0, 0) and then jumping. still may not be fully
+ * initialized, as the image/coordinates are not transformed for the
+ * monitor when displayed here */
+ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y);
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+
+ /* Run the Wayland event loop. This does not return until you exit the
+ * compositor. Starting the backend rigged up all of the necessary event
+ * loop configuration to listen to libinput events, DRM events, generate
+ * frame events at the refresh rate, and so on. */
+ wl_display_run(dpy);
+}
+
+void
+setcursor(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the seat when a client provides a cursor image */
+ struct wlr_seat_pointer_request_set_cursor_event *event = data;
+ /* If we're "grabbing" the cursor, don't use the client's image, we will
+ * restore it after "grabbing" sending a leave event, followed by a enter
+ * event, which will result in the client requesting set the cursor surface */
+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
+ return;
+ /* This can be sent by any client, so we check to make sure this one is
+ * actually has pointer focus first. If so, we can tell the cursor to
+ * use the provided surface as the cursor image. It will set the
+ * hardware cursor on the output that it's currently on and continue to
+ * do so as the cursor moves between outputs. */
+ if (event->seat_client == seat->pointer_state.focused_client)
+ wlr_cursor_set_surface(cursor, event->surface,
+ event->hotspot_x, event->hotspot_y);
+}
+
+void
+setcursorshape(struct wl_listener *listener, void *data)
+{
+ struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
+ return;
+ /* This can be sent by any client, so we check to make sure this one is
+ * actually has pointer focus first. If so, we can tell the cursor to
+ * use the provided cursor shape. */
+ if (event->seat_client == seat->pointer_state.focused_client)
+ wlr_cursor_set_xcursor(cursor, cursor_mgr,
+ wlr_cursor_shape_v1_name(event->shape));
+}
+
+void
+setfloating(Client *c, int floating)
+{
+ c->isfloating = floating;
+ if (!c->mon)
+ return;
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+ arrange(c->mon);
+ printstatus();
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+ c->isfullscreen = fullscreen;
+ if (!c->mon)
+ return;
+ c->bw = fullscreen ? 0 : borderpx;
+ client_set_fullscreen(c, fullscreen);
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+
+ if (fullscreen) {
+ c->prev = c->geom;
+ resize(c, c->mon->m, 0);
+ } else {
+ /* restore previous size instead of arrange for floating windows since
+ * client positions are set by the user and cannot be recalculated */
+ resize(c, c->prev, 0);
+ }
+ arrange(c->mon);
+ printstatus();
+}
+
+void
+setgamma(struct wl_listener *listener, void *data)
+{
+ struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
+ Monitor *m = event->output->data;
+ m->gamma_lut_changed = 1;
+ wlr_output_schedule_frame(m->wlr_output);
+}
+
+void
+setgaps(int oh, int ov, int ih, int iv)
+{
+ selmon->gappoh = MAX(oh, 0);
+ selmon->gappov = MAX(ov, 0);
+ selmon->gappih = MAX(ih, 0);
+ selmon->gappiv = MAX(iv, 0);
+ arrange(selmon);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!selmon)
+ return;
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
+ arrange(selmon);
+ printstatus();
+}
+
+/* arg > 1.0 will set mfact absolutely */
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ selmon->mfact = f;
+ arrange(selmon);
+}
+
+void
+setmon(Client *c, Monitor *m, uint32_t newtags)
+{
+ Monitor *oldmon = c->mon;
+
+ if (oldmon == m)
+ return;
+ c->mon = m;
+ c->prev = c->geom;
+
+ /* Scene graph sends surface leave/enter events on move and resize */
+ if (oldmon)
+ arrange(oldmon);
+ if (m) {
+ /* Make sure window actually overlaps with the monitor */
+ resize(c, c->geom, 0);
+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
+ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
+ setfloating(c, c->isfloating);
+ }
+ focusclient(focustop(selmon), 1);
+}
+
+void
+setpsel(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the seat when a client wants to set the selection,
+ * usually when the user copies something. wlroots allows compositors to
+ * ignore such requests if they so choose, but in dwl we always honor
+ */
+ struct wlr_seat_request_set_primary_selection_event *event = data;
+ wlr_seat_set_primary_selection(seat, event->source, event->serial);
+}
+
+void
+setsel(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the seat when a client wants to set the selection,
+ * usually when the user copies something. wlroots allows compositors to
+ * ignore such requests if they so choose, but in dwl we always honor
+ */
+ struct wlr_seat_request_set_selection_event *event = data;
+ wlr_seat_set_selection(seat, event->source, event->serial);
+}
+
+void
+setup(void)
+{
+ int i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
+ struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
+ sigemptyset(&sa.sa_mask);
+
+ for (i = 0; i < LENGTH(sig); i++)
+ sigaction(sig[i], &sa, NULL);
+
+ wlr_log_init(log_level, NULL);
+
+ /* The Wayland display is managed by libwayland. It handles accepting
+ * clients from the Unix socket, manging Wayland globals, and so on. */
+ dpy = wl_display_create();
+
+ /* The backend is a wlroots feature which abstracts the underlying input and
+ * output hardware. The autocreate option will choose the most suitable
+ * backend based on the current environment, such as opening an X11 window
+ * if an X11 server is running. */
+ if (!(backend = wlr_backend_autocreate(dpy, &session)))
+ die("couldn't create backend");
+
+ /* Initialize the scene graph used to lay out windows */
+ scene = wlr_scene_create();
+ for (i = 0; i < NUM_LAYERS; i++)
+ layers[i] = wlr_scene_tree_create(&scene->tree);
+ drag_icon = wlr_scene_tree_create(&scene->tree);
+ wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node);
+
+ /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
+ * can also specify a renderer using the WLR_RENDERER env var.
+ * The renderer is responsible for defining the various pixel formats it
+ * supports for shared memory, this configures that for clients. */
+ if (!(drw = wlr_renderer_autocreate(backend)))
+ die("couldn't create renderer");
+
+ /* Create shm, drm and linux_dmabuf interfaces by ourselves.
+ * The simplest way is call:
+ * wlr_renderer_init_wl_display(drw);
+ * but we need to create manually the linux_dmabuf interface to integrate it
+ * with wlr_scene. */
+ wlr_renderer_init_wl_shm(drw, dpy);
+
+ if (wlr_renderer_get_dmabuf_texture_formats(drw)) {
+ wlr_drm_create(dpy, drw);
+ wlr_scene_set_linux_dmabuf_v1(scene,
+ wlr_linux_dmabuf_v1_create_with_renderer(dpy, 4, drw));
+ }
+
+ /* Autocreates an allocator for us.
+ * The allocator is the bridge between the renderer and the backend. It
+ * handles the buffer creation, allowing wlroots to render onto the
+ * screen */
+ if (!(alloc = wlr_allocator_autocreate(backend, drw)))
+ die("couldn't create allocator");
+
+ /* This creates some hands-off wlroots interfaces. The compositor is
+ * necessary for clients to allocate surfaces and the data device manager
+ * handles the clipboard. Each of these wlroots interfaces has room for you
+ * to dig your fingers in and play with their behavior if you want. Note that
+ * the clients cannot set the selection directly without compositor approval,
+ * see the setsel() function. */
+ compositor = wlr_compositor_create(dpy, 6, drw);
+ wlr_subcompositor_create(dpy);
+ wlr_data_device_manager_create(dpy);
+ wlr_export_dmabuf_manager_v1_create(dpy);
+ wlr_screencopy_manager_v1_create(dpy);
+ wlr_data_control_manager_v1_create(dpy);
+ wlr_primary_selection_v1_device_manager_create(dpy);
+ wlr_viewporter_create(dpy);
+ wlr_single_pixel_buffer_manager_v1_create(dpy);
+ wlr_fractional_scale_manager_v1_create(dpy, 1);
+
+ /* Initializes the interface used to implement urgency hints */
+ activation = wlr_xdg_activation_v1_create(dpy);
+ LISTEN_STATIC(&activation->events.request_activate, urgent);
+
+ gamma_control_mgr = wlr_gamma_control_manager_v1_create(dpy);
+ LISTEN_STATIC(&gamma_control_mgr->events.set_gamma, setgamma);
+
+ /* Creates an output layout, which a wlroots utility for working with an
+ * arrangement of screens in a physical layout. */
+ output_layout = wlr_output_layout_create();
+ LISTEN_STATIC(&output_layout->events.change, updatemons);
+ wlr_xdg_output_manager_v1_create(dpy, output_layout);
+
+ /* Configure a listener to be notified when new outputs are available on the
+ * backend. */
+ wl_list_init(&mons);
+ LISTEN_STATIC(&backend->events.new_output, createmon);
+
+ /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a
+ * Wayland protocol which is used for application windows. For more
+ * detail on shells, refer to the article:
+ *
+ * https://drewdevault.com/2018/07/29/Wayland-shells.html
+ */
+ wl_list_init(&clients);
+ wl_list_init(&fstack);
+
+ xdg_shell = wlr_xdg_shell_create(dpy, 6);
+ LISTEN_STATIC(&xdg_shell->events.new_surface, createnotify);
+
+ layer_shell = wlr_layer_shell_v1_create(dpy, 3);
+ LISTEN_STATIC(&layer_shell->events.new_surface, createlayersurface);
+
+ idle_notifier = wlr_idle_notifier_v1_create(dpy);
+
+ idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
+ LISTEN_STATIC(&idle_inhibit_mgr->events.new_inhibitor, createidleinhibitor);
+
+ session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
+ wl_signal_add(&session_lock_mgr->events.new_lock, &lock_listener);
+ LISTEN_STATIC(&session_lock_mgr->events.destroy, destroysessionmgr);
+ locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
+ (float [4]){0.1, 0.1, 0.1, 1.0});
+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
+
+ /* Use decoration protocols to negotiate server-side decorations */
+ wlr_server_decoration_manager_set_default_mode(
+ wlr_server_decoration_manager_create(dpy),
+ WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
+ xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
+ LISTEN_STATIC(&xdg_decoration_mgr->events.new_toplevel_decoration, createdecoration);
+
+ /*
+ * Creates a cursor, which is a wlroots utility for tracking the cursor
+ * image shown on screen.
+ */
+ cursor = wlr_cursor_create();
+ wlr_cursor_attach_output_layout(cursor, output_layout);
+
+ /* Creates an xcursor manager, another wlroots utility which loads up
+ * Xcursor themes to source cursor images from and makes sure that cursor
+ * images are available at all scale factors on the screen (necessary for
+ * HiDPI support). Scaled cursors will be loaded with each output. */
+ cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
+ setenv("XCURSOR_SIZE", "24", 1);
+
+ /*
+ * wlr_cursor *only* displays an image on screen. It does not move around
+ * when the pointer moves. However, we can attach input devices to it, and
+ * it will generate aggregate events for all of them. In these events, we
+ * can choose how we want to process them, forwarding them to clients and
+ * moving the cursor around. More detail on this process is described in
+ * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
+ *
+ * And more comments are sprinkled throughout the notify functions above.
+ */
+ LISTEN_STATIC(&cursor->events.motion, motionrelative);
+ LISTEN_STATIC(&cursor->events.motion_absolute, motionabsolute);
+ LISTEN_STATIC(&cursor->events.button, buttonpress);
+ LISTEN_STATIC(&cursor->events.axis, axisnotify);
+ LISTEN_STATIC(&cursor->events.frame, cursorframe);
+
+ cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
+ LISTEN_STATIC(&cursor_shape_mgr->events.request_set_shape, setcursorshape);
+
+ /*
+ * Configures a seat, which is a single "seat" at which a user sits and
+ * operates the computer. This conceptually includes up to one keyboard,
+ * pointer, touch, and drawing tablet device. We also rig up a listener to
+ * let us know when new input devices are available on the backend.
+ */
+ wl_list_init(&keyboards);
+ LISTEN_STATIC(&backend->events.new_input, inputdevice);
+ virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
+ LISTEN_STATIC(&virtual_keyboard_mgr->events.new_virtual_keyboard, virtualkeyboard);
+ seat = wlr_seat_create(dpy, "seat0");
+ LISTEN_STATIC(&seat->events.request_set_cursor, setcursor);
+ LISTEN_STATIC(&seat->events.request_set_selection, setsel);
+ LISTEN_STATIC(&seat->events.request_set_primary_selection, setpsel);
+ LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag);
+ LISTEN_STATIC(&seat->events.start_drag, startdrag);
+
+ output_mgr = wlr_output_manager_v1_create(dpy);
+ LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
+ LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
+
+ wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend));
+ wl_global_create(dpy, &znet_tapesoftware_dwl_wm_v1_interface, 1, NULL, dwl_wm_bind);
+
+#ifdef XWAYLAND
+ /*
+ * Initialise the XWayland X server.
+ * It will be started when the first X client is started.
+ */
+ xwayland = wlr_xwayland_create(dpy, compositor, 1);
+ if (xwayland) {
+ LISTEN_STATIC(&xwayland->events.ready, xwaylandready);
+ LISTEN_STATIC(&xwayland->events.new_surface, createnotifyx11);
+
+ setenv("DISPLAY", xwayland->display_name, 1);
+ } else {
+ fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
+ }
+#endif
+}
+
+void
+spawn(const Arg *arg)
+{
+ if (fork() == 0) {
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+ setsid();
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ die("dwl: execvp %s failed:", ((char **)arg->v)[0]);
+ }
+}
+
+void
+startdrag(struct wl_listener *listener, void *data)
+{
+ struct wlr_drag *drag = data;
+ if (!drag->icon)
+ return;
+
+ drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node;
+ LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
+}
+
+void
+tag(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (!sel || (arg->ui & TAGMASK) == 0)
+ return;
+
+ sel->tags = arg->ui & TAGMASK;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+tagmon(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ setmon(sel, dirtomon(arg->i), 0);
+}
+
+void
+tile(Monitor *m)
+{
+ unsigned int i, n = 0, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ n++;
+ if (n == 0)
+ return;
+
+ if (smartgaps == n) {
+ oe = 0; // outer gaps disabled
+ }
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? (m->w.width + m->gappiv*ie) * m->mfact : 0;
+ else
+ mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie;
+ i = 0;
+ my = ty = m->gappoh*oe;
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+ if (i < m->nmaster) {
+ r = MIN(n, m->nmaster) - i;
+ h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my,
+ .width = mw - m->gappiv*ie, .height = h}, 0);
+ my += c->geom.height + m->gappih*ie;
+ } else {
+ r = n - i;
+ h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty,
+ .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0);
+ ty += c->geom.height + m->gappih*ie;
+ }
+ i++;
+ }
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ /* return if fullscreen */
+ if (sel && !sel->isfullscreen)
+ setfloating(sel, !sel->isfloating);
+}
+
+void
+togglefullscreen(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ setfullscreen(sel, !sel->isfullscreen);
+}
+
+void
+togglegaps(const Arg *arg)
+{
+ enablegaps = !enablegaps;
+ arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ uint32_t newtags;
+ Client *sel = focustop(selmon);
+ if (!sel)
+ return;
+ newtags = sel->tags ^ (arg->ui & TAGMASK);
+ if (!newtags)
+ return;
+
+ sel->tags = newtags;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+toggleview(const Arg *arg)
+{
+ uint32_t newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0;
+
+ if (!newtagset)
+ return;
+
+ selmon->tagset[selmon->seltags] = newtagset;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+unlocksession(struct wl_listener *listener, void *data)
+{
+ SessionLock *lock = wl_container_of(listener, lock, unlock);
+ destroylock(lock, 1);
+}
+
+void
+unmaplayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap);
+
+ layersurface->mapped = 0;
+ wlr_scene_node_set_enabled(&layersurface->scene->node, 0);
+ if (layersurface == exclusive_focus)
+ exclusive_focus = NULL;
+ if (layersurface->layer_surface->output
+ && (layersurface->mon = layersurface->layer_surface->output->data))
+ arrangelayers(layersurface->mon);
+ if (layersurface->layer_surface->surface ==
+ seat->keyboard_state.focused_surface)
+ focusclient(focustop(selmon), 1);
+ motionnotify(0);
+}
+
+void
+unmapnotify(struct wl_listener *listener, void *data)
+{
+ /* Called when the surface is unmapped, and should no longer be shown. */
+ Client *c = wl_container_of(listener, c, unmap);
+ if (c == grabc) {
+ cursor_mode = CurNormal;
+ grabc = NULL;
+ }
+
+ if (client_is_unmanaged(c)) {
+ if (c == exclusive_focus)
+ exclusive_focus = NULL;
+ if (client_surface(c) == seat->keyboard_state.focused_surface)
+ focusclient(focustop(selmon), 1);
+ } else {
+ wl_list_remove(&c->link);
+ setmon(c, NULL, 0);
+ wl_list_remove(&c->flink);
+ }
+
+ wlr_scene_node_destroy(&c->scene->node);
+ printstatus();
+ motionnotify(0);
+}
+
+void
+updatemons(struct wl_listener *listener, void *data)
+{
+ /*
+ * Called whenever the output layout changes: adding or removing a
+ * monitor, changing an output's mode or position, etc. This is where
+ * the change officially happens and we update geometry, window
+ * positions, focus, and the stored configuration in wlroots'
+ * output-manager implementation.
+ */
+ struct wlr_output_configuration_v1 *config =
+ wlr_output_configuration_v1_create();
+ Client *c;
+ struct wlr_output_configuration_head_v1 *config_head;
+ Monitor *m;
+
+ /* First remove from the layout the disabled monitors */
+ wl_list_for_each(m, &mons, link) {
+ if (m->wlr_output->enabled)
+ continue;
+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
+ config_head->state.enabled = 0;
+ /* Remove this output from the layout to avoid cursor enter inside it */
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ closemon(m);
+ memset(&m->m, 0, sizeof(m->m));
+ memset(&m->w, 0, sizeof(m->w));
+ }
+ /* Insert outputs that need to */
+ wl_list_for_each(m, &mons, link)
+ if (m->wlr_output->enabled
+ && !wlr_output_layout_get(output_layout, m->wlr_output))
+ wlr_output_layout_add_auto(output_layout, m->wlr_output);
+ /* Now that we update the output layout we can get its box */
+ wlr_output_layout_get_box(output_layout, NULL, &sgeom);
+
+ /* Make sure the clients are hidden when dwl is locked */
+ wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y);
+ wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height);
+
+ wl_list_for_each(m, &mons, link) {
+ if (!m->wlr_output->enabled)
+ continue;
+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
+
+ /* Get the effective monitor geometry to use for surfaces */
+ wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m);
+ m->w = m->m;
+ wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y);
+
+ wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y);
+ wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height);
+
+ if (m->lock_surface) {
+ struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
+ wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width,
+ m->m.height);
+ }
+
+ /* Calculate the effective monitor geometry to use for clients */
+ arrangelayers(m);
+ /* Don't move clients to the left output when plugging monitors */
+ arrange(m);
+ /* make sure fullscreen clients have the right size */
+ if ((c = focustop(m)) && c->isfullscreen)
+ resize(c, m->m, 0);
+
+ m->gamma_lut_changed = 1;
+ config_head->state.enabled = 1;
+ config_head->state.mode = m->wlr_output->current_mode;
+ config_head->state.x = m->m.x;
+ config_head->state.y = m->m.y;
+ }
+
+ if (selmon && selmon->wlr_output->enabled) {
+ wl_list_for_each(c, &clients, link)
+ if (!c->mon && client_surface(c)->mapped)
+ setmon(c, selmon, c->tags);
+ focusclient(focustop(selmon), 1);
+ if (selmon->lock_surface) {
+ client_notify_enter(selmon->lock_surface->surface,
+ wlr_seat_get_keyboard(seat));
+ client_activate_surface(selmon->lock_surface->surface, 1);
+ }
+ }
+
+ /* FIXME: figure out why the cursor image is at 0,0 after turning all
+ * the monitors on.
+ * Move the cursor image where it used to be. It does not generate a
+ * wl_pointer.motion event for the clients, it's only the image what it's
+ * at the wrong position after all. */
+ wlr_cursor_move(cursor, NULL, 0, 0);
+
+ wlr_output_manager_v1_set_configuration(output_mgr, config);
+}
+
+void
+updatetitle(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, set_title);
+ if (c == focustop(c->mon))
+ printstatus();
+}
+
+void
+urgent(struct wl_listener *listener, void *data)
+{
+ struct wlr_xdg_activation_v1_request_activate_event *event = data;
+ Client *c = NULL;
+ toplevel_from_wlr_surface(event->surface, &c, NULL);
+ if (!c || c == focustop(selmon))
+ return;
+
+ if (client_surface(c)->mapped)
+ client_set_border_color(c, urgentcolor);
+ c->isurgent = 1;
+ printstatus();
+}
+
+void
+view(const Arg *arg)
+{
+ if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+virtualkeyboard(struct wl_listener *listener, void *data)
+{
+ struct wlr_virtual_keyboard_v1 *keyboard = data;
+ createkeyboard(&keyboard->keyboard);
+}
+
+Monitor *
+xytomon(double x, double y)
+{
+ struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
+ return o ? o->data : NULL;
+}
+
+void
+xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny)
+{
+ struct wlr_scene_node *node, *pnode;
+ struct wlr_surface *surface = NULL;
+ Client *c = NULL;
+ LayerSurface *l = NULL;
+ int layer;
+
+ for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
+ if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
+ continue;
+
+ if (node->type == WLR_SCENE_NODE_BUFFER)
+ surface = wlr_scene_surface_try_from_buffer(
+ wlr_scene_buffer_from_node(node))->surface;
+ /* Walk the tree to find a node that knows the client */
+ for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
+ c = pnode->data;
+ if (c && c->type == LayerShell) {
+ c = NULL;
+ l = pnode->data;
+ }
+ }
+
+ if (psurface) *psurface = surface;
+ if (pc) *pc = c;
+ if (pl) *pl = l;
+}
+
+void
+zoom(const Arg *arg)
+{
+ Client *c, *sel = focustop(selmon);
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating)
+ return;
+
+ /* Search for the first tiled window that is not sel, marking sel as
+ * NULL if we pass it along the way */
+ wl_list_for_each(c, &clients, link)
+ if (VISIBLEON(c, selmon) && !c->isfloating) {
+ if (c != sel)
+ break;
+ sel = NULL;
+ }
+
+ /* Return if no other tiled window was found */
+ if (&c->link == &clients)
+ return;
+
+ /* If we passed sel, move c to the front; otherwise, move sel to the
+ * front */
+ if (!sel)
+ sel = c;
+ wl_list_remove(&sel->link);
+ wl_list_insert(&clients, &sel->link);
+
+ focusclient(sel, 1);
+ arrange(selmon);
+}
+
+#ifdef XWAYLAND
+void
+activatex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, activate);
+
+ /* Only "managed" windows can be activated */
+ if (c->type == X11Managed)
+ wlr_xwayland_surface_activate(c->surface.xwayland, 1);
+}
+
+void
+associatex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, associate);
+
+ LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
+ LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify);
+}
+
+void
+configurex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, configure);
+ struct wlr_xwayland_surface_configure_event *event = data;
+ if (!c->mon)
+ return;
+ if (c->isfloating || c->type == X11Unmanaged)
+ resize(c, (struct wlr_box){.x = event->x, .y = event->y,
+ .width = event->width, .height = event->height}, 0);
+ else
+ arrange(c->mon);
+}
+
+void
+createnotifyx11(struct wl_listener *listener, void *data)
+{
+ struct wlr_xwayland_surface *xsurface = data;
+ Client *c;
+
+ /* Allocate a Client for this surface */
+ c = xsurface->data = ecalloc(1, sizeof(*c));
+ c->surface.xwayland = xsurface;
+ c->type = xsurface->override_redirect ? X11Unmanaged : X11Managed;
+ c->bw = borderpx;
+
+ /* Listen to the various events it can emit */
+ LISTEN(&xsurface->events.associate, &c->associate, associatex11);
+ LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
+ LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
+ LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
+ LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
+ LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle);
+ LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
+ LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify);
+}
+
+void
+dissociatex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, dissociate);
+ wl_list_remove(&c->map.link);
+ wl_list_remove(&c->unmap.link);
+}
+
+xcb_atom_t
+getatom(xcb_connection_t *xc, const char *name)
+{
+ xcb_atom_t atom = 0;
+ xcb_intern_atom_reply_t *reply;
+ xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc, 0, strlen(name), name);
+ if ((reply = xcb_intern_atom_reply(xc, cookie, NULL)))
+ atom = reply->atom;
+ free(reply);
+
+ return atom;
+}
+
+void
+sethints(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, set_hints);
+ struct wlr_surface *surface = client_surface(c);
+ if (c == focustop(selmon))
+ return;
+
+ c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
+
+ if (c->isurgent && surface && surface->mapped)
+ client_set_border_color(c, urgentcolor);
+
+ printstatus();
+}
+
+void
+xwaylandready(struct wl_listener *listener, void *data)
+{
+ struct wlr_xcursor *xcursor;
+ xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL);
+ int err = xcb_connection_has_error(xc);
+ if (err) {
+ fprintf(stderr, "xcb_connect to X server failed with code %d\n. Continuing with degraded functionality.\n", err);
+ return;
+ }
+
+ /* Collect atoms we are interested in. If getatom returns 0, we will
+ * not detect that window type. */
+ netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG");
+ netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH");
+ netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR");
+ netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY");
+
+ /* assign the one and only seat */
+ wlr_xwayland_set_seat(xwayland, seat);
+
+ /* Set the default XWayland cursor to match the rest of dwl. */
+ if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
+ wlr_xwayland_set_cursor(xwayland,
+ xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
+ xcursor->images[0]->width, xcursor->images[0]->height,
+ xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
+
+ xcb_disconnect(xc);
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ char *startup_cmd = NULL;
+ int c;
+
+ while ((c = getopt(argc, argv, "s:hdv")) != -1) {
+ if (c == 's')
+ startup_cmd = optarg;
+ else if (c == 'd')
+ log_level = WLR_DEBUG;
+ else if (c == 'v')
+ die("dwl " VERSION);
+ else
+ goto usage;
+ }
+ if (optind < argc)
+ goto usage;
+
+ /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */
+ if (!getenv("XDG_RUNTIME_DIR"))
+ die("XDG_RUNTIME_DIR must be set");
+ setup();
+ run(startup_cmd);
+ cleanup();
+ return EXIT_SUCCESS;
+
+usage:
+ die("Usage: %s [-v] [-d] [-s startup command]", argv[0]);
+}
+
+/* dwl_wm_monitor_v1 */
+static void
+dwl_wm_monitor_handle_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+dwl_wm_monitor_handle_destroy(struct wl_resource *resource)
+{
+ DwlWmMonitor *mon = wl_resource_get_user_data(resource);
+ if (mon) {
+ wl_list_remove(&mon->link);
+ free(mon);
+ }
+}
+
+static void
+dwl_wm_printstatus_to(Monitor *m, const DwlWmMonitor *mon)
+{
+ Client *c, *focused;
+ int tagmask, state, numclients, focused_client;
+ focused = focustop(m);
+ znet_tapesoftware_dwl_wm_monitor_v1_send_selected(mon->resource, m == selmon);
+
+ for (int tag = 0; tag<LENGTH(tags); tag++) {
+ numclients = state = 0;
+ focused_client = -1;
+ tagmask = 1 << tag;
+ if ((tagmask & m->tagset[m->seltags]) != 0)
+ state = state | ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE;
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon != m)
+ continue;
+ if (!(c->tags & tagmask))
+ continue;
+ if (c == focused)
+ focused_client = numclients;
+ numclients++;
+ if (c->isurgent)
+ state = state | ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT;
+ }
+ znet_tapesoftware_dwl_wm_monitor_v1_send_tag(mon->resource,
+ tag, state, numclients, focused_client);
+ }
+ znet_tapesoftware_dwl_wm_monitor_v1_send_layout(mon->resource, m->lt[m->sellt] - layouts);
+ znet_tapesoftware_dwl_wm_monitor_v1_send_title(mon->resource,
+ focused ? client_get_title(focused) : "");
+ znet_tapesoftware_dwl_wm_monitor_v1_send_frame(mon->resource);
+}
+
+static void
+dwl_wm_printstatus(Monitor *m)
+{
+ DwlWmMonitor *mon;
+ wl_list_for_each(mon, &m->dwl_wm_monitor_link, link) {
+ dwl_wm_printstatus_to(m, mon);
+ }
+}
+
+static void
+dwl_wm_monitor_handle_set_tags(struct wl_client *client, struct wl_resource *resource,
+ uint32_t t, uint32_t toggle_tagset)
+{
+ DwlWmMonitor *mon;
+ Monitor *m;
+ mon = wl_resource_get_user_data(resource);
+ if (!mon)
+ return;
+ m = mon->monitor;
+ if ((t & TAGMASK) == m->tagset[m->seltags])
+ return;
+ if (toggle_tagset)
+ m->seltags ^= 1;
+ if (t & TAGMASK)
+ m->tagset[m->seltags] = t & TAGMASK;
+
+ focusclient(focustop(m), 1);
+ arrange(m);
+ printstatus();
+}
+
+static void
+dwl_wm_monitor_handle_set_layout(struct wl_client *client, struct wl_resource *resource,
+ uint32_t layout)
+{
+ DwlWmMonitor *mon;
+ Monitor *m;
+ mon = wl_resource_get_user_data(resource);
+ if (!mon)
+ return;
+ m = mon->monitor;
+ if (layout >= LENGTH(layouts))
+ return;
+ if (layout != m->lt[m->sellt] - layouts)
+ m->sellt ^= 1;
+
+ m->lt[m->sellt] = &layouts[layout];
+ arrange(m);
+ printstatus();
+}
+
+static void
+dwl_wm_monitor_handle_set_client_tags(struct wl_client *client, struct wl_resource *resource,
+ uint32_t and, uint32_t xor)
+{
+ DwlWmMonitor *mon;
+ Client *sel;
+ unsigned int newtags;
+ mon = wl_resource_get_user_data(resource);
+ if (!mon)
+ return;
+ sel = focustop(mon->monitor);
+ if (!sel)
+ return;
+ newtags = (sel->tags & and) ^ xor;
+ if (newtags) {
+ sel->tags = newtags;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+ }
+}
+
+static const struct znet_tapesoftware_dwl_wm_monitor_v1_interface dwl_wm_monitor_implementation = {
+ .release = dwl_wm_monitor_handle_release,
+ .set_tags = dwl_wm_monitor_handle_set_tags,
+ .set_layout = dwl_wm_monitor_handle_set_layout,
+ .set_client_tags = dwl_wm_monitor_handle_set_client_tags,
+};
+
+/* dwl_wm_v1 */
+static void
+dwl_wm_handle_release(struct wl_client *client, struct wl_resource *resource)
+{
+ wl_resource_destroy(resource);
+}
+
+static void
+dwl_wm_handle_get_monitor(struct wl_client *client, struct wl_resource *resource,
+ uint32_t id, struct wl_resource *output)
+{
+ DwlWmMonitor *dwl_wm_monitor;
+ struct wlr_output *wlr_output = wlr_output_from_resource(output);
+ struct Monitor *m = wlr_output->data;
+ struct wl_resource *dwlOutputResource = wl_resource_create(client,
+ &znet_tapesoftware_dwl_wm_monitor_v1_interface, wl_resource_get_version(resource), id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+ dwl_wm_monitor = calloc(1, sizeof(DwlWmMonitor));
+ dwl_wm_monitor->resource = dwlOutputResource;
+ dwl_wm_monitor->monitor = m;
+ wl_resource_set_implementation(dwlOutputResource, &dwl_wm_monitor_implementation,
+ dwl_wm_monitor, dwl_wm_monitor_handle_destroy);
+ wl_list_insert(&m->dwl_wm_monitor_link, &dwl_wm_monitor->link);
+ dwl_wm_printstatus_to(m, dwl_wm_monitor);
+}
+
+static void
+dwl_wm_handle_destroy(struct wl_resource *resource)
+{
+ /* no state to destroy */
+}
+
+static const struct znet_tapesoftware_dwl_wm_v1_interface dwl_wm_implementation = {
+ .release = dwl_wm_handle_release,
+ .get_monitor = dwl_wm_handle_get_monitor,
+};
+
+static void
+dwl_wm_bind(struct wl_client *client, void *data,
+ uint32_t version, uint32_t id)
+{
+ struct wl_resource *resource = wl_resource_create(client,
+ &znet_tapesoftware_dwl_wm_v1_interface, version, id);
+ if (!resource) {
+ wl_client_post_no_memory(client);
+ return;
+ }
+
+ wl_resource_set_implementation(resource, &dwl_wm_implementation, NULL, dwl_wm_handle_destroy);
+
+ for (int i = 0; i < LENGTH(tags); i++)
+ znet_tapesoftware_dwl_wm_v1_send_tag(resource, tags[i]);
+ for (int i = 0; i < LENGTH(layouts); i++)
+ znet_tapesoftware_dwl_wm_v1_send_layout(resource, layouts[i].symbol);
+}
diff --git a/dwl-v0.5/dwl.c.orig b/dwl-v0.5/dwl.c.orig
new file mode 100644
index 0000000..6d75e3d
--- /dev/null
+++ b/dwl-v0.5/dwl.c.orig
@@ -0,0 +1,3096 @@
+/*
+ * See LICENSE file for copyright and license details.
+ */
+#include <getopt.h>
+#include <libinput.h>
+#include <linux/input-event-codes.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+#include <wayland-server-core.h>
+#include <wlr/backend.h>
+#include <wlr/backend/libinput.h>
+#include <wlr/render/allocator.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_cursor_shape_v1.h>
+#include <wlr/types/wlr_data_control_v1.h>
+#include <wlr/types/wlr_data_device.h>
+#include <wlr/types/wlr_drm.h>
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
+#include <wlr/types/wlr_export_dmabuf_v1.h>
+#include <wlr/types/wlr_fractional_scale_v1.h>
+#include <wlr/types/wlr_gamma_control_v1.h>
+#include <wlr/types/wlr_idle_inhibit_v1.h>
+#include <wlr/types/wlr_idle_notify_v1.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_output_management_v1.h>
+#include <wlr/types/wlr_pointer.h>
+#include <wlr/types/wlr_presentation_time.h>
+#include <wlr/types/wlr_primary_selection.h>
+#include <wlr/types/wlr_primary_selection_v1.h>
+#include <wlr/types/wlr_scene.h>
+#include <wlr/types/wlr_screencopy_v1.h>
+#include <wlr/types/wlr_seat.h>
+#include <wlr/types/wlr_server_decoration.h>
+#include <wlr/types/wlr_session_lock_v1.h>
+#include <wlr/types/wlr_single_pixel_buffer_v1.h>
+#include <wlr/types/wlr_subcompositor.h>
+#include <wlr/types/wlr_viewporter.h>
+#include <wlr/types/wlr_virtual_keyboard_v1.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_activation_v1.h>
+#include <wlr/types/wlr_xdg_decoration_v1.h>
+#include <wlr/types/wlr_xdg_output_v1.h>
+#include <wlr/types/wlr_xdg_shell.h>
+#include <wlr/util/log.h>
+#include <xkbcommon/xkbcommon.h>
+#ifdef XWAYLAND
+#include <wlr/xwayland.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#endif
+
+#include "util.h"
+
+/* macros */
+#define MAX(A, B) ((A) > (B) ? (A) : (B))
+#define MIN(A, B) ((A) < (B) ? (A) : (B))
+#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
+#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
+#define LENGTH(X) (sizeof X / sizeof X[0])
+#define END(A) ((A) + LENGTH(A))
+#define TAGMASK ((1u << TAGCOUNT) - 1)
+#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
+#define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0)
+
+/* enums */
+enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
+enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */
+enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrFS, LyrTop, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
+#ifdef XWAYLAND
+enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar,
+ NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */
+#endif
+
+typedef union {
+ int i;
+ uint32_t ui;
+ float f;
+ const void *v;
+} Arg;
+
+typedef struct {
+ unsigned int mod;
+ unsigned int button;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct {
+ /* Must keep these three elements in this order */
+ unsigned int type; /* XDGShell or X11* */
+ struct wlr_box geom; /* layout-relative, includes border */
+ Monitor *mon;
+ struct wlr_scene_tree *scene;
+ struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
+ struct wlr_scene_tree *scene_surface;
+ struct wl_list link;
+ struct wl_list flink;
+ union {
+ struct wlr_xdg_surface *xdg;
+ struct wlr_xwayland_surface *xwayland;
+ } surface;
+ struct wl_listener commit;
+ struct wl_listener map;
+ struct wl_listener maximize;
+ struct wl_listener unmap;
+ struct wl_listener destroy;
+ struct wl_listener set_title;
+ struct wl_listener fullscreen;
+ struct wlr_box prev; /* layout-relative, includes border */
+ struct wlr_box bounds;
+#ifdef XWAYLAND
+ struct wl_listener activate;
+ struct wl_listener associate;
+ struct wl_listener dissociate;
+ struct wl_listener configure;
+ struct wl_listener set_hints;
+#endif
+ unsigned int bw;
+ uint32_t tags;
+ int isfloating, isurgent, isfullscreen;
+ uint32_t resize; /* configure serial of a pending resize */
+} Client;
+
+typedef struct {
+ uint32_t mod;
+ xkb_keysym_t keysym;
+ void (*func)(const Arg *);
+ const Arg arg;
+} Key;
+
+typedef struct {
+ struct wl_list link;
+ struct wlr_keyboard *wlr_keyboard;
+
+ int nsyms;
+ const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
+ uint32_t mods; /* invalid if nsyms == 0 */
+ struct wl_event_source *key_repeat_source;
+
+ struct wl_listener modifiers;
+ struct wl_listener key;
+ struct wl_listener destroy;
+} Keyboard;
+
+typedef struct {
+ /* Must keep these three elements in this order */
+ unsigned int type; /* LayerShell */
+ struct wlr_box geom;
+ Monitor *mon;
+ struct wlr_scene_tree *scene;
+ struct wlr_scene_tree *popups;
+ struct wlr_scene_layer_surface_v1 *scene_layer;
+ struct wl_list link;
+ int mapped;
+ struct wlr_layer_surface_v1 *layer_surface;
+
+ struct wl_listener destroy;
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener surface_commit;
+} LayerSurface;
+
+typedef struct {
+ const char *symbol;
+ void (*arrange)(Monitor *);
+} Layout;
+
+struct Monitor {
+ struct wl_list link;
+ struct wlr_output *wlr_output;
+ struct wlr_scene_output *scene_output;
+ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
+ struct wl_listener frame;
+ struct wl_listener destroy;
+ struct wl_listener request_state;
+ struct wl_listener destroy_lock_surface;
+ struct wlr_session_lock_surface_v1 *lock_surface;
+ struct wlr_box m; /* monitor area, layout-relative */
+ struct wlr_box w; /* window area, layout-relative */
+ struct wl_list layers[4]; /* LayerSurface::link */
+ const Layout *lt[2];
+ int gappih; /* horizontal gap between windows */
+ int gappiv; /* vertical gap between windows */
+ int gappoh; /* horizontal outer gaps */
+ int gappov; /* vertical outer gaps */
+ unsigned int seltags;
+ unsigned int sellt;
+ uint32_t tagset[2];
+ double mfact;
+ int gamma_lut_changed;
+ int nmaster;
+ char ltsymbol[16];
+};
+
+typedef struct {
+ const char *name;
+ float mfact;
+ int nmaster;
+ float scale;
+ const Layout *lt;
+ enum wl_output_transform rr;
+ int x, y;
+} MonitorRule;
+
+typedef struct {
+ const char *id;
+ const char *title;
+ uint32_t tags;
+ int isfloating;
+ int monitor;
+} Rule;
+
+typedef struct {
+ struct wlr_scene_tree *scene;
+
+ struct wlr_session_lock_v1 *lock;
+ struct wl_listener new_surface;
+ struct wl_listener unlock;
+ struct wl_listener destroy;
+} SessionLock;
+
+/* function declarations */
+static void applybounds(Client *c, struct wlr_box *bbox);
+static void autostartexec(void);
+static void applyrules(Client *c);
+static void arrange(Monitor *m);
+static void arrangelayer(Monitor *m, struct wl_list *list,
+ struct wlr_box *usable_area, int exclusive);
+static void arrangelayers(Monitor *m);
+static void axisnotify(struct wl_listener *listener, void *data);
+static void buttonpress(struct wl_listener *listener, void *data);
+static void chvt(const Arg *arg);
+static void checkidleinhibitor(struct wlr_surface *exclude);
+static void cleanup(void);
+static void cleanupkeyboard(struct wl_listener *listener, void *data);
+static void cleanupmon(struct wl_listener *listener, void *data);
+static void closemon(Monitor *m);
+static void commitlayersurfacenotify(struct wl_listener *listener, void *data);
+static void commitnotify(struct wl_listener *listener, void *data);
+static void createdecoration(struct wl_listener *listener, void *data);
+static void createidleinhibitor(struct wl_listener *listener, void *data);
+static void createkeyboard(struct wlr_keyboard *keyboard);
+static void createlayersurface(struct wl_listener *listener, void *data);
+static void createlocksurface(struct wl_listener *listener, void *data);
+static void createmon(struct wl_listener *listener, void *data);
+static void createnotify(struct wl_listener *listener, void *data);
+static void createpointer(struct wlr_pointer *pointer);
+static void cursorframe(struct wl_listener *listener, void *data);
+static void defaultgaps(const Arg *arg);
+static void destroydragicon(struct wl_listener *listener, void *data);
+static void destroyidleinhibitor(struct wl_listener *listener, void *data);
+static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
+static void destroylock(SessionLock *lock, int unlocked);
+static void destroylocksurface(struct wl_listener *listener, void *data);
+static void destroynotify(struct wl_listener *listener, void *data);
+static void destroysessionlock(struct wl_listener *listener, void *data);
+static void destroysessionmgr(struct wl_listener *listener, void *data);
+static Monitor *dirtomon(enum wlr_direction dir);
+static void focusclient(Client *c, int lift);
+static void focusmon(const Arg *arg);
+static void focusstack(const Arg *arg);
+static Client *focustop(Monitor *m);
+static void fullscreennotify(struct wl_listener *listener, void *data);
+static void handlesig(int signo);
+static void incnmaster(const Arg *arg);
+static void incgaps(const Arg *arg);
+static void incigaps(const Arg *arg);
+static void incihgaps(const Arg *arg);
+static void incivgaps(const Arg *arg);
+static void incogaps(const Arg *arg);
+static void incohgaps(const Arg *arg);
+static void incovgaps(const Arg *arg);
+static void inputdevice(struct wl_listener *listener, void *data);
+static int keybinding(uint32_t mods, xkb_keysym_t sym);
+static void keypress(struct wl_listener *listener, void *data);
+static void keypressmod(struct wl_listener *listener, void *data);
+static int keyrepeat(void *data);
+static void killclient(const Arg *arg);
+static void locksession(struct wl_listener *listener, void *data);
+static void maplayersurfacenotify(struct wl_listener *listener, void *data);
+static void mapnotify(struct wl_listener *listener, void *data);
+static void maximizenotify(struct wl_listener *listener, void *data);
+static void monocle(Monitor *m);
+static void motionabsolute(struct wl_listener *listener, void *data);
+static void motionnotify(uint32_t time);
+static void motionrelative(struct wl_listener *listener, void *data);
+static void moveresize(const Arg *arg);
+static void outputmgrapply(struct wl_listener *listener, void *data);
+static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
+static void outputmgrtest(struct wl_listener *listener, void *data);
+static void pointerfocus(Client *c, struct wlr_surface *surface,
+ double sx, double sy, uint32_t time);
+static void printstatus(void);
+static void quit(const Arg *arg);
+static void rendermon(struct wl_listener *listener, void *data);
+static void restartdwl(const Arg *arg);
+static void requeststartdrag(struct wl_listener *listener, void *data);
+static void requestmonstate(struct wl_listener *listener, void *data);
+static void resize(Client *c, struct wlr_box geo, int interact);
+static void run(char *startup_cmd);
+static void setcursor(struct wl_listener *listener, void *data);
+static void setcursorshape(struct wl_listener *listener, void *data);
+static void setfloating(Client *c, int floating);
+static void setfullscreen(Client *c, int fullscreen);
+static void setgamma(struct wl_listener *listener, void *data);
+static void setgaps(int oh, int ov, int ih, int iv);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setmon(Client *c, Monitor *m, uint32_t newtags);
+static int enablegaps = 1;
+static void setpsel(struct wl_listener *listener, void *data);
+static void setsel(struct wl_listener *listener, void *data);
+static void setup(void);
+static void spawn(const Arg *arg);
+static void startdrag(struct wl_listener *listener, void *data);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *m);
+static void togglefloating(const Arg *arg);
+static void togglefullscreen(const Arg *arg);
+static void togglegaps(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void unlocksession(struct wl_listener *listener, void *data);
+static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
+static void unmapnotify(struct wl_listener *listener, void *data);
+static void updatemons(struct wl_listener *listener, void *data);
+static void updatetitle(struct wl_listener *listener, void *data);
+static void urgent(struct wl_listener *listener, void *data);
+static void view(const Arg *arg);
+static void virtualkeyboard(struct wl_listener *listener, void *data);
+static Monitor *xytomon(double x, double y);
+static void xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+static void zoom(const Arg *arg);
+
+/* variables */
+static const char broken[] = "broken";
+static pid_t child_pid = -1;
+static int locked;
+static void *exclusive_focus;
+static struct wl_display *dpy;
+static struct wlr_backend *backend;
+static struct wlr_scene *scene;
+static struct wlr_scene_tree *layers[NUM_LAYERS];
+static struct wlr_scene_tree *drag_icon;
+/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */
+static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay };
+static struct wlr_renderer *drw;
+static struct wlr_allocator *alloc;
+static struct wlr_compositor *compositor;
+static struct wlr_session *session;
+
+static struct wlr_xdg_shell *xdg_shell;
+static struct wlr_xdg_activation_v1 *activation;
+static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
+static struct wl_list clients; /* tiling order */
+static struct wl_list fstack; /* focus order */
+static struct wlr_idle_notifier_v1 *idle_notifier;
+static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr;
+static struct wlr_layer_shell_v1 *layer_shell;
+static struct wlr_output_manager_v1 *output_mgr;
+static struct wlr_gamma_control_manager_v1 *gamma_control_mgr;
+static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
+static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
+
+static struct wlr_cursor *cursor;
+static struct wlr_xcursor_manager *cursor_mgr;
+
+static struct wlr_session_lock_manager_v1 *session_lock_mgr;
+static struct wlr_scene_rect *locked_bg;
+static struct wlr_session_lock_v1 *cur_lock;
+static struct wl_listener lock_listener = {.notify = locksession};
+
+static struct wlr_seat *seat;
+static struct wl_list keyboards;
+static unsigned int cursor_mode;
+static Client *grabc;
+static int grabcx, grabcy; /* client-relative */
+
+static struct wlr_output_layout *output_layout;
+static struct wlr_box sgeom;
+static struct wl_list mons;
+static Monitor *selmon;
+
+#ifdef XWAYLAND
+static void activatex11(struct wl_listener *listener, void *data);
+static void associatex11(struct wl_listener *listener, void *data);
+static void configurex11(struct wl_listener *listener, void *data);
+static void createnotifyx11(struct wl_listener *listener, void *data);
+static void dissociatex11(struct wl_listener *listener, void *data);
+static xcb_atom_t getatom(xcb_connection_t *xc, const char *name);
+static void sethints(struct wl_listener *listener, void *data);
+static void xwaylandready(struct wl_listener *listener, void *data);
+static struct wlr_xwayland *xwayland;
+static xcb_atom_t netatom[NetLast];
+#endif
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* attempt to encapsulate suck into one file */
+#include "client.h"
+
+static pid_t *autostart_pids;
+static size_t autostart_len;
+
+/* function implementations */
+void
+applybounds(Client *c, struct wlr_box *bbox)
+{
+ /* set minimum possible */
+ c->geom.width = MAX(1, c->geom.width);
+ c->geom.height = MAX(1, c->geom.height);
+
+ if (c->geom.x >= bbox->x + bbox->width)
+ c->geom.x = bbox->x + bbox->width - c->geom.width;
+ if (c->geom.y >= bbox->y + bbox->height)
+ c->geom.y = bbox->y + bbox->height - c->geom.height;
+ if (c->geom.x + c->geom.width + 2 * c->bw <= bbox->x)
+ c->geom.x = bbox->x;
+ if (c->geom.y + c->geom.height + 2 * c->bw <= bbox->y)
+ c->geom.y = bbox->y;
+}
+
+void
+autostartexec(void) {
+ const char *const *p;
+ size_t i = 0;
+
+ /* count entries */
+ for (p = autostart; *p; autostart_len++, p++)
+ while (*++p);
+
+ autostart_pids = calloc(autostart_len, sizeof(pid_t));
+ for (p = autostart; *p; i++, p++) {
+ if ((autostart_pids[i] = fork()) == 0) {
+ setsid();
+ execvp(*p, (char *const *)p);
+ die("dwl: execvp %s:", *p);
+ }
+ /* skip arguments */
+ while (*++p);
+ }
+}
+
+void
+applyrules(Client *c)
+{
+ /* rule matching */
+ const char *appid, *title;
+ uint32_t i, newtags = 0;
+ const Rule *r;
+ Monitor *mon = selmon, *m;
+
+ c->isfloating = client_is_float_type(c);
+ if (!(appid = client_get_appid(c)))
+ appid = broken;
+ if (!(title = client_get_title(c)))
+ title = broken;
+
+ for (r = rules; r < END(rules); r++) {
+ if ((!r->title || strstr(title, r->title))
+ && (!r->id || strstr(appid, r->id))) {
+ c->isfloating = r->isfloating;
+ newtags |= r->tags;
+ i = 0;
+ wl_list_for_each(m, &mons, link)
+ if (r->monitor == i++)
+ mon = m;
+ }
+ }
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfloating ? LyrFloat : LyrTile]);
+ setmon(c, mon, newtags);
+}
+
+void
+arrange(Monitor *m)
+{
+ Client *c;
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon == m) {
+ wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m));
+ client_set_suspended(c, !VISIBLEON(c, m));
+ }
+ }
+
+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
+ (c = focustop(m)) && c->isfullscreen);
+
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
+
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+ motionnotify(0);
+ checkidleinhibitor(NULL);
+}
+
+void
+arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive)
+{
+ LayerSurface *layersurface;
+ struct wlr_box full_area = m->m;
+
+ wl_list_for_each(layersurface, list, link) {
+ struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
+ struct wlr_layer_surface_v1_state *state = &wlr_layer_surface->current;
+
+ if (exclusive != (state->exclusive_zone > 0))
+ continue;
+
+ wlr_scene_layer_surface_v1_configure(layersurface->scene_layer, &full_area, usable_area);
+ wlr_scene_node_set_position(&layersurface->popups->node,
+ layersurface->scene->node.x, layersurface->scene->node.y);
+ layersurface->geom.x = layersurface->scene->node.x;
+ layersurface->geom.y = layersurface->scene->node.y;
+ }
+}
+
+void
+arrangelayers(Monitor *m)
+{
+ int i;
+ struct wlr_box usable_area = m->m;
+ uint32_t layers_above_shell[] = {
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+ LayerSurface *layersurface;
+ if (!m->wlr_output->enabled)
+ return;
+
+ /* Arrange exclusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+ arrangelayer(m, &m->layers[i], &usable_area, 1);
+
+ if (memcmp(&usable_area, &m->w, sizeof(struct wlr_box))) {
+ m->w = usable_area;
+ arrange(m);
+ }
+
+ /* Arrange non-exlusive surfaces from top->bottom */
+ for (i = 3; i >= 0; i--)
+ arrangelayer(m, &m->layers[i], &usable_area, 0);
+
+ /* Find topmost keyboard interactive layer, if such a layer exists */
+ for (i = 0; i < LENGTH(layers_above_shell); i++) {
+ wl_list_for_each_reverse(layersurface,
+ &m->layers[layers_above_shell[i]], link) {
+ if (!locked && layersurface->layer_surface->current.keyboard_interactive
+ && layersurface->mapped) {
+ /* Deactivate the focused client. */
+ focusclient(NULL, 0);
+ exclusive_focus = layersurface;
+ client_notify_enter(layersurface->layer_surface->surface, wlr_seat_get_keyboard(seat));
+ return;
+ }
+ }
+ }
+}
+
+void
+axisnotify(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits an axis event,
+ * for example when you move the scroll wheel. */
+ struct wlr_pointer_axis_event *event = data;
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ /* TODO: allow usage of scroll whell for mousebindings, it can be implemented
+ * checking the event's orientation and the delta of the event */
+ /* Notify the client with pointer focus of the axis event. */
+ wlr_seat_pointer_notify_axis(seat,
+ event->time_msec, event->orientation, event->delta,
+ event->delta_discrete, event->source);
+}
+
+void
+buttonpress(struct wl_listener *listener, void *data)
+{
+ struct wlr_pointer_button_event *event = data;
+ struct wlr_keyboard *keyboard;
+ uint32_t mods;
+ Client *c;
+ const Button *b;
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ switch (event->state) {
+ case WLR_BUTTON_PRESSED:
+ cursor_mode = CurPressed;
+ if (locked)
+ break;
+
+ /* Change focus if the button was _pressed_ over a client */
+ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
+ if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
+ focusclient(c, 1);
+
+ keyboard = wlr_seat_get_keyboard(seat);
+ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
+ for (b = buttons; b < END(buttons); b++) {
+ if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
+ event->button == b->button && b->func) {
+ b->func(&b->arg);
+ return;
+ }
+ }
+ break;
+ case WLR_BUTTON_RELEASED:
+ /* If you released any buttons, we exit interactive move/resize mode. */
+ /* TODO should reset to the pointer focus's current setcursor */
+ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+ cursor_mode = CurNormal;
+ /* Drop the window off on its new monitor */
+ selmon = xytomon(cursor->x, cursor->y);
+ setmon(grabc, selmon, 0);
+ return;
+ } else {
+ cursor_mode = CurNormal;
+ }
+ break;
+ }
+ /* If the event wasn't handled by the compositor, notify the client with
+ * pointer focus that a button press has occurred */
+ wlr_seat_pointer_notify_button(seat,
+ event->time_msec, event->button, event->state);
+}
+
+void
+chvt(const Arg *arg)
+{
+ wlr_session_change_vt(session, arg->ui);
+}
+
+void
+checkidleinhibitor(struct wlr_surface *exclude)
+{
+ int inhibited = 0, unused_lx, unused_ly;
+ struct wlr_idle_inhibitor_v1 *inhibitor;
+ wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) {
+ struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface);
+ struct wlr_scene_tree *tree = surface->data;
+ if (exclude != surface && (bypass_surface_visibility || (!tree
+ || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) {
+ inhibited = 1;
+ break;
+ }
+ }
+
+ wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
+}
+
+void
+cleanup(void)
+{
+ size_t i;
+#ifdef XWAYLAND
+ wlr_xwayland_destroy(xwayland);
+ xwayland = NULL;
+#endif
+ wl_display_destroy_clients(dpy);
+
+ /* kill child processes */
+ for (i = 0; i < autostart_len; i++) {
+ if (0 < autostart_pids[i]) {
+ kill(autostart_pids[i], SIGTERM);
+ waitpid(autostart_pids[i], NULL, 0);
+ }
+ }
+
+ if (child_pid > 0) {
+ kill(child_pid, SIGTERM);
+ waitpid(child_pid, NULL, 0);
+ }
+ wlr_xcursor_manager_destroy(cursor_mgr);
+ wlr_output_layout_destroy(output_layout);
+ wl_display_destroy(dpy);
+ /* Destroy after the wayland display (when the monitors are already destroyed)
+ to avoid destroying them with an invalid scene output. */
+ wlr_scene_node_destroy(&scene->tree.node);
+}
+
+void
+cleanupkeyboard(struct wl_listener *listener, void *data)
+{
+ Keyboard *kb = wl_container_of(listener, kb, destroy);
+
+ wl_event_source_remove(kb->key_repeat_source);
+ wl_list_remove(&kb->link);
+ wl_list_remove(&kb->modifiers.link);
+ wl_list_remove(&kb->key.link);
+ wl_list_remove(&kb->destroy.link);
+ free(kb);
+}
+
+void
+cleanupmon(struct wl_listener *listener, void *data)
+{
+ Monitor *m = wl_container_of(listener, m, destroy);
+ LayerSurface *l, *tmp;
+ int i;
+
+ for (i = 0; i <= ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY; i++)
+ wl_list_for_each_safe(l, tmp, &m->layers[i], link)
+ wlr_layer_surface_v1_destroy(l->layer_surface);
+
+ wl_list_remove(&m->destroy.link);
+ wl_list_remove(&m->frame.link);
+ wl_list_remove(&m->link);
+ m->wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ wlr_scene_output_destroy(m->scene_output);
+ wlr_scene_node_destroy(&m->fullscreen_bg->node);
+
+ closemon(m);
+ free(m);
+}
+
+void
+closemon(Monitor *m)
+{
+ /* update selmon if needed and
+ * move closed monitor's clients to the focused one */
+ Client *c;
+ if (wl_list_empty(&mons)) {
+ selmon = NULL;
+ } else if (m == selmon) {
+ int nmons = wl_list_length(&mons), i = 0;
+ do /* don't switch to disabled mons */
+ selmon = wl_container_of(mons.next, selmon, link);
+ while (!selmon->wlr_output->enabled && i++ < nmons);
+ }
+
+ wl_list_for_each(c, &clients, link) {
+ if (c->isfloating && c->geom.x > m->m.width)
+ resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
+ .width = c->geom.width, .height = c->geom.height}, 0);
+ if (c->mon == m)
+ setmon(c, selmon, c->tags);
+ }
+ focusclient(focustop(selmon), 1);
+ printstatus();
+}
+
+void
+commitlayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, surface_commit);
+ struct wlr_layer_surface_v1 *wlr_layer_surface = layersurface->layer_surface;
+ struct wlr_output *wlr_output = wlr_layer_surface->output;
+ struct wlr_scene_tree *layer = layers[layermap[wlr_layer_surface->current.layer]];
+
+ /* For some reason this layersurface have no monitor, this can be because
+ * its monitor has just been destroyed */
+ if (!wlr_output || !(layersurface->mon = wlr_output->data))
+ return;
+
+ if (layer != layersurface->scene->node.parent) {
+ wlr_scene_node_reparent(&layersurface->scene->node, layer);
+ wlr_scene_node_reparent(&layersurface->popups->node, layer);
+ wl_list_remove(&layersurface->link);
+ wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->current.layer],
+ &layersurface->link);
+ }
+ if (wlr_layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP)
+ wlr_scene_node_reparent(&layersurface->popups->node, layers[LyrTop]);
+
+ if (wlr_layer_surface->current.committed == 0
+ && layersurface->mapped == wlr_layer_surface->surface->mapped)
+ return;
+ layersurface->mapped = wlr_layer_surface->surface->mapped;
+
+ arrangelayers(layersurface->mon);
+}
+
+void
+commitnotify(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, commit);
+
+ if (client_surface(c)->mapped)
+ resize(c, c->geom, (c->isfloating && !c->isfullscreen));
+
+ /* mark a pending resize as completed */
+ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
+ c->resize = 0;
+}
+
+void
+createdecoration(struct wl_listener *listener, void *data)
+{
+ struct wlr_xdg_toplevel_decoration_v1 *dec = data;
+ wlr_xdg_toplevel_decoration_v1_set_mode(dec, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+}
+
+void
+createidleinhibitor(struct wl_listener *listener, void *data)
+{
+ struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
+ LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor);
+
+ checkidleinhibitor(NULL);
+}
+
+void
+createkeyboard(struct wlr_keyboard *keyboard)
+{
+ struct xkb_context *context;
+ struct xkb_keymap *keymap;
+ Keyboard *kb = keyboard->data = ecalloc(1, sizeof(*kb));
+ kb->wlr_keyboard = keyboard;
+
+ /* Prepare an XKB keymap and assign it to the keyboard. */
+ context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ keymap = xkb_keymap_new_from_names(context, &xkb_rules,
+ XKB_KEYMAP_COMPILE_NO_FLAGS);
+ if (!keymap)
+ die("createkeyboard: failed to compile keymap");
+
+ wlr_keyboard_set_keymap(keyboard, keymap);
+ xkb_keymap_unref(keymap);
+ xkb_context_unref(context);
+ wlr_keyboard_set_repeat_info(keyboard, repeat_rate, repeat_delay);
+
+ /* Here we set up listeners for keyboard events. */
+ LISTEN(&keyboard->events.modifiers, &kb->modifiers, keypressmod);
+ LISTEN(&keyboard->events.key, &kb->key, keypress);
+ LISTEN(&keyboard->base.events.destroy, &kb->destroy, cleanupkeyboard);
+
+ wlr_seat_set_keyboard(seat, keyboard);
+
+ kb->key_repeat_source = wl_event_loop_add_timer(
+ wl_display_get_event_loop(dpy), keyrepeat, kb);
+
+ /* And add the keyboard to our list of keyboards */
+ wl_list_insert(&keyboards, &kb->link);
+}
+
+void
+createlayersurface(struct wl_listener *listener, void *data)
+{
+ struct wlr_layer_surface_v1 *wlr_layer_surface = data;
+ LayerSurface *layersurface;
+ struct wlr_layer_surface_v1_state old_state;
+ struct wlr_scene_tree *l = layers[layermap[wlr_layer_surface->pending.layer]];
+
+ if (!wlr_layer_surface->output)
+ wlr_layer_surface->output = selmon ? selmon->wlr_output : NULL;
+
+ if (!wlr_layer_surface->output) {
+ wlr_layer_surface_v1_destroy(wlr_layer_surface);
+ return;
+ }
+
+ layersurface = wlr_layer_surface->data = ecalloc(1, sizeof(LayerSurface));
+ layersurface->type = LayerShell;
+ LISTEN(&wlr_layer_surface->surface->events.commit,
+ &layersurface->surface_commit, commitlayersurfacenotify);
+ LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy,
+ destroylayersurfacenotify);
+ LISTEN(&wlr_layer_surface->surface->events.map, &layersurface->map,
+ maplayersurfacenotify);
+ LISTEN(&wlr_layer_surface->surface->events.unmap, &layersurface->unmap,
+ unmaplayersurfacenotify);
+
+ layersurface->layer_surface = wlr_layer_surface;
+ layersurface->mon = wlr_layer_surface->output->data;
+ layersurface->scene_layer = wlr_scene_layer_surface_v1_create(l, wlr_layer_surface);
+ layersurface->scene = layersurface->scene_layer->tree;
+ layersurface->popups = wlr_layer_surface->surface->data = wlr_scene_tree_create(l);
+
+ layersurface->scene->node.data = layersurface;
+
+ wl_list_insert(&layersurface->mon->layers[wlr_layer_surface->pending.layer],
+ &layersurface->link);
+
+ /* Temporarily set the layer's current state to pending
+ * so that we can easily arrange it
+ */
+ old_state = wlr_layer_surface->current;
+ wlr_layer_surface->current = wlr_layer_surface->pending;
+ layersurface->mapped = 1;
+ arrangelayers(layersurface->mon);
+ wlr_layer_surface->current = old_state;
+}
+
+void
+createlocksurface(struct wl_listener *listener, void *data)
+{
+ SessionLock *lock = wl_container_of(listener, lock, new_surface);
+ struct wlr_session_lock_surface_v1 *lock_surface = data;
+ Monitor *m = lock_surface->output->data;
+ struct wlr_scene_tree *scene_tree = lock_surface->surface->data =
+ wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface);
+ m->lock_surface = lock_surface;
+
+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
+ wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height);
+
+ LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface);
+
+ if (m == selmon)
+ client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
+}
+
+void
+createmon(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the backend when a new output (aka a display or
+ * monitor) becomes available. */
+ struct wlr_output *wlr_output = data;
+ const MonitorRule *r;
+ size_t i;
+ Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m));
+ m->wlr_output = wlr_output;
+
+ wlr_output_init_render(wlr_output, alloc, drw);
+
+ /* Initialize monitor state using configured rules */
+ for (i = 0; i < LENGTH(m->layers); i++)
+ wl_list_init(&m->layers[i]);
+
+ m->gappih = gappih;
+ m->gappiv = gappiv;
+ m->gappoh = gappoh;
+ m->gappov = gappov;
+ m->tagset[0] = m->tagset[1] = 1;
+ for (r = monrules; r < END(monrules); r++) {
+ if (!r->name || strstr(wlr_output->name, r->name)) {
+ m->mfact = r->mfact;
+ m->nmaster = r->nmaster;
+ wlr_output_set_scale(wlr_output, r->scale);
+ m->lt[0] = m->lt[1] = r->lt;
+ wlr_output_set_transform(wlr_output, r->rr);
+ m->m.x = r->x;
+ m->m.y = r->y;
+ break;
+ }
+ }
+
+ /* The mode is a tuple of (width, height, refresh rate), and each
+ * monitor supports only a specific set of modes. We just pick the
+ * monitor's preferred mode; a more sophisticated compositor would let
+ * the user configure it. */
+ wlr_output_set_mode(wlr_output, wlr_output_preferred_mode(wlr_output));
+
+ /* Set up event listeners */
+ LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
+ LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon);
+ LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate);
+
+ wlr_output_enable(wlr_output, 1);
+ if (!wlr_output_commit(wlr_output))
+ return;
+
+ wl_list_insert(&mons, &m->link);
+ printstatus();
+
+ /* The xdg-protocol specifies:
+ *
+ * If the fullscreened surface is not opaque, the compositor must make
+ * sure that other screen content not part of the same surface tree (made
+ * up of subsurfaces, popups or similarly coupled surfaces) are not
+ * visible below the fullscreened surface.
+ *
+ */
+ /* updatemons() will resize and set correct position */
+ m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg);
+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0);
+
+ /* Adds this to the output layout in the order it was configured in.
+ *
+ * The output layout utility automatically adds a wl_output global to the
+ * display, which Wayland clients can see to find out information about the
+ * output (such as DPI, scale factor, manufacturer, etc).
+ */
+ m->scene_output = wlr_scene_output_create(scene, wlr_output);
+ if (m->m.x < 0 || m->m.y < 0)
+ wlr_output_layout_add_auto(output_layout, wlr_output);
+ else
+ wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
+}
+
+void
+createnotify(struct wl_listener *listener, void *data)
+{
+ /* This event is raised when wlr_xdg_shell receives a new xdg surface from a
+ * client, either a toplevel (application window) or popup,
+ * or when wlr_layer_shell receives a new popup from a layer.
+ * If you want to do something tricky with popups you should check if
+ * its parent is wlr_xdg_shell or wlr_layer_shell */
+ struct wlr_xdg_surface *xdg_surface = data;
+ Client *c = NULL;
+ LayerSurface *l = NULL;
+
+ if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) {
+ struct wlr_box box;
+ int type = toplevel_from_wlr_surface(xdg_surface->surface, &c, &l);
+ if (!xdg_surface->popup->parent || type < 0)
+ return;
+ xdg_surface->surface->data = wlr_scene_xdg_surface_create(
+ xdg_surface->popup->parent->data, xdg_surface);
+ if ((l && !l->mon) || (c && !c->mon))
+ return;
+ box = type == LayerShell ? l->mon->m : c->mon->w;
+ box.x -= (type == LayerShell ? l->geom.x : c->geom.x);
+ box.y -= (type == LayerShell ? l->geom.y : c->geom.y);
+ wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box);
+ return;
+ } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE)
+ return;
+
+ /* Allocate a Client for this surface */
+ c = xdg_surface->data = ecalloc(1, sizeof(*c));
+ c->surface.xdg = xdg_surface;
+ c->bw = borderpx;
+
+ wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel,
+ WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
+
+ LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify);
+ LISTEN(&xdg_surface->surface->events.map, &c->map, mapnotify);
+ LISTEN(&xdg_surface->surface->events.unmap, &c->unmap, unmapnotify);
+ LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify);
+ LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle);
+ LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen,
+ fullscreennotify);
+ LISTEN(&xdg_surface->toplevel->events.request_maximize, &c->maximize,
+ maximizenotify);
+}
+
+void
+createpointer(struct wlr_pointer *pointer)
+{
+ if (wlr_input_device_is_libinput(&pointer->base)) {
+ struct libinput_device *libinput_device = (struct libinput_device*)
+ wlr_libinput_get_device_handle(&pointer->base);
+
+ if (libinput_device_config_tap_get_finger_count(libinput_device)) {
+ libinput_device_config_tap_set_enabled(libinput_device, tap_to_click);
+ libinput_device_config_tap_set_drag_enabled(libinput_device, tap_and_drag);
+ libinput_device_config_tap_set_drag_lock_enabled(libinput_device, drag_lock);
+ libinput_device_config_tap_set_button_map(libinput_device, button_map);
+ }
+
+ if (libinput_device_config_scroll_has_natural_scroll(libinput_device))
+ libinput_device_config_scroll_set_natural_scroll_enabled(libinput_device, natural_scrolling);
+
+ if (libinput_device_config_dwt_is_available(libinput_device))
+ libinput_device_config_dwt_set_enabled(libinput_device, disable_while_typing);
+
+ if (libinput_device_config_left_handed_is_available(libinput_device))
+ libinput_device_config_left_handed_set(libinput_device, left_handed);
+
+ if (libinput_device_config_middle_emulation_is_available(libinput_device))
+ libinput_device_config_middle_emulation_set_enabled(libinput_device, middle_button_emulation);
+
+ if (libinput_device_config_scroll_get_methods(libinput_device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
+ libinput_device_config_scroll_set_method (libinput_device, scroll_method);
+
+ if (libinput_device_config_click_get_methods(libinput_device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
+ libinput_device_config_click_set_method (libinput_device, click_method);
+
+ if (libinput_device_config_send_events_get_modes(libinput_device))
+ libinput_device_config_send_events_set_mode(libinput_device, send_events_mode);
+
+ if (libinput_device_config_accel_is_available(libinput_device)) {
+ libinput_device_config_accel_set_profile(libinput_device, accel_profile);
+ libinput_device_config_accel_set_speed(libinput_device, accel_speed);
+ }
+ }
+
+ wlr_cursor_attach_input_device(cursor, &pointer->base);
+}
+
+void
+cursorframe(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits an frame
+ * event. Frame events are sent after regular pointer events to group
+ * multiple events together. For instance, two axis events may happen at the
+ * same time, in which case a frame event won't be sent in between. */
+ /* Notify the client with pointer focus of the frame event. */
+ wlr_seat_pointer_notify_frame(seat);
+}
+
+void
+defaultgaps(const Arg *arg)
+{
+ setgaps(gappoh, gappov, gappih, gappiv);
+}
+
+void
+destroydragicon(struct wl_listener *listener, void *data)
+{
+ /* Focus enter isn't sent during drag, so refocus the focused node. */
+ focusclient(focustop(selmon), 1);
+ motionnotify(0);
+}
+
+void
+destroyidleinhibitor(struct wl_listener *listener, void *data)
+{
+ /* `data` is the wlr_surface of the idle inhibitor being destroyed,
+ * at this point the idle inhibitor is still in the list of the manager */
+ checkidleinhibitor(wlr_surface_get_root_surface(data));
+}
+
+void
+destroylayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, destroy);
+
+ wl_list_remove(&layersurface->link);
+ wl_list_remove(&layersurface->destroy.link);
+ wl_list_remove(&layersurface->map.link);
+ wl_list_remove(&layersurface->unmap.link);
+ wl_list_remove(&layersurface->surface_commit.link);
+ wlr_scene_node_destroy(&layersurface->scene->node);
+ free(layersurface);
+}
+
+void
+destroylock(SessionLock *lock, int unlock)
+{
+ wlr_seat_keyboard_notify_clear_focus(seat);
+ if ((locked = !unlock))
+ goto destroy;
+
+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
+
+ focusclient(focustop(selmon), 0);
+ motionnotify(0);
+
+destroy:
+ wl_list_remove(&lock->new_surface.link);
+ wl_list_remove(&lock->unlock.link);
+ wl_list_remove(&lock->destroy.link);
+
+ wlr_scene_node_destroy(&lock->scene->node);
+ cur_lock = NULL;
+ free(lock);
+}
+
+void
+destroylocksurface(struct wl_listener *listener, void *data)
+{
+ Monitor *m = wl_container_of(listener, m, destroy_lock_surface);
+ struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
+
+ m->lock_surface = NULL;
+ wl_list_remove(&m->destroy_lock_surface.link);
+
+ if (lock_surface->surface != seat->keyboard_state.focused_surface)
+ return;
+
+ if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {
+ surface = wl_container_of(cur_lock->surfaces.next, surface, link);
+ client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
+ } else if (!locked) {
+ focusclient(focustop(selmon), 1);
+ } else {
+ wlr_seat_keyboard_clear_focus(seat);
+ }
+}
+
+void
+destroynotify(struct wl_listener *listener, void *data)
+{
+ /* Called when the xdg_toplevel is destroyed. */
+ Client *c = wl_container_of(listener, c, destroy);
+ wl_list_remove(&c->destroy.link);
+ wl_list_remove(&c->set_title.link);
+ wl_list_remove(&c->fullscreen.link);
+#ifdef XWAYLAND
+ if (c->type != XDGShell) {
+ wl_list_remove(&c->activate.link);
+ wl_list_remove(&c->associate.link);
+ wl_list_remove(&c->configure.link);
+ wl_list_remove(&c->dissociate.link);
+ wl_list_remove(&c->set_hints.link);
+ } else
+#endif
+ {
+ wl_list_remove(&c->commit.link);
+ wl_list_remove(&c->map.link);
+ wl_list_remove(&c->unmap.link);
+ }
+ free(c);
+}
+
+void
+destroysessionlock(struct wl_listener *listener, void *data)
+{
+ SessionLock *lock = wl_container_of(listener, lock, destroy);
+ destroylock(lock, 0);
+}
+
+void
+destroysessionmgr(struct wl_listener *listener, void *data)
+{
+ wl_list_remove(&lock_listener.link);
+ wl_list_remove(&listener->link);
+}
+
+Monitor *
+dirtomon(enum wlr_direction dir)
+{
+ struct wlr_output *next;
+ if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
+ return selmon;
+ if ((next = wlr_output_layout_adjacent_output(output_layout,
+ dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
+ return next->data;
+ if ((next = wlr_output_layout_farthest_output(output_layout,
+ dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT),
+ selmon->wlr_output, selmon->m.x, selmon->m.y)))
+ return next->data;
+ return selmon;
+}
+
+void
+focusclient(Client *c, int lift)
+{
+ struct wlr_surface *old = seat->keyboard_state.focused_surface;
+ int unused_lx, unused_ly, old_client_type;
+ Client *old_c = NULL;
+ LayerSurface *old_l = NULL;
+
+ if (locked)
+ return;
+
+ /* Raise client in stacking order if requested */
+ if (c && lift)
+ wlr_scene_node_raise_to_top(&c->scene->node);
+
+ if (c && client_surface(c) == old)
+ return;
+
+ if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) {
+ struct wlr_xdg_popup *popup, *tmp;
+ wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link)
+ wlr_xdg_popup_destroy(popup);
+ }
+
+ /* Put the new client atop the focus stack and select its monitor */
+ if (c && !client_is_unmanaged(c)) {
+ wl_list_remove(&c->flink);
+ wl_list_insert(&fstack, &c->flink);
+ selmon = c->mon;
+ c->isurgent = 0;
+ client_restack_surface(c);
+
+ /* Don't change border color if there is an exclusive focus or we are
+ * handling a drag operation */
+ if (!exclusive_focus && !seat->drag)
+ client_set_border_color(c, focuscolor);
+ }
+
+ /* Deactivate old client if focus is changing */
+ if (old && (!c || client_surface(c) != old)) {
+ /* If an overlay is focused, don't focus or activate the client,
+ * but only update its position in fstack to render its border with focuscolor
+ * and focus it after the overlay is closed. */
+ if (old_client_type == LayerShell && wlr_scene_node_coords(
+ &old_l->scene->node, &unused_lx, &unused_ly)
+ && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
+ return;
+ } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
+ return;
+ /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
+ * and probably other clients */
+ } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
+ client_set_border_color(old_c, bordercolor);
+
+ client_activate_surface(old, 0);
+ }
+ }
+ printstatus();
+
+ if (!c) {
+ /* With no client, all we have left is to clear focus */
+ wlr_seat_keyboard_notify_clear_focus(seat);
+ return;
+ }
+
+ /* Change cursor surface */
+ motionnotify(0);
+
+ /* Have a client, so focus its top-level wlr_surface */
+ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
+
+ /* Activate the new client */
+ client_activate_surface(client_surface(c), 1);
+}
+
+void
+focusmon(const Arg *arg)
+{
+ int i = 0, nmons = wl_list_length(&mons);
+ if (nmons)
+ do /* don't switch to disabled mons */
+ selmon = dirtomon(arg->i);
+ while (!selmon->wlr_output->enabled && i++ < nmons);
+ focusclient(focustop(selmon), 1);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ /* Focus the next or previous client (in tiling order) on selmon */
+ Client *c, *sel = focustop(selmon);
+ if (!sel || sel->isfullscreen)
+ return;
+ if (arg->i > 0) {
+ wl_list_for_each(c, &sel->link, link) {
+ if (&c->link == &clients)
+ continue; /* wrap past the sentinel node */
+ if (VISIBLEON(c, selmon))
+ break; /* found it */
+ }
+ } else {
+ wl_list_for_each_reverse(c, &sel->link, link) {
+ if (&c->link == &clients)
+ continue; /* wrap past the sentinel node */
+ if (VISIBLEON(c, selmon))
+ break; /* found it */
+ }
+ }
+ /* If only one client is visible on selmon, then c == sel */
+ focusclient(c, 1);
+}
+
+/* We probably should change the name of this, it sounds like
+ * will focus the topmost client of this mon, when actually will
+ * only return that client */
+Client *
+focustop(Monitor *m)
+{
+ Client *c;
+ wl_list_for_each(c, &fstack, flink)
+ if (VISIBLEON(c, m))
+ return c;
+ return NULL;
+}
+
+void
+fullscreennotify(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, fullscreen);
+ setfullscreen(c, client_wants_fullscreen(c));
+}
+
+void
+handlesig(int signo)
+{
+ if (signo == SIGCHLD) {
+ siginfo_t in;
+ /* wlroots expects to reap the XWayland process itself, so we
+ * use WNOWAIT to keep the child waitable until we know it's not
+ * XWayland.
+ */
+ while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid
+ #ifdef XWAYLAND
+ && (!xwayland || in.si_pid != xwayland->server->pid)
+#endif
+ ) {
+ pid_t *p, *lim;
+ waitpid(in.si_pid, NULL, 0);
+ if (in.si_pid == child_pid)
+ child_pid = -1;
+ if (!(p = autostart_pids))
+ continue;
+ lim = &p[autostart_len];
+
+ for (; p < lim; p++) {
+ if (*p == in.si_pid) {
+ *p = -1;
+ break;
+ }
+ }
+ }
+ } else if (signo == SIGINT || signo == SIGTERM) {
+ quit(NULL);
+ }
+}
+
+void
+incnmaster(const Arg *arg)
+{
+ if (!arg || !selmon)
+ return;
+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
+ arrange(selmon);
+}
+
+void
+incgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incigaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incihgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih + arg->i,
+ selmon->gappiv
+ );
+}
+
+void
+incivgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv + arg->i
+ );
+}
+
+void
+incogaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incohgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh + arg->i,
+ selmon->gappov,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+incovgaps(const Arg *arg)
+{
+ setgaps(
+ selmon->gappoh,
+ selmon->gappov + arg->i,
+ selmon->gappih,
+ selmon->gappiv
+ );
+}
+
+void
+inputdevice(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the backend when a new input device becomes
+ * available. */
+ struct wlr_input_device *device = data;
+ uint32_t caps;
+
+ switch (device->type) {
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ createkeyboard(wlr_keyboard_from_input_device(device));
+ break;
+ case WLR_INPUT_DEVICE_POINTER:
+ createpointer(wlr_pointer_from_input_device(device));
+ break;
+ default:
+ /* TODO handle other input device types */
+ break;
+ }
+
+ /* We need to let the wlr_seat know what our capabilities are, which is
+ * communiciated to the client. In dwl we always have a cursor, even if
+ * there are no pointer devices, so we always include that capability. */
+ /* TODO do we actually require a cursor? */
+ caps = WL_SEAT_CAPABILITY_POINTER;
+ if (!wl_list_empty(&keyboards))
+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
+ wlr_seat_set_capabilities(seat, caps);
+}
+
+int
+keybinding(uint32_t mods, xkb_keysym_t sym)
+{
+ /*
+ * Here we handle compositor keybindings. This is when the compositor is
+ * processing keys, rather than passing them on to the client for its own
+ * processing.
+ */
+ int handled = 0;
+ const Key *k;
+ for (k = keys; k < END(keys); k++) {
+ if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
+ sym == k->keysym && k->func) {
+ k->func(&k->arg);
+ handled = 1;
+ }
+ }
+ return handled;
+}
+
+void
+keypress(struct wl_listener *listener, void *data)
+{
+ int i;
+ /* This event is raised when a key is pressed or released. */
+ Keyboard *kb = wl_container_of(listener, kb, key);
+ struct wlr_keyboard_key_event *event = data;
+
+ /* Translate libinput keycode -> xkbcommon */
+ uint32_t keycode = event->keycode + 8;
+ /* Get a list of keysyms based on the keymap for this keyboard */
+ const xkb_keysym_t *syms;
+ int nsyms = xkb_state_key_get_syms(
+ kb->wlr_keyboard->xkb_state, keycode, &syms);
+
+ int handled = 0;
+ uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard);
+
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ /* On _press_ if there is no active screen locker,
+ * attempt to process a compositor keybinding. */
+ if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
+ for (i = 0; i < nsyms; i++)
+ handled = keybinding(mods, syms[i]) || handled;
+
+ if (handled && kb->wlr_keyboard->repeat_info.delay > 0) {
+ kb->mods = mods;
+ kb->keysyms = syms;
+ kb->nsyms = nsyms;
+ wl_event_source_timer_update(kb->key_repeat_source,
+ kb->wlr_keyboard->repeat_info.delay);
+ } else {
+ kb->nsyms = 0;
+ wl_event_source_timer_update(kb->key_repeat_source, 0);
+ }
+
+ if (handled)
+ return;
+
+ /* Pass unhandled keycodes along to the client. */
+ wlr_seat_set_keyboard(seat, kb->wlr_keyboard);
+ wlr_seat_keyboard_notify_key(seat, event->time_msec,
+ event->keycode, event->state);
+}
+
+void
+keypressmod(struct wl_listener *listener, void *data)
+{
+ /* This event is raised when a modifier key, such as shift or alt, is
+ * pressed. We simply communicate this to the client. */
+ Keyboard *kb = wl_container_of(listener, kb, modifiers);
+ /*
+ * A seat can only have one keyboard, but this is a limitation of the
+ * Wayland protocol - not wlroots. We assign all connected keyboards to the
+ * same seat. You can swap out the underlying wlr_keyboard like this and
+ * wlr_seat handles this transparently.
+ */
+ wlr_seat_set_keyboard(seat, kb->wlr_keyboard);
+ /* Send modifiers to the client. */
+ wlr_seat_keyboard_notify_modifiers(seat,
+ &kb->wlr_keyboard->modifiers);
+}
+
+int
+keyrepeat(void *data)
+{
+ Keyboard *kb = data;
+ int i;
+ if (!kb->nsyms || kb->wlr_keyboard->repeat_info.rate <= 0)
+ return 0;
+
+ wl_event_source_timer_update(kb->key_repeat_source,
+ 1000 / kb->wlr_keyboard->repeat_info.rate);
+
+ for (i = 0; i < kb->nsyms; i++)
+ keybinding(kb->mods, kb->keysyms[i]);
+
+ return 0;
+}
+
+void
+killclient(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ client_send_close(sel);
+}
+
+void
+locksession(struct wl_listener *listener, void *data)
+{
+ struct wlr_session_lock_v1 *session_lock = data;
+ SessionLock *lock;
+ wlr_scene_node_set_enabled(&locked_bg->node, 1);
+ if (cur_lock) {
+ wlr_session_lock_v1_destroy(session_lock);
+ return;
+ }
+ lock = session_lock->data = ecalloc(1, sizeof(*lock));
+ focusclient(NULL, 0);
+
+ lock->scene = wlr_scene_tree_create(layers[LyrBlock]);
+ cur_lock = lock->lock = session_lock;
+ locked = 1;
+
+ LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface);
+ LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock);
+ LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession);
+
+ wlr_session_lock_v1_send_locked(session_lock);
+}
+
+void
+maplayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *l = wl_container_of(listener, l, map);
+ motionnotify(0);
+}
+
+void
+mapnotify(struct wl_listener *listener, void *data)
+{
+ /* Called when the surface is mapped, or ready to display on-screen. */
+ Client *p, *w, *c = wl_container_of(listener, c, map);
+ Monitor *m;
+ int i;
+
+ /* Create scene tree for this client and its border */
+ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
+ wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell);
+ c->scene_surface = c->type == XDGShell
+ ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
+ : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
+ c->scene->node.data = c->scene_surface->node.data = c;
+
+ /* Handle unmanaged clients first so we can return prior create borders */
+ if (client_is_unmanaged(c)) {
+ client_get_geometry(c, &c->geom);
+ /* Unmanaged clients always are floating */
+ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
+ wlr_scene_node_set_position(&c->scene->node, c->geom.x + borderpx,
+ c->geom.y + borderpx);
+ if (client_wants_focus(c)) {
+ focusclient(c, 1);
+ exclusive_focus = c;
+ }
+ goto unset_fullscreen;
+ }
+
+ for (i = 0; i < 4; i++) {
+ c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor);
+ c->border[i]->node.data = c;
+ }
+
+ /* Initialize client geometry with room for border */
+ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
+ client_get_geometry(c, &c->geom);
+ c->geom.width += 2 * c->bw;
+ c->geom.height += 2 * c->bw;
+
+ /* Insert this client into client lists. */
+ wl_list_insert(&clients, &c->link);
+ wl_list_insert(&fstack, &c->flink);
+
+ /* Set initial monitor, tags, floating status, and focus:
+ * we always consider floating, clients that have parent and thus
+ * we set the same tags and monitor than its parent, if not
+ * try to apply rules for them */
+ /* TODO: https://github.com/djpohly/dwl/pull/334#issuecomment-1330166324 */
+ if (c->type == XDGShell && (p = client_get_parent(c))) {
+ c->isfloating = 1;
+ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
+ setmon(c, p->mon, p->tags);
+ } else {
+ applyrules(c);
+ }
+ printstatus();
+
+unset_fullscreen:
+ m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
+ wl_list_for_each(w, &clients, link)
+ if (w != c && w->isfullscreen && m == w->mon && (w->tags & c->tags))
+ setfullscreen(w, 0);
+}
+
+void
+maximizenotify(struct wl_listener *listener, void *data)
+{
+ /* This event is raised when a client would like to maximize itself,
+ * typically because the user clicked on the maximize button on
+ * client-side decorations. dwl doesn't support maximization, but
+ * to conform to xdg-shell protocol we still must send a configure.
+ * Since xdg-shell protocol v5 we should ignore request of unsupported
+ * capabilities, just schedule a empty configure when the client uses <5
+ * protocol version
+ * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
+ Client *c = wl_container_of(listener, c, maximize);
+ if (wl_resource_get_version(c->surface.xdg->resource)
+ < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
+ wlr_xdg_surface_schedule_configure(c->surface.xdg);
+}
+
+void
+monocle(Monitor *m)
+{
+ Client *c;
+ int n = 0;
+
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+ n++;
+ if (!monoclegaps)
+ resize(c, m->w, 0);
+ else
+ resize(c, (struct wlr_box){.x = m->w.x + gappoh, .y = m->w.y + gappov,
+ .width = m->w.width - 2 * gappoh, .height = m->w.height - 2 * gappov}, 0);
+ }
+ if (n)
+ snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n);
+ if ((c = focustop(m)))
+ wlr_scene_node_raise_to_top(&c->scene->node);
+}
+
+void
+motionabsolute(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits an _absolute_
+ * motion event, from 0..1 on each axis. This happens, for example, when
+ * wlroots is running under a Wayland window rather than KMS+DRM, and you
+ * move the mouse over the window. You could enter the window from any edge,
+ * so we have to warp the mouse there. There is also some hardware which
+ * emits these events. */
+ struct wlr_pointer_motion_absolute_event *event = data;
+ wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
+ motionnotify(event->time_msec);
+}
+
+void
+motionnotify(uint32_t time)
+{
+ double sx = 0, sy = 0;
+ Client *c = NULL, *w = NULL;
+ LayerSurface *l = NULL;
+ int type;
+ struct wlr_surface *surface = NULL;
+
+ /* time is 0 in internal calls meant to restore pointer focus. */
+ if (time) {
+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+ /* Update selmon (even while dragging a window) */
+ if (sloppyfocus)
+ selmon = xytomon(cursor->x, cursor->y);
+ }
+
+ /* Update drag icon's position */
+ wlr_scene_node_set_position(&drag_icon->node, cursor->x, cursor->y);
+
+ /* If we are currently grabbing the mouse, handle and return */
+ if (cursor_mode == CurMove) {
+ /* Move the grabbed client to the new position. */
+ resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy,
+ .width = grabc->geom.width, .height = grabc->geom.height}, 1);
+ return;
+ } else if (cursor_mode == CurResize) {
+ resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
+ .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1);
+ return;
+ }
+
+ /* Find the client under the pointer and send the event along. */
+ xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy);
+
+ if (cursor_mode == CurPressed && !seat->drag) {
+ if ((type = toplevel_from_wlr_surface(
+ seat->pointer_state.focused_surface, &w, &l)) >= 0) {
+ c = w;
+ surface = seat->pointer_state.focused_surface;
+ sx = cursor->x - (type == LayerShell ? l->geom.x : w->geom.x);
+ sy = cursor->y - (type == LayerShell ? l->geom.y : w->geom.y);
+ }
+ }
+
+ /* If there's no client surface under the cursor, set the cursor image to a
+ * default. This is what makes the cursor image appear when you move it
+ * off of a client or over its border. */
+ if (!surface && !seat->drag)
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+
+ pointerfocus(c, surface, sx, sy, time);
+}
+
+void
+motionrelative(struct wl_listener *listener, void *data)
+{
+ /* This event is forwarded by the cursor when a pointer emits a _relative_
+ * pointer motion event (i.e. a delta) */
+ struct wlr_pointer_motion_event *event = data;
+ /* The cursor doesn't move unless we tell it to. The cursor automatically
+ * handles constraining the motion to the output layout, as well as any
+ * special configuration applied for the specific input device which
+ * generated the event. You can pass NULL for the device if you want to move
+ * the cursor around without any input. */
+ wlr_cursor_move(cursor, &event->pointer->base, event->delta_x, event->delta_y);
+ motionnotify(event->time_msec);
+}
+
+void
+moveresize(const Arg *arg)
+{
+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
+ return;
+ xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
+ if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
+ return;
+
+ /* Float the window and tell motionnotify to grab it */
+ setfloating(grabc, 1);
+ switch (cursor_mode = arg->ui) {
+ case CurMove:
+ grabcx = cursor->x - grabc->geom.x;
+ grabcy = cursor->y - grabc->geom.y;
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur");
+ break;
+ case CurResize:
+ /* Doesn't work for X11 output - the next absolute motion event
+ * returns the cursor to where it started */
+ wlr_cursor_warp_closest(cursor, NULL,
+ grabc->geom.x + grabc->geom.width,
+ grabc->geom.y + grabc->geom.height);
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
+ break;
+ }
+}
+
+void
+outputmgrapply(struct wl_listener *listener, void *data)
+{
+ struct wlr_output_configuration_v1 *config = data;
+ outputmgrapplyortest(config, 0);
+}
+
+void
+outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
+{
+ /*
+ * Called when a client such as wlr-randr requests a change in output
+ * configuration. This is only one way that the layout can be changed,
+ * so any Monitor information should be updated by updatemons() after an
+ * output_layout.change event, not here.
+ */
+ struct wlr_output_configuration_head_v1 *config_head;
+ int ok = 1;
+
+ wl_list_for_each(config_head, &config->heads, link) {
+ struct wlr_output *wlr_output = config_head->state.output;
+ Monitor *m = wlr_output->data;
+
+ wlr_output_enable(wlr_output, config_head->state.enabled);
+ if (!config_head->state.enabled)
+ goto apply_or_test;
+ if (config_head->state.mode)
+ wlr_output_set_mode(wlr_output, config_head->state.mode);
+ else
+ wlr_output_set_custom_mode(wlr_output,
+ config_head->state.custom_mode.width,
+ config_head->state.custom_mode.height,
+ config_head->state.custom_mode.refresh);
+
+ /* Don't move monitors if position wouldn't change, this to avoid
+ * wlroots marking the output as manually configured */
+ if (m->m.x != config_head->state.x || m->m.y != config_head->state.y)
+ wlr_output_layout_add(output_layout, wlr_output,
+ config_head->state.x, config_head->state.y);
+ wlr_output_set_transform(wlr_output, config_head->state.transform);
+ wlr_output_set_scale(wlr_output, config_head->state.scale);
+ wlr_output_enable_adaptive_sync(wlr_output,
+ config_head->state.adaptive_sync_enabled);
+
+apply_or_test:
+ if (test) {
+ ok &= wlr_output_test(wlr_output);
+ wlr_output_rollback(wlr_output);
+ } else {
+ ok &= wlr_output_commit(wlr_output);
+ }
+ }
+
+ if (ok)
+ wlr_output_configuration_v1_send_succeeded(config);
+ else
+ wlr_output_configuration_v1_send_failed(config);
+ wlr_output_configuration_v1_destroy(config);
+
+ /* TODO: use a wrapper function? */
+ updatemons(NULL, NULL);
+}
+
+void
+outputmgrtest(struct wl_listener *listener, void *data)
+{
+ struct wlr_output_configuration_v1 *config = data;
+ outputmgrapplyortest(config, 1);
+}
+
+void
+pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
+ uint32_t time)
+{
+ struct timespec now;
+ int internal_call = !time;
+
+ if (sloppyfocus && !internal_call && c && !client_is_unmanaged(c))
+ focusclient(c, 0);
+
+ /* If surface is NULL, clear pointer focus */
+ if (!surface) {
+ wlr_seat_pointer_notify_clear_focus(seat);
+ return;
+ }
+
+ if (internal_call) {
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
+ }
+
+ /* Let the client know that the mouse cursor has entered one
+ * of its surfaces, and make keyboard focus follow if desired.
+ * wlroots makes this a no-op if surface is already focused */
+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+
+}
+
+void
+printstatus(void)
+{
+ Monitor *m = NULL;
+ Client *c;
+ uint32_t occ, urg, sel;
+ const char *appid, *title;
+
+ wl_list_for_each(m, &mons, link) {
+ occ = urg = 0;
+ wl_list_for_each(c, &clients, link) {
+ if (c->mon != m)
+ continue;
+ occ |= c->tags;
+ if (c->isurgent)
+ urg |= c->tags;
+ }
+ if ((c = focustop(m))) {
+ title = client_get_title(c);
+ appid = client_get_appid(c);
+ printf("%s title %s\n", m->wlr_output->name, title ? title : broken);
+ printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken);
+ printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen);
+ printf("%s floating %u\n", m->wlr_output->name, c->isfloating);
+ sel = c->tags;
+ } else {
+ printf("%s title \n", m->wlr_output->name);
+ printf("%s appid \n", m->wlr_output->name);
+ printf("%s fullscreen \n", m->wlr_output->name);
+ printf("%s floating \n", m->wlr_output->name);
+ sel = 0;
+ }
+
+ printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
+ printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags],
+ sel, urg);
+ printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
+ }
+ fflush(stdout);
+}
+
+void
+quit(const Arg *arg)
+{
+ wl_display_terminate(dpy);
+}
+
+void
+restartdwl(const Arg *arg)
+{
+ FILE *fp;
+ fp = fopen ("/tmp/restart_dwl", "w");
+ fclose(fp);
+ quit(0);
+}
+
+void
+rendermon(struct wl_listener *listener, void *data)
+{
+ /* This function is called every time an output is ready to display a frame,
+ * generally at the output's refresh rate (e.g. 60Hz). */
+ Monitor *m = wl_container_of(listener, m, frame);
+ Client *c;
+ struct wlr_output_state pending = {0};
+ struct wlr_gamma_control_v1 *gamma_control;
+ struct timespec now;
+
+ /* Render if no XDG clients have an outstanding resize and are visible on
+ * this monitor. */
+ wl_list_for_each(c, &clients, link)
+ if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
+ goto skip;
+
+ /*
+ * HACK: The "correct" way to set the gamma is to commit it together with
+ * the rest of the state in one go, but to do that we would need to rewrite
+ * wlr_scene_output_commit() in order to add the gamma to the pending
+ * state before committing, instead try to commit the gamma in one frame,
+ * and commit the rest of the state in the next one (or in the same frame if
+ * the gamma can not be committed).
+ */
+ if (m->gamma_lut_changed) {
+ gamma_control = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output);
+ m->gamma_lut_changed = 0;
+
+ if (!wlr_gamma_control_v1_apply(gamma_control, &pending))
+ goto commit;
+
+ if (!wlr_output_test_state(m->wlr_output, &pending)) {
+ wlr_gamma_control_v1_send_failed_and_destroy(gamma_control);
+ goto commit;
+ }
+ wlr_output_commit_state(m->wlr_output, &pending);
+ wlr_output_schedule_frame(m->wlr_output);
+ } else {
+commit:
+ wlr_scene_output_commit(m->scene_output, NULL);
+ }
+
+skip:
+ /* Let clients know a frame has been rendered */
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ wlr_scene_output_send_frame_done(m->scene_output, &now);
+ wlr_output_state_finish(&pending);
+}
+
+void
+requeststartdrag(struct wl_listener *listener, void *data)
+{
+ struct wlr_seat_request_start_drag_event *event = data;
+
+ if (wlr_seat_validate_pointer_grab_serial(seat, event->origin,
+ event->serial))
+ wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
+ else
+ wlr_data_source_destroy(event->drag->source);
+}
+
+void
+requestmonstate(struct wl_listener *listener, void *data)
+{
+ struct wlr_output_event_request_state *event = data;
+ wlr_output_commit_state(event->output, event->state);
+ updatemons(NULL, NULL);
+}
+
+void
+resize(Client *c, struct wlr_box geo, int interact)
+{
+ struct wlr_box *bbox = interact ? &sgeom : &c->mon->w;
+ struct wlr_box clip;
+ client_set_bounds(c, geo.width, geo.height);
+ c->geom = geo;
+ applybounds(c, bbox);
+
+ /* Update scene-graph, including borders */
+ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
+ wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
+ wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
+ wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
+ wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
+ wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
+ wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
+ wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
+ wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
+
+ /* this is a no-op if size hasn't changed */
+ c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
+ c->geom.height - 2 * c->bw);
+ client_get_clip(c, &clip);
+ wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
+}
+
+void
+run(char *startup_cmd)
+{
+ /* Add a Unix socket to the Wayland display. */
+ const char *socket = wl_display_add_socket_auto(dpy);
+ if (!socket)
+ die("startup: display_add_socket_auto");
+ setenv("WAYLAND_DISPLAY", socket, 1);
+
+ /* Start the backend. This will enumerate outputs and inputs, become the DRM
+ * master, etc */
+ if (!wlr_backend_start(backend))
+ die("startup: backend_start");
+
+ /* Now that the socket exists and the backend is started, run the startup command */
+ autostartexec();
+ if (startup_cmd) {
+ int piperw[2];
+ if (pipe(piperw) < 0)
+ die("startup: pipe:");
+ if ((child_pid = fork()) < 0)
+ die("startup: fork:");
+ if (child_pid == 0) {
+ dup2(piperw[0], STDIN_FILENO);
+ close(piperw[0]);
+ close(piperw[1]);
+ execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
+ die("startup: execl:");
+ }
+ dup2(piperw[1], STDOUT_FILENO);
+ close(piperw[1]);
+ close(piperw[0]);
+ }
+ printstatus();
+
+ /* At this point the outputs are initialized, choose initial selmon based on
+ * cursor position, and set default cursor image */
+ selmon = xytomon(cursor->x, cursor->y);
+
+ /* TODO hack to get cursor to display in its initial location (100, 100)
+ * instead of (0, 0) and then jumping. still may not be fully
+ * initialized, as the image/coordinates are not transformed for the
+ * monitor when displayed here */
+ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y);
+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
+
+ /* Run the Wayland event loop. This does not return until you exit the
+ * compositor. Starting the backend rigged up all of the necessary event
+ * loop configuration to listen to libinput events, DRM events, generate
+ * frame events at the refresh rate, and so on. */
+ wl_display_run(dpy);
+}
+
+void
+setcursor(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the seat when a client provides a cursor image */
+ struct wlr_seat_pointer_request_set_cursor_event *event = data;
+ /* If we're "grabbing" the cursor, don't use the client's image, we will
+ * restore it after "grabbing" sending a leave event, followed by a enter
+ * event, which will result in the client requesting set the cursor surface */
+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
+ return;
+ /* This can be sent by any client, so we check to make sure this one is
+ * actually has pointer focus first. If so, we can tell the cursor to
+ * use the provided surface as the cursor image. It will set the
+ * hardware cursor on the output that it's currently on and continue to
+ * do so as the cursor moves between outputs. */
+ if (event->seat_client == seat->pointer_state.focused_client)
+ wlr_cursor_set_surface(cursor, event->surface,
+ event->hotspot_x, event->hotspot_y);
+}
+
+void
+setcursorshape(struct wl_listener *listener, void *data)
+{
+ struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
+ return;
+ /* This can be sent by any client, so we check to make sure this one is
+ * actually has pointer focus first. If so, we can tell the cursor to
+ * use the provided cursor shape. */
+ if (event->seat_client == seat->pointer_state.focused_client)
+ wlr_cursor_set_xcursor(cursor, cursor_mgr,
+ wlr_cursor_shape_v1_name(event->shape));
+}
+
+void
+setfloating(Client *c, int floating)
+{
+ c->isfloating = floating;
+ if (!c->mon)
+ return;
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+ arrange(c->mon);
+ printstatus();
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+ c->isfullscreen = fullscreen;
+ if (!c->mon)
+ return;
+ c->bw = fullscreen ? 0 : borderpx;
+ client_set_fullscreen(c, fullscreen);
+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
+
+ if (fullscreen) {
+ c->prev = c->geom;
+ resize(c, c->mon->m, 0);
+ } else {
+ /* restore previous size instead of arrange for floating windows since
+ * client positions are set by the user and cannot be recalculated */
+ resize(c, c->prev, 0);
+ }
+ arrange(c->mon);
+ printstatus();
+}
+
+void
+setgamma(struct wl_listener *listener, void *data)
+{
+ struct wlr_gamma_control_manager_v1_set_gamma_event *event = data;
+ Monitor *m = event->output->data;
+ m->gamma_lut_changed = 1;
+ wlr_output_schedule_frame(m->wlr_output);
+}
+
+void
+setgaps(int oh, int ov, int ih, int iv)
+{
+ selmon->gappoh = MAX(oh, 0);
+ selmon->gappov = MAX(ov, 0);
+ selmon->gappih = MAX(ih, 0);
+ selmon->gappiv = MAX(iv, 0);
+ arrange(selmon);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!selmon)
+ return;
+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
+ selmon->sellt ^= 1;
+ if (arg && arg->v)
+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
+ arrange(selmon);
+ printstatus();
+}
+
+/* arg > 1.0 will set mfact absolutely */
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + selmon->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ selmon->mfact = f;
+ arrange(selmon);
+}
+
+void
+setmon(Client *c, Monitor *m, uint32_t newtags)
+{
+ Monitor *oldmon = c->mon;
+
+ if (oldmon == m)
+ return;
+ c->mon = m;
+ c->prev = c->geom;
+
+ /* Scene graph sends surface leave/enter events on move and resize */
+ if (oldmon)
+ arrange(oldmon);
+ if (m) {
+ /* Make sure window actually overlaps with the monitor */
+ resize(c, c->geom, 0);
+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
+ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
+ setfloating(c, c->isfloating);
+ }
+ focusclient(focustop(selmon), 1);
+}
+
+void
+setpsel(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the seat when a client wants to set the selection,
+ * usually when the user copies something. wlroots allows compositors to
+ * ignore such requests if they so choose, but in dwl we always honor
+ */
+ struct wlr_seat_request_set_primary_selection_event *event = data;
+ wlr_seat_set_primary_selection(seat, event->source, event->serial);
+}
+
+void
+setsel(struct wl_listener *listener, void *data)
+{
+ /* This event is raised by the seat when a client wants to set the selection,
+ * usually when the user copies something. wlroots allows compositors to
+ * ignore such requests if they so choose, but in dwl we always honor
+ */
+ struct wlr_seat_request_set_selection_event *event = data;
+ wlr_seat_set_selection(seat, event->source, event->serial);
+}
+
+void
+setup(void)
+{
+ int i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
+ struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
+ sigemptyset(&sa.sa_mask);
+
+ for (i = 0; i < LENGTH(sig); i++)
+ sigaction(sig[i], &sa, NULL);
+
+ wlr_log_init(log_level, NULL);
+
+ /* The Wayland display is managed by libwayland. It handles accepting
+ * clients from the Unix socket, manging Wayland globals, and so on. */
+ dpy = wl_display_create();
+
+ /* The backend is a wlroots feature which abstracts the underlying input and
+ * output hardware. The autocreate option will choose the most suitable
+ * backend based on the current environment, such as opening an X11 window
+ * if an X11 server is running. */
+ if (!(backend = wlr_backend_autocreate(dpy, &session)))
+ die("couldn't create backend");
+
+ /* Initialize the scene graph used to lay out windows */
+ scene = wlr_scene_create();
+ for (i = 0; i < NUM_LAYERS; i++)
+ layers[i] = wlr_scene_tree_create(&scene->tree);
+ drag_icon = wlr_scene_tree_create(&scene->tree);
+ wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node);
+
+ /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
+ * can also specify a renderer using the WLR_RENDERER env var.
+ * The renderer is responsible for defining the various pixel formats it
+ * supports for shared memory, this configures that for clients. */
+ if (!(drw = wlr_renderer_autocreate(backend)))
+ die("couldn't create renderer");
+
+ /* Create shm, drm and linux_dmabuf interfaces by ourselves.
+ * The simplest way is call:
+ * wlr_renderer_init_wl_display(drw);
+ * but we need to create manually the linux_dmabuf interface to integrate it
+ * with wlr_scene. */
+ wlr_renderer_init_wl_shm(drw, dpy);
+
+ if (wlr_renderer_get_dmabuf_texture_formats(drw)) {
+ wlr_drm_create(dpy, drw);
+ wlr_scene_set_linux_dmabuf_v1(scene,
+ wlr_linux_dmabuf_v1_create_with_renderer(dpy, 4, drw));
+ }
+
+ /* Autocreates an allocator for us.
+ * The allocator is the bridge between the renderer and the backend. It
+ * handles the buffer creation, allowing wlroots to render onto the
+ * screen */
+ if (!(alloc = wlr_allocator_autocreate(backend, drw)))
+ die("couldn't create allocator");
+
+ /* This creates some hands-off wlroots interfaces. The compositor is
+ * necessary for clients to allocate surfaces and the data device manager
+ * handles the clipboard. Each of these wlroots interfaces has room for you
+ * to dig your fingers in and play with their behavior if you want. Note that
+ * the clients cannot set the selection directly without compositor approval,
+ * see the setsel() function. */
+ compositor = wlr_compositor_create(dpy, 6, drw);
+ wlr_subcompositor_create(dpy);
+ wlr_data_device_manager_create(dpy);
+ wlr_export_dmabuf_manager_v1_create(dpy);
+ wlr_screencopy_manager_v1_create(dpy);
+ wlr_data_control_manager_v1_create(dpy);
+ wlr_primary_selection_v1_device_manager_create(dpy);
+ wlr_viewporter_create(dpy);
+ wlr_single_pixel_buffer_manager_v1_create(dpy);
+ wlr_fractional_scale_manager_v1_create(dpy, 1);
+
+ /* Initializes the interface used to implement urgency hints */
+ activation = wlr_xdg_activation_v1_create(dpy);
+ LISTEN_STATIC(&activation->events.request_activate, urgent);
+
+ gamma_control_mgr = wlr_gamma_control_manager_v1_create(dpy);
+ LISTEN_STATIC(&gamma_control_mgr->events.set_gamma, setgamma);
+
+ /* Creates an output layout, which a wlroots utility for working with an
+ * arrangement of screens in a physical layout. */
+ output_layout = wlr_output_layout_create();
+ LISTEN_STATIC(&output_layout->events.change, updatemons);
+ wlr_xdg_output_manager_v1_create(dpy, output_layout);
+
+ /* Configure a listener to be notified when new outputs are available on the
+ * backend. */
+ wl_list_init(&mons);
+ LISTEN_STATIC(&backend->events.new_output, createmon);
+
+ /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a
+ * Wayland protocol which is used for application windows. For more
+ * detail on shells, refer to the article:
+ *
+ * https://drewdevault.com/2018/07/29/Wayland-shells.html
+ */
+ wl_list_init(&clients);
+ wl_list_init(&fstack);
+
+ xdg_shell = wlr_xdg_shell_create(dpy, 6);
+ LISTEN_STATIC(&xdg_shell->events.new_surface, createnotify);
+
+ layer_shell = wlr_layer_shell_v1_create(dpy, 3);
+ LISTEN_STATIC(&layer_shell->events.new_surface, createlayersurface);
+
+ idle_notifier = wlr_idle_notifier_v1_create(dpy);
+
+ idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
+ LISTEN_STATIC(&idle_inhibit_mgr->events.new_inhibitor, createidleinhibitor);
+
+ session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
+ wl_signal_add(&session_lock_mgr->events.new_lock, &lock_listener);
+ LISTEN_STATIC(&session_lock_mgr->events.destroy, destroysessionmgr);
+ locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
+ (float [4]){0.1, 0.1, 0.1, 1.0});
+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
+
+ /* Use decoration protocols to negotiate server-side decorations */
+ wlr_server_decoration_manager_set_default_mode(
+ wlr_server_decoration_manager_create(dpy),
+ WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
+ xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
+ LISTEN_STATIC(&xdg_decoration_mgr->events.new_toplevel_decoration, createdecoration);
+
+ /*
+ * Creates a cursor, which is a wlroots utility for tracking the cursor
+ * image shown on screen.
+ */
+ cursor = wlr_cursor_create();
+ wlr_cursor_attach_output_layout(cursor, output_layout);
+
+ /* Creates an xcursor manager, another wlroots utility which loads up
+ * Xcursor themes to source cursor images from and makes sure that cursor
+ * images are available at all scale factors on the screen (necessary for
+ * HiDPI support). Scaled cursors will be loaded with each output. */
+ cursor_mgr = wlr_xcursor_manager_create(NULL, 24);
+ setenv("XCURSOR_SIZE", "24", 1);
+
+ /*
+ * wlr_cursor *only* displays an image on screen. It does not move around
+ * when the pointer moves. However, we can attach input devices to it, and
+ * it will generate aggregate events for all of them. In these events, we
+ * can choose how we want to process them, forwarding them to clients and
+ * moving the cursor around. More detail on this process is described in
+ * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
+ *
+ * And more comments are sprinkled throughout the notify functions above.
+ */
+ LISTEN_STATIC(&cursor->events.motion, motionrelative);
+ LISTEN_STATIC(&cursor->events.motion_absolute, motionabsolute);
+ LISTEN_STATIC(&cursor->events.button, buttonpress);
+ LISTEN_STATIC(&cursor->events.axis, axisnotify);
+ LISTEN_STATIC(&cursor->events.frame, cursorframe);
+
+ cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
+ LISTEN_STATIC(&cursor_shape_mgr->events.request_set_shape, setcursorshape);
+
+ /*
+ * Configures a seat, which is a single "seat" at which a user sits and
+ * operates the computer. This conceptually includes up to one keyboard,
+ * pointer, touch, and drawing tablet device. We also rig up a listener to
+ * let us know when new input devices are available on the backend.
+ */
+ wl_list_init(&keyboards);
+ LISTEN_STATIC(&backend->events.new_input, inputdevice);
+ virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
+ LISTEN_STATIC(&virtual_keyboard_mgr->events.new_virtual_keyboard, virtualkeyboard);
+ seat = wlr_seat_create(dpy, "seat0");
+ LISTEN_STATIC(&seat->events.request_set_cursor, setcursor);
+ LISTEN_STATIC(&seat->events.request_set_selection, setsel);
+ LISTEN_STATIC(&seat->events.request_set_primary_selection, setpsel);
+ LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag);
+ LISTEN_STATIC(&seat->events.start_drag, startdrag);
+
+ output_mgr = wlr_output_manager_v1_create(dpy);
+ LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
+ LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
+
+ wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend));
+
+#ifdef XWAYLAND
+ /*
+ * Initialise the XWayland X server.
+ * It will be started when the first X client is started.
+ */
+ xwayland = wlr_xwayland_create(dpy, compositor, 1);
+ if (xwayland) {
+ LISTEN_STATIC(&xwayland->events.ready, xwaylandready);
+ LISTEN_STATIC(&xwayland->events.new_surface, createnotifyx11);
+
+ setenv("DISPLAY", xwayland->display_name, 1);
+ } else {
+ fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
+ }
+#endif
+}
+
+void
+spawn(const Arg *arg)
+{
+ if (fork() == 0) {
+ dup2(STDERR_FILENO, STDOUT_FILENO);
+ setsid();
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+ die("dwl: execvp %s failed:", ((char **)arg->v)[0]);
+ }
+}
+
+void
+startdrag(struct wl_listener *listener, void *data)
+{
+ struct wlr_drag *drag = data;
+ if (!drag->icon)
+ return;
+
+ drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node;
+ LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
+}
+
+void
+tag(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (!sel || (arg->ui & TAGMASK) == 0)
+ return;
+
+ sel->tags = arg->ui & TAGMASK;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+tagmon(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ setmon(sel, dirtomon(arg->i), 0);
+}
+
+void
+tile(Monitor *m)
+{
+ unsigned int i, n = 0, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
+ n++;
+ if (n == 0)
+ return;
+
+ if (smartgaps == n) {
+ oe = 0; // outer gaps disabled
+ }
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? (m->w.width + m->gappiv*ie) * m->mfact : 0;
+ else
+ mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie;
+ i = 0;
+ my = ty = m->gappoh*oe;
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+ if (i < m->nmaster) {
+ r = MIN(n, m->nmaster) - i;
+ h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my,
+ .width = mw - m->gappiv*ie, .height = h}, 0);
+ my += c->geom.height + m->gappih*ie;
+ } else {
+ r = n - i;
+ h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
+ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty,
+ .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0);
+ ty += c->geom.height + m->gappih*ie;
+ }
+ i++;
+ }
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ /* return if fullscreen */
+ if (sel && !sel->isfullscreen)
+ setfloating(sel, !sel->isfloating);
+}
+
+void
+togglefullscreen(const Arg *arg)
+{
+ Client *sel = focustop(selmon);
+ if (sel)
+ setfullscreen(sel, !sel->isfullscreen);
+}
+
+void
+togglegaps(const Arg *arg)
+{
+ enablegaps = !enablegaps;
+ arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ uint32_t newtags;
+ Client *sel = focustop(selmon);
+ if (!sel)
+ return;
+ newtags = sel->tags ^ (arg->ui & TAGMASK);
+ if (!newtags)
+ return;
+
+ sel->tags = newtags;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+toggleview(const Arg *arg)
+{
+ uint32_t newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0;
+
+ if (!newtagset)
+ return;
+
+ selmon->tagset[selmon->seltags] = newtagset;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+unlocksession(struct wl_listener *listener, void *data)
+{
+ SessionLock *lock = wl_container_of(listener, lock, unlock);
+ destroylock(lock, 1);
+}
+
+void
+unmaplayersurfacenotify(struct wl_listener *listener, void *data)
+{
+ LayerSurface *layersurface = wl_container_of(listener, layersurface, unmap);
+
+ layersurface->mapped = 0;
+ wlr_scene_node_set_enabled(&layersurface->scene->node, 0);
+ if (layersurface == exclusive_focus)
+ exclusive_focus = NULL;
+ if (layersurface->layer_surface->output
+ && (layersurface->mon = layersurface->layer_surface->output->data))
+ arrangelayers(layersurface->mon);
+ if (layersurface->layer_surface->surface ==
+ seat->keyboard_state.focused_surface)
+ focusclient(focustop(selmon), 1);
+ motionnotify(0);
+}
+
+void
+unmapnotify(struct wl_listener *listener, void *data)
+{
+ /* Called when the surface is unmapped, and should no longer be shown. */
+ Client *c = wl_container_of(listener, c, unmap);
+ if (c == grabc) {
+ cursor_mode = CurNormal;
+ grabc = NULL;
+ }
+
+ if (client_is_unmanaged(c)) {
+ if (c == exclusive_focus)
+ exclusive_focus = NULL;
+ if (client_surface(c) == seat->keyboard_state.focused_surface)
+ focusclient(focustop(selmon), 1);
+ } else {
+ wl_list_remove(&c->link);
+ setmon(c, NULL, 0);
+ wl_list_remove(&c->flink);
+ }
+
+ wlr_scene_node_destroy(&c->scene->node);
+ printstatus();
+ motionnotify(0);
+}
+
+void
+updatemons(struct wl_listener *listener, void *data)
+{
+ /*
+ * Called whenever the output layout changes: adding or removing a
+ * monitor, changing an output's mode or position, etc. This is where
+ * the change officially happens and we update geometry, window
+ * positions, focus, and the stored configuration in wlroots'
+ * output-manager implementation.
+ */
+ struct wlr_output_configuration_v1 *config =
+ wlr_output_configuration_v1_create();
+ Client *c;
+ struct wlr_output_configuration_head_v1 *config_head;
+ Monitor *m;
+
+ /* First remove from the layout the disabled monitors */
+ wl_list_for_each(m, &mons, link) {
+ if (m->wlr_output->enabled)
+ continue;
+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
+ config_head->state.enabled = 0;
+ /* Remove this output from the layout to avoid cursor enter inside it */
+ wlr_output_layout_remove(output_layout, m->wlr_output);
+ closemon(m);
+ memset(&m->m, 0, sizeof(m->m));
+ memset(&m->w, 0, sizeof(m->w));
+ }
+ /* Insert outputs that need to */
+ wl_list_for_each(m, &mons, link)
+ if (m->wlr_output->enabled
+ && !wlr_output_layout_get(output_layout, m->wlr_output))
+ wlr_output_layout_add_auto(output_layout, m->wlr_output);
+ /* Now that we update the output layout we can get its box */
+ wlr_output_layout_get_box(output_layout, NULL, &sgeom);
+
+ /* Make sure the clients are hidden when dwl is locked */
+ wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y);
+ wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height);
+
+ wl_list_for_each(m, &mons, link) {
+ if (!m->wlr_output->enabled)
+ continue;
+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
+
+ /* Get the effective monitor geometry to use for surfaces */
+ wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m);
+ m->w = m->m;
+ wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y);
+
+ wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y);
+ wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height);
+
+ if (m->lock_surface) {
+ struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
+ wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width,
+ m->m.height);
+ }
+
+ /* Calculate the effective monitor geometry to use for clients */
+ arrangelayers(m);
+ /* Don't move clients to the left output when plugging monitors */
+ arrange(m);
+ /* make sure fullscreen clients have the right size */
+ if ((c = focustop(m)) && c->isfullscreen)
+ resize(c, m->m, 0);
+
+ m->gamma_lut_changed = 1;
+ config_head->state.enabled = 1;
+ config_head->state.mode = m->wlr_output->current_mode;
+ config_head->state.x = m->m.x;
+ config_head->state.y = m->m.y;
+ }
+
+ if (selmon && selmon->wlr_output->enabled) {
+ wl_list_for_each(c, &clients, link)
+ if (!c->mon && client_surface(c)->mapped)
+ setmon(c, selmon, c->tags);
+ focusclient(focustop(selmon), 1);
+ if (selmon->lock_surface) {
+ client_notify_enter(selmon->lock_surface->surface,
+ wlr_seat_get_keyboard(seat));
+ client_activate_surface(selmon->lock_surface->surface, 1);
+ }
+ }
+
+ /* FIXME: figure out why the cursor image is at 0,0 after turning all
+ * the monitors on.
+ * Move the cursor image where it used to be. It does not generate a
+ * wl_pointer.motion event for the clients, it's only the image what it's
+ * at the wrong position after all. */
+ wlr_cursor_move(cursor, NULL, 0, 0);
+
+ wlr_output_manager_v1_set_configuration(output_mgr, config);
+}
+
+void
+updatetitle(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, set_title);
+ if (c == focustop(c->mon))
+ printstatus();
+}
+
+void
+urgent(struct wl_listener *listener, void *data)
+{
+ struct wlr_xdg_activation_v1_request_activate_event *event = data;
+ Client *c = NULL;
+ toplevel_from_wlr_surface(event->surface, &c, NULL);
+ if (!c || c == focustop(selmon))
+ return;
+
+ if (client_surface(c)->mapped)
+ client_set_border_color(c, urgentcolor);
+ c->isurgent = 1;
+ printstatus();
+}
+
+void
+view(const Arg *arg)
+{
+ if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
+ return;
+ selmon->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ focusclient(focustop(selmon), 1);
+ arrange(selmon);
+ printstatus();
+}
+
+void
+virtualkeyboard(struct wl_listener *listener, void *data)
+{
+ struct wlr_virtual_keyboard_v1 *keyboard = data;
+ createkeyboard(&keyboard->keyboard);
+}
+
+Monitor *
+xytomon(double x, double y)
+{
+ struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
+ return o ? o->data : NULL;
+}
+
+void
+xytonode(double x, double y, struct wlr_surface **psurface,
+ Client **pc, LayerSurface **pl, double *nx, double *ny)
+{
+ struct wlr_scene_node *node, *pnode;
+ struct wlr_surface *surface = NULL;
+ Client *c = NULL;
+ LayerSurface *l = NULL;
+ int layer;
+
+ for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
+ if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
+ continue;
+
+ if (node->type == WLR_SCENE_NODE_BUFFER)
+ surface = wlr_scene_surface_try_from_buffer(
+ wlr_scene_buffer_from_node(node))->surface;
+ /* Walk the tree to find a node that knows the client */
+ for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
+ c = pnode->data;
+ if (c && c->type == LayerShell) {
+ c = NULL;
+ l = pnode->data;
+ }
+ }
+
+ if (psurface) *psurface = surface;
+ if (pc) *pc = c;
+ if (pl) *pl = l;
+}
+
+void
+zoom(const Arg *arg)
+{
+ Client *c, *sel = focustop(selmon);
+
+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating)
+ return;
+
+ /* Search for the first tiled window that is not sel, marking sel as
+ * NULL if we pass it along the way */
+ wl_list_for_each(c, &clients, link)
+ if (VISIBLEON(c, selmon) && !c->isfloating) {
+ if (c != sel)
+ break;
+ sel = NULL;
+ }
+
+ /* Return if no other tiled window was found */
+ if (&c->link == &clients)
+ return;
+
+ /* If we passed sel, move c to the front; otherwise, move sel to the
+ * front */
+ if (!sel)
+ sel = c;
+ wl_list_remove(&sel->link);
+ wl_list_insert(&clients, &sel->link);
+
+ focusclient(sel, 1);
+ arrange(selmon);
+}
+
+#ifdef XWAYLAND
+void
+activatex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, activate);
+
+ /* Only "managed" windows can be activated */
+ if (c->type == X11Managed)
+ wlr_xwayland_surface_activate(c->surface.xwayland, 1);
+}
+
+void
+associatex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, associate);
+
+ LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
+ LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify);
+}
+
+void
+configurex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, configure);
+ struct wlr_xwayland_surface_configure_event *event = data;
+ if (!c->mon)
+ return;
+ if (c->isfloating || c->type == X11Unmanaged)
+ resize(c, (struct wlr_box){.x = event->x, .y = event->y,
+ .width = event->width, .height = event->height}, 0);
+ else
+ arrange(c->mon);
+}
+
+void
+createnotifyx11(struct wl_listener *listener, void *data)
+{
+ struct wlr_xwayland_surface *xsurface = data;
+ Client *c;
+
+ /* Allocate a Client for this surface */
+ c = xsurface->data = ecalloc(1, sizeof(*c));
+ c->surface.xwayland = xsurface;
+ c->type = xsurface->override_redirect ? X11Unmanaged : X11Managed;
+ c->bw = borderpx;
+
+ /* Listen to the various events it can emit */
+ LISTEN(&xsurface->events.associate, &c->associate, associatex11);
+ LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
+ LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
+ LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
+ LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
+ LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle);
+ LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
+ LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify);
+}
+
+void
+dissociatex11(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, dissociate);
+ wl_list_remove(&c->map.link);
+ wl_list_remove(&c->unmap.link);
+}
+
+xcb_atom_t
+getatom(xcb_connection_t *xc, const char *name)
+{
+ xcb_atom_t atom = 0;
+ xcb_intern_atom_reply_t *reply;
+ xcb_intern_atom_cookie_t cookie = xcb_intern_atom(xc, 0, strlen(name), name);
+ if ((reply = xcb_intern_atom_reply(xc, cookie, NULL)))
+ atom = reply->atom;
+ free(reply);
+
+ return atom;
+}
+
+void
+sethints(struct wl_listener *listener, void *data)
+{
+ Client *c = wl_container_of(listener, c, set_hints);
+ struct wlr_surface *surface = client_surface(c);
+ if (c == focustop(selmon))
+ return;
+
+ c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
+
+ if (c->isurgent && surface && surface->mapped)
+ client_set_border_color(c, urgentcolor);
+
+ printstatus();
+}
+
+void
+xwaylandready(struct wl_listener *listener, void *data)
+{
+ struct wlr_xcursor *xcursor;
+ xcb_connection_t *xc = xcb_connect(xwayland->display_name, NULL);
+ int err = xcb_connection_has_error(xc);
+ if (err) {
+ fprintf(stderr, "xcb_connect to X server failed with code %d\n. Continuing with degraded functionality.\n", err);
+ return;
+ }
+
+ /* Collect atoms we are interested in. If getatom returns 0, we will
+ * not detect that window type. */
+ netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG");
+ netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH");
+ netatom[NetWMWindowTypeToolbar] = getatom(xc, "_NET_WM_WINDOW_TYPE_TOOLBAR");
+ netatom[NetWMWindowTypeUtility] = getatom(xc, "_NET_WM_WINDOW_TYPE_UTILITY");
+
+ /* assign the one and only seat */
+ wlr_xwayland_set_seat(xwayland, seat);
+
+ /* Set the default XWayland cursor to match the rest of dwl. */
+ if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
+ wlr_xwayland_set_cursor(xwayland,
+ xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
+ xcursor->images[0]->width, xcursor->images[0]->height,
+ xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
+
+ xcb_disconnect(xc);
+}
+#endif
+
+int
+main(int argc, char *argv[])
+{
+ char *startup_cmd = NULL;
+ int c;
+
+ while ((c = getopt(argc, argv, "s:hdv")) != -1) {
+ if (c == 's')
+ startup_cmd = optarg;
+ else if (c == 'd')
+ log_level = WLR_DEBUG;
+ else if (c == 'v')
+ die("dwl " VERSION);
+ else
+ goto usage;
+ }
+ if (optind < argc)
+ goto usage;
+
+ /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */
+ if (!getenv("XDG_RUNTIME_DIR"))
+ die("XDG_RUNTIME_DIR must be set");
+ setup();
+ run(startup_cmd);
+ cleanup();
+ return EXIT_SUCCESS;
+
+usage:
+ die("Usage: %s [-v] [-d] [-s startup command]", argv[0]);
+}
diff --git a/dwl-v0.5/dwl.c.rej b/dwl-v0.5/dwl.c.rej
new file mode 100644
index 0000000..1e840df
--- /dev/null
+++ b/dwl-v0.5/dwl.c.rej
@@ -0,0 +1,10 @@
+--- dwl.c
++++ dwl.c
+@@ -1870,6 +1888,7 @@ printstatus(void)
+ printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags],
+ sel, urg);
+ printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol);
++ dwl_wm_printstatus(m);
+ }
+ fflush(stdout);
+ }
diff --git a/dwl-v0.5/dwl.desktop b/dwl-v0.5/dwl.desktop
new file mode 100644
index 0000000..e1380f7
--- /dev/null
+++ b/dwl-v0.5/dwl.desktop
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Name=dwl
+Comment=dwm for Wayland
+Exec=dwl
+Type=Application
diff --git a/dwl-v0.5/net-tapesoftware-dwl-wm-unstable-v1-protocol.c b/dwl-v0.5/net-tapesoftware-dwl-wm-unstable-v1-protocol.c
new file mode 100644
index 0000000..7e97754
--- /dev/null
+++ b/dwl-v0.5/net-tapesoftware-dwl-wm-unstable-v1-protocol.c
@@ -0,0 +1,89 @@
+/* Generated by wayland-scanner 1.22.0 */
+
+/*
+ * Copyright (c) 2021 Raphael Robatsch
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include <stdlib.h>
+#include <stdint.h>
+#include "wayland-util.h"
+
+#ifndef __has_attribute
+# define __has_attribute(x) 0 /* Compatibility with non-clang compilers. */
+#endif
+
+#if (__has_attribute(visibility) || defined(__GNUC__) && __GNUC__ >= 4)
+#define WL_PRIVATE __attribute__ ((visibility("hidden")))
+#else
+#define WL_PRIVATE
+#endif
+
+extern const struct wl_interface wl_output_interface;
+extern const struct wl_interface znet_tapesoftware_dwl_wm_monitor_v1_interface;
+
+static const struct wl_interface *net_tapesoftware_dwl_wm_unstable_v1_types[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ &znet_tapesoftware_dwl_wm_monitor_v1_interface,
+ &wl_output_interface,
+};
+
+static const struct wl_message znet_tapesoftware_dwl_wm_v1_requests[] = {
+ { "release", "", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "get_monitor", "no", net_tapesoftware_dwl_wm_unstable_v1_types + 4 },
+};
+
+static const struct wl_message znet_tapesoftware_dwl_wm_v1_events[] = {
+ { "tag", "s", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "layout", "s", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface znet_tapesoftware_dwl_wm_v1_interface = {
+ "znet_tapesoftware_dwl_wm_v1", 1,
+ 2, znet_tapesoftware_dwl_wm_v1_requests,
+ 2, znet_tapesoftware_dwl_wm_v1_events,
+};
+
+static const struct wl_message znet_tapesoftware_dwl_wm_monitor_v1_requests[] = {
+ { "release", "", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "set_tags", "uu", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "set_client_tags", "uu", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "set_layout", "u", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+};
+
+static const struct wl_message znet_tapesoftware_dwl_wm_monitor_v1_events[] = {
+ { "selected", "u", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "tag", "uuui", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "layout", "u", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "title", "s", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+ { "frame", "", net_tapesoftware_dwl_wm_unstable_v1_types + 0 },
+};
+
+WL_PRIVATE const struct wl_interface znet_tapesoftware_dwl_wm_monitor_v1_interface = {
+ "znet_tapesoftware_dwl_wm_monitor_v1", 1,
+ 4, znet_tapesoftware_dwl_wm_monitor_v1_requests,
+ 5, znet_tapesoftware_dwl_wm_monitor_v1_events,
+};
+
diff --git a/dwl-v0.5/patches/autostart.patch b/dwl-v0.5/patches/autostart.patch
new file mode 100644
index 0000000..05606ce
--- /dev/null
+++ b/dwl-v0.5/patches/autostart.patch
@@ -0,0 +1,140 @@
+commit 3ec7bf9dc4fbf2404cf9a21bc0b65c3b137a70c6
+Author: Nicola Ferru Aka NFVblog <ask.nfvblog@outlook.it>
+Date: Thu Nov 30 15:12:19 2023 +0100
+
+ autostart patch
+
+diff --git a/config.def.h b/config.def.h
+index db0babc..416f5da 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -13,6 +13,12 @@ static const float urgentcolor[] = COLOR(0xff0000ff);
+ /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
+ static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; /* You can also use glsl colors */
+
++/* Autostart */
++static const char *const autostart[] = {
++ "wbg", "/path/to/your/image", NULL,
++ NULL /* terminate */
++};
++
+ /* tagging - TAGCOUNT must be no greater than 31 */
+ #define TAGCOUNT (9)
+
+diff --git a/dwl.c b/dwl.c
+index ef27a1d..70c4f61 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -228,6 +228,7 @@ typedef struct {
+
+ /* function declarations */
+ static void applybounds(Client *c, struct wlr_box *bbox);
++static void autostartexec(void);
+ static void applyrules(Client *c);
+ static void arrange(Monitor *m);
+ static void arrangelayer(Monitor *m, struct wl_list *list,
+@@ -396,6 +397,9 @@ static xcb_atom_t netatom[NetLast];
+ /* attempt to encapsulate suck into one file */
+ #include "client.h"
+
++static pid_t *autostart_pids;
++static size_t autostart_len;
++
+ /* function implementations */
+ void
+ applybounds(Client *c, struct wlr_box *bbox)
+@@ -414,6 +418,27 @@ applybounds(Client *c, struct wlr_box *bbox)
+ c->geom.y = bbox->y;
+ }
+
++void
++autostartexec(void) {
++ const char *const *p;
++ size_t i = 0;
++
++ /* count entries */
++ for (p = autostart; *p; autostart_len++, p++)
++ while (*++p);
++
++ autostart_pids = calloc(autostart_len, sizeof(pid_t));
++ for (p = autostart; *p; i++, p++) {
++ if ((autostart_pids[i] = fork()) == 0) {
++ setsid();
++ execvp(*p, (char *const *)p);
++ die("dwl: execvp %s:", *p);
++ }
++ /* skip arguments */
++ while (*++p);
++ }
++}
++
+ void
+ applyrules(Client *c)
+ {
+@@ -624,11 +649,21 @@ checkidleinhibitor(struct wlr_surface *exclude)
+ void
+ cleanup(void)
+ {
++ size_t i;
+ #ifdef XWAYLAND
+ wlr_xwayland_destroy(xwayland);
+ xwayland = NULL;
+ #endif
+ wl_display_destroy_clients(dpy);
++
++ /* kill child processes */
++ for (i = 0; i < autostart_len; i++) {
++ if (0 < autostart_pids[i]) {
++ kill(autostart_pids[i], SIGTERM);
++ waitpid(autostart_pids[i], NULL, 0);
++ }
++ }
++
+ if (child_pid > 0) {
+ kill(child_pid, SIGTERM);
+ waitpid(child_pid, NULL, 0);
+@@ -1306,18 +1341,31 @@ void
+ handlesig(int signo)
+ {
+ if (signo == SIGCHLD) {
+-#ifdef XWAYLAND
+ siginfo_t in;
+ /* wlroots expects to reap the XWayland process itself, so we
+ * use WNOWAIT to keep the child waitable until we know it's not
+ * XWayland.
+ */
+ while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid
+- && (!xwayland || in.si_pid != xwayland->server->pid))
+- waitpid(in.si_pid, NULL, 0);
+-#else
+- while (waitpid(-1, NULL, WNOHANG) > 0);
++ #ifdef XWAYLAND
++ && (!xwayland || in.si_pid != xwayland->server->pid)
+ #endif
++ ) {
++ pid_t *p, *lim;
++ waitpid(in.si_pid, NULL, 0);
++ if (in.si_pid == child_pid)
++ child_pid = -1;
++ if (!(p = autostart_pids))
++ continue;
++ lim = &p[autostart_len];
++
++ for (; p < lim; p++) {
++ if (*p == in.si_pid) {
++ *p = -1;
++ break;
++ }
++ }
++ }
+ } else if (signo == SIGINT || signo == SIGTERM) {
+ quit(NULL);
+ }
+@@ -1973,6 +2021,7 @@ run(char *startup_cmd)
+ die("startup: backend_start");
+
+ /* Now that the socket exists and the backend is started, run the startup command */
++ autostartexec();
+ if (startup_cmd) {
+ int piperw[2];
+ if (pipe(piperw) < 0)
diff --git a/dwl-v0.5/patches/main...krypciak:patch-restartdwl.patch b/dwl-v0.5/patches/main...krypciak:patch-restartdwl.patch
new file mode 100644
index 0000000..5efeec7
--- /dev/null
+++ b/dwl-v0.5/patches/main...krypciak:patch-restartdwl.patch
@@ -0,0 +1,52 @@
+From 8cee0841d0abfe783a2668bf49523c094b77a055 Mon Sep 17 00:00:00 2001
+From: krypek <115574014+krypciak@users.noreply.github.com>
+Date: Thu, 27 Oct 2022 20:09:27 +0200
+Subject: [PATCH] Add restartdwl function
+
+---
+ config.def.h | 3 +++
+ dwl.c | 10 ++++++++++
+ 2 files changed, 13 insertions(+)
+
+diff --git a/config.def.h b/config.def.h
+index ec1f05289..2f4845410 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -104,6 +104,9 @@ static const char *menucmd[] = { "bemenu-run", NULL };
+ static const Key keys[] = {
+ /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
+ /* modifier key function argument */
++
++ { MODKEY|WLR_MODIFIER_SHIFT|WLR_MODIFIER_CTRL, XKB_KEY_M, restartdwl, {0} },
++
+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
+diff --git a/dwl.c b/dwl.c
+index a80de0542..70c1da639 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -260,6 +260,7 @@ static void pointerfocus(Client *c, struct wlr_surface *surface,
+ static void printstatus(void);
+ static void quit(const Arg *arg);
+ static void quitsignal(int signo);
++static void restartdwl(const Arg *arg);
+ static void rendermon(struct wl_listener *listener, void *data);
+ static void requeststartdrag(struct wl_listener *listener, void *data);
+ static void resize(Client *c, struct wlr_box geo, int interact);
+@@ -1781,6 +1782,15 @@ quitsignal(int signo)
+ quit(NULL);
+ }
+
++void
++restartdwl(const Arg *arg)
++{
++ FILE *fp;
++ fp = fopen ("/tmp/restart_dwl", "w");
++ fclose(fp);
++ quit(0);
++}
++
+ void
+ rendermon(struct wl_listener *listener, void *data)
+ {
diff --git a/dwl-v0.5/patches/main...sevz17:vanitygaps.patch b/dwl-v0.5/patches/main...sevz17:vanitygaps.patch
new file mode 100644
index 0000000..b000e92
--- /dev/null
+++ b/dwl-v0.5/patches/main...sevz17:vanitygaps.patch
@@ -0,0 +1,345 @@
+From 4299a2f759318262f2f2ec8467eb48feb912ccf4 Mon Sep 17 00:00:00 2001
+From: Bonicgamer <44382222+Bonicgamer@users.noreply.github.com>
+Date: Mon, 17 Aug 2020 14:48:24 -0400
+Subject: [PATCH 1/2] Implement vanitygaps
+
+---
+ config.def.h | 21 ++++++++
+ dwl.c | 150 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 161 insertions(+), 10 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 447ba0051..7958f3436 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -1,7 +1,12 @@
+ /* appearance */
+ static const int sloppyfocus = 1; /* focus follows mouse */
+ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
++static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
++static const unsigned int gappih = 10; /* horiz inner gap between windows */
++static const unsigned int gappiv = 10; /* vert inner gap between windows */
++static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */
++static const unsigned int gappov = 10; /* vert outer gap between windows and screen edge */
+ static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
+ static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0};
+ /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */
+@@ -117,6 +122,22 @@ static const Key keys[] = {
+ { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
++ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
++ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
++ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
++ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
++ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
+ { MODKEY, XKB_KEY_Return, zoom, {0} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
+diff --git a/dwl.c b/dwl.c
+index 4ff5c37f7..57a4a95f1 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -185,6 +185,10 @@ struct Monitor {
+ struct wlr_box w; /* window area, layout-relative */
+ struct wl_list layers[4]; /* LayerSurface::link */
+ const Layout *lt[2];
++ int gappih; /* horizontal gap between windows */
++ int gappiv; /* vertical gap between windows */
++ int gappoh; /* horizontal outer gaps */
++ int gappov; /* vertical outer gaps */
+ unsigned int seltags;
+ unsigned int sellt;
+ uint32_t tagset[2];
+@@ -246,6 +250,7 @@ static void createmon(struct wl_listener *listener, void *data);
+ static void createnotify(struct wl_listener *listener, void *data);
+ static void createpointer(struct wlr_pointer *pointer);
+ static void cursorframe(struct wl_listener *listener, void *data);
++static void defaultgaps(const Arg *arg);
+ static void destroydragicon(struct wl_listener *listener, void *data);
+ static void destroyidleinhibitor(struct wl_listener *listener, void *data);
+ static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
+@@ -262,6 +267,13 @@ static Client *focustop(Monitor *m);
+ static void fullscreennotify(struct wl_listener *listener, void *data);
+ static void handlesig(int signo);
+ static void incnmaster(const Arg *arg);
++static void incgaps(const Arg *arg);
++static void incigaps(const Arg *arg);
++static void incihgaps(const Arg *arg);
++static void incivgaps(const Arg *arg);
++static void incogaps(const Arg *arg);
++static void incohgaps(const Arg *arg);
++static void incovgaps(const Arg *arg);
+ static void inputdevice(struct wl_listener *listener, void *data);
+ static int keybinding(uint32_t mods, xkb_keysym_t sym);
+ static void keypress(struct wl_listener *listener, void *data);
+@@ -291,6 +303,7 @@ static void run(char *startup_cmd);
+ static void setcursor(struct wl_listener *listener, void *data);
+ static void setfloating(Client *c, int floating);
+ static void setfullscreen(Client *c, int fullscreen);
++static void setgaps(int oh, int ov, int ih, int iv);
+ static void setlayout(const Arg *arg);
+ static void setmfact(const Arg *arg);
+ static void setmon(Client *c, Monitor *m, uint32_t newtags);
+@@ -304,6 +317,7 @@ static void tagmon(const Arg *arg);
+ static void tile(Monitor *m);
+ static void togglefloating(const Arg *arg);
+ static void togglefullscreen(const Arg *arg);
++static void togglegaps(const Arg *arg);
+ static void toggletag(const Arg *arg);
+ static void toggleview(const Arg *arg);
+ static void unlocksession(struct wl_listener *listener, void *data);
+@@ -367,6 +381,8 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+
++static int enablegaps = 1; /* enables gaps, used by togglegaps */
++
+ /* global event handlers */
+ static struct wl_listener cursor_axis = {.notify = axisnotify};
+ static struct wl_listener cursor_button = {.notify = buttonpress};
+@@ -907,6 +923,11 @@ createmon(struct wl_listener *listener, void *data)
+ /* Initialize monitor state using configured rules */
+ for (i = 0; i < LENGTH(m->layers); i++)
+ wl_list_init(&m->layers[i]);
++
++ m->gappih = gappih;
++ m->gappiv = gappiv;
++ m->gappoh = gappoh;
++ m->gappov = gappov;
+ m->tagset[0] = m->tagset[1] = 1;
+ for (r = monrules; r < END(monrules); r++) {
+ if (!r->name || strstr(wlr_output->name, r->name)) {
+@@ -1069,6 +1090,12 @@ cursorframe(struct wl_listener *listener, void *data)
+ wlr_seat_pointer_notify_frame(seat);
+ }
+
++void
++defaultgaps(const Arg *arg)
++{
++ setgaps(gappoh, gappov, gappih, gappiv);
++}
++
+ void
+ destroydragicon(struct wl_listener *listener, void *data)
+ {
+@@ -1359,6 +1386,83 @@ incnmaster(const Arg *arg)
+ arrange(selmon);
+ }
+
++void
++incgaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh + arg->i,
++ selmon->gappov + arg->i,
++ selmon->gappih + arg->i,
++ selmon->gappiv + arg->i
++ );
++}
++
++void
++incigaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh,
++ selmon->gappov,
++ selmon->gappih + arg->i,
++ selmon->gappiv + arg->i
++ );
++}
++
++void
++incihgaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh,
++ selmon->gappov,
++ selmon->gappih + arg->i,
++ selmon->gappiv
++ );
++}
++
++void
++incivgaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh,
++ selmon->gappov,
++ selmon->gappih,
++ selmon->gappiv + arg->i
++ );
++}
++
++void
++incogaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh + arg->i,
++ selmon->gappov + arg->i,
++ selmon->gappih,
++ selmon->gappiv
++ );
++}
++
++void
++incohgaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh + arg->i,
++ selmon->gappov,
++ selmon->gappih,
++ selmon->gappiv
++ );
++}
++
++void
++incovgaps(const Arg *arg)
++{
++ setgaps(
++ selmon->gappoh,
++ selmon->gappov + arg->i,
++ selmon->gappih,
++ selmon->gappiv
++ );
++}
++
+ void
+ inputdevice(struct wl_listener *listener, void *data)
+ {
+@@ -2054,6 +2158,16 @@ setfullscreen(Client *c, int fullscreen)
+ printstatus();
+ }
+
++void
++setgaps(int oh, int ov, int ih, int iv)
++{
++ selmon->gappoh = MAX(oh, 0);
++ selmon->gappov = MAX(ov, 0);
++ selmon->gappih = MAX(ih, 0);
++ selmon->gappiv = MAX(iv, 0);
++ arrange(selmon);
++}
++
+ void
+ setlayout(const Arg *arg)
+ {
+@@ -2355,7 +2469,7 @@ tagmon(const Arg *arg)
+ void
+ tile(Monitor *m)
+ {
+- unsigned int i, n = 0, mw, my, ty;
++ unsigned int i, n = 0, h, r, oe = enablegaps, ie = enablegaps, mw, my, ty;
+ Client *c;
+
+ wl_list_for_each(c, &clients, link)
+@@ -2363,23 +2477,32 @@ tile(Monitor *m)
+ n++;
+ if (n == 0)
+ return;
++
++ if (smartgaps == n) {
++ oe = 0; // outer gaps disabled
++ }
+
+ if (n > m->nmaster)
+- mw = m->nmaster ? m->w.width * m->mfact : 0;
++ mw = m->nmaster ? (m->w.width + m->gappiv*ie) * m->mfact : 0;
+ else
+- mw = m->w.width;
+- i = my = ty = 0;
++ mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie;
++ i = 0;
++ my = ty = m->gappoh*oe;
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+ if (i < m->nmaster) {
+- resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
+- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
+- my += c->geom.height;
++ r = MIN(n, m->nmaster) - i;
++ h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
++ resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my,
++ .width = mw - m->gappiv*ie, .height = h}, 0);
++ my += c->geom.height + m->gappih*ie;
+ } else {
+- resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
+- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
+- ty += c->geom.height;
++ r = n - i;
++ h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r;
++ resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty,
++ .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0);
++ ty += c->geom.height + m->gappih*ie;
+ }
+ i++;
+ }
+@@ -2402,6 +2525,13 @@ togglefullscreen(const Arg *arg)
+ setfullscreen(sel, !sel->isfullscreen);
+ }
+
++void
++togglegaps(const Arg *arg)
++{
++ enablegaps = !enablegaps;
++ arrange(selmon);
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+
+From ff6efd2fe93e2dcb2885951dc64eefdb2afd61da Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?=
+ <leohdz172@protonmail.com>
+Date: Wed, 20 Jul 2022 00:15:32 -0500
+Subject: [PATCH 2/2] allow gaps in monocle layout if requested
+
+---
+ config.def.h | 1 +
+ dwl.c | 6 +++++-
+ 2 files changed, 6 insertions(+), 1 deletion(-)
+
+diff --git a/config.def.h b/config.def.h
+index 7958f3436..ed2cdae36 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -2,6 +2,7 @@
+ static const int sloppyfocus = 1; /* focus follows mouse */
+ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
+ static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */
++static const int monoclegaps = 0; /* 1 means outer gaps in monocle layout */
+ static const unsigned int borderpx = 1; /* border pixel of windows */
+ static const unsigned int gappih = 10; /* horiz inner gap between windows */
+ static const unsigned int gappiv = 10; /* vert inner gap between windows */
+diff --git a/dwl.c b/dwl.c
+index 57a4a95f1..1705bce5c 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -1728,8 +1728,12 @@ monocle(Monitor *m)
+ wl_list_for_each(c, &clients, link) {
+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
+ continue;
+- resize(c, m->w, 0);
+ n++;
++ if (!monoclegaps)
++ resize(c, m->w, 0);
++ else
++ resize(c, (struct wlr_box){.x = m->w.x + gappoh, .y = m->w.y + gappov,
++ .width = m->w.width - 2 * gappoh, .height = m->w.height - 2 * gappov}, 0);
+ }
+ if (n)
+ snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n);
diff --git a/dwl-v0.5/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml b/dwl-v0.5/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
new file mode 100644
index 0000000..4fe5b73
--- /dev/null
+++ b/dwl-v0.5/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="net_tapesoftware_dwl_wm_unstable_v1">
+ <copyright>
+ Copyright (c) 2021 Raphael Robatsch
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice (including the
+ next paragraph) shall be included in all copies or substantial portions
+ of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ </copyright>
+
+ <interface name="znet_tapesoftware_dwl_wm_v1" version="1">
+ <description summary="control the dwl state">
+ This interface is exposed as a global in the wl_registry.
+
+ Clients can use this protocol to receive updates of the window manager
+ state (active tags, active layout, and focused window).
+ Clients can also control this state.
+
+ After binding, the client will receive the available tags and layouts
+ with the 'tag' and 'layout' events. These can be used in subsequent
+ dwl_wm_monitor_v1.set_tags/set_layout requests, and to interpret the
+ dwl_wm_monitor_v1.layout/tag events.
+ </description>
+
+ <request name="release" type="destructor">
+ <description summary="release dwl_wm">
+ This request indicates that the client will not use the dwl_wm
+ object any more. Objects that have been created through this instance
+ are not affected.
+ </description>
+ </request>
+
+ <request name="get_monitor">
+ <description summary="gets a dwl monitor from an output">
+ Gets a dwl monitor for the specified output. The window manager
+ state on the output can be controlled using the monitor.
+ </description>
+ <arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" />
+ <arg name="output" type="object" interface="wl_output" />
+ </request>
+
+ <event name="tag">
+ <description summary="announces the presence of a tag">
+ This event is sent immediately after binding.
+ A roundtrip after binding guarantees that the client has received all tags.
+ </description>
+ <arg name="name" type="string"/>
+ </event>
+
+ <event name="layout">
+ <description summary="announces the presence of a layout">
+ This event is sent immediately after binding.
+ A roundtrip after binding guarantees that the client has received all layouts.
+ </description>
+ <arg name="name" type="string"/>
+ </event>
+ </interface>
+
+ <interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1">
+ <description summary="control one monitor">
+ Observes and controls one monitor.
+
+ Events are double-buffered: Clients should cache all events and only
+ redraw themselves once the 'frame' event is sent.
+
+ Requests are not double-buffered: The compositor will update itself
+ immediately.
+ </description>
+
+ <enum name="tag_state">
+ <entry name="none" value="0" summary="no state"/>
+ <entry name="active" value="1" summary="tag is active"/>
+ <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
+ </enum>
+
+ <request name="release" type="destructor">
+ <description summary="release dwl_monitor">
+ This request indicates that the client is done with this dwl_monitor.
+ All further requests are ignored.
+ </description>
+ </request>
+
+ <event name="selected">
+ <description summary="updates the selected state of the monitor">
+ If 'selected' is nonzero, this monitor is the currently selected one.
+ </description>
+ <arg name="selected" type="uint"/>
+ </event>
+
+ <event name="tag">
+ <description summary="updates the state of one tag">
+ Announces the update of a tag. num_clients and focused_client can be
+ used to draw client indicators.
+ </description>
+ <arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." />
+ <arg name="state" type="uint" enum="tag_state"/>
+ <arg name="num_clients" type="uint" summary="number of clients on this tag"/>
+ <arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/>
+ </event>
+
+ <event name="layout">
+ <description summary="updates the selected layout">
+ Announces the update of the selected layout.
+ </description>
+ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
+ </event>
+
+ <event name="title">
+ <description summary="updates the focused client">
+ Announces the update of the selected client.
+ </description>
+ <arg name="title" type="string"/>
+ </event>
+
+ <event name="frame">
+ <description summary="end of status update sequence">
+ Sent after all other events belonging to the status update has been sent.
+ Clients should redraw themselves now.
+ </description>
+ </event>
+
+ <request name="set_tags">
+ <description summary="sets the active tags on this monitor.">
+ Changes are applied immediately.
+ </description>
+ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
+ <arg name="toggle_tagset" type="uint"/>
+ </request>
+
+ <request name="set_client_tags">
+ <description summary="updates the tags of the focused client.">
+ tags are updated as follows:
+ new_tags = (current_tags AND and_tags) XOR xor_tags
+
+ Changes are applied immediately.
+ </description>
+ <arg name="and_tags" type="uint"/>
+ <arg name="xor_tags" type="uint"/>
+ </request>
+
+ <request name="set_layout">
+ <description summary="sets the active layout on this monitor.">
+ Changes are applied immediately.
+ </description>
+ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
+ </request>
+ </interface>
+</protocol>
diff --git a/dwl-v0.5/protocols/wlr-layer-shell-unstable-v1.xml b/dwl-v0.5/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 0000000..d62fd51
--- /dev/null
+++ b/dwl-v0.5/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_layer_shell_unstable_v1">
+ <copyright>
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_layer_shell_v1" version="4">
+ <description summary="create surfaces that are layers of the desktop">
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+ </description>
+
+ <request name="get_layer_surface">
+ <description summary="create a layer_surface from a surface">
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer surface from a wl_surface which has a buffer attached
+ or committed is a client error, and any attempts by a client to attach
+ or manipulate a buffer prior to the first layer_surface.configure call
+ must also be treated as errors.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
+ <arg name="namespace" type="string" summary="namespace for the layer surface"/>
+ </request>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface has another role"/>
+ <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
+ <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
+ </enum>
+
+ <enum name="layer">
+ <description summary="available layers for surfaces">
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+ </description>
+
+ <entry name="background" value="0"/>
+ <entry name="bottom" value="1"/>
+ <entry name="top" value="2"/>
+ <entry name="overlay" value="3"/>
+ </enum>
+
+ <!-- Version 3 additions -->
+
+ <request name="destroy" type="destructor" since="3">
+ <description summary="destroy the layer_shell object">
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="4">
+ <description summary="layer metadata interface">
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (layer, size, anchor, exclusive zone,
+ margin, interactivity) is double-buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+ </description>
+
+ <request name="set_size">
+ <description summary="sets the size of the surface">
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </request>
+
+ <request name="set_anchor">
+ <description summary="configures the anchor point of the surface">
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"/>
+ </request>
+
+ <request name="set_exclusive_zone">
+ <description summary="configures the exclusive geometry of this surface">
+ Requests that the compositor avoids occluding an area with other
+ surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="zone" type="int"/>
+ </request>
+
+ <request name="set_margin">
+ <description summary="sets a margin from the anchor point">
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="right" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="left" type="int"/>
+ </request>
+
+ <enum name="keyboard_interactivity">
+ <description summary="types of keyboard interaction possible for a layer shell surface">
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+ </description>
+
+ <entry name="none" value="0">
+ <description summary="no keyboard focus is possible">
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+ </description>
+ </entry>
+ <entry name="exclusive" value="1">
+ <description summary="request exclusive keyboard focus">
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+ </description>
+ </entry>
+ <entry name="on_demand" value="2" since="4">
+ <description summary="request regular keyboard focus semantics">
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign this layer_surface as an xdg_popup parent">
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="popup" type="object" interface="xdg_popup"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the layer_surface">
+ This request destroys the layer surface.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event asks the client to resize its surface.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ The client is free to dismiss all but the last configure event it
+ received.
+
+ The width and height arguments specify the size of the window in
+ surface-local coordinates.
+
+ The size is a hint, in the sense that the client is free to ignore it if
+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+ resize in steps of NxM pixels). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+ </description>
+ <arg name="serial" type="uint"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </event>
+
+ <event name="closed">
+ <description summary="surface should be closed">
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+ </description>
+ </event>
+
+ <enum name="error">
+ <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
+ <entry name="invalid_size" value="1" summary="size is invalid"/>
+ <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
+ <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
+ </enum>
+
+ <enum name="anchor" bitfield="true">
+ <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
+ <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
+ <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
+ <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
+ </enum>
+
+ <!-- Version 2 additions -->
+
+ <request name="set_layer" since="2">
+ <description summary="change the layer of the surface">
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/dwl-v0.5/util.c b/dwl-v0.5/util.c
new file mode 100644
index 0000000..cca7c19
--- /dev/null
+++ b/dwl-v0.5/util.c
@@ -0,0 +1,35 @@
+/* See LICENSE.dwm file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "util.h"
+
+void
+die(const char *fmt, ...) {
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+
+ exit(1);
+}
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = calloc(nmemb, size)))
+ die("calloc:");
+ return p;
+}
diff --git a/dwl-v0.5/util.h b/dwl-v0.5/util.h
new file mode 100644
index 0000000..4c94117
--- /dev/null
+++ b/dwl-v0.5/util.h
@@ -0,0 +1,4 @@
+/* See LICENSE.dwm file for copyright and license details. */
+
+void die(const char *fmt, ...);
+void *ecalloc(size_t nmemb, size_t size);
diff --git a/dwl-v0.5/wayland-ipc.patch b/dwl-v0.5/wayland-ipc.patch
new file mode 100644
index 0000000..5283eaa
--- /dev/null
+++ b/dwl-v0.5/wayland-ipc.patch
@@ -0,0 +1,498 @@
+Add wayland protocol extenion for controlling dwl
+
+This adds a wayland protocol extension that allows clients to observe and control the window management state.
+
+Supported features:
+ * observing/updating active tags and layout
+ * observing the selected output and client
+ * updating the tags of the selected client
+
+This patch adds the .xml file and feeds it into wayland-scanner.
+
+Co-authored-by: Palanix <palanixyt@gmail.com>
+diff --git a/Makefile b/Makefile
+index ccca079..d72dea8 100644
+--- a/Makefile
++++ b/Makefile
+@@ -14,9 +14,9 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CF
+ LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS)
+
+ all: dwl
+-dwl: dwl.o util.o
+- $(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
+-dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h
++dwl: dwl.o util.o net-tapesoftware-dwl-wm-unstable-v1-protocol.o
++ $(CC) dwl.o util.o net-tapesoftware-dwl-wm-unstable-v1-protocol.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@
++dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h net-tapesoftware-dwl-wm-unstable-v1-protocol.o
+ util.o: util.c util.h
+
+ # wayland-scanner is a tool which generates C headers and rigging for Wayland
+@@ -32,6 +32,14 @@ wlr-layer-shell-unstable-v1-protocol.h:
+ $(WAYLAND_SCANNER) server-header \
+ protocols/wlr-layer-shell-unstable-v1.xml $@
+
++net-tapesoftware-dwl-wm-unstable-v1-protocol.h: protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
++ $(WAYLAND_SCANNER) server-header \
++ protocols/net-tapesoftware-dwl-wm-unstable-v1.xml $@
++net-tapesoftware-dwl-wm-unstable-v1-protocol.c: protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
++ $(WAYLAND_SCANNER) private-code \
++ protocols/net-tapesoftware-dwl-wm-unstable-v1.xml $@
++net-tapesoftware-dwl-wm-unstable-v1-protocol.o: net-tapesoftware-dwl-wm-unstable-v1-protocol.h
++
+ config.h:
+ cp config.def.h $@
+ clean:
+diff --git a/dwl.c b/dwl.c
+index 8043bf9..f2977fc 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -52,6 +52,7 @@
+ #include <wlr/types/wlr_xdg_shell.h>
+ #include <wlr/util/log.h>
+ #include <xkbcommon/xkbcommon.h>
++#include "net-tapesoftware-dwl-wm-unstable-v1-protocol.h"
+ #ifdef XWAYLAND
+ #include <wlr/xwayland.h>
+ #include <X11/Xlib.h>
+@@ -172,6 +173,12 @@ typedef struct {
+ void (*arrange)(Monitor *);
+ } Layout;
+
++typedef struct {
++ struct wl_list link;
++ struct wl_resource *resource;
++ struct Monitor *monitor;
++} DwlWmMonitor;
++
+ struct Monitor {
+ struct wl_list link;
+ struct wlr_output *wlr_output;
+@@ -184,6 +191,7 @@ struct Monitor {
+ struct wlr_box m; /* monitor area, layout-relative */
+ struct wlr_box w; /* window area, layout-relative */
+ struct wl_list layers[4]; /* LayerSurface::link */
++ struct wl_list dwl_wm_monitor_link;
+ const Layout *lt[2];
+ unsigned int seltags;
+ unsigned int sellt;
+@@ -318,6 +326,10 @@ static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **
+ Client **pc, LayerSurface **pl, double *nx, double *ny);
+ static void zoom(const Arg *arg);
+
++static void dwl_wm_bind(struct wl_client *client, void *data,
++ uint32_t version, uint32_t id);
++static void dwl_wm_printstatus(Monitor *monitor);
++
+ /* variables */
+ static const char broken[] = "broken";
+ static const char *cursor_image = "left_ptr";
+@@ -685,6 +697,7 @@ void
+ cleanupmon(struct wl_listener *listener, void *data)
+ {
+ Monitor *m = wl_container_of(listener, m, destroy);
++ DwlWmMonitor *mon, *montmp;
+ LayerSurface *l, *tmp;
+ int i;
+
+@@ -697,6 +710,10 @@ cleanupmon(struct wl_listener *listener, void *data)
+ wl_list_remove(&m->link);
+ m->wlr_output->data = NULL;
+ wlr_output_layout_remove(output_layout, m->wlr_output);
++ wl_list_for_each_safe(mon, montmp, &m->dwl_wm_monitor_link, link) {
++ wl_resource_set_user_data(mon->resource, NULL);
++ free(mon);
++ }
+ wlr_scene_output_destroy(m->scene_output);
+ wlr_scene_node_destroy(&m->fullscreen_bg->node);
+
+@@ -903,6 +920,7 @@ createmon(struct wl_listener *listener, void *data)
+ const MonitorRule *r;
+ size_t i;
+ Monitor *m = wlr_output->data = ecalloc(1, sizeof(*m));
++ wl_list_init(&m->dwl_wm_monitor_link);
+ m->wlr_output = wlr_output;
+
+ wlr_output_init_render(wlr_output, alloc, drw);
+@@ -1852,6 +1870,7 @@ printstatus(void)
+ printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags],
+ sel, urg);
+ printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol);
++ dwl_wm_printstatus(m);
+ }
+ fflush(stdout);
+ }
+@@ -2289,6 +2308,7 @@ setup(void)
+ wl_signal_add(&output_mgr->events.test, &output_mgr_test);
+
+ wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend));
++ wl_global_create(dpy, &znet_tapesoftware_dwl_wm_v1_interface, 1, NULL, dwl_wm_bind);
+
+ #ifdef XWAYLAND
+ /*
+@@ -2828,3 +2848,193 @@ main(int argc, char *argv[])
+ usage:
+ die("Usage: %s [-v] [-s startup command]", argv[0]);
+ }
++
++/* dwl_wm_monitor_v1 */
++static void
++dwl_wm_monitor_handle_release(struct wl_client *client, struct wl_resource *resource)
++{
++ wl_resource_destroy(resource);
++}
++
++static void
++dwl_wm_monitor_handle_destroy(struct wl_resource *resource)
++{
++ DwlWmMonitor *mon = wl_resource_get_user_data(resource);
++ if (mon) {
++ wl_list_remove(&mon->link);
++ free(mon);
++ }
++}
++
++static void
++dwl_wm_printstatus_to(Monitor *m, const DwlWmMonitor *mon)
++{
++ Client *c, *focused;
++ int tagmask, state, numclients, focused_client;
++ focused = focustop(m);
++ znet_tapesoftware_dwl_wm_monitor_v1_send_selected(mon->resource, m == selmon);
++
++ for (int tag = 0; tag<LENGTH(tags); tag++) {
++ numclients = state = 0;
++ focused_client = -1;
++ tagmask = 1 << tag;
++ if ((tagmask & m->tagset[m->seltags]) != 0)
++ state = state | ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE;
++ wl_list_for_each(c, &clients, link) {
++ if (c->mon != m)
++ continue;
++ if (!(c->tags & tagmask))
++ continue;
++ if (c == focused)
++ focused_client = numclients;
++ numclients++;
++ if (c->isurgent)
++ state = state | ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT;
++ }
++ znet_tapesoftware_dwl_wm_monitor_v1_send_tag(mon->resource,
++ tag, state, numclients, focused_client);
++ }
++ znet_tapesoftware_dwl_wm_monitor_v1_send_layout(mon->resource, m->lt[m->sellt] - layouts);
++ znet_tapesoftware_dwl_wm_monitor_v1_send_title(mon->resource,
++ focused ? client_get_title(focused) : "");
++ znet_tapesoftware_dwl_wm_monitor_v1_send_frame(mon->resource);
++}
++
++static void
++dwl_wm_printstatus(Monitor *m)
++{
++ DwlWmMonitor *mon;
++ wl_list_for_each(mon, &m->dwl_wm_monitor_link, link) {
++ dwl_wm_printstatus_to(m, mon);
++ }
++}
++
++static void
++dwl_wm_monitor_handle_set_tags(struct wl_client *client, struct wl_resource *resource,
++ uint32_t t, uint32_t toggle_tagset)
++{
++ DwlWmMonitor *mon;
++ Monitor *m;
++ mon = wl_resource_get_user_data(resource);
++ if (!mon)
++ return;
++ m = mon->monitor;
++ if ((t & TAGMASK) == m->tagset[m->seltags])
++ return;
++ if (toggle_tagset)
++ m->seltags ^= 1;
++ if (t & TAGMASK)
++ m->tagset[m->seltags] = t & TAGMASK;
++
++ focusclient(focustop(m), 1);
++ arrange(m);
++ printstatus();
++}
++
++static void
++dwl_wm_monitor_handle_set_layout(struct wl_client *client, struct wl_resource *resource,
++ uint32_t layout)
++{
++ DwlWmMonitor *mon;
++ Monitor *m;
++ mon = wl_resource_get_user_data(resource);
++ if (!mon)
++ return;
++ m = mon->monitor;
++ if (layout >= LENGTH(layouts))
++ return;
++ if (layout != m->lt[m->sellt] - layouts)
++ m->sellt ^= 1;
++
++ m->lt[m->sellt] = &layouts[layout];
++ arrange(m);
++ printstatus();
++}
++
++static void
++dwl_wm_monitor_handle_set_client_tags(struct wl_client *client, struct wl_resource *resource,
++ uint32_t and, uint32_t xor)
++{
++ DwlWmMonitor *mon;
++ Client *sel;
++ unsigned int newtags;
++ mon = wl_resource_get_user_data(resource);
++ if (!mon)
++ return;
++ sel = focustop(mon->monitor);
++ if (!sel)
++ return;
++ newtags = (sel->tags & and) ^ xor;
++ if (newtags) {
++ sel->tags = newtags;
++ focusclient(focustop(selmon), 1);
++ arrange(selmon);
++ printstatus();
++ }
++}
++
++static const struct znet_tapesoftware_dwl_wm_monitor_v1_interface dwl_wm_monitor_implementation = {
++ .release = dwl_wm_monitor_handle_release,
++ .set_tags = dwl_wm_monitor_handle_set_tags,
++ .set_layout = dwl_wm_monitor_handle_set_layout,
++ .set_client_tags = dwl_wm_monitor_handle_set_client_tags,
++};
++
++/* dwl_wm_v1 */
++static void
++dwl_wm_handle_release(struct wl_client *client, struct wl_resource *resource)
++{
++ wl_resource_destroy(resource);
++}
++
++static void
++dwl_wm_handle_get_monitor(struct wl_client *client, struct wl_resource *resource,
++ uint32_t id, struct wl_resource *output)
++{
++ DwlWmMonitor *dwl_wm_monitor;
++ struct wlr_output *wlr_output = wlr_output_from_resource(output);
++ struct Monitor *m = wlr_output->data;
++ struct wl_resource *dwlOutputResource = wl_resource_create(client,
++ &znet_tapesoftware_dwl_wm_monitor_v1_interface, wl_resource_get_version(resource), id);
++ if (!resource) {
++ wl_client_post_no_memory(client);
++ return;
++ }
++ dwl_wm_monitor = calloc(1, sizeof(DwlWmMonitor));
++ dwl_wm_monitor->resource = dwlOutputResource;
++ dwl_wm_monitor->monitor = m;
++ wl_resource_set_implementation(dwlOutputResource, &dwl_wm_monitor_implementation,
++ dwl_wm_monitor, dwl_wm_monitor_handle_destroy);
++ wl_list_insert(&m->dwl_wm_monitor_link, &dwl_wm_monitor->link);
++ dwl_wm_printstatus_to(m, dwl_wm_monitor);
++}
++
++static void
++dwl_wm_handle_destroy(struct wl_resource *resource)
++{
++ /* no state to destroy */
++}
++
++static const struct znet_tapesoftware_dwl_wm_v1_interface dwl_wm_implementation = {
++ .release = dwl_wm_handle_release,
++ .get_monitor = dwl_wm_handle_get_monitor,
++};
++
++static void
++dwl_wm_bind(struct wl_client *client, void *data,
++ uint32_t version, uint32_t id)
++{
++ struct wl_resource *resource = wl_resource_create(client,
++ &znet_tapesoftware_dwl_wm_v1_interface, version, id);
++ if (!resource) {
++ wl_client_post_no_memory(client);
++ return;
++ }
++
++ wl_resource_set_implementation(resource, &dwl_wm_implementation, NULL, dwl_wm_handle_destroy);
++
++ for (int i = 0; i < LENGTH(tags); i++)
++ znet_tapesoftware_dwl_wm_v1_send_tag(resource, tags[i]);
++ for (int i = 0; i < LENGTH(layouts); i++)
++ znet_tapesoftware_dwl_wm_v1_send_layout(resource, layouts[i].symbol);
++}
+diff --git a/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml b/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
+new file mode 100644
+index 0000000..4fe5b73
+--- /dev/null
++++ b/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
+@@ -0,0 +1,164 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<protocol name="net_tapesoftware_dwl_wm_unstable_v1">
++ <copyright>
++ Copyright (c) 2021 Raphael Robatsch
++
++ Permission is hereby granted, free of charge, to any person obtaining a
++ copy of this software and associated documentation files (the
++ "Software"), to deal in the Software without restriction, including
++ without limitation the rights to use, copy, modify, merge, publish,
++ distribute, sublicense, and/or sell copies of the Software, and to
++ permit persons to whom the Software is furnished to do so, subject to
++ the following conditions:
++
++ The above copyright notice and this permission notice (including the
++ next paragraph) shall be included in all copies or substantial portions
++ of the Software.
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
++ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
++ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ </copyright>
++
++ <interface name="znet_tapesoftware_dwl_wm_v1" version="1">
++ <description summary="control the dwl state">
++ This interface is exposed as a global in the wl_registry.
++
++ Clients can use this protocol to receive updates of the window manager
++ state (active tags, active layout, and focused window).
++ Clients can also control this state.
++
++ After binding, the client will receive the available tags and layouts
++ with the 'tag' and 'layout' events. These can be used in subsequent
++ dwl_wm_monitor_v1.set_tags/set_layout requests, and to interpret the
++ dwl_wm_monitor_v1.layout/tag events.
++ </description>
++
++ <request name="release" type="destructor">
++ <description summary="release dwl_wm">
++ This request indicates that the client will not use the dwl_wm
++ object any more. Objects that have been created through this instance
++ are not affected.
++ </description>
++ </request>
++
++ <request name="get_monitor">
++ <description summary="gets a dwl monitor from an output">
++ Gets a dwl monitor for the specified output. The window manager
++ state on the output can be controlled using the monitor.
++ </description>
++ <arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" />
++ <arg name="output" type="object" interface="wl_output" />
++ </request>
++
++ <event name="tag">
++ <description summary="announces the presence of a tag">
++ This event is sent immediately after binding.
++ A roundtrip after binding guarantees that the client has received all tags.
++ </description>
++ <arg name="name" type="string"/>
++ </event>
++
++ <event name="layout">
++ <description summary="announces the presence of a layout">
++ This event is sent immediately after binding.
++ A roundtrip after binding guarantees that the client has received all layouts.
++ </description>
++ <arg name="name" type="string"/>
++ </event>
++ </interface>
++
++ <interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1">
++ <description summary="control one monitor">
++ Observes and controls one monitor.
++
++ Events are double-buffered: Clients should cache all events and only
++ redraw themselves once the 'frame' event is sent.
++
++ Requests are not double-buffered: The compositor will update itself
++ immediately.
++ </description>
++
++ <enum name="tag_state">
++ <entry name="none" value="0" summary="no state"/>
++ <entry name="active" value="1" summary="tag is active"/>
++ <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
++ </enum>
++
++ <request name="release" type="destructor">
++ <description summary="release dwl_monitor">
++ This request indicates that the client is done with this dwl_monitor.
++ All further requests are ignored.
++ </description>
++ </request>
++
++ <event name="selected">
++ <description summary="updates the selected state of the monitor">
++ If 'selected' is nonzero, this monitor is the currently selected one.
++ </description>
++ <arg name="selected" type="uint"/>
++ </event>
++
++ <event name="tag">
++ <description summary="updates the state of one tag">
++ Announces the update of a tag. num_clients and focused_client can be
++ used to draw client indicators.
++ </description>
++ <arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." />
++ <arg name="state" type="uint" enum="tag_state"/>
++ <arg name="num_clients" type="uint" summary="number of clients on this tag"/>
++ <arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/>
++ </event>
++
++ <event name="layout">
++ <description summary="updates the selected layout">
++ Announces the update of the selected layout.
++ </description>
++ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
++ </event>
++
++ <event name="title">
++ <description summary="updates the focused client">
++ Announces the update of the selected client.
++ </description>
++ <arg name="title" type="string"/>
++ </event>
++
++ <event name="frame">
++ <description summary="end of status update sequence">
++ Sent after all other events belonging to the status update has been sent.
++ Clients should redraw themselves now.
++ </description>
++ </event>
++
++ <request name="set_tags">
++ <description summary="sets the active tags on this monitor.">
++ Changes are applied immediately.
++ </description>
++ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
++ <arg name="toggle_tagset" type="uint"/>
++ </request>
++
++ <request name="set_client_tags">
++ <description summary="updates the tags of the focused client.">
++ tags are updated as follows:
++ new_tags = (current_tags AND and_tags) XOR xor_tags
++
++ Changes are applied immediately.
++ </description>
++ <arg name="and_tags" type="uint"/>
++ <arg name="xor_tags" type="uint"/>
++ </request>
++
++ <request name="set_layout">
++ <description summary="sets the active layout on this monitor.">
++ Changes are applied immediately.
++ </description>
++ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
++ </request>
++ </interface>
++</protocol>
+--
+2.39.1
+
diff --git a/scripts/swaybg_set b/scripts/swaybg_set
new file mode 100755
index 0000000..be29142
--- /dev/null
+++ b/scripts/swaybg_set
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+swaybg -i ~/.config/wallpaper.png
diff --git a/somebar/.builds/archlinux.yml b/somebar/.builds/archlinux.yml
new file mode 100644
index 0000000..7c4f209
--- /dev/null
+++ b/somebar/.builds/archlinux.yml
@@ -0,0 +1,19 @@
+image: archlinux
+packages:
+ - base-devel
+ - meson
+ - wayland
+ - wayland-protocols
+ - cairo
+ - pango
+sources:
+ - https://git.sr.ht/~raphi/somebar
+tasks:
+ - setup: |
+ cd somebar
+ meson build --fatal-meson-warnings
+ cp src/config.def.hpp src/config.hpp
+ - build: |
+ cd somebar/build
+ ninja
+ sudo ninja install
diff --git a/somebar/.builds/freebsd.yml b/somebar/.builds/freebsd.yml
new file mode 100644
index 0000000..90e6f1a
--- /dev/null
+++ b/somebar/.builds/freebsd.yml
@@ -0,0 +1,20 @@
+image: freebsd/latest
+packages:
+ - devel/evdev-proto
+ - devel/meson
+ - devel/pkgconf
+ - graphics/cairo
+ - graphics/wayland
+ - graphics/wayland-protocols
+ - x11-toolkits/pango
+sources:
+ - https://git.sr.ht/~raphi/somebar
+tasks:
+ - setup: |
+ cd somebar
+ meson build --fatal-meson-warnings
+ cp src/config.def.hpp src/config.hpp
+ - build: |
+ cd somebar/build
+ ninja
+ sudo ninja install \ No newline at end of file
diff --git a/somebar/.editorconfig b/somebar/.editorconfig
new file mode 100644
index 0000000..5c4a037
--- /dev/null
+++ b/somebar/.editorconfig
@@ -0,0 +1,5 @@
+root = true
+
+[*]
+indent_style = tab
+indent_brace_style = K&R
diff --git a/somebar/CHANGELOG.md b/somebar/CHANGELOG.md
new file mode 100644
index 0000000..a24e7ab
--- /dev/null
+++ b/somebar/CHANGELOG.md
@@ -0,0 +1,19 @@
+## [1.0.3] - 2022-12-11
+### Added
+- New patches: markup-in-status-messages, show-status-on-selected-monitor (benc)
+### Fixed
+- Fixed crash when an output disappears
+
+## [1.0.2] - 2022-11-27
+### Fixed
+- Fixed bug where hidden bar could not be shown anymore
+
+## [1.0.1] - 2022-11-25
+### Added
+- Manpage
+- New patches: indicator-size-props, hide-vacant-tags, colorless-status (medanisjbara)
+### Fixed
+- Remove spaces from title and layout symbol (benc)
+
+## [1.0.0] - 2022-04-23
+Initial release
diff --git a/somebar/LICENSE b/somebar/LICENSE
new file mode 100644
index 0000000..7bebc11
--- /dev/null
+++ b/somebar/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2021 Raphael Robatsch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/somebar/README.md b/somebar/README.md
new file mode 100644
index 0000000..b61c935
--- /dev/null
+++ b/somebar/README.md
@@ -0,0 +1,113 @@
+# somebar - dwm-like bar for dwl
+
+![Screenshot](screenshot.png)
+
+The mailing list for this project is
+[~raphi/public-inbox@lists.sr.ht](mailto:~raphi/public-inbox@lists.sr.ht).
+
+## Dependencies
+
+* c++ compiler, meson, and ninja
+* wayland-scanner
+* libwayland-client
+* libwayland-cursor
+* libcairo
+* libpango
+* libpangocairo
+
+```
+sudo apt install build-essential meson ninja-build \
+ libwayland-bin libwayland-client0 libwayland-cursor0 libwayland-dev \
+ libcairo2 libcairo2-dev \
+ libpango-1.0-0 libpango1.0-dev libpangocairo-1.0-0
+
+# or
+
+sudo pacman -S base-devel meson \
+ wayland wayland-protocols cairo pango
+```
+
+## Configuration
+
+Copy `src/config.def.hpp` to `src/config.hpp`, and adjust if needed.
+
+## Building
+
+```
+cp src/config.def.hpp src/config.hpp
+meson setup build
+ninja -C build
+sudo ninja -C build install
+```
+
+## Usage
+
+You must start somebar using dwl's `-s` flag, e.g. `dwl -s somebar`.
+
+Somebar can be controlled by writing to `$XDG_RUNTIME_DIR/somebar-0`
+or the path defined by `-s` argument.
+The following commands are supported:
+
+* `status TEXT`: Updates the status bar
+* `hide MONITOR` Hides somebar on the specified monitor
+* `show MONITOR` Shows somebar on the specified monitor
+* `toggle MONITOR` Toggles somebar on the specified monitor
+
+MONITOR is an zxdg_output_v1 name, which can be determined e.g. using `weston-info`.
+Additionally, MONITOR can be `all` (all monitors) or `selected` (the monitor with focus).
+
+Commands can be sent either by writing to the file name above, or equivalently by calling
+somebar with the `-c` argument. For example: `somebar -c toggle all`. This is recommended
+for shell scripts, as there is no race-free way to write to a file only if it exists.
+
+The maintainer of somebar also maintains
+[someblocks](https://git.sr.ht/~raphi/someblocks/),
+a fork of [dwmblocks](https://github.com/torrinfail/dwmblocks) that outputs
+to somebar instead of dwm's bar.
+
+## IPC
+
+Out of the box, somebar cannot control dwl. Clicking on the tag bar has no
+effect, because there is no communication channel from somebar back to dwl.
+
+If you apply the patch `contrib/ipc.patch`, then somebar will
+
+1. Not read stdin anymore, and instead use a wayland extension to read dwl's
+ state. This means you must close stdin yourself, if you choose to launch
+ somebar using dwl's -s flag.
+2. somebar can use the same wayland extension to send commands back to dwl.
+ This means that clicking on tags will switch to that tag (this can of course
+ be customized in config.h).
+
+If you apply the IPC patch to somebar, then
+**dwl must have the [wayland-ipc patch](https://git.sr.ht/~raphi/dwl/blob/master/patches/wayland-ipc.patch) applied too**,
+since dwl must implement the wayland extension too.
+
+## Other patches
+
+Like all suckless software, somebar can be customized via patches. You can find some patches in the contrib folder with descriptions written in them.
+
+## License
+
+somebar - dwm-like bar for dwl
+
+Copyright (c) 2021 Raphael Robatsch
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/somebar/contrib/clickable-tags-using-wtype.patch b/somebar/contrib/clickable-tags-using-wtype.patch
new file mode 100644
index 0000000..92ce15c
--- /dev/null
+++ b/somebar/contrib/clickable-tags-using-wtype.patch
@@ -0,0 +1,91 @@
+From: Ben Collerson <benc@benc.cc>
+Date: Sat, 1 Apr 2023 09:16:19 +1000
+Subject: [PATCH somebar] clickable tags using wtype
+
+---
+ src/bar.cpp | 3 ++-
+ src/common.hpp | 2 ++
+ src/config.def.hpp | 5 +++++
+ src/main.cpp | 15 +++++++++++++++
+ 4 files changed, 24 insertions(+), 1 deletion(-)
+
+diff --git a/src/bar.cpp b/src/bar.cpp
+index af92f49..8b68759 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -182,9 +182,10 @@ void Bar::click(Monitor* mon, int x, int, int btn)
+ } else if (x > _layoutCmp.x) {
+ control = ClkLayoutSymbol;
+ } else for (int tag = _tags.size()-1; tag >= 0; tag--) {
++ // you may need logic to skip tags if they are hidden elsewhere.
+ if (x > _tags[tag].component.x) {
+ control = ClkTagBar;
+- arg.ui = 1<<tag;
++ arg.ui = tag;
+ argp = &arg;
+ break;
+ }
+diff --git a/src/common.hpp b/src/common.hpp
+index c905358..a386029 100644
+--- a/src/common.hpp
++++ b/src/common.hpp
+@@ -39,6 +39,8 @@ extern wl_compositor* compositor;
+ extern wl_shm* shm;
+ extern zwlr_layer_shell_v1* wlrLayerShell;
+
++void view(Monitor& m, const Arg& arg);
++void tag(Monitor& m, const Arg& arg);
+ void spawn(Monitor&, const Arg& arg);
+ void setCloexec(int fd);
+ [[noreturn]] void die(const char* why);
+diff --git a/src/config.def.hpp b/src/config.def.hpp
+index 40a8c95..de193ea 100644
+--- a/src/config.def.hpp
++++ b/src/config.def.hpp
+@@ -4,6 +4,9 @@
+ #pragma once
+ #include "common.hpp"
+
++#define WTYPE "/usr/bin/wtype"
++#define MODKEY "alt"
++
+ constexpr bool topbar = true;
+
+ constexpr int paddingX = 10;
+@@ -23,5 +26,7 @@ static std::vector<std::string> tagNames = {
+ };
+
+ constexpr Button buttons[] = {
++ { ClkTagBar, BTN_LEFT, view, {0} },
++ { ClkTagBar, BTN_RIGHT, tag, {0} },
+ { ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
+ };
+diff --git a/src/main.cpp b/src/main.cpp
+index 6274959..3c35b20 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -85,6 +85,21 @@ static int statusFifoFd {-1};
+ static int statusFifoWriter {-1};
+ static bool quitting {false};
+
++// update keybindings to match your dwl
++void view(Monitor& m, const Arg& arg)
++{
++ char tag[2];
++ snprintf(tag, 2, "%i", arg.ui + 1);
++ if(fork() == 0)
++ execl(WTYPE, "wtype", "-M", MODKEY, tag, (char*)NULL);
++}
++void tag(Monitor& m, const Arg& arg)
++{
++ char tag[2];
++ snprintf(tag, 2, "%i", arg.ui + 1);
++ if(fork() == 0)
++ execl(WTYPE, "wtype", "-M", MODKEY, "-M", "shift", tag, (char*)NULL);
++}
+ void spawn(Monitor&, const Arg& arg)
+ {
+ if (fork() == 0) {
+--
+2.40.1
+
diff --git a/somebar/contrib/colorless-status.patch b/somebar/contrib/colorless-status.patch
new file mode 100644
index 0000000..f280070
--- /dev/null
+++ b/somebar/contrib/colorless-status.patch
@@ -0,0 +1,15 @@
+From: medanisjbara anis2834133766619@gmail.com
+Date: Mon, 14 Nov 2022 10:28:00
+Description: sets the color of status component to inactive
+diff --git a/src/bar.cpp b/src/bar.cpp
+index fab5a8f..aebe28b 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -266,6 +266,7 @@ void Bar::renderStatus()
+ cairo_fill(_painter);
+
+ _x = start;
++ setColorScheme(colorInactive);
+ renderComponent(_statusCmp);
+ }
+
diff --git a/somebar/contrib/disable-window-title.patch b/somebar/contrib/disable-window-title.patch
new file mode 100644
index 0000000..b1dc9cd
--- /dev/null
+++ b/somebar/contrib/disable-window-title.patch
@@ -0,0 +1,15 @@
+From: Sam Nystrom <samuel.l.nystrom@gmail.com>
+Date: Sat, 4 Mar 2023 17:38:12 -0500
+Description: disable window title
+diff --git a/src/bar.cpp b/src/bar.cpp
+index 507ce62..1b6f771 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -227,7 +227,6 @@ void Bar::render()
+ renderTags();
+ setColorScheme(_selected ? colorActive : colorInactive);
+ renderComponent(_layoutCmp);
+- renderComponent(_titleCmp);
+ renderStatus();
+
+ _painter = nullptr;
diff --git a/somebar/contrib/dwm-like-tag-indicator.patch b/somebar/contrib/dwm-like-tag-indicator.patch
new file mode 100644
index 0000000..c4458e9
--- /dev/null
+++ b/somebar/contrib/dwm-like-tag-indicator.patch
@@ -0,0 +1,34 @@
+From: Henrique Possatto <henriquempossatto@gmail.com>
+Date: Mon, 26 Dec 2022 18:01:35 -0300
+Subject: [PATCH somebar] patch to show square tag indicator like dwm
+diff --git a/src/bar.cpp b/src/bar.cpp
+index 507ce62..4fda8b0 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -245,12 +245,17 @@ void Bar::renderTags()
+ tag.state & TagState::Active ? colorActive : colorInactive,
+ tag.state & TagState::Urgent);
+ renderComponent(tag.component);
+- auto indicators = std::min(tag.numClients, static_cast<int>(_bufs->height/2));
+- for (auto ind = 0; ind < indicators; ind++) {
+- auto w = ind == tag.focusedClient ? 7 : 1;
+- cairo_move_to(_painter, tag.component.x, ind*2+0.5);
+- cairo_rel_line_to(_painter, w, 0);
+- cairo_close_path(_painter);
++
++ if(!tag.numClients)
++ continue;
++
++ int s = barfont.height / 9;
++ int w = barfont.height / 6 + 2;
++ if (tag.focusedClient != -1) {
++ cairo_rectangle(_painter, tag.component.x + s, s, w, w);
++ cairo_fill(_painter);
++ } else {
++ cairo_rectangle(_painter, (double)(tag.component.x + s) + 0.5, (double)(s) + 0.5, w, w);
+ cairo_set_line_width(_painter, 1);
+ cairo_stroke(_painter);
+ }
+--
+2.39.0
+
diff --git a/somebar/contrib/hide-vacant-tags.patch b/somebar/contrib/hide-vacant-tags.patch
new file mode 100644
index 0000000..055dd51
--- /dev/null
+++ b/somebar/contrib/hide-vacant-tags.patch
@@ -0,0 +1,54 @@
+From: medanisjbara anis2834133766619@gmail.com
+Date: Mon, 14 Nov 2022 22:52:00
+Description: somebar equivalent of https://dwm.suckless.org/patches/hide_vacant_tags
+diff --git a/src/bar.cpp b/src/bar.cpp
+index fab5a8f..38e7b5f 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -240,13 +240,36 @@ void Bar::render()
+
+ void Bar::renderTags()
+ {
++ // Check if all tags are active (Mod+0)
++ bool allActive = true;
+ for (auto &tag : _tags) {
++ if (tag.state & TagState::Active){
++ if (!allActive){
++ allActive = true;
++ break;
++ }
++ allActive = false;
++ }
++ }
++
++ bool renderThis;
++ for (auto &tag : _tags) {
++ renderThis = false;
+ setColorScheme(
+ tag.state & TagState::Active ? colorActive : colorInactive,
+ tag.state & TagState::Urgent);
+- renderComponent(tag.component);
++ // Reder active tag if it's the only one active
++ if (!allActive && tag.state & TagState::Active)
++ renderThis = true;
+ auto indicators = std::min(tag.numClients, static_cast<int>(_bufs->height/2));
+ for (auto ind = 0; ind < indicators; ind++) {
++ // render tags having indicators
++ if (tag.focusedClient == -1)
++ renderThis = true;
++ // render tags having the focused client
++ if (tag.focusedClient == 0){
++ renderThis = true;
++ }
+ auto w = ind == tag.focusedClient ? 7 : 1;
+ cairo_move_to(_painter, tag.component.x, ind*2+0.5);
+ cairo_rel_line_to(_painter, w, 0);
+@@ -254,6 +277,8 @@ void Bar::renderTags()
+ cairo_set_line_width(_painter, 1);
+ cairo_stroke(_painter);
+ }
++ if (renderThis)
++ renderComponent(tag.component);
+ }
+ }
+
diff --git a/somebar/contrib/indicator-size-props.patch b/somebar/contrib/indicator-size-props.patch
new file mode 100644
index 0000000..ac17520
--- /dev/null
+++ b/somebar/contrib/indicator-size-props.patch
@@ -0,0 +1,54 @@
+From: medanisjbara anis2834133766619@gmail.com
+Date: Mon, 15 Nov 2022 08:16:00
+Description: lets config.h customize indicators sizes
+diff --git a/src/bar.cpp b/src/bar.cpp
+index fab5a8f..c0e070c 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -247,11 +247,11 @@ void Bar::renderTags()
+ renderComponent(tag.component);
+ auto indicators = std::min(tag.numClients, static_cast<int>(_bufs->height/2));
+ for (auto ind = 0; ind < indicators; ind++) {
+- auto w = ind == tag.focusedClient ? 7 : 1;
++ auto w = ind == tag.focusedClient ? indprops.focused_width : indprops.width;
+ cairo_move_to(_painter, tag.component.x, ind*2+0.5);
+ cairo_rel_line_to(_painter, w, 0);
+ cairo_close_path(_painter);
+- cairo_set_line_width(_painter, 1);
++ cairo_set_line_width(_painter, ind == tag.focusedClient ? indprops.focused_height : indprops.height);
+ cairo_stroke(_painter);
+ }
+ }
+diff --git a/src/common.hpp b/src/common.hpp
+index aed4480..acdca1b 100644
+--- a/src/common.hpp
++++ b/src/common.hpp
+@@ -34,6 +34,13 @@ struct Button {
+ const Arg arg;
+ };
+
++struct IndicatorProps {
++ int width;
++ int height;
++ int focused_width;
++ int focused_height;
++};
++
+ extern wl_display* display;
+ extern wl_compositor* compositor;
+ extern wl_shm* shm;
+diff --git a/src/config.def.hpp b/src/config.def.hpp
+index 40a8c95..d51fee8 100644
+--- a/src/config.def.hpp
++++ b/src/config.def.hpp
+@@ -25,3 +25,10 @@ static std::vector<std::string> tagNames = {
+ constexpr Button buttons[] = {
+ { ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
+ };
++
++constexpr IndicatorProps indprops = {
++ 5, /* unfocused indicator width */
++ 5, /* unfocused indicator height */
++ 7, /* focused indicator width */
++ 1 /* focused indicator height */
++};
diff --git a/somebar/contrib/ipc.patch b/somebar/contrib/ipc.patch
new file mode 100644
index 0000000..80c1a56
--- /dev/null
+++ b/somebar/contrib/ipc.patch
@@ -0,0 +1,506 @@
+Replaces somebar's channel to dwl from stdin to a wayland extension.
+
+Normally, somebar reads dwl's state by reading from stdin. This requires
+that somebar is started from dwl, and does not allow sending commands back
+to dwl.
+
+This patch replaces this channel with a wayland protocol extension. somebar
+can use this protocol extension to observe and update the dwl window management
+state.
+
+Important! dwl must have the wayland-ipc patch applied for this to work!
+https://git.sr.ht/~raphi/dwl/blob/master/patches/wayland-ipc.patch
+diff --git a/protocols/meson.build b/protocols/meson.build
+index 7bd222b..5752608 100644
+--- a/protocols/meson.build
++++ b/protocols/meson.build
+@@ -15,6 +15,7 @@ wayland_xmls = [
+ wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml',
+ wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml',
+ 'wlr-layer-shell-unstable-v1.xml',
++ 'net-tapesoftware-dwl-wm-unstable-v1.xml',
+ ]
+ wayland_sources = [
+ wayland_scanner_code.process(wayland_xmls),
+diff --git a/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml b/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
+new file mode 100644
+index 0000000..390f5a1
+--- /dev/null
++++ b/protocols/net-tapesoftware-dwl-wm-unstable-v1.xml
+@@ -0,0 +1,164 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<protocol name="net_tapesoftware_dwl_wm_unstable_v1">
++ <copyright>
++ Copyright (c) 2021 Raphael Robatsch
++
++ Permission is hereby granted, free of charge, to any person obtaining a
++ copy of this software and associated documentation files (the
++ "Software"), to deal in the Software without restriction, including
++ without limitation the rights to use, copy, modify, merge, publish,
++ distribute, sublicense, and/or sell copies of the Software, and to
++ permit persons to whom the Software is furnished to do so, subject to
++ the following conditions:
++
++ The above copyright notice and this permission notice (including the
++ next paragraph) shall be included in all copies or substantial portions
++ of the Software.
++
++ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
++ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
++ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
++ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
++ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ </copyright>
++
++ <interface name="znet_tapesoftware_dwl_wm_v1" version="1">
++ <description summary="control the dwl state">
++ This interface is exposed as a global in the wl_registry.
++
++ Clients can use this protocol to receive updates of the window manager
++ state (active tags, active layout, and focused window).
++ Clients can also control this state.
++
++ After binding, the client will receive the available tags and layouts
++ with the 'tag' and 'layout' events. These can be used in subsequent
++ dwl_wm_monitor_v1.set_tags/set_layout requests, and to interpret the
++ dwl_wm_monitor_v1.layout/tag events.
++ </description>
++
++ <request name="release" type="destructor">
++ <description summary="release dwl_wm">
++ This request indicates that the client will not use the dwl_wm
++ object any more. Objects that have been created through this instance
++ are not affected.
++ </description>
++ </request>
++
++ <request name="get_monitor">
++ <description summary="gets a dwl monitor from an output">
++ Gets a dwl monitor for the specified output. The window manager
++ state on the output can be controlled using the monitor.
++ </description>
++ <arg name="id" type="new_id" interface="znet_tapesoftware_dwl_wm_monitor_v1" />
++ <arg name="output" type="object" interface="wl_output" />
++ </request>
++
++ <event name="tag">
++ <description summary="announces the presence of a tag">
++ This event is sent immediately after binding.
++ A roundtrip after binding guarantees that the client has received all tags.
++ </description>
++ <arg name="name" type="string"/>
++ </event>
++
++ <event name="layout">
++ <description summary="announces the presence of a layout">
++ This event is sent immediately after binding.
++ A roundtrip after binding guarantees that the client has received all layouts.
++ </description>
++ <arg name="name" type="string"/>
++ </event>
++ </interface>
++
++ <interface name="znet_tapesoftware_dwl_wm_monitor_v1" version="1">
++ <description summary="control one monitor">
++ Observes and controls one monitor.
++
++ Events are double-buffered: Clients should cache all events and only
++ redraw themselves once the 'frame' event is sent.
++
++ Requests are not double-buffered: The compositor will update itself
++ immediately.
++ </description>
++
++ <enum name="tag_state">
++ <entry name="none" value="0" summary="no state"/>
++ <entry name="active" value="1" summary="tag is active"/>
++ <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
++ </enum>
++
++ <request name="release" type="destructor">
++ <description summary="release dwl_monitor">
++ This request indicates that the client is done with this dwl_monitor.
++ All further requests are ignored.
++ </description>
++ </request>
++
++ <event name="selected">
++ <description summary="updates the selected state of the monitor">
++ If 'selected' is nonzero, this monitor is the currently selected one.
++ </description>
++ <arg name="selected" type="uint"/>
++ </event>
++
++ <event name="tag">
++ <description summary="updates the state of one tag">
++ Announces the update of a tag. num_clients and focused_client can be
++ used to draw client indicators.
++ </description>
++ <arg name="tag" type="uint" summary="index of a tag received by the dwl_wm_v1.tag event." />
++ <arg name="state" type="uint" enum="tag_state"/>
++ <arg name="num_clients" type="uint" summary="number of clients on this tag"/>
++ <arg name="focused_client" type="int" summary="out of num_clients. -1 if there is no focused client"/>
++ </event>
++
++ <event name="layout">
++ <description summary="updates the selected layout">
++ Announces the update of the selected layout.
++ </description>
++ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
++ </event>
++
++ <event name="title">
++ <description summary="updates the focused client">
++ Announces the update of the selected client.
++ </description>
++ <arg name="title" type="string"/>
++ </event>
++
++ <event name="frame">
++ <description summary="end of status update sequence">
++ Sent after all other events belonging to the status update has been sent.
++ Clients should redraw themselves now.
++ </description>
++ </event>
++
++ <request name="set_tags">
++ <description summary="sets the active tags on this monitor.">
++ Changes are applied immediately.
++ </description>
++ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
++ <arg name="toggle_tagset" type="uint"/>
++ </request>
++
++ <request name="set_client_tags">
++ <description summary="updates the tags of the focused client.">
++ tags are updated as follows:
++ new_tags = (current_tags AND and_tags) XOR xor_tags
++
++ Changes are applied immediately.
++ </description>
++ <arg name="and_tags" type="uint"/>
++ <arg name="xor_tags" type="uint"/>
++ </request>
++
++ <request name="set_layout">
++ <description summary="sets the active layout on this monitor.">
++ Changes are applied immediately.
++ </description>
++ <arg name="layout" type="uint" summary="index of a layout received by the dwl_wm_v1.layout event."/>
++ </request>
++ </interface>
++</protocol>
+diff --git a/src/common.hpp b/src/common.hpp
+index c905358..9b62a94 100644
+--- a/src/common.hpp
++++ b/src/common.hpp
+@@ -10,6 +10,7 @@
+ #include <cairo/cairo.h>
+ #include <pango/pango.h>
+ #include "wlr-layer-shell-unstable-v1-client-protocol.h"
++#include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h"
+
+ struct Color {
+ Color() {}
+@@ -38,7 +39,14 @@ extern wl_display* display;
+ extern wl_compositor* compositor;
+ extern wl_shm* shm;
+ extern zwlr_layer_shell_v1* wlrLayerShell;
++extern std::vector<std::string> tagNames;
++extern std::vector<std::string> layoutNames;
+
++void view(Monitor& m, const Arg& arg);
++void toggleview(Monitor& m, const Arg& arg);
++void setlayout(Monitor& m, const Arg& arg);
++void tag(Monitor& m, const Arg& arg);
++void toggletag(Monitor& m, const Arg& arg);
+ void spawn(Monitor&, const Arg& arg);
+ void setCloexec(int fd);
+ [[noreturn]] void die(const char* why);
+@@ -65,6 +73,7 @@ WL_DELETER(wl_output, wl_output_release_checked);
+ WL_DELETER(wl_pointer, wl_pointer_release);
+ WL_DELETER(wl_seat, wl_seat_release);
+ WL_DELETER(wl_surface, wl_surface_destroy);
++WL_DELETER(znet_tapesoftware_dwl_wm_monitor_v1, znet_tapesoftware_dwl_wm_monitor_v1_release);
+ WL_DELETER(zwlr_layer_surface_v1, zwlr_layer_surface_v1_destroy);
+
+ WL_DELETER(cairo_t, cairo_destroy);
+diff --git a/src/config.def.hpp b/src/config.def.hpp
+index 40a8c95..a9560cb 100644
+--- a/src/config.def.hpp
++++ b/src/config.def.hpp
+@@ -16,12 +16,11 @@ constexpr ColorScheme colorInactive = {Color(0xbb, 0xbb, 0xbb), Color(0x22, 0x22
+ constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, 0x77)};
+ constexpr const char* termcmd[] = {"foot", nullptr};
+
+-static std::vector<std::string> tagNames = {
+- "1", "2", "3",
+- "4", "5", "6",
+- "7", "8", "9",
+-};
+-
+ constexpr Button buttons[] = {
++ { ClkTagBar, BTN_LEFT, view, {0} },
++ { ClkTagBar, BTN_RIGHT, tag, {0} },
++ { ClkTagBar, BTN_MIDDLE, toggletag, {0} },
++ { ClkLayoutSymbol, BTN_LEFT, setlayout, {.ui = 0} },
++ { ClkLayoutSymbol, BTN_RIGHT, setlayout, {.ui = 2} },
+ { ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
+ };
+diff --git a/src/main.cpp b/src/main.cpp
+index 6274959..01be870 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -3,7 +3,6 @@
+
+ #include <algorithm>
+ #include <cstdio>
+-#include <sstream>
+ #include <list>
+ #include <optional>
+ #include <utility>
+@@ -21,8 +20,8 @@
+ #include "wlr-layer-shell-unstable-v1-client-protocol.h"
+ #include "xdg-output-unstable-v1-client-protocol.h"
+ #include "xdg-shell-client-protocol.h"
++#include "net-tapesoftware-dwl-wm-unstable-v1-client-protocol.h"
+ #include "common.hpp"
+-#include "config.hpp"
+ #include "bar.hpp"
+ #include "line_buffer.hpp"
+
+@@ -34,6 +33,7 @@ struct Monitor {
+ bool desiredVisibility {true};
+ bool hasData;
+ uint32_t tags;
++ wl_unique_ptr<znet_tapesoftware_dwl_wm_monitor_v1> dwlMonitor;
+ };
+
+ struct SeatPointer {
+@@ -54,8 +54,6 @@ static void updatemon(Monitor &mon);
+ static void onReady();
+ static void setupStatusFifo();
+ static void onStatus();
+-static void onStdin();
+-static void handleStdin(const std::string& line);
+ static void updateVisibility(const std::string& name, bool(*updater)(bool));
+ static void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
+ static void onGlobalRemove(void*, wl_registry* registry, uint32_t name);
+@@ -67,6 +65,9 @@ wl_display* display;
+ wl_compositor* compositor;
+ wl_shm* shm;
+ zwlr_layer_shell_v1* wlrLayerShell;
++znet_tapesoftware_dwl_wm_v1* dwlWm;
++std::vector<std::string> tagNames;
++std::vector<std::string> layoutNames;
+ static xdg_wm_base* xdgWmBase;
+ static zxdg_output_manager_v1* xdgOutputManager;
+ static wl_surface* cursorSurface;
+@@ -85,6 +86,26 @@ static int statusFifoFd {-1};
+ static int statusFifoWriter {-1};
+ static bool quitting {false};
+
++void view(Monitor& m, const Arg& arg)
++{
++ znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), arg.ui, 1);
++}
++void toggleview(Monitor& m, const Arg& arg)
++{
++ znet_tapesoftware_dwl_wm_monitor_v1_set_tags(m.dwlMonitor.get(), m.tags ^ arg.ui, 0);
++}
++void setlayout(Monitor& m, const Arg& arg)
++{
++ znet_tapesoftware_dwl_wm_monitor_v1_set_layout(m.dwlMonitor.get(), arg.ui);
++}
++void tag(Monitor& m, const Arg& arg)
++{
++ znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), 0, arg.ui);
++}
++void toggletag(Monitor& m, const Arg& arg)
++{
++ znet_tapesoftware_dwl_wm_monitor_v1_set_client_tags(m.dwlMonitor.get(), ~0, arg.ui);
++}
+ void spawn(Monitor&, const Arg& arg)
+ {
+ if (fork() == 0) {
+@@ -189,11 +210,62 @@ static const struct wl_seat_listener seatListener = {
+ .name = [](void*, wl_seat*, const char* name) { }
+ };
+
++static const struct znet_tapesoftware_dwl_wm_v1_listener dwlWmListener = {
++ .tag = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) {
++ tagNames.push_back(name);
++ },
++ .layout = [](void*, znet_tapesoftware_dwl_wm_v1*, const char* name) {
++ layoutNames.push_back(name);
++ },
++};
++
++static const struct znet_tapesoftware_dwl_wm_monitor_v1_listener dwlWmMonitorListener {
++ .selected = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t selected) {
++ auto mon = static_cast<Monitor*>(mv);
++ if (selected) {
++ selmon = mon;
++ } else if (selmon == mon) {
++ selmon = nullptr;
++ }
++ mon->bar.setSelected(selected);
++ },
++ .tag = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t tag, uint32_t state, uint32_t numClients, int32_t focusedClient) {
++ auto mon = static_cast<Monitor*>(mv);
++ int tagState = TagState::None;
++ if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_ACTIVE)
++ tagState |= TagState::Active;
++ if (state & ZNET_TAPESOFTWARE_DWL_WM_MONITOR_V1_TAG_STATE_URGENT)
++ tagState |= TagState::Urgent;
++ mon->bar.setTag(tag, tagState, numClients, focusedClient);
++ uint32_t mask = 1 << tag;
++ if (tagState & TagState::Active) {
++ mon->tags |= mask;
++ } else {
++ mon->tags &= ~mask;
++ }
++ },
++ .layout = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, uint32_t layout) {
++ auto mon = static_cast<Monitor*>(mv);
++ mon->bar.setLayout(layoutNames[layout]);
++ },
++ .title = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*, const char* title) {
++ auto mon = static_cast<Monitor*>(mv);
++ mon->bar.setTitle(title);
++ },
++ .frame = [](void* mv, znet_tapesoftware_dwl_wm_monitor_v1*) {
++ auto mon = static_cast<Monitor*>(mv);
++ mon->hasData = true;
++ updatemon(*mon);
++ }
++};
++
+ void setupMonitor(uint32_t name, wl_output* output) {
+ auto& monitor = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}});
+ monitor.bar.setStatus(lastStatus);
+ auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get());
+ zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor);
++ monitor.dwlMonitor.reset(znet_tapesoftware_dwl_wm_v1_get_monitor(dwlWm, monitor.wlOutput.get()));
++ znet_tapesoftware_dwl_wm_monitor_v1_add_listener(monitor.dwlMonitor.get(), &dwlWmMonitorListener, &monitor);
+ }
+
+ void updatemon(Monitor& mon)
+@@ -219,6 +291,7 @@ void onReady()
+ requireGlobal(shm, "wl_shm");
+ requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1");
+ requireGlobal(xdgOutputManager, "zxdg_output_manager_v1");
++ requireGlobal(dwlWm, "znet_tapesoftware_dwl_wm_v1");
+ setupStatusFifo();
+ wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
+
+@@ -226,7 +299,6 @@ void onReady()
+ for (auto output : uninitializedOutputs) {
+ setupMonitor(output.first, output.second);
+ }
+- wl_display_roundtrip(display); // wait for xdg_output names before we read stdin
+ }
+
+ bool createFifo(std::string path)
+@@ -273,68 +345,6 @@ void setupStatusFifo()
+ }
+ }
+
+-static LineBuffer<512> stdinBuffer;
+-static void onStdin()
+-{
+- auto res = stdinBuffer.readLines(
+- [](void* p, size_t size) { return read(0, p, size); },
+- [](char* p, size_t size) { handleStdin({p, size}); });
+- if (res == 0) {
+- quitting = true;
+- }
+-}
+-
+-static void handleStdin(const std::string& line)
+-{
+- // this parses the lines that dwl sends in printstatus()
+- std::string monName, command;
+- auto stream = std::istringstream {line};
+- stream >> monName >> command;
+- if (!stream.good()) {
+- return;
+- }
+- auto mon = std::find_if(begin(monitors), end(monitors), [&](const Monitor& mon) {
+- return mon.xdgName == monName;
+- });
+- if (mon == end(monitors))
+- return;
+- if (command == "title") {
+- auto title = std::string {};
+- stream >> std::ws;
+- std::getline(stream, title);
+- mon->bar.setTitle(title);
+- } else if (command == "selmon") {
+- uint32_t selected;
+- stream >> selected;
+- mon->bar.setSelected(selected);
+- if (selected) {
+- selmon = &*mon;
+- } else if (selmon == &*mon) {
+- selmon = nullptr;
+- }
+- } else if (command == "tags") {
+- uint32_t occupied, tags, clientTags, urgent;
+- stream >> occupied >> tags >> clientTags >> urgent;
+- for (auto i=0u; i<tagNames.size(); i++) {
+- auto tagMask = 1 << i;
+- int state = TagState::None;
+- if (tags & tagMask)
+- state |= TagState::Active;
+- if (urgent & tagMask)
+- state |= TagState::Urgent;
+- mon->bar.setTag(i, state, occupied & tagMask ? 1 : 0, clientTags & tagMask ? 0 : -1);
+- }
+- mon->tags = tags;
+- } else if (command == "layout") {
+- auto layout = std::string {};
+- stream >> std::ws;
+- std::getline(stream, layout);
+- mon->bar.setLayout(layout);
+- }
+- mon->hasData = true;
+- updatemon(*mon);
+-}
+-
+ const std::string prefixStatus = "status ";
+ const std::string prefixShow = "show ";
+ const std::string prefixHide = "hide ";
+@@ -409,6 +419,10 @@ void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interf
+ xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr);
+ return;
+ }
++ if (reg.handle(dwlWm, znet_tapesoftware_dwl_wm_v1_interface, 1)) {
++ znet_tapesoftware_dwl_wm_v1_add_listener(dwlWm, &dwlWmListener, nullptr);
++ return;
++ }
+ if (wl_seat* wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) {
+ auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}});
+ wl_seat_add_listener(wlSeat, &seatListener, &seat);
+@@ -522,10 +536,6 @@ int main(int argc, char* argv[])
+ .fd = displayFd,
+ .events = POLLIN,
+ });
+- pollfds.push_back({
+- .fd = STDIN_FILENO,
+- .events = POLLIN,
+- });
+ if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) {
+ diesys("fcntl F_SETFL");
+ }
+@@ -550,8 +560,6 @@ int main(int argc, char* argv[])
+ ev.events = POLLIN;
+ waylandFlush();
+ }
+- } else if (ev.fd == STDIN_FILENO && (ev.revents & POLLIN)) {
+- onStdin();
+ } else if (ev.fd == statusFifoFd && (ev.revents & POLLIN)) {
+ onStatus();
+ } else if (ev.fd == signalSelfPipe[0] && (ev.revents & POLLIN)) {
diff --git a/somebar/contrib/markup-in-status-messages.patch b/somebar/contrib/markup-in-status-messages.patch
new file mode 100644
index 0000000..ab7e998
--- /dev/null
+++ b/somebar/contrib/markup-in-status-messages.patch
@@ -0,0 +1,65 @@
+From: Ben Collerson <benc@benc.cc>
+Date: Tue, 29 Nov 2022 11:29:15 +1000
+Subject: [PATCH] markup in status messages
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Allows pango markup to be used in status messages which allow colours to
+be used to highlight parts of status bar messages. eg:
+
+battery: full <span color="#ffff00">🔇0</span> Tue 11-20 20:10
+---
+ src/bar.cpp | 16 +++++++++++++++-
+ src/bar.hpp | 1 +
+ 2 files changed, 16 insertions(+), 1 deletion(-)
+
+diff --git a/src/bar.cpp b/src/bar.cpp
+index 507ce62..a96c547 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -81,6 +81,11 @@ void BarComponent::setText(const std::string& text)
+ pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size());
+ }
+
++void BarComponent::setAttributes(PangoAttrList *attrs)
++{
++ pango_layout_set_attributes(pangoLayout.get(), attrs);
++}
++
+ Bar::Bar()
+ {
+ _pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default()));
+@@ -156,7 +161,16 @@ void Bar::setTitle(const std::string& title)
+ }
+ void Bar::setStatus(const std::string& status)
+ {
+- _statusCmp.setText(status);
++ char *buf;
++ GError *error = NULL;
++ PangoAttrList *attrs;
++ if (pango_parse_markup(status.c_str(), -1, 0, &attrs, &buf, NULL, &error)) {
++ _statusCmp.setText(buf);
++ _statusCmp.setAttributes(attrs);
++ }
++ else {
++ _statusCmp.setText(error->message);
++ }
+ }
+
+ void Bar::invalidate()
+diff --git a/src/bar.hpp b/src/bar.hpp
+index 176a1bc..dfc2043 100644
+--- a/src/bar.hpp
++++ b/src/bar.hpp
+@@ -17,6 +17,7 @@ public:
+ explicit BarComponent(wl_unique_ptr<PangoLayout> layout);
+ int width() const;
+ void setText(const std::string& text);
++ void setAttributes(PangoAttrList *attrs);
+ wl_unique_ptr<PangoLayout> pangoLayout;
+ int x {0};
+ };
+--
+2.38.1
+
diff --git a/somebar/contrib/show-status-on-selected-monitor.patch b/somebar/contrib/show-status-on-selected-monitor.patch
new file mode 100644
index 0000000..ab26835
--- /dev/null
+++ b/somebar/contrib/show-status-on-selected-monitor.patch
@@ -0,0 +1,43 @@
+From 1ca4603b79e888980fb46d5dc6aa0d6f8afa2b3c Mon Sep 17 00:00:00 2001
+From: Ben Collerson <benc@benc.cc>
+Date: Mon, 28 Nov 2022 13:22:16 +1000
+Subject: [PATCH] show status on selected monitor
+
+---
+ src/bar.cpp | 7 ++++++-
+ src/main.cpp | 1 +
+ 2 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/src/bar.cpp b/src/bar.cpp
+index 507ce62..f962927 100644
+--- a/src/bar.cpp
++++ b/src/bar.cpp
+@@ -156,7 +156,12 @@ void Bar::setTitle(const std::string& title)
+ }
+ void Bar::setStatus(const std::string& status)
+ {
+- _statusCmp.setText(status);
++ if (_selected) {
++ _statusCmp.setText(status);
++ }
++ else {
++ _statusCmp.setText("");
++ }
+ }
+
+ void Bar::invalidate()
+diff --git a/src/main.cpp b/src/main.cpp
+index 6274959..c6fd401 100644
+--- a/src/main.cpp
++++ b/src/main.cpp
+@@ -307,6 +307,7 @@ static void handleStdin(const std::string& line)
+ uint32_t selected;
+ stream >> selected;
+ mon->bar.setSelected(selected);
++ mon->bar.setStatus(lastStatus);
+ if (selected) {
+ selmon = &*mon;
+ } else if (selmon == &*mon) {
+--
+2.38.1
+
diff --git a/somebar/meson.build b/somebar/meson.build
new file mode 100644
index 0000000..6ad5a0f
--- /dev/null
+++ b/somebar/meson.build
@@ -0,0 +1,31 @@
+project('somebar', ['c', 'cpp'],
+ version: '0.1.0',
+ default_options: [
+ 'cpp_std=c++17',
+ 'cpp_args=-Wno-parentheses',
+ ])
+
+wayland_dep = dependency('wayland-client')
+wayland_cursor_dep = dependency('wayland-cursor')
+cairo_dep = dependency('cairo')
+pango_dep = dependency('pango')
+pangocairo_dep = dependency('pangocairo')
+
+subdir('protocols')
+
+executable('somebar',
+ 'src/main.cpp',
+ 'src/shm_buffer.cpp',
+ 'src/bar.cpp',
+ wayland_sources,
+ dependencies: [
+ wayland_dep,
+ wayland_cursor_dep,
+ cairo_dep,
+ pango_dep,
+ pangocairo_dep,
+ ],
+ install: true,
+ cpp_args: '-DSOMEBAR_VERSION="@0@"'.format(meson.project_version()))
+
+install_man('somebar.1')
diff --git a/somebar/protocols/meson.build b/somebar/protocols/meson.build
new file mode 100644
index 0000000..7bd222b
--- /dev/null
+++ b/somebar/protocols/meson.build
@@ -0,0 +1,22 @@
+# adapted from https://github.com/swaywm/swayidle/blob/0467c1e03a5780ed8e3ba611f099a838822ab550/meson.build
+wayland_scanner = find_program('wayland-scanner')
+wayland_protos_dep = dependency('wayland-protocols')
+wl_protocol_dir = wayland_protos_dep.get_pkgconfig_variable('pkgdatadir')
+wayland_scanner_code = generator(
+ wayland_scanner,
+ output: '@BASENAME@-protocol.c',
+ arguments: ['private-code', '@INPUT@', '@OUTPUT@'])
+wayland_scanner_client = generator(
+ wayland_scanner,
+ output: '@BASENAME@-client-protocol.h',
+ arguments: ['client-header', '@INPUT@', '@OUTPUT@'])
+
+wayland_xmls = [
+ wl_protocol_dir + '/stable/xdg-shell/xdg-shell.xml',
+ wl_protocol_dir + '/unstable/xdg-output/xdg-output-unstable-v1.xml',
+ 'wlr-layer-shell-unstable-v1.xml',
+]
+wayland_sources = [
+ wayland_scanner_code.process(wayland_xmls),
+ wayland_scanner_client.process(wayland_xmls),
+]
diff --git a/somebar/protocols/wlr-layer-shell-unstable-v1.xml b/somebar/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 0000000..78ba050
--- /dev/null
+++ b/somebar/protocols/wlr-layer-shell-unstable-v1.xml
@@ -0,0 +1,390 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_layer_shell_unstable_v1">
+ <copyright>
+ Copyright © 2017 Drew DeVault
+
+ Permission to use, copy, modify, distribute, and sell this
+ software and its documentation for any purpose is hereby granted
+ without fee, provided that the above copyright notice appear in
+ all copies and that both that copyright notice and this permission
+ notice appear in supporting documentation, and that the name of
+ the copyright holders not be used in advertising or publicity
+ pertaining to distribution of the software without specific,
+ written prior permission. The copyright holders make no
+ representations about the suitability of this software for any
+ purpose. It is provided "as is" without express or implied
+ warranty.
+
+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
+ THIS SOFTWARE.
+ </copyright>
+
+ <interface name="zwlr_layer_shell_v1" version="4">
+ <description summary="create surfaces that are layers of the desktop">
+ Clients can use this interface to assign the surface_layer role to
+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
+ rendered with a defined z-depth respective to each other. They may also be
+ anchored to the edges and corners of a screen and specify input handling
+ semantics. This interface should be suitable for the implementation of
+ many desktop shell components, and a broad number of other applications
+ that interact with the desktop.
+ </description>
+
+ <request name="get_layer_surface">
+ <description summary="create a layer_surface from a surface">
+ Create a layer surface for an existing surface. This assigns the role of
+ layer_surface, or raises a protocol error if another role is already
+ assigned.
+
+ Creating a layer surface from a wl_surface which has a buffer attached
+ or committed is a client error, and any attempts by a client to attach
+ or manipulate a buffer prior to the first layer_surface.configure call
+ must also be treated as errors.
+
+ After creating a layer_surface object and setting it up, the client
+ must perform an initial commit without any buffer attached.
+ The compositor will reply with a layer_surface.configure event.
+ The client must acknowledge it and is then allowed to attach a buffer
+ to map the surface.
+
+ You may pass NULL for output to allow the compositor to decide which
+ output to use. Generally this will be the one that the user most
+ recently interacted with.
+
+ Clients can specify a namespace that defines the purpose of the layer
+ surface.
+ </description>
+ <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
+ <arg name="surface" type="object" interface="wl_surface"/>
+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
+ <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
+ <arg name="name_space" type="string" summary="namespace for the layer surface"/>
+ </request>
+
+ <enum name="error">
+ <entry name="role" value="0" summary="wl_surface has another role"/>
+ <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
+ <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
+ </enum>
+
+ <enum name="layer">
+ <description summary="available layers for surfaces">
+ These values indicate which layers a surface can be rendered in. They
+ are ordered by z depth, bottom-most first. Traditional shell surfaces
+ will typically be rendered between the bottom and top layers.
+ Fullscreen shell surfaces are typically rendered at the top layer.
+ Multiple surfaces can share a single layer, and ordering within a
+ single layer is undefined.
+ </description>
+
+ <entry name="background" value="0"/>
+ <entry name="bottom" value="1"/>
+ <entry name="top" value="2"/>
+ <entry name="overlay" value="3"/>
+ </enum>
+
+ <!-- Version 3 additions -->
+
+ <request name="destroy" type="destructor" since="3">
+ <description summary="destroy the layer_shell object">
+ This request indicates that the client will not use the layer_shell
+ object any more. Objects that have been created through this instance
+ are not affected.
+ </description>
+ </request>
+ </interface>
+
+ <interface name="zwlr_layer_surface_v1" version="4">
+ <description summary="layer metadata interface">
+ An interface that may be implemented by a wl_surface, for surfaces that
+ are designed to be rendered as a layer of a stacked desktop-like
+ environment.
+
+ Layer surface state (layer, size, anchor, exclusive zone,
+ margin, interactivity) is double-buffered, and will be applied at the
+ time wl_surface.commit of the corresponding wl_surface is called.
+
+ Attaching a null buffer to a layer surface unmaps it.
+
+ Unmapping a layer_surface means that the surface cannot be shown by the
+ compositor until it is explicitly mapped again. The layer_surface
+ returns to the state it had right after layer_shell.get_layer_surface.
+ The client can re-map the surface by performing a commit without any
+ buffer attached, waiting for a configure event and handling it as usual.
+ </description>
+
+ <request name="set_size">
+ <description summary="sets the size of the surface">
+ Sets the size of the surface in surface-local coordinates. The
+ compositor will display the surface centered with respect to its
+ anchors.
+
+ If you pass 0 for either value, the compositor will assign it and
+ inform you of the assignment in the configure event. You must set your
+ anchor to opposite edges in the dimensions you omit; not doing so is a
+ protocol error. Both values are 0 by default.
+
+ Size is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </request>
+
+ <request name="set_anchor">
+ <description summary="configures the anchor point of the surface">
+ Requests that the compositor anchor the surface to the specified edges
+ and corners. If two orthogonal edges are specified (e.g. 'top' and
+ 'left'), then the anchor point will be the intersection of the edges
+ (e.g. the top left corner of the output); otherwise the anchor point
+ will be centered on that edge, or in the center if none is specified.
+
+ Anchor is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="anchor" type="uint" enum="anchor"/>
+ </request>
+
+ <request name="set_exclusive_zone">
+ <description summary="configures the exclusive geometry of this surface">
+ Requests that the compositor avoids occluding an area with other
+ surfaces. The compositor's use of this information is
+ implementation-dependent - do not assume that this region will not
+ actually be occluded.
+
+ A positive value is only meaningful if the surface is anchored to one
+ edge or an edge and both perpendicular edges. If the surface is not
+ anchored, anchored to only two perpendicular edges (a corner), anchored
+ to only two parallel edges or anchored to all edges, a positive value
+ will be treated the same as zero.
+
+ A positive zone is the distance from the edge in surface-local
+ coordinates to consider exclusive.
+
+ Surfaces that do not wish to have an exclusive zone may instead specify
+ how they should interact with surfaces that do. If set to zero, the
+ surface indicates that it would like to be moved to avoid occluding
+ surfaces with a positive exclusive zone. If set to -1, the surface
+ indicates that it would not like to be moved to accommodate for other
+ surfaces, and the compositor should extend it all the way to the edges
+ it is anchored to.
+
+ For example, a panel might set its exclusive zone to 10, so that
+ maximized shell surfaces are not shown on top of it. A notification
+ might set its exclusive zone to 0, so that it is moved to avoid
+ occluding the panel, but shell surfaces are shown underneath it. A
+ wallpaper or lock screen might set their exclusive zone to -1, so that
+ they stretch below or over the panel.
+
+ The default value is 0.
+
+ Exclusive zone is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="zone" type="int"/>
+ </request>
+
+ <request name="set_margin">
+ <description summary="sets a margin from the anchor point">
+ Requests that the surface be placed some distance away from the anchor
+ point on the output, in surface-local coordinates. Setting this value
+ for edges you are not anchored to has no effect.
+
+ The exclusive zone includes the margin.
+
+ Margin is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="top" type="int"/>
+ <arg name="right" type="int"/>
+ <arg name="bottom" type="int"/>
+ <arg name="left" type="int"/>
+ </request>
+
+ <enum name="keyboard_interactivity">
+ <description summary="types of keyboard interaction possible for a layer shell surface">
+ Types of keyboard interaction possible for layer shell surfaces. The
+ rationale for this is twofold: (1) some applications are not interested
+ in keyboard events and not allowing them to be focused can improve the
+ desktop experience; (2) some applications will want to take exclusive
+ keyboard focus.
+ </description>
+
+ <entry name="none" value="0">
+ <description summary="no keyboard focus is possible">
+ This value indicates that this surface is not interested in keyboard
+ events and the compositor should never assign it the keyboard focus.
+
+ This is the default value, set for newly created layer shell surfaces.
+
+ This is useful for e.g. desktop widgets that display information or
+ only have interaction with non-keyboard input devices.
+ </description>
+ </entry>
+ <entry name="exclusive" value="1">
+ <description summary="request exclusive keyboard focus">
+ Request exclusive keyboard focus if this surface is above the shell surface layer.
+
+ For the top and overlay layers, the seat will always give
+ exclusive keyboard focus to the top-most layer which has keyboard
+ interactivity set to exclusive. If this layer contains multiple
+ surfaces with keyboard interactivity set to exclusive, the compositor
+ determines the one receiving keyboard events in an implementation-
+ defined manner. In this case, no guarantee is made when this surface
+ will receive keyboard focus (if ever).
+
+ For the bottom and background layers, the compositor is allowed to use
+ normal focus semantics.
+
+ This setting is mainly intended for applications that need to ensure
+ they receive all keyboard events, such as a lock screen or a password
+ prompt.
+ </description>
+ </entry>
+ <entry name="on_demand" value="2" since="4">
+ <description summary="request regular keyboard focus semantics">
+ This requests the compositor to allow this surface to be focused and
+ unfocused by the user in an implementation-defined manner. The user
+ should be able to unfocus this surface even regardless of the layer
+ it is on.
+
+ Typically, the compositor will want to use its normal mechanism to
+ manage keyboard focus between layer shell surfaces with this setting
+ and regular toplevels on the desktop layer (e.g. click to focus).
+ Nevertheless, it is possible for a compositor to require a special
+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
+ a click even if focus follows the mouse normally, or providing a
+ keybinding to switch focus between layers).
+
+ This setting is mainly intended for desktop shell components (e.g.
+ panels) that allow keyboard interaction. Using this option can allow
+ implementing a desktop shell that can be fully usable without the
+ mouse.
+ </description>
+ </entry>
+ </enum>
+
+ <request name="set_keyboard_interactivity">
+ <description summary="requests keyboard events">
+ Set how keyboard events are delivered to this surface. By default,
+ layer shell surfaces do not receive keyboard events; this request can
+ be used to change this.
+
+ This setting is inherited by child surfaces set by the get_popup
+ request.
+
+ Layer surfaces receive pointer, touch, and tablet events normally. If
+ you do not want to receive them, set the input region on your surface
+ to an empty region.
+
+ Keyboard interactivity is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
+ </request>
+
+ <request name="get_popup">
+ <description summary="assign this layer_surface as an xdg_popup parent">
+ This assigns an xdg_popup's parent to this layer_surface. This popup
+ should have been created via xdg_surface::get_popup with the parent set
+ to NULL, and this request must be invoked before committing the popup's
+ initial state.
+
+ See the documentation of xdg_popup for more details about what an
+ xdg_popup is and how it is used.
+ </description>
+ <arg name="popup" type="object" interface="xdg_popup"/>
+ </request>
+
+ <request name="ack_configure">
+ <description summary="ack a configure event">
+ When a configure event is received, if a client commits the
+ surface in response to the configure event, then the client
+ must make an ack_configure request sometime before the commit
+ request, passing along the serial of the configure event.
+
+ If the client receives multiple configure events before it
+ can respond to one, it only has to ack the last configure event.
+
+ A client is not required to commit immediately after sending
+ an ack_configure request - it may even ack_configure several times
+ before its next surface commit.
+
+ A client may send multiple ack_configure requests before committing, but
+ only the last request sent before a commit indicates which configure
+ event the client really is responding to.
+ </description>
+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
+ </request>
+
+ <request name="destroy" type="destructor">
+ <description summary="destroy the layer_surface">
+ This request destroys the layer surface.
+ </description>
+ </request>
+
+ <event name="configure">
+ <description summary="suggest a surface change">
+ The configure event asks the client to resize its surface.
+
+ Clients should arrange their surface for the new states, and then send
+ an ack_configure request with the serial sent in this configure event at
+ some point before committing the new surface.
+
+ The client is free to dismiss all but the last configure event it
+ received.
+
+ The width and height arguments specify the size of the window in
+ surface-local coordinates.
+
+ The size is a hint, in the sense that the client is free to ignore it if
+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
+ resize in steps of NxM pixels). If the client picks a smaller size and
+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
+ surface will be centered on this axis.
+
+ If the width or height arguments are zero, it means the client should
+ decide its own window dimension.
+ </description>
+ <arg name="serial" type="uint"/>
+ <arg name="width" type="uint"/>
+ <arg name="height" type="uint"/>
+ </event>
+
+ <event name="closed">
+ <description summary="surface should be closed">
+ The closed event is sent by the compositor when the surface will no
+ longer be shown. The output may have been destroyed or the user may
+ have asked for it to be removed. Further changes to the surface will be
+ ignored. The client should destroy the resource after receiving this
+ event, and create a new surface if they so choose.
+ </description>
+ </event>
+
+ <enum name="error">
+ <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
+ <entry name="invalid_size" value="1" summary="size is invalid"/>
+ <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
+ <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
+ </enum>
+
+ <enum name="anchor" bitfield="true">
+ <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
+ <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
+ <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
+ <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
+ </enum>
+
+ <!-- Version 2 additions -->
+
+ <request name="set_layer" since="2">
+ <description summary="change the layer of the surface">
+ Change the layer that the surface is rendered on.
+
+ Layer is double-buffered, see wl_surface.commit.
+ </description>
+ <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
+ </request>
+ </interface>
+</protocol>
diff --git a/somebar/screenshot.png b/somebar/screenshot.png
new file mode 100644
index 0000000..7a800fb
--- /dev/null
+++ b/somebar/screenshot.png
Binary files differ
diff --git a/somebar/somebar.1 b/somebar/somebar.1
new file mode 100644
index 0000000..d822770
--- /dev/null
+++ b/somebar/somebar.1
@@ -0,0 +1,55 @@
+.TH somebar 1 somebar\-1.0
+.SH NAME
+somebar \- dwm-like bar for dwl
+.SH SYNOPSIS
+.B somebar
+.RB [ \-h ]
+.RB [ \-v ]
+.RB [ \-s
+.IR path ]
+.RB [ \-c
+.IR command
+arguments... ]
+.SH DESCRIPTION
+somebar is a status bar for dwl, visually and functionally resembling the
+dwm bar.
+.SH USAGE
+You must start somebar using dwl's `-s` flag, e.g. `dwl -s somebar`.
+
+Somebar can be controlled by writing to $XDG_RUNTIME_DIR/somebar-0, or the path
+defined by the `-s` argument. The following commands are supported:
+.TP
+.B status TEXT
+Updates the status bar
+.TP
+.B hide MONITOR
+Hides somebar on the specified monitor
+.TP
+.B show MONITOR
+Shows somebar on the specified monitor
+.TP
+.B toggle MONITOR
+Toggles somebar on the specified monitor
+.P
+MONITOR is an zxdg_output_v1 name, which can be determined e.g. using `weston-info`.
+Additionally, MONITOR can be `all` (all monitors) or `selected` (the monitor with focus).
+
+Commands can be sent either by writing to the file name above, or equivalently by calling
+somebar with the `-c` argument. For example: `somebar -c toggle all`. This is recommended
+for shell scripts, as there is no race-free way to write to a file only if it exists.
+.SH OPTIONS
+.TP
+.B \-h
+Displays a short help text and exits
+.TP
+.B \-v
+Displays version information and exits
+.TP
+.B \-s
+Sets the path to the somebar control FIFO. The default value is
+$XDG_RUNTIME_DIR/somebar-0
+.TP
+.B \-c
+Sends a command to the control FIFO. See the USAGE section.
+.SH BUGS
+Send bug reports to ~raphi/public-inbox@lists.sr.ht
diff --git a/somebar/src/bar.cpp b/somebar/src/bar.cpp
new file mode 100644
index 0000000..af92f49
--- /dev/null
+++ b/somebar/src/bar.cpp
@@ -0,0 +1,315 @@
+// somebar - dwl barbar
+// See LICENSE file for copyright and license details.
+
+#include <wayland-client-protocol.h>
+#include <pango/pangocairo.h>
+#include "bar.hpp"
+#include "cairo.h"
+#include "config.hpp"
+#include "pango/pango-font.h"
+#include "pango/pango-fontmap.h"
+#include "pango/pango-layout.h"
+
+const zwlr_layer_surface_v1_listener Bar::_layerSurfaceListener = {
+ [](void* owner, zwlr_layer_surface_v1*, uint32_t serial, uint32_t width, uint32_t height)
+ {
+ static_cast<Bar*>(owner)->layerSurfaceConfigure(serial, width, height);
+ }
+};
+const wl_callback_listener Bar::_frameListener = {
+ [](void* owner, wl_callback* cb, uint32_t)
+ {
+ static_cast<Bar*>(owner)->render();
+ wl_callback_destroy(cb);
+ }
+};
+
+struct Font {
+ PangoFontDescription* description;
+ int height {0};
+};
+static Font getFont()
+{
+ auto fontMap = pango_cairo_font_map_get_default();
+ if (!fontMap) {
+ die("pango_cairo_font_map_get_default");
+ }
+ auto fontDesc = pango_font_description_from_string(font);
+ if (!fontDesc) {
+ die("pango_font_description_from_string");
+ }
+ auto tempContext = pango_font_map_create_context(fontMap);
+ if (!tempContext) {
+ die("pango_font_map_create_context");
+ }
+ auto font = pango_font_map_load_font(fontMap, tempContext, fontDesc);
+ if (!font) {
+ die("pango_font_map_load_font");
+ }
+ auto metrics = pango_font_get_metrics(font, pango_language_get_default());
+ if (!metrics) {
+ die("pango_font_get_metrics");
+ }
+
+ auto res = Font {};
+ res.description = fontDesc;
+ res.height = PANGO_PIXELS(pango_font_metrics_get_height(metrics));
+
+ pango_font_metrics_unref(metrics);
+ g_object_unref(font);
+ g_object_unref(tempContext);
+ return res;
+}
+static Font barfont = getFont();
+
+BarComponent::BarComponent() { }
+BarComponent::BarComponent(wl_unique_ptr<PangoLayout> layout)
+ : pangoLayout {std::move(layout)}
+{
+}
+
+int BarComponent::width() const
+{
+ int w, h;
+ pango_layout_get_size(pangoLayout.get(), &w, &h);
+ return PANGO_PIXELS(w);
+}
+
+void BarComponent::setText(const std::string& text)
+{
+ _text = std::make_unique<std::string>(text);
+ pango_layout_set_text(pangoLayout.get(), _text->c_str(), _text->size());
+}
+
+Bar::Bar()
+{
+ _pangoContext.reset(pango_font_map_create_context(pango_cairo_font_map_get_default()));
+ if (!_pangoContext) {
+ die("pango_font_map_create_context");
+ }
+ for (const auto& tagName : tagNames) {
+ _tags.push_back({ TagState::None, 0, 0, createComponent(tagName) });
+ }
+ _layoutCmp = createComponent();
+ _titleCmp = createComponent();
+ _statusCmp = createComponent();
+}
+
+const wl_surface* Bar::surface() const
+{
+ return _surface.get();
+}
+
+bool Bar::visible() const
+{
+ return _surface.get();
+}
+
+void Bar::show(wl_output* output)
+{
+ if (visible()) {
+ return;
+ }
+ _surface.reset(wl_compositor_create_surface(compositor));
+ _layerSurface.reset(zwlr_layer_shell_v1_get_layer_surface(wlrLayerShell,
+ _surface.get(), output, ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM, "net.tapesoftware.Somebar"));
+ zwlr_layer_surface_v1_add_listener(_layerSurface.get(), &_layerSurfaceListener, this);
+ auto anchor = topbar ? ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP : ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ zwlr_layer_surface_v1_set_anchor(_layerSurface.get(),
+ anchor | ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT);
+
+ auto barSize = barfont.height + paddingY * 2;
+ zwlr_layer_surface_v1_set_size(_layerSurface.get(), 0, barSize);
+ zwlr_layer_surface_v1_set_exclusive_zone(_layerSurface.get(), barSize);
+ wl_surface_commit(_surface.get());
+}
+
+void Bar::hide()
+{
+ if (!visible()) {
+ return;
+ }
+ _layerSurface.reset();
+ _surface.reset();
+ _bufs.reset();
+}
+
+void Bar::setTag(int tag, int state, int numClients, int focusedClient)
+{
+ auto& t = _tags[tag];
+ t.state = state;
+ t.numClients = numClients;
+ t.focusedClient = focusedClient;
+}
+
+void Bar::setSelected(bool selected)
+{
+ _selected = selected;
+}
+void Bar::setLayout(const std::string& layout)
+{
+ _layoutCmp.setText(layout);
+}
+void Bar::setTitle(const std::string& title)
+{
+ _titleCmp.setText(title);
+}
+void Bar::setStatus(const std::string& status)
+{
+ _statusCmp.setText(status);
+}
+
+void Bar::invalidate()
+{
+ if (_invalid || !visible()) {
+ return;
+ }
+ _invalid = true;
+ auto frame = wl_surface_frame(_surface.get());
+ wl_callback_add_listener(frame, &_frameListener, this);
+ wl_surface_commit(_surface.get());
+}
+
+void Bar::click(Monitor* mon, int x, int, int btn)
+{
+ Arg arg = {0};
+ Arg* argp = nullptr;
+ int control = ClkNone;
+ if (x > _statusCmp.x) {
+ control = ClkStatusText;
+ } else if (x > _titleCmp.x) {
+ control = ClkWinTitle;
+ } else if (x > _layoutCmp.x) {
+ control = ClkLayoutSymbol;
+ } else for (int tag = _tags.size()-1; tag >= 0; tag--) {
+ if (x > _tags[tag].component.x) {
+ control = ClkTagBar;
+ arg.ui = 1<<tag;
+ argp = &arg;
+ break;
+ }
+ }
+ for (const auto& button : buttons) {
+ if (button.control == control && button.btn == btn) {
+ button.func(*mon, *(argp ? argp : &button.arg));
+ return;
+ }
+ }
+}
+
+void Bar::layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height)
+{
+ zwlr_layer_surface_v1_ack_configure(_layerSurface.get(), serial);
+ if (_bufs && width == _bufs->width && height == _bufs->height) {
+ return;
+ }
+ _bufs.emplace(width, height, WL_SHM_FORMAT_XRGB8888);
+ render();
+}
+
+void Bar::render()
+{
+ if (!_bufs) {
+ return;
+ }
+ auto img = wl_unique_ptr<cairo_surface_t> {cairo_image_surface_create_for_data(
+ _bufs->data(),
+ CAIRO_FORMAT_ARGB32,
+ _bufs->width,
+ _bufs->height,
+ _bufs->stride
+ )};
+ auto painter = wl_unique_ptr<cairo_t> {cairo_create(img.get())};
+ _painter = painter.get();
+ pango_cairo_update_context(_painter, _pangoContext.get());
+ _x = 0;
+
+ renderTags();
+ setColorScheme(_selected ? colorActive : colorInactive);
+ renderComponent(_layoutCmp);
+ renderComponent(_titleCmp);
+ renderStatus();
+
+ _painter = nullptr;
+ wl_surface_attach(_surface.get(), _bufs->buffer(), 0, 0);
+ wl_surface_damage(_surface.get(), 0, 0, _bufs->width, _bufs->height);
+ wl_surface_commit(_surface.get());
+ _bufs->flip();
+ _invalid = false;
+}
+
+void Bar::renderTags()
+{
+ for (auto &tag : _tags) {
+ setColorScheme(
+ tag.state & TagState::Active ? colorActive : colorInactive,
+ tag.state & TagState::Urgent);
+ renderComponent(tag.component);
+ auto indicators = std::min(tag.numClients, static_cast<int>(_bufs->height/2));
+ for (auto ind = 0; ind < indicators; ind++) {
+ auto w = ind == tag.focusedClient ? 7 : 1;
+ cairo_move_to(_painter, tag.component.x, ind*2+0.5);
+ cairo_rel_line_to(_painter, w, 0);
+ cairo_close_path(_painter);
+ cairo_set_line_width(_painter, 1);
+ cairo_stroke(_painter);
+ }
+ }
+}
+
+void Bar::renderStatus()
+{
+ pango_cairo_update_layout(_painter, _statusCmp.pangoLayout.get());
+ beginBg();
+ auto start = _bufs->width - _statusCmp.width() - paddingX*2;
+ cairo_rectangle(_painter, _x, 0, _bufs->width-_x+start, _bufs->height);
+ cairo_fill(_painter);
+
+ _x = start;
+ renderComponent(_statusCmp);
+}
+
+void Bar::setColorScheme(const ColorScheme& scheme, bool invert)
+{
+ _colorScheme = invert
+ ? ColorScheme {scheme.bg, scheme.fg}
+ : ColorScheme {scheme.fg, scheme.bg};
+}
+static void setColor(cairo_t* painter, const Color& color)
+{
+ cairo_set_source_rgba(painter,
+ color.r/255.0, color.g/255.0, color.b/255.0, color.a/255.0);
+}
+void Bar::beginFg()
+{
+ setColor(_painter, _colorScheme.fg);
+}
+void Bar::beginBg()
+{
+ setColor(_painter, _colorScheme.bg);
+}
+
+void Bar::renderComponent(BarComponent& component)
+{
+ pango_cairo_update_layout(_painter, component.pangoLayout.get());
+ auto size = component.width() + paddingX*2;
+ component.x = _x;
+
+ beginBg();
+ cairo_rectangle(_painter, _x, 0, size, _bufs->height);
+ cairo_fill(_painter);
+ cairo_move_to(_painter, _x+paddingX, paddingY);
+
+ beginFg();
+ pango_cairo_show_layout(_painter, component.pangoLayout.get());
+ _x += size;
+}
+
+BarComponent Bar::createComponent(const std::string &initial)
+{
+ auto layout = pango_layout_new(_pangoContext.get());
+ pango_layout_set_font_description(layout, barfont.description);
+ auto res = BarComponent {wl_unique_ptr<PangoLayout> {layout}};
+ res.setText(initial);
+ return res;
+}
diff --git a/somebar/src/bar.hpp b/somebar/src/bar.hpp
new file mode 100644
index 0000000..176a1bc
--- /dev/null
+++ b/somebar/src/bar.hpp
@@ -0,0 +1,74 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#pragma once
+#include <optional>
+#include <string>
+#include <vector>
+#include <wayland-client.h>
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
+#include "common.hpp"
+#include "shm_buffer.hpp"
+
+class BarComponent {
+ std::unique_ptr<std::string> _text;
+public:
+ BarComponent();
+ explicit BarComponent(wl_unique_ptr<PangoLayout> layout);
+ int width() const;
+ void setText(const std::string& text);
+ wl_unique_ptr<PangoLayout> pangoLayout;
+ int x {0};
+};
+
+struct Tag {
+ int state;
+ int numClients;
+ int focusedClient;
+ BarComponent component;
+};
+
+struct Monitor;
+class Bar {
+ static const zwlr_layer_surface_v1_listener _layerSurfaceListener;
+ static const wl_callback_listener _frameListener;
+
+ wl_unique_ptr<wl_surface> _surface;
+ wl_unique_ptr<zwlr_layer_surface_v1> _layerSurface;
+ wl_unique_ptr<PangoContext> _pangoContext;
+ std::optional<ShmBuffer> _bufs;
+ std::vector<Tag> _tags;
+ BarComponent _layoutCmp, _titleCmp, _statusCmp;
+ bool _selected;
+ bool _invalid {false};
+
+ // only vaild during render()
+ cairo_t* _painter {nullptr};
+ int _x;
+ ColorScheme _colorScheme;
+
+ void layerSurfaceConfigure(uint32_t serial, uint32_t width, uint32_t height);
+ void render();
+ void renderTags();
+ void renderStatus();
+
+ // low-level rendering
+ void setColorScheme(const ColorScheme& scheme, bool invert = false);
+ void beginFg();
+ void beginBg();
+ void renderComponent(BarComponent& component);
+ BarComponent createComponent(const std::string& initial = {});
+public:
+ Bar();
+ const wl_surface* surface() const;
+ bool visible() const;
+ void show(wl_output* output);
+ void hide();
+ void setTag(int tag, int state, int numClients, int focusedClient);
+ void setSelected(bool selected);
+ void setLayout(const std::string& layout);
+ void setTitle(const std::string& title);
+ void setStatus(const std::string& status);
+ void invalidate();
+ void click(Monitor* mon, int x, int y, int btn);
+};
diff --git a/somebar/src/common.hpp b/somebar/src/common.hpp
new file mode 100644
index 0000000..c905358
--- /dev/null
+++ b/somebar/src/common.hpp
@@ -0,0 +1,76 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#pragma once
+#include <memory>
+#include <string>
+#include <vector>
+#include <wayland-client.h>
+#include <linux/input-event-codes.h>
+#include <cairo/cairo.h>
+#include <pango/pango.h>
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
+
+struct Color {
+ Color() {}
+ constexpr Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a=255) : r(r), g(g), b(b), a(a) { }
+ uint8_t r, g, b, a {255};
+};
+struct ColorScheme {
+ Color fg, bg;
+};
+union Arg {
+ unsigned int ui;
+ const void* v;
+};
+struct Monitor;
+
+enum TagState { None, Active = 0x01, Urgent = 0x02 };
+enum Control { ClkNone, ClkTagBar, ClkLayoutSymbol, ClkWinTitle, ClkStatusText };
+struct Button {
+ int control;
+ int btn; // <linux/input-event-codes.h>
+ void (*func)(Monitor& mon, const Arg& arg);
+ const Arg arg;
+};
+
+extern wl_display* display;
+extern wl_compositor* compositor;
+extern wl_shm* shm;
+extern zwlr_layer_shell_v1* wlrLayerShell;
+
+void spawn(Monitor&, const Arg& arg);
+void setCloexec(int fd);
+[[noreturn]] void die(const char* why);
+[[noreturn]] void diesys(const char* why);
+
+// wayland smart pointers
+template<typename T>
+struct WlDeleter;
+#define WL_DELETER(type, fn) template<> struct WlDeleter<type> { \
+ void operator()(type* v) { if(v) fn(v); } \
+ }
+
+template<typename T>
+using wl_unique_ptr = std::unique_ptr<T, WlDeleter<T>>;
+
+inline void wl_output_release_checked(wl_output* output) {
+ if (wl_output_get_version(output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
+ wl_output_release(output);
+ }
+}
+
+WL_DELETER(wl_buffer, wl_buffer_destroy);
+WL_DELETER(wl_output, wl_output_release_checked);
+WL_DELETER(wl_pointer, wl_pointer_release);
+WL_DELETER(wl_seat, wl_seat_release);
+WL_DELETER(wl_surface, wl_surface_destroy);
+WL_DELETER(zwlr_layer_surface_v1, zwlr_layer_surface_v1_destroy);
+
+WL_DELETER(cairo_t, cairo_destroy);
+WL_DELETER(cairo_surface_t, cairo_surface_destroy);
+
+WL_DELETER(PangoContext, g_object_unref);
+WL_DELETER(PangoLayout, g_object_unref);
+
+#undef WL_DELETER
diff --git a/somebar/src/config.def.hpp b/somebar/src/config.def.hpp
new file mode 100644
index 0000000..40a8c95
--- /dev/null
+++ b/somebar/src/config.def.hpp
@@ -0,0 +1,27 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#pragma once
+#include "common.hpp"
+
+constexpr bool topbar = true;
+
+constexpr int paddingX = 10;
+constexpr int paddingY = 3;
+
+// See https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
+constexpr const char* font = "Sans 12";
+
+constexpr ColorScheme colorInactive = {Color(0xbb, 0xbb, 0xbb), Color(0x22, 0x22, 0x22)};
+constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, 0x77)};
+constexpr const char* termcmd[] = {"foot", nullptr};
+
+static std::vector<std::string> tagNames = {
+ "1", "2", "3",
+ "4", "5", "6",
+ "7", "8", "9",
+};
+
+constexpr Button buttons[] = {
+ { ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
+};
diff --git a/somebar/src/config.hpp b/somebar/src/config.hpp
new file mode 100644
index 0000000..40a8c95
--- /dev/null
+++ b/somebar/src/config.hpp
@@ -0,0 +1,27 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#pragma once
+#include "common.hpp"
+
+constexpr bool topbar = true;
+
+constexpr int paddingX = 10;
+constexpr int paddingY = 3;
+
+// See https://docs.gtk.org/Pango/type_func.FontDescription.from_string.html
+constexpr const char* font = "Sans 12";
+
+constexpr ColorScheme colorInactive = {Color(0xbb, 0xbb, 0xbb), Color(0x22, 0x22, 0x22)};
+constexpr ColorScheme colorActive = {Color(0xee, 0xee, 0xee), Color(0x00, 0x55, 0x77)};
+constexpr const char* termcmd[] = {"foot", nullptr};
+
+static std::vector<std::string> tagNames = {
+ "1", "2", "3",
+ "4", "5", "6",
+ "7", "8", "9",
+};
+
+constexpr Button buttons[] = {
+ { ClkStatusText, BTN_RIGHT, spawn, {.v = termcmd} },
+};
diff --git a/somebar/src/line_buffer.hpp b/somebar/src/line_buffer.hpp
new file mode 100644
index 0000000..a5497bf
--- /dev/null
+++ b/somebar/src/line_buffer.hpp
@@ -0,0 +1,71 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#pragma once
+#include <array>
+#include <algorithm>
+#include <sys/types.h>
+
+// reads data from Reader, and passes complete lines to Consumer.
+template<size_t BufSize>
+class LineBuffer {
+ using Iterator = typename std::array<char, BufSize>::iterator;
+ std::array<char, BufSize> _buffer;
+ Iterator _bufferedTo;
+ Iterator _consumedTo;
+ bool _discardLine {false};
+public:
+ LineBuffer()
+ : _bufferedTo {_buffer.begin()}
+ , _consumedTo {_buffer.begin()}
+ {
+ }
+
+ template<typename Reader, typename Consumer>
+ ssize_t readLines(const Reader& reader, const Consumer& consumer)
+ {
+ while (true) {
+ auto bytesRead = reader(_bufferedTo, _buffer.end() - _bufferedTo);
+ if (bytesRead <= 0) {
+ return bytesRead;
+ }
+ _bufferedTo += bytesRead;
+ dispatchLines(consumer);
+ resetBuffer();
+ }
+ }
+private:
+ template<typename Consumer>
+ void dispatchLines(const Consumer& consumer)
+ {
+ while (true) {
+ auto separator = std::find(_consumedTo, _bufferedTo, '\n');
+ if (separator == _bufferedTo) {
+ break;
+ }
+ size_t lineLength = separator - _consumedTo;
+ if (!_discardLine) {
+ consumer(_consumedTo, lineLength);
+ }
+ _consumedTo = separator + 1;
+ _discardLine = false;
+ }
+ }
+
+ void resetBuffer()
+ {
+ size_t bytesRemaining = _bufferedTo - _consumedTo;
+ if (bytesRemaining == _buffer.size()) {
+ // line too long
+ _discardLine = true;
+ _consumedTo = _buffer.begin();
+ _bufferedTo = _buffer.begin();
+ } else {
+ // move the last partial message to the front of the buffer, so a full-sized
+ // message will fit
+ std::copy(_consumedTo, _bufferedTo, _buffer.begin());
+ _consumedTo = _buffer.begin();
+ _bufferedTo = _consumedTo + bytesRemaining;
+ }
+ }
+};
diff --git a/somebar/src/main.cpp b/somebar/src/main.cpp
new file mode 100644
index 0000000..15a749a
--- /dev/null
+++ b/somebar/src/main.cpp
@@ -0,0 +1,613 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#include <algorithm>
+#include <cstdio>
+#include <sstream>
+#include <list>
+#include <optional>
+#include <utility>
+#include <vector>
+#include <fcntl.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <linux/input-event-codes.h>
+#include <wayland-client.h>
+#include <wayland-cursor.h>
+#include "wlr-layer-shell-unstable-v1-client-protocol.h"
+#include "xdg-output-unstable-v1-client-protocol.h"
+#include "xdg-shell-client-protocol.h"
+#include "common.hpp"
+#include "config.hpp"
+#include "bar.hpp"
+#include "line_buffer.hpp"
+
+struct Monitor {
+ uint32_t registryName;
+ std::string xdgName;
+ wl_unique_ptr<wl_output> wlOutput;
+ Bar bar;
+ bool desiredVisibility {true};
+ bool hasData;
+ uint32_t tags;
+};
+
+struct SeatPointer {
+ wl_unique_ptr<wl_pointer> wlPointer;
+ Monitor* focusedMonitor;
+ int x, y;
+ std::vector<int> btns;
+};
+struct Seat {
+ uint32_t name;
+ wl_unique_ptr<wl_seat> wlSeat;
+ std::optional<SeatPointer> pointer;
+};
+
+static Monitor* monitorFromSurface(const wl_surface* surface);
+static void setupMonitor(uint32_t name, wl_output* output);
+static void updatemon(Monitor &mon);
+static void onReady();
+static void setupStatusFifo();
+static void onStatus();
+static void onStdin();
+static void handleStdin(const std::string& line);
+static void updateVisibility(const std::string& name, bool(*updater)(bool));
+static void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version);
+static void onGlobalRemove(void*, wl_registry* registry, uint32_t name);
+static void requireGlobal(const void* p, const char* name);
+static void waylandFlush();
+static void cleanup();
+
+wl_display* display;
+wl_compositor* compositor;
+wl_shm* shm;
+zwlr_layer_shell_v1* wlrLayerShell;
+static xdg_wm_base* xdgWmBase;
+static zxdg_output_manager_v1* xdgOutputManager;
+static wl_surface* cursorSurface;
+static wl_cursor_image* cursorImage;
+static bool ready;
+static std::list<Monitor> monitors;
+static std::vector<std::pair<uint32_t, wl_output*>> uninitializedOutputs;
+static std::list<Seat> seats;
+static Monitor* selmon;
+static std::string lastStatus;
+static std::string statusFifoName;
+static std::vector<pollfd> pollfds;
+static std::array<int, 2> signalSelfPipe;
+static int displayFd {-1};
+static int statusFifoFd {-1};
+static int statusFifoWriter {-1};
+static bool quitting {false};
+
+void spawn(Monitor&, const Arg& arg)
+{
+ if (fork() == 0) {
+ auto argv = static_cast<char* const*>(arg.v);
+ setsid();
+ execvp(argv[0], argv);
+ fprintf(stderr, "somebar: execvp %s ", argv[0]);
+ perror(" failed");
+ exit(1);
+ }
+}
+
+static const struct xdg_wm_base_listener xdgWmBaseListener = {
+ [](void*, xdg_wm_base* sender, uint32_t serial) {
+ xdg_wm_base_pong(sender, serial);
+ }
+};
+
+static const struct zxdg_output_v1_listener xdgOutputListener = {
+ .logical_position = [](void*, zxdg_output_v1*, int, int) { },
+ .logical_size = [](void*, zxdg_output_v1*, int, int) { },
+ .done = [](void*, zxdg_output_v1*) { },
+ .name = [](void* mp, zxdg_output_v1* xdgOutput, const char* name) {
+ auto& monitor = *static_cast<Monitor*>(mp);
+ monitor.xdgName = name;
+ zxdg_output_v1_destroy(xdgOutput);
+ },
+ .description = [](void*, zxdg_output_v1*, const char*) { },
+};
+
+Monitor* monitorFromSurface(const wl_surface* surface)
+{
+ auto mon = std::find_if(begin(monitors), end(monitors), [surface](const Monitor& mon) {
+ return mon.bar.surface() == surface;
+ });
+ return mon != end(monitors) ? &*mon : nullptr;
+}
+static const struct wl_pointer_listener pointerListener = {
+ .enter = [](void* sp, wl_pointer* pointer, uint32_t serial,
+ wl_surface* surface, wl_fixed_t x, wl_fixed_t y)
+ {
+ auto& seat = *static_cast<Seat*>(sp);
+ seat.pointer->focusedMonitor = monitorFromSurface(surface);
+ if (!cursorImage) {
+ auto cursorTheme = wl_cursor_theme_load(nullptr, 24, shm);
+ cursorImage = wl_cursor_theme_get_cursor(cursorTheme, "left_ptr")->images[0];
+ cursorSurface = wl_compositor_create_surface(compositor);
+ wl_surface_attach(cursorSurface, wl_cursor_image_get_buffer(cursorImage), 0, 0);
+ wl_surface_commit(cursorSurface);
+ }
+ wl_pointer_set_cursor(pointer, serial, cursorSurface,
+ cursorImage->hotspot_x, cursorImage->hotspot_y);
+ },
+ .leave = [](void* sp, wl_pointer*, uint32_t serial, wl_surface*) {
+ auto& seat = *static_cast<Seat*>(sp);
+ seat.pointer->focusedMonitor = nullptr;
+ },
+ .motion = [](void* sp, wl_pointer*, uint32_t, wl_fixed_t x, wl_fixed_t y) {
+ auto& seat = *static_cast<Seat*>(sp);
+ seat.pointer->x = wl_fixed_to_int(x);
+ seat.pointer->y = wl_fixed_to_int(y);
+ },
+ .button = [](void* sp, wl_pointer*, uint32_t, uint32_t, uint32_t button, uint32_t pressed) {
+ auto& seat = *static_cast<Seat*>(sp);
+ auto it = std::find(begin(seat.pointer->btns), end(seat.pointer->btns), button);
+ if (pressed == WL_POINTER_BUTTON_STATE_PRESSED && it == end(seat.pointer->btns)) {
+ seat.pointer->btns.push_back(button);
+ } else if (pressed == WL_POINTER_BUTTON_STATE_RELEASED && it != end(seat.pointer->btns)) {
+ seat.pointer->btns.erase(it);
+ }
+ },
+ .axis = [](void* sp, wl_pointer*, uint32_t, uint32_t, wl_fixed_t) { },
+ .frame = [](void* sp, wl_pointer*) {
+ auto& seat = *static_cast<Seat*>(sp);
+ auto mon = seat.pointer->focusedMonitor;
+ if (!mon) {
+ return;
+ }
+ for (auto btn : seat.pointer->btns) {
+ mon->bar.click(mon, seat.pointer->x, seat.pointer->y, btn);
+ }
+ seat.pointer->btns.clear();
+ },
+ .axis_source = [](void*, wl_pointer*, uint32_t) { },
+ .axis_stop = [](void*, wl_pointer*, uint32_t, uint32_t) { },
+ .axis_discrete = [](void*, wl_pointer*, uint32_t, int32_t) { },
+};
+
+static const struct wl_seat_listener seatListener = {
+ .capabilities = [](void* sp, wl_seat*, uint32_t cap)
+ {
+ auto& seat = *static_cast<Seat*>(sp);
+ auto hasPointer = cap & WL_SEAT_CAPABILITY_POINTER;
+ if (!seat.pointer && hasPointer) {
+ auto &pointer = seat.pointer.emplace();
+ pointer.wlPointer = wl_unique_ptr<wl_pointer> {wl_seat_get_pointer(seat.wlSeat.get())};
+ wl_pointer_add_listener(seat.pointer->wlPointer.get(), &pointerListener, &seat);
+ } else if (seat.pointer && !hasPointer) {
+ seat.pointer.reset();
+ }
+ },
+ .name = [](void*, wl_seat*, const char* name) { }
+};
+
+void setupMonitor(uint32_t name, wl_output* output) {
+ auto& monitor = monitors.emplace_back(Monitor {name, {}, wl_unique_ptr<wl_output> {output}});
+ monitor.bar.setStatus(lastStatus);
+ auto xdgOutput = zxdg_output_manager_v1_get_xdg_output(xdgOutputManager, monitor.wlOutput.get());
+ zxdg_output_v1_add_listener(xdgOutput, &xdgOutputListener, &monitor);
+}
+
+void updatemon(Monitor& mon)
+{
+ if (!mon.hasData) {
+ return;
+ }
+ if (mon.desiredVisibility) {
+ if (mon.bar.visible()) {
+ mon.bar.invalidate();
+ } else {
+ mon.bar.show(mon.wlOutput.get());
+ }
+ } else if (mon.bar.visible()) {
+ mon.bar.hide();
+ }
+}
+
+// called after we have received the initial batch of globals
+void onReady()
+{
+ requireGlobal(compositor, "wl_compositor");
+ requireGlobal(shm, "wl_shm");
+ requireGlobal(wlrLayerShell, "zwlr_layer_shell_v1");
+ requireGlobal(xdgOutputManager, "zxdg_output_manager_v1");
+ setupStatusFifo();
+ wl_display_roundtrip(display); // roundtrip so we receive all dwl tags etc.
+
+ ready = true;
+ for (auto output : uninitializedOutputs) {
+ setupMonitor(output.first, output.second);
+ }
+ wl_display_roundtrip(display); // wait for xdg_output names before we read stdin
+}
+
+bool createFifo(std::string path)
+{
+ auto result = mkfifo(path.c_str(), 0666);
+ if (result == 0) {
+ auto fd = open(path.c_str(), O_CLOEXEC | O_NONBLOCK | O_RDONLY);
+ if (fd < 0) {
+ diesys("open status fifo reader");
+ }
+ statusFifoName = path;
+ statusFifoFd = fd;
+
+ fd = open(path.c_str(), O_CLOEXEC | O_WRONLY);
+ if (fd < 0) {
+ diesys("open status fifo writer");
+ }
+ statusFifoWriter = fd;
+
+ pollfds.push_back({
+ .fd = statusFifoFd,
+ .events = POLLIN,
+ });
+ return true;
+ } else if (errno != EEXIST) {
+ diesys("mkfifo");
+ }
+
+ return false;
+}
+
+void setupStatusFifo()
+{
+ if (!statusFifoName.empty()) {
+ createFifo(statusFifoName);
+ return;
+ }
+
+ for (auto i=0; i<100; i++) {
+ auto path = std::string{getenv("XDG_RUNTIME_DIR")} + "/somebar-" + std::to_string(i);
+ if (createFifo(path)) {
+ return;
+ }
+ }
+}
+
+static LineBuffer<512> stdinBuffer;
+static void onStdin()
+{
+ auto res = stdinBuffer.readLines(
+ [](void* p, size_t size) { return read(0, p, size); },
+ [](char* p, size_t size) { handleStdin({p, size}); });
+ if (res == 0) {
+ quitting = true;
+ }
+}
+
+static void handleStdin(const std::string& line)
+{
+ // this parses the lines that dwl sends in printstatus()
+ std::string monName, command;
+ auto stream = std::istringstream {line};
+ stream >> monName >> command;
+ if (!stream.good()) {
+ return;
+ }
+ auto mon = std::find_if(begin(monitors), end(monitors), [&](const Monitor& mon) {
+ return mon.xdgName == monName;
+ });
+ if (mon == end(monitors))
+ return;
+ if (command == "title") {
+ auto title = std::string {};
+ stream >> std::ws;
+ std::getline(stream, title);
+ mon->bar.setTitle(title);
+ } else if (command == "selmon") {
+ uint32_t selected;
+ stream >> selected;
+ mon->bar.setSelected(selected);
+ if (selected) {
+ selmon = &*mon;
+ } else if (selmon == &*mon) {
+ selmon = nullptr;
+ }
+ } else if (command == "tags") {
+ uint32_t occupied, tags, clientTags, urgent;
+ stream >> occupied >> tags >> clientTags >> urgent;
+ for (auto i=0u; i<tagNames.size(); i++) {
+ auto tagMask = 1 << i;
+ int state = TagState::None;
+ if (tags & tagMask)
+ state |= TagState::Active;
+ if (urgent & tagMask)
+ state |= TagState::Urgent;
+ mon->bar.setTag(i, state, occupied & tagMask ? 1 : 0, clientTags & tagMask ? 0 : -1);
+ }
+ mon->tags = tags;
+ } else if (command == "layout") {
+ auto layout = std::string {};
+ stream >> std::ws;
+ std::getline(stream, layout);
+ mon->bar.setLayout(layout);
+ }
+ mon->hasData = true;
+ updatemon(*mon);
+}
+
+const std::string prefixStatus = "status ";
+const std::string prefixShow = "show ";
+const std::string prefixHide = "hide ";
+const std::string prefixToggle = "toggle ";
+const std::string argAll = "all";
+const std::string argSelected = "selected";
+
+static LineBuffer<512> statusBuffer;
+void onStatus()
+{
+ statusBuffer.readLines(
+ [](void* p, size_t size) {
+ return read(statusFifoFd, p, size);
+ },
+ [](const char* buffer, size_t n) {
+ auto str = std::string {buffer, n};
+ if (str.rfind(prefixStatus, 0) == 0) {
+ lastStatus = str.substr(prefixStatus.size());
+ for (auto &monitor : monitors) {
+ monitor.bar.setStatus(lastStatus);
+ monitor.bar.invalidate();
+ }
+ } else if (str.rfind(prefixShow, 0) == 0) {
+ updateVisibility(str.substr(prefixShow.size()), [](bool) { return true; });
+ } else if (str.rfind(prefixHide, 0) == 0) {
+ updateVisibility(str.substr(prefixHide.size()), [](bool) { return false; });
+ } else if (str.rfind(prefixToggle, 0) == 0) {
+ updateVisibility(str.substr(prefixToggle.size()), [](bool vis) { return !vis; });
+ }
+ });
+}
+
+void updateVisibility(const std::string& name, bool(*updater)(bool))
+{
+ auto isCurrent = name == argSelected;
+ auto isAll = name == argAll;
+ for (auto& mon : monitors) {
+ if (isAll ||
+ isCurrent && &mon == selmon ||
+ mon.xdgName == name) {
+ auto newVisibility = updater(mon.desiredVisibility);
+ if (newVisibility != mon.desiredVisibility) {
+ mon.desiredVisibility = newVisibility;
+ updatemon(mon);
+ }
+ }
+ }
+}
+
+struct HandleGlobalHelper {
+ wl_registry* registry;
+ uint32_t name;
+ const char* interface;
+
+ template<typename T>
+ bool handle(T& store, const wl_interface& iface, int version) {
+ if (strcmp(interface, iface.name)) {
+ return false;
+ }
+ store = static_cast<T>(wl_registry_bind(registry, name, &iface, version));
+ return true;
+ }
+};
+void onGlobalAdd(void*, wl_registry* registry, uint32_t name, const char* interface, uint32_t version)
+{
+ auto reg = HandleGlobalHelper { registry, name, interface };
+ if (reg.handle(compositor, wl_compositor_interface, 4)) return;
+ if (reg.handle(shm, wl_shm_interface, 1)) return;
+ if (reg.handle(wlrLayerShell, zwlr_layer_shell_v1_interface, 1)) return;
+ if (reg.handle(xdgOutputManager, zxdg_output_manager_v1_interface, 3)) return;
+ if (reg.handle(xdgWmBase, xdg_wm_base_interface, 2)) {
+ xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr);
+ return;
+ }
+ if (wl_seat* wlSeat; reg.handle(wlSeat, wl_seat_interface, 7)) {
+ auto& seat = seats.emplace_back(Seat {name, wl_unique_ptr<wl_seat> {wlSeat}});
+ wl_seat_add_listener(wlSeat, &seatListener, &seat);
+ return;
+ }
+ if (wl_output* output; reg.handle(output, wl_output_interface, 1)) {
+ if (ready) {
+ setupMonitor(name, output);
+ } else {
+ uninitializedOutputs.push_back({name, output});
+ }
+ return;
+ }
+}
+void onGlobalRemove(void*, wl_registry* registry, uint32_t name)
+{
+ monitors.remove_if([name](const Monitor &mon) { return mon.registryName == name; });
+ seats.remove_if([name](const Seat &seat) { return seat.name == name; });
+}
+static const struct wl_registry_listener registry_listener = {
+ .global = onGlobalAdd,
+ .global_remove = onGlobalRemove,
+};
+
+int main(int argc, char* argv[])
+{
+ int opt;
+ while ((opt = getopt(argc, argv, "chvs:")) != -1) {
+ switch (opt) {
+ case 's':
+ statusFifoName = optarg;
+ break;
+ case 'h':
+ printf("Usage: %s [-h] [-v] [-s path to the fifo] [-c command]\n", argv[0]);
+ printf(" -h: Show this help\n");
+ printf(" -v: Show somebar version\n");
+ printf(" -s: Change path to the fifo (default is \"$XDG_RUNTIME_DIR/somebar-0\")\n");
+ printf(" -c: Sends a command to sombar. See README for details.\n");
+ printf("If any of these are specified (except -s), somebar exits after the action.\n");
+ printf("Otherwise, somebar will display itself.\n");
+ exit(0);
+ case 'v':
+ printf("somebar " SOMEBAR_VERSION "\n");
+ exit(0);
+ case 'c':
+ if (optind >= argc) {
+ die("Expected command");
+ }
+ if (statusFifoName.empty()) {
+ statusFifoName = std::string {getenv("XDG_RUNTIME_DIR")} + "/somebar-0";
+ }
+ statusFifoWriter = open(statusFifoName.c_str(), O_WRONLY | O_CLOEXEC);
+ if (statusFifoWriter < 0) {
+ fprintf(stderr, "could not open %s: ", statusFifoName.c_str());
+ perror("");
+ exit(1);
+ }
+ auto str = std::string {};
+ for (auto i = optind; i<argc; i++) {
+ if (i > optind) str += " ";
+ str += argv[i];
+ }
+ str += "\n";
+ write(statusFifoWriter, str.c_str(), str.size());
+ exit(0);
+ }
+ }
+
+ if (pipe(signalSelfPipe.data()) < 0) {
+ diesys("pipe");
+ }
+ setCloexec(signalSelfPipe[0]);
+ setCloexec(signalSelfPipe[1]);
+
+ struct sigaction sighandler = {};
+ sighandler.sa_handler = [](int) {
+ if (write(signalSelfPipe[1], "0", 1) < 0) {
+ diesys("write");
+ }
+ };
+ if (sigaction(SIGTERM, &sighandler, nullptr) < 0) {
+ diesys("sigaction");
+ }
+ if (sigaction(SIGINT, &sighandler, nullptr) < 0) {
+ diesys("sigaction");
+ }
+
+ struct sigaction chld_handler = {};
+ chld_handler.sa_handler = SIG_IGN;
+ if (sigaction(SIGCHLD, &chld_handler, nullptr) < 0) {
+ die("sigaction");
+ }
+
+ pollfds.push_back({
+ .fd = signalSelfPipe[0],
+ .events = POLLIN,
+ });
+
+ display = wl_display_connect(nullptr);
+ if (!display) {
+ die("Failed to connect to Wayland display");
+ }
+ displayFd = wl_display_get_fd(display);
+
+ auto registry = wl_display_get_registry(display);
+ wl_registry_add_listener(registry, &registry_listener, nullptr);
+ wl_display_roundtrip(display);
+ onReady();
+
+ pollfds.push_back({
+ .fd = displayFd,
+ .events = POLLIN,
+ });
+ pollfds.push_back({
+ .fd = STDIN_FILENO,
+ .events = POLLIN,
+ });
+ if (fcntl(STDIN_FILENO, F_SETFL, O_NONBLOCK) < 0) {
+ diesys("fcntl F_SETFL");
+ }
+
+ while (!quitting) {
+ waylandFlush();
+ if (poll(pollfds.data(), pollfds.size(), -1) < 0) {
+ if (errno != EINTR) {
+ diesys("poll");
+ }
+ } else {
+ for (auto& ev : pollfds) {
+ if (ev.revents & POLLNVAL) {
+ die("poll revents contains POLLNVAL");
+ } else if (ev.fd == displayFd) {
+ if (ev.revents & POLLIN) {
+ if (wl_display_dispatch(display) < 0) {
+ die("wl_display_dispatch");
+ }
+ }
+ if (ev.revents & POLLOUT) {
+ ev.events = POLLIN;
+ waylandFlush();
+ }
+ } else if (ev.fd == STDIN_FILENO && (ev.revents & POLLIN)) {
+ onStdin();
+ } else if (ev.fd == statusFifoFd && (ev.revents & POLLIN)) {
+ onStatus();
+ } else if (ev.fd == signalSelfPipe[0] && (ev.revents & POLLIN)) {
+ quitting = true;
+ }
+ }
+ }
+ }
+ cleanup();
+}
+
+void requireGlobal(const void* p, const char* name)
+{
+ if (p) return;
+ fprintf(stderr, "Wayland compositor does not export required global %s, aborting.\n", name);
+ cleanup();
+ exit(1);
+}
+
+void waylandFlush()
+{
+ wl_display_dispatch_pending(display);
+ if (wl_display_flush(display) < 0 && errno == EAGAIN) {
+ for (auto& ev : pollfds) {
+ if (ev.fd == displayFd) {
+ ev.events |= POLLOUT;
+ }
+ }
+ }
+}
+
+void setCloexec(int fd)
+{
+ int flags = fcntl(fd, F_GETFD);
+ if (flags == -1) {
+ diesys("fcntl FD_GETFD");
+ }
+ if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) {
+ diesys("fcntl FD_SETFD");
+ }
+}
+
+void cleanup() {
+ if (!statusFifoName.empty()) {
+ unlink(statusFifoName.c_str());
+ }
+}
+
+void die(const char* why) {
+ fprintf(stderr, "error: %s failed, aborting\n", why);
+ cleanup();
+ exit(1);
+}
+
+void diesys(const char* why) {
+ perror(why);
+ cleanup();
+ exit(1);
+}
diff --git a/somebar/src/shm_buffer.cpp b/somebar/src/shm_buffer.cpp
new file mode 100644
index 0000000..59baf6f
--- /dev/null
+++ b/somebar/src/shm_buffer.cpp
@@ -0,0 +1,85 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "shm_buffer.hpp"
+#include "common.hpp"
+
+static int createAnonShm();
+constexpr int n = 2;
+
+ShmBuffer::ShmBuffer(int w, int h, wl_shm_format format)
+ : width(w)
+ , height(h)
+ , stride(w*4)
+{
+ auto oneSize = stride*size_t(h);
+ auto totalSize = oneSize * n;
+ auto fd = createAnonShm();
+ if (fd < 0) {
+ diesys("memfd_create");
+ }
+ if (ftruncate(fd, totalSize) < 0) {
+ diesys("ftruncate");
+ }
+ auto pool = wl_shm_create_pool(shm, fd, totalSize);
+ auto ptr = reinterpret_cast<uint8_t*>(mmap(nullptr, totalSize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0));
+ if (!ptr) {
+ diesys("mmap");
+ }
+ _mapping = MemoryMapping {ptr, totalSize};
+ close(fd);
+ for (auto i=0; i<n; i++) {
+ auto offset = oneSize*i;
+ _buffers[i] = {
+ ptr+offset,
+ wl_unique_ptr<wl_buffer> { wl_shm_pool_create_buffer(pool, offset, width, height, stride, format) },
+ };
+ }
+ wl_shm_pool_destroy(pool);
+}
+
+uint8_t* ShmBuffer::data()
+{
+ return _buffers[_current].data;
+}
+
+wl_buffer* ShmBuffer::buffer()
+{
+ return _buffers[_current].buffer.get();
+}
+
+void ShmBuffer::flip()
+{
+ _current = 1-_current;
+}
+
+#if defined(__linux__)
+int createAnonShm() {
+ return memfd_create("wl_shm", MFD_CLOEXEC);
+}
+#elif defined(__FreeBSD__)
+int createAnonShm() {
+ auto fd = shm_open(SHM_ANON, O_CREAT | O_RDWR, 0600);
+ setCloexec(fd);
+ return fd;
+}
+#elif defined(__OpenBSD__)
+int createAnonShm() {
+ char name[] = "/wl_shm-XXXXXX";
+ auto fd = shm_mkstemp(name);
+ if (fd >= 0) {
+ auto res = shm_unlink(name);
+ if (res < 0) {
+ return res;
+ }
+ }
+ setCloexec(fd);
+ return fd;
+}
+#else
+#error "your system has no sane method of creating an anonymous shared memory object. no, calling shm_open in a loop is not sane."
+#endif
diff --git a/somebar/src/shm_buffer.hpp b/somebar/src/shm_buffer.hpp
new file mode 100644
index 0000000..f6927dd
--- /dev/null
+++ b/somebar/src/shm_buffer.hpp
@@ -0,0 +1,45 @@
+// somebar - dwl bar
+// See LICENSE file for copyright and license details.
+
+#pragma once
+#include <array>
+#include <sys/mman.h>
+#include <wayland-client.h>
+#include "common.hpp"
+
+class MemoryMapping {
+ void* _ptr {nullptr};
+ size_t _size {0};
+public:
+ MemoryMapping() { }
+ explicit MemoryMapping(void* ptr, size_t size) : _ptr(ptr), _size(size) { }
+ MemoryMapping(const MemoryMapping&) = delete;
+ MemoryMapping(MemoryMapping&& other) { swap(other); }
+ MemoryMapping& operator=(const MemoryMapping& other) = delete;
+ MemoryMapping& operator=(MemoryMapping&& other) { swap(other); return *this; }
+ ~MemoryMapping() { if (_ptr) munmap(_ptr, _size); }
+ void swap(MemoryMapping &other) {
+ using std::swap;
+ swap(_ptr, other._ptr);
+ swap(_size, other._size);
+ }
+};
+
+// double buffered shm
+// format is must be 32-bit
+class ShmBuffer {
+ struct Buf {
+ uint8_t* data {nullptr};
+ wl_unique_ptr<wl_buffer> buffer;
+ };
+ std::array<Buf, 2> _buffers;
+ int _current {0};
+ MemoryMapping _mapping;
+public:
+ const uint32_t width, height, stride;
+
+ explicit ShmBuffer(int width, int height, wl_shm_format format);
+ uint8_t* data();
+ wl_buffer* buffer();
+ void flip();
+};