nixos-dotfiles

nixos-dotfiles

https://git.tonybtw.com/nixos-dotfiles.git git://git.tonybtw.com/nixos-dotfiles.git

Initial commit.

Commit
247691b4e2a0c14010112db6a659a878da4a23ab
Author
tonybanters <tonybanters@gmail.com>
Date
2026-01-26 08:26:42

Diff

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..f22b670
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,8 @@
+dwl
+*.o
+*-protocol.c
+*-protocol.h
+.ccls-cache
+config.h
+.cache
+.clangd
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..f288702
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+                    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/Makefile b/Makefile
new file mode 100644
index 0000000..279b1c0
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,79 @@
+.POSIX:
+.SUFFIXES:
+
+include config.mk
+
+# flags for compiling
+DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \
+	-DVERSION=\"$(VERSION)\" $(XWAYLAND)
+DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
+	-Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \
+	-Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \
+	-Wfloat-conversion
+
+# CFLAGS / LDFLAGS
+PKGS      = wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS)
+DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
+LDLIBS    = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
+
+all: dwl
+dwl: dwl.o util.o
+	$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
+dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
+	pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
+	wlr-output-power-management-unstable-v1-protocol.h xdg-shell-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`
+
+cursor-shape-v1-protocol.h:
+	$(WAYLAND_SCANNER) enum-header \
+		$(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@
+pointer-constraints-unstable-v1-protocol.h:
+	$(WAYLAND_SCANNER) enum-header \
+		$(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
+wlr-layer-shell-unstable-v1-protocol.h:
+	$(WAYLAND_SCANNER) enum-header \
+		protocols/wlr-layer-shell-unstable-v1.xml $@
+wlr-output-power-management-unstable-v1-protocol.h:
+	$(WAYLAND_SCANNER) server-header \
+		protocols/wlr-output-power-management-unstable-v1.xml $@
+xdg-shell-protocol.h:
+	$(WAYLAND_SCANNER) server-header \
+		$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.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
+	rm -f $(DESTDIR)$(PREFIX)/bin/dwl
+	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) -o $@ -c $<
diff --git a/README.org b/README.org
new file mode 100644
index 0000000..9d36435
--- /dev/null
+++ b/README.org
@@ -0,0 +1,205 @@
+#+TITLE: DWL Window Manager Setup
+#+AUTHOR: Tony, btw
+#+DATE: 9-21-2025
+
+* Prerequisites
+
+Before building and using dwl, you'll need to install the following dependencies:
+
+1. wayland, wayland-protocols
+2. wlroots_0_19 (dependency for dwl)
+3. foot (terminal emulator)
+4. base-devel (so we can compile dwl)
+5. git (to clone the 2 repos for dwl, and slstatus)
+6. wmenu (a dmenu clone for wayland)
+7. wl-clipboard (wayland clipboard tool)
+8. grim, slurp for screenshots
+9. swaybg (for wallpapers)
+10. firefox (web browser)
+11. ttf-jetbrains-mono-nerd (font)
+
+** Installation Commands by Distribution
+
+*** Arch Linux
+#+begin_src sh
+sudo pacman -S wayland wayland-protocols wlroots_0_19 foot base-devel git wmenu wl-clipboard grim slurp swaybg firefox ttf-jetbrains-mono-nerd
+#+end_src
+
+*** Gentoo Linux
+#+begin_src sh
+sudo emerge -av dev-libs/wayland dev-libs/wayland-protocols gui-libs/wlroots x11-terms/foot sys-devel/base-devel dev-vcs/git gui-apps/wmenu gui-apps/wl-clipboard media-gfx/grim gui-apps/slurp gui-apps/swaybg www-client/firefox media-fonts/jetbrains-mono
+#+end_src
+
+*** NixOS
+Add to your =configuration.nix=:
+#+begin_src nix
+environment.systemPackages = with pkgs; [
+  wayland
+  wayland-protocols
+  wlroots_0_19
+  foot
+  git
+  wmenu
+  wl-clipboard
+  grim
+  slurp
+  swaybg
+  firefox
+  jetbrains-mono
+];
+#+end_src
+
+* Colors and Customization
+
+To customize the colors in dwl, you'll need to edit the =config.h= file before compiling. This setup uses the TokyoNight color scheme.
+
+** TokyoNight Color Scheme
+The current configuration uses TokyoNight colors with 32-bit RGBA values:
+
+#+begin_src c
+/* TokyoNight colors (0xRRGGBBAA) */
+static const uint32_t col_bg    = 0x1a1b26ff; /* background */
+static const uint32_t col_fg    = 0xa9b1d6ff; /* foreground */
+static const uint32_t col_blk   = 0x32344aff; /* black (normal) */
+static const uint32_t col_red   = 0xf7768eff; /* red */
+static const uint32_t col_grn   = 0x9ece6aff; /* green */
+static const uint32_t col_ylw   = 0xe0af68ff; /* yellow */
+static const uint32_t col_blu   = 0x7aa2f7ff; /* blue */
+static const uint32_t col_mag   = 0xad8ee6ff; /* magenta */
+static const uint32_t col_cyn   = 0x0db9d7ff; /* cyan (highlight) */
+static const uint32_t col_brblk = 0x444b6aff; /* bright black */
+#+end_src
+
+** Color Applications
+These colors are used in different parts of the interface:
+
+- =SchemeNorm= (vacant tags): Uses =col_fg= on =col_bg= with =col_brblk= borders
+- =SchemeSel= (selected tag): Uses =col_cyn= on =col_bg= with =col_mag= borders
+- =SchemeOcc= (occupied tags): Uses =col_cyn= on =col_bg= with =col_cyn= borders
+- =SchemeUnder= (underlines): Uses =col_mag= on =col_bg= with =col_mag= borders
+
+** Changing Colors for Your Setup
+1. Edit =config.h= in your dwl source directory
+2. Modify the color hex values (format: 0xRRGGBBAA where AA is alpha)
+3. Update the =colors= array to use your preferred color combinations
+4. Recompile dwl with =make clean && make=
+5. Reinstall with =sudo make install=
+
+Popular alternative color schemes:
+- Gruvbox: =#282828=, =#3c3836=, =#ebdbb2=, =#fb4934=
+- Nord: =#2e3440=, =#3b4252=, =#eceff4=, =#88c0d0=
+- Dracula: =#282a36=, =#44475a=, =#f8f8f2=, =#bd93f9=
+
+* slstatus
+
+slstatus is a status bar for dwl that displays system information like time, battery, CPU usage, etc. It uses the same TokyoNight colors for consistency.
+
+** Installation
+#+begin_src sh
+git clone https://git.suckless.org/slstatus
+cd slstatus
+sudo make clean install
+#+end_src
+
+** Configuration
+Edit =config.h= to customize what information is displayed:
+#+begin_src c
+static const struct arg args[] = {
+    /* function format          argument */
+    { datetime, "%s",           "%F %T" },
+    { separator, " | " },
+    { battery_perc, "%s%%",     "BAT0" },
+};
+#+end_src
+
+** Using the Launch Script
+
+The provided script starts both slstatus and dwl together, with slstatus feeding information to dwl's status bar and swaybg setting a wallpaper.
+
+*** Script Content
+#+begin_src sh
+#!/bin/sh
+slstatus -s | dwl -s "sh -c 'swaybg -i /home/tony/walls/wall1.png &'"
+#+end_src
+
+*** Adding Script to PATH
+If you want to add this to your PATH for easy access:
+
+#+begin_src sh
+mkdir ~/.local/bin
+cp ~/start_dwl.sh ~/.local/bin/start_dwl
+vim ~/.bashrc
+# Add this line to ~/.bashrc:
+export PATH="$HOME/.local/bin:$PATH"
+source ~/.bashrc
+#+end_src
+
+Now you can start your desktop environment by simply running =start_dwl= from anywhere.
+
+* Keybinds
+
+Here are all the configured keybinds for this dwl setup. MODKEY is typically the Super/Windows key.
+
+** Application Launchers
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + d= | wmenu | Launch application menu |
+| =MODKEY + Return= | foot | Launch terminal |
+
+** Screenshots
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =Ctrl + F12= | snip script | Take screenshot (via script) |
+| =MODKEY + s= | /home/tony/scripts/snip.sh | Take screenshot |
+| =MODKEY + Shift + S= | Screenshot selection | Select area and copy to clipboard |
+
+** Window Management
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + j= | Focus next | Focus next window in stack |
+| =MODKEY + k= | Focus previous | Focus previous window in stack |
+| =MODKEY + q= | Kill client | Close focused window |
+| =MODKEY + Return= | Zoom | Move focused window to master |
+| =MODKEY + Tab= | View last tag | Switch to previously viewed tag |
+| =MODKEY + e= | Toggle fullscreen | Make window fullscreen |
+| =MODKEY + Shift + Space= | Toggle floating | Make window floating/tiled |
+
+** Layout Management
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + t= | Tiled layout | Set layout to tiled |
+| =MODKEY + f= | Floating layout | Set layout to floating |
+| =MODKEY + m= | Monocle layout | Set layout to monocle |
+| =MODKEY + Space= | Toggle layout | Cycle through layouts |
+| =MODKEY + h= | Decrease master | Decrease master area size |
+| =MODKEY + l= | Increase master | Increase master area size |
+| =MODKEY + i= | Increase masters | Increase number of masters |
+| =MODKEY + p= | Decrease masters | Decrease number of masters |
+
+** Status Bar and Gaps
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + b= | Toggle bar | Show/hide status bar |
+| =MODKEY + a= | Toggle gaps | Enable/disable window gaps |
+
+** Tag Management (Workspaces)
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + [1-9]= | View tag | Switch to tag 1-9 |
+| =MODKEY + Shift + [1-9]= | Move to tag | Move window to tag 1-9 |
+| =MODKEY + 0= | View all tags | Show windows from all tags |
+| =MODKEY + Shift + )= | Tag all | Tag window with all tags |
+
+** Monitor Management
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + ,= | Focus left monitor | Focus monitor to the left |
+| =MODKEY + .= | Focus right monitor | Focus monitor to the right |
+| =MODKEY + Shift + <= | Move to left monitor | Move window to left monitor |
+| =MODKEY + Shift + >= | Move to right monitor | Move window to right monitor |
+
+** System Control
+| Key Combination | Action | Description |
+|-----------------|--------|-------------|
+| =MODKEY + Shift + Q= | Quit dwl | Exit window manager |
+| =Ctrl + Alt + Backspace= | Terminate server | Force quit (emergency exit) |
diff --git a/client.h b/client.h
new file mode 100644
index 0000000..d9f90bb
--- /dev/null
+++ b/client.h
@@ -0,0 +1,404 @@
+/*
+ * 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 == X11;
+#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 ? c->surface.xwayland->class : "broken";
+#endif
+	return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken";
+}
+
+static inline void
+client_get_clip(Client *c, struct wlr_box *clip)
+{
+	*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
+
+	clip->x = c->surface.xdg->geometry.x;
+	clip->y = c->surface.xdg->geometry.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
+	*geom = c->surface.xdg->geometry;
+}
+
+static inline Client *
+client_get_parent(Client *c)
+{
+	Client *p = NULL;
+#ifdef XWAYLAND
+	if (client_is_x11(c)) {
+		if (c->surface.xwayland->parent)
+			toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL);
+		return p;
+	}
+#endif
+	if (c->surface.xdg->toplevel->parent)
+		toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL);
+	return p;
+}
+
+static inline int
+client_has_children(Client *c)
+{
+#ifdef XWAYLAND
+	if (client_is_x11(c))
+		return !wl_list_empty(&c->surface.xwayland->children);
+#endif
+	/* surface.xdg->link is never empty because it always contains at least the
+	 * surface itself. */
+	return wl_list_length(&c->surface.xdg->link) > 1;
+}
+
+static inline const char *
+client_get_title(Client *c)
+{
+#ifdef XWAYLAND
+	if (client_is_x11(c))
+		return c->surface.xwayland->title ? c->surface.xwayland->title : "broken";
+#endif
+	return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken";
+}
+
+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;
+		if (surface->modal)
+			return 1;
+
+		if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG)
+				|| wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH)
+				|| wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR)
+				|| wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) {
+			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 unlikely 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
+	if (client_is_x11(c))
+		return c->surface.xwayland->override_redirect;
+#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_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 void
+client_set_scale(struct wlr_surface *s, float scale)
+{
+	wlr_fractional_scale_v1_notify_scale(s, scale);
+	wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
+}
+
+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->bw, c->geom.y + c->bw, width, height);
+		return 0;
+	}
+#endif
+	if ((int32_t)width == c->surface.xdg->toplevel->current.width
+			&& (int32_t)height == c->surface.xdg->toplevel->current.height)
+		return 0;
+	return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
+}
+
+static inline void
+client_set_tiled(Client *c, uint32_t edges)
+{
+#ifdef XWAYLAND
+	if (client_is_x11(c)) {
+		wlr_xwayland_surface_set_maximized(c->surface.xwayland,
+				edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE);
+		return;
+  }
+#endif
+	if (wl_resource_get_version(c->surface.xdg->toplevel->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))
+		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_surface_override_redirect_wants_focus(c->surface.xwayland)
+		&& wlr_xwayland_surface_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/config.def.h b/config.def.h
new file mode 100644
index 0000000..eeb52f5
--- /dev/null
+++ b/config.def.h
@@ -0,0 +1,257 @@
+/* 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 int smartgaps                 = 0;  /* 1 means no outer gap when there is only one window */
+static int gaps                            = 1;  /* 1 means gaps between windows are added */
+static const unsigned int gappx            = 3; /* gap pixel between windows */
+static const unsigned int borderpx         = 0;  /* border pixel of windows */
+static const int showbar                   = 1; /* 0 means no bar */
+static const int topbar                    = 1; /* 0 means bottom bar */
+static const char *fonts[]                 = {"JetBrainsMono Nerd Font Mono:style=Bold:size=16"};
+static const float rootcolor[]             = COLOR(0x000000ff);
+
+/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
+static const float fullscreen_bg[]         = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
+
+/* Base grays/cyan */
+static const uint32_t col_gray1 = 0x000000ff;
+static const uint32_t col_gray2 = 0x444444ff;
+static const uint32_t col_gray3 = 0xbbbbbbff;
+static const uint32_t col_gray4 = 0xeeeeeeff;
+static const uint32_t col_cyan  = 0x4d6a8eff;
+
+/* TokyoNight colors (0xRRGGBBAA) */
+static const uint32_t col_bg    = 0x1a1b26ff; /* background */
+static const uint32_t col_fg    = 0xa9b1d6ff; /* foreground */
+static const uint32_t col_blk   = 0x32344aff; /* black (normal) */
+static const uint32_t col_red   = 0xf7768eff; /* red */
+static const uint32_t col_grn   = 0x9ece6aff; /* green */
+static const uint32_t col_ylw   = 0xe0af68ff; /* yellow */
+static const uint32_t col_blu   = 0x7aa2f7ff; /* blue */
+static const uint32_t col_mag   = 0xad8ee6ff; /* magenta */
+static const uint32_t col_cyn   = 0x0db9d7ff; /* cyan (highlight) */
+static const uint32_t col_brblk = 0x444b6aff; /* bright black */
+
+// ANSI 16-color table for SGR (0..7 normal, 8..15 bright)
+static const uint32_t barcolors[16] = {
+    col_bg,      // 0: black
+    col_red,     // 1: red
+    col_grn,     // 2: green
+    col_ylw,     // 3: yellow
+    col_blu,     // 4: blue
+    col_mag,     // 5: magenta
+    col_cyn,     // 6: cyan
+    col_fg,      // 7: white (normal foreground)
+    col_brblk,   // 8: bright black (gray)
+    col_red,     // 9: bright red
+    col_grn,     // 10: bright green
+    col_ylw,     // 11: bright yellow
+    col_blu,     // 12: bright blue
+    col_mag,     // 13: bright magenta
+    col_cyn,     // 14: bright cyan
+    0xffffffff   // 15: bright white (fallback)
+};
+
+
+static uint32_t colors[][3] = {
+	/*               fg          bg          border    */
+    [SchemeNorm]  = { col_fg,   col_bg,  col_brblk }, // vacant tags
+    [SchemeSel]   = { col_cyn,  col_bg,  col_mag   }, // selected tag
+    [SchemeOcc]   = { col_cyn,  col_bg,  col_cyn   }, // occupied tags
+    [SchemeUrg]   = { 0,        0,       0x770000ff },
+    [SchemeUnder] = { col_mag,  col_bg,  col_mag   }, // underline color
+    [SchemeBg]    = { col_bg,  col_bg,  col_bg   }, // underline color
+};
+
+static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+/* logging */
+static int log_level = WLR_ERROR;
+
+/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
+static const Rule rules[] = {
+	/* app_id             title       tags mask     isfloating   monitor */
+	/* examples: */
+	{ "Gimp_EXAMPLE",     NULL,       0,            1,           -1 }, /* Start on currently visible tags floating, not tiled */
+	{ "firefox_EXAMPLE",  NULL,       1 << 8,       0,           -1 }, /* Start on ONLY tag "9" */
+};
+
+/* layout(s) */
+static const Layout layouts[] = {
+	/* symbol     arrange function */
+	{ "[]=",      tile },
+	{ "><>",      NULL },    /* no layout function means floating behavior */
+	{ "[M]",      monocle },
+};
+
+/* monitors */
+/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
+ * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients
+ * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899
+*/
+/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */
+static const MonitorRule monrules[] = {
+	/* name       mfact  nmaster scale layout       rotate/reflect                x    y */
+	/* example of a HiDPI laptop monitor:
+	{ "eDP-1",    0.5f,  1,      2,    &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL,   -1,  -1 },
+	*/
+	/* defaults */
+	{ NULL,       0.55f, 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 = 35;
+static const int repeat_delay = 200;
+
+/* 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_LOGO
+
+#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[] = { "foot", NULL };
+static const char *menucmd[] = { "wmenu-run", NULL };
+static const char *screenshotcmd[] = {"/run/current-system/sw/bin/snip", NULL };
+static const char *snip[] = {"$HOME/scripts/snip.sh", NULL };
+static const char *bemenucmd[] = {
+    "bemenu-run",
+    "--fn", "JetBrainsMono Nerd Font 22",
+    "-l", "10",
+    NULL
+};
+static const char *wmenucmd[] = {
+    "wmenu-run",
+    "-f", "JetBrainsMono Nerd Font 18",
+    "-l", "10",
+    NULL
+};
+
+static const Key keys[] = {
+	/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
+	/* modifier                  key                 function        argument */
+	// { MODKEY,                    XKB_KEY_d,          spawn,          {.v = bemenucmd} },
+	{ MODKEY,                    XKB_KEY_d,          spawn,          {.v = wmenucmd} },
+	{ MODKEY,                    XKB_KEY_Return,     spawn,          {.v = termcmd} },
+    { WLR_MODIFIER_CTRL,         XKB_KEY_F12,        spawn,          {.v = snip} },
+	{ MODKEY,                    XKB_KEY_s,          spawn,          SHCMD("/home/tony/scripts/snip.sh") },
+	{ MODKEY,                    XKB_KEY_j,          focusstack,     {.i = +1} },
+	{ MODKEY,                    XKB_KEY_k,          focusstack,     {.i = -1} },
+    { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_S,          spawn,          SHCMD("geom=\"$(slurp -f '%x,%y %wx%h')\"; grim -l 0 -g \"$geom\" - | wl-copy") },
+	{ MODKEY,                    XKB_KEY_b,          togglebar,      {0} },
+	{ MODKEY,                    XKB_KEY_i,          incnmaster,     {.i = +1} },
+	{ MODKEY,                    XKB_KEY_p,          incnmaster,     {.i = -1} },
+	{ MODKEY,                    XKB_KEY_h,          setmfact,       {.f = -0.05f} },
+	{ MODKEY,                    XKB_KEY_l,          setmfact,       {.f = +0.05f} },
+	{ MODKEY,                    XKB_KEY_Return,     zoom,           {0} },
+	{ MODKEY,                    XKB_KEY_Tab,        view,           {0} },
+	{ MODKEY,                    XKB_KEY_a,          togglegaps,     {0} },
+	{ MODKEY,                    XKB_KEY_q,          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[] = {
+	{ ClkLtSymbol, 0,      BTN_LEFT,   setlayout,      {.v = &layouts[0]} },
+	{ ClkLtSymbol, 0,      BTN_RIGHT,  setlayout,      {.v = &layouts[2]} },
+	{ ClkTitle,    0,      BTN_MIDDLE, zoom,           {0} },
+	{ ClkStatus,   0,      BTN_MIDDLE, spawn,          {.v = termcmd} },
+	{ ClkClient,   MODKEY, BTN_LEFT,   moveresize,     {.ui = CurMove} },
+	{ ClkClient,   MODKEY, BTN_MIDDLE, togglefloating, {0} },
+	{ ClkClient,   MODKEY, BTN_RIGHT,  moveresize,     {.ui = CurResize} },
+	{ ClkTagBar,   0,      BTN_LEFT,   view,           {0} },
+	{ ClkTagBar,   0,      BTN_RIGHT,  toggleview,     {0} },
+	{ ClkTagBar,   MODKEY, BTN_LEFT,   tag,            {0} },
+	{ ClkTagBar,   MODKEY, BTN_RIGHT,  toggletag,      {0} },
+};
diff --git a/config.mk b/config.mk
new file mode 100644
index 0000000..eb08a05
--- /dev/null
+++ b/config.mk
@@ -0,0 +1,36 @@
+_VERSION = 0.8-dev
+VERSION  = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)`
+
+PKG_CONFIG = pkg-config
+
+# paths
+PREFIX = /usr/local
+MANDIR = $(PREFIX)/share/man
+DATADIR = $(PREFIX)/share
+
+WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19`
+WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19`
+
+# Allow using an alternative wlroots installation
+# This has to have all the includes required by wlroots, e.g:
+# Assuming wlroots git repo is "${PWD}/wlroots" and you only ran "meson setup build && ninja -C build"
+#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
+#	-I$(PWD)/wlroots/include
+# Set -rpath to avoid using the wrong library.
+#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19
+
+# Assuming you ran "meson setup --prefix ${PWD}/0.19 build && ninja -C build install"
+#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
+#	-I$(PWD)/wlroots/0.19/include/wlroots-0.19
+#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.19/lib64 -L$(PWD)/wlroots/0.19/lib64 -lwlroots-0.19
+
+XWAYLAND =
+XLIBS =
+# Uncomment to build XWayland support
+#XWAYLAND = -DXWAYLAND
+#XLIBS = xcb xcb-icccm
+
+# dwl itself only uses C99 features, but wlroots' headers use anonymous unions (C11).
+# To avoid warnings about them, we do not use -std=c99 and instead of using the
+# gmake default 'CC=c99', we use cc.
+CC = cc
diff --git a/drwl.h b/drwl.h
new file mode 100644
index 0000000..4446035
--- /dev/null
+++ b/drwl.h
@@ -0,0 +1,312 @@
+/*
+ * drwl - https://codeberg.org/sewn/drwl
+ *
+ * Copyright (c) 2023-2025 sewn <sewn@disroot.org>
+ * Copyright (c) 2024 notchoc <notchoc@disroot.org>
+ * 
+ * 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.
+ *
+ * The UTF-8 Decoder included is from Bjoern Hoehrmann:
+ * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+ */
+#pragma once
+
+#include <stdlib.h>
+#include <fcft/fcft.h>
+#include <pixman-1/pixman.h>
+
+enum { ColFg, ColBg, ColBorder }; /* colorscheme index */
+
+typedef struct fcft_font Fnt;
+typedef pixman_image_t Img;
+
+typedef struct {
+	Img *image;
+	Fnt *font;
+	uint32_t *scheme;
+} Drwl;
+
+
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+#define UTF8_INVALID 0xFFFD
+
+static const uint8_t utf8d[] = {
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+	 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+	 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+	10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+	 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+	12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+	12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+	12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+	12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static inline uint32_t
+utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte)
+{
+	uint32_t type = utf8d[byte];
+
+	*codep = (*state != UTF8_ACCEPT) ?
+		(byte & 0x3fu) | (*codep << 6) :
+		(0xff >> type) & (byte);
+
+	*state = utf8d[256 + *state + type];
+	return *state;
+}
+
+static int
+drwl_init(void)
+{
+	fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3);
+	return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR);
+}
+
+static Drwl *
+drwl_create(void)
+{
+	Drwl *drwl;
+	
+	if (!(drwl = calloc(1, sizeof(Drwl))))
+		return NULL;
+
+	return drwl;
+}
+
+static void
+drwl_setfont(Drwl *drwl, Fnt *font)
+{
+	if (drwl)
+		drwl->font = font;
+}
+
+static void
+drwl_setimage(Drwl *drwl, Img *image)
+{
+	if (drwl)
+		drwl->image = image;
+}
+
+static Fnt *
+drwl_font_create(Drwl *drwl, size_t count,
+		const char *names[static count], const char *attributes)
+{
+	Fnt *font = fcft_from_name(count, names, attributes);
+	if (drwl)
+		drwl_setfont(drwl, font);
+	return font;
+}
+
+static void
+drwl_font_destroy(Fnt *font)
+{
+	fcft_destroy(font);
+}
+
+static inline pixman_color_t
+convert_color(uint32_t clr)
+{
+	return (pixman_color_t){
+		((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
+		((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
+		((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
+		(clr & 0xFF) * 0x101
+	};
+}
+
+static void
+drwl_setscheme(Drwl *drwl, uint32_t *scm)
+{
+	if (drwl)
+		drwl->scheme = scm;
+}
+
+static Img *
+drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits)
+{
+	Img *image;
+	pixman_region32_t clip;
+
+	image = pixman_image_create_bits_no_clear(
+		PIXMAN_a8r8g8b8, w, h, bits, w * 4);
+	if (!image)
+		return NULL;
+	pixman_region32_init_rect(&clip, 0, 0, w, h);
+	pixman_image_set_clip_region32(image, &clip);
+	pixman_region32_fini(&clip);
+
+	if (drwl)
+		drwl_setimage(drwl, image);
+	return image;
+}
+
+static void
+drwl_rect(Drwl *drwl,
+		int x, int y, unsigned int w, unsigned int h,
+		int filled, int invert)
+{
+	pixman_color_t clr;
+	if (!drwl || !drwl->scheme || !drwl->image)
+		return;
+
+	clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
+	if (filled)
+		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1,
+			&(pixman_rectangle16_t){x, y, w, h});
+	else
+		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4,
+			(pixman_rectangle16_t[4]){
+				{ x,         y,         w, 1 },
+				{ x,         y + h - 1, w, 1 },
+				{ x,         y,         1, h },
+				{ x + w - 1, y,         1, h }});
+}
+
+static int
+drwl_text(Drwl *drwl,
+		int x, int y, unsigned int w, unsigned int h,
+		unsigned int lpad, const char *text, int invert)
+{
+	int ty;
+	int render = x || y || w || h;
+	long x_kern;
+	uint32_t cp = 0, last_cp = 0, state;
+	pixman_color_t clr;
+	pixman_image_t *fg_pix = NULL;
+	int noellipsis = 0;
+	const struct fcft_glyph *glyph, *eg = NULL;
+	int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT;
+
+	if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font)
+		return 0;
+
+	if (!render) {
+		w = invert ? invert : ~invert;
+	} else {
+		clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
+		fg_pix = pixman_image_create_solid_fill(&clr);
+
+		drwl_rect(drwl, x, y, w, h, 1, !invert);
+
+		x += lpad;
+		w -= lpad;
+	}
+
+	if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF)
+		fcft_subpixel_mode = FCFT_SUBPIXEL_NONE;
+
+	if (render)
+		eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode);
+
+	for (const char *p = text, *pp; pp = p, *p; p++) {
+		for (state = UTF8_ACCEPT; *p &&
+		     utf8decode(&state, &cp, *p) > UTF8_REJECT; p++)
+			;
+		if (!*p || state == UTF8_REJECT) {
+			cp = UTF8_INVALID;
+			if (p > pp)
+				p--;
+		}
+
+		glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode);
+		if (!glyph)
+			continue;
+
+		x_kern = 0;
+		if (last_cp)
+			fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL);
+		last_cp = cp;
+
+		ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent;
+
+		if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w &&
+		    *(p + 1) != '\0') {
+			/* cannot fit ellipsis after current codepoint */
+			if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) {
+				noellipsis = 1;
+			} else {
+				w -= eg->advance.x;
+				pixman_image_composite32(
+					PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0,
+					x + eg->x, ty - eg->y, eg->width, eg->height);
+			}
+		}
+
+		if ((x_kern + glyph->advance.x) > w)
+			break;
+
+		x += x_kern;
+
+		if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)
+			/* pre-rendered glyphs (eg. emoji) */
+			pixman_image_composite32(
+				PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0,
+				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
+		else if (render)
+			pixman_image_composite32(
+				PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0,
+				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
+
+		x += glyph->advance.x;
+		w -= glyph->advance.x;
+	}
+
+	if (render)
+		pixman_image_unref(fg_pix);
+
+	return x + (render ? w : 0);
+}
+
+static unsigned int
+drwl_font_getwidth(Drwl *drwl, const char *text)
+{
+	if (!drwl || !drwl->font || !text)
+		return 0;
+	return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0);
+}
+
+static void
+drwl_image_destroy(Img *image)
+{
+	pixman_image_unref(image);
+}
+
+static void
+drwl_destroy(Drwl *drwl)
+{
+	if (drwl->font)
+		drwl_font_destroy(drwl->font);
+	if (drwl->image)
+		drwl_image_destroy(drwl->image);
+	free(drwl);
+}
+
+static void
+drwl_fini(void)
+{
+	fcft_fini();
+}
diff --git a/dwl.1 b/dwl.1
new file mode 100644
index 0000000..7fee870
--- /dev/null
+++ b/dwl.1
@@ -0,0 +1,258 @@
+.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 group 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
+.Xr wmenu-run 1 .
+.It Mod-Shift-Return
+Spawn
+.Xr foot 1 .
+.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.
+.Ss Mouse commands
+.Bl -tag -width 20n -offset indent -compact
+.It Mod-Button1
+Move focused window while dragging.
+Tiled windows will be toggled to the floating state.
+.It Mod-Button2
+Toggle focused window between floating and tiled state.
+.It Mod-Button3
+Resize focused window while dragging.
+Tiled windows will be toggled to the floating state.
+.El
+.Sh STATUS INFORMATION
+.Nm
+writes its status information to standard output.
+If the
+.Fl s
+option is given, the status information is written to the standard input of the
+child process instead.
+.Pp
+Said information has the following format:
+.Bd -ragged -offset indent
+.Ar <monitor>
+.Ar <component>
+.Ar <data>
+.Ed
+.Pp
+.Bl -tag -width 11n -offset 0 -compact
+.It Ar <monitor>
+is the name given to the output.
+.It Ar <component>
+is one of (in order)
+.Em title ,
+.Em appid ,
+.Em fullscreen ,
+.Em floating ,
+.Em selmon ,
+.Em tags ,
+.Em layout .
+.It Ar <data>
+changes depending on
+.Ar <component> .
+.Bl -tag -width 10n -compact
+.It Em title
+The title of the focused window on
+.Ar <monitor>
+or nothing if there is no focused window.
+.It Em appid
+The app_id of the focused window on
+.Ar <monitor>
+or nothing if there is no focused window.
+.It Em fullscreen
+Prints 1 if the focused window on
+.Ar <monitor>
+is in fullscreen state, otherwise prints 0. If there is no focused
+window it prints nothing.
+.It Em floating
+Prints 1 if the focused window on
+.Ar <monitor>
+is in floating state, otherwise prints 0. If there is no focused
+window it prints nothing.
+.It Em selmon
+Prints 1 if
+.Ar <monitor>
+is the selected monitor, otherwise prints 0.
+.It Em tags
+Prints four bitmasks in the following order:
+.Bl -bullet -width 2n -compact
+.It
+Occupied tags of
+.Ar <monitor> .
+.It
+Selected tags of
+.Ar <monitor> .
+.It
+Tags of the focused window on
+.Ar <monitor> .
+.It
+Tags where a window on
+.Ar <monitor>
+requested activation or has urgency hints.
+.El
+The bitmasks are 32-bit unsigned decimal integers.
+.It Em layout
+Prints the symbol of the current layout.
+.El
+.El
+.Ss Examples
+When there is a selected window:
+.Bd -literal -offset indent
+HDMI\-A\-1 title \(ti/source/repos/dwl > man \-l dwl.1
+HDMI\-A\-1 appid footclient
+HDMI\-A\-1 fullscreen 0
+HDMI\-A\-1 floating 0
+HDMI\-A\-1 selmon 1
+HDMI\-A\-1 tags 271 4 4 0
+HDMI\-A\-1 layout [T]
+.Ed
+.Pp
+When there is no selected window:
+.Bd -literal -offset indent
+HDMI\-A\-1 title
+HDMI\-A\-1 appid
+HDMI\-A\-1 fullscreen
+HDMI\-A\-1 floating
+HDMI\-A\-1 selmon 1
+HDMI\-A\-1 tags 271 512 0 0
+HDMI\-A\-1 layout [T]
+.Ed
+.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 \(aqs6\-svscan <&\-\(aq
+.Sh SEE ALSO
+.Xr dwm 1 ,
+.Xr foot 1 ,
+.Xr wmenu 1 ,
+.Xr xkeyboard-config 7
+.Sh BUGS
+All of them.
diff --git a/dwl.c b/dwl.c
new file mode 100644
index 0000000..cd35044
--- /dev/null
+++ b/dwl.c
@@ -0,0 +1,3681 @@
+#include <getopt.h>
+#include <libinput.h>
+#include <linux/input-event-codes.h>
+#include <math.h>
+#include <libdrm/drm_fourcc.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_alpha_modifier_v1.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_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_keyboard_group.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
+#include <wlr/types/wlr_linux_dmabuf_v1.h>
+#include <wlr/types/wlr_linux_drm_syncobj_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_output_power_management_v1.h>
+#include <wlr/types/wlr_pointer.h>
+#include <wlr/types/wlr_pointer_constraints_v1.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_relative_pointer_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_virtual_pointer_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/interfaces/wlr_buffer.h>
+#include <wlr/util/log.h>
+#include <wlr/util/region.h>
+#include <xkbcommon/xkbcommon.h>
+#ifdef XWAYLAND
+#include <wlr/xwayland.h>
+#include <xcb/xcb.h>
+#include <xcb/xcb_icccm.h>
+#endif
+
+#include "util.h"
+#include "drwl.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 << LENGTH(tags)) - 1)
+#define LISTEN(E, L, H)         wl_signal_add((E), ((L)->notify = (H), (L)))
+#define LISTEN_STATIC(E, H)     do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
+#define TEXTW(mon, text)        (drwl_font_getwidth(mon->drw, text) + mon->lrpad)
+
+/* enums */
+enum { SchemeNorm, SchemeSel, SchemeOcc, SchemeUrg, SchemeUnder, SchemeLast, SchemeBg };
+// enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */
+enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
+enum { XDGShell, LayerShell, X11 }; /* client types */
+enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
+enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */
+
+typedef union {
+	int i;
+	uint32_t ui;
+	float f;
+	const void *v;
+} Arg;
+
+typedef struct {
+	unsigned int click;
+	unsigned int mod;
+	unsigned int button;
+	void (*func)(const Arg *);
+	const Arg arg;
+} Button;
+
+typedef struct Monitor Monitor;
+typedef struct {
+	/* Must keep this field first */
+	unsigned int type; /* XDGShell or X11* */
+
+	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;
+	struct wlr_box geom; /* layout-relative, includes border */
+	struct wlr_box prev; /* layout-relative, includes border */
+	struct wlr_box bounds; /* only width and height are used */
+	union {
+		struct wlr_xdg_surface *xdg;
+		struct wlr_xwayland_surface *xwayland;
+	} surface;
+	struct wlr_xdg_toplevel_decoration_v1 *decoration;
+	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 wl_listener set_decoration_mode;
+	struct wl_listener destroy_decoration;
+#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 wlr_keyboard_group *wlr_group;
+
+	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;
+} KeyboardGroup;
+
+typedef struct {
+	/* Must keep this field first */
+	unsigned int type; /* LayerShell */
+
+	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 unmap;
+	struct wl_listener surface_commit;
+} LayerSurface;
+
+typedef struct {
+	const char *symbol;
+	void (*arrange)(Monitor *);
+} Layout;
+
+typedef struct {
+	struct wlr_buffer base;
+	struct wl_listener release;
+	bool busy;
+	Img *image;
+	uint32_t data[];
+} Buffer;
+
+struct Monitor {
+	struct wl_list link;
+	struct wlr_output *wlr_output;
+	struct wlr_scene_output *scene_output;
+	struct wlr_scene_buffer *scene_buffer; /* bar buffer */
+	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 {
+		int width, height;
+		int real_width, real_height; /* non-scaled */
+		float scale;
+	} b; /* bar area */
+	struct wlr_box w; /* window area, layout-relative */
+	struct wl_list layers[4]; /* LayerSurface.link */
+	const Layout *lt[2];
+	int gaps;
+	unsigned int seltags;
+	unsigned int sellt;
+	uint32_t tagset[2];
+	float mfact;
+	int gamma_lut_changed;
+	int nmaster;
+	char ltsymbol[16];
+	int asleep;
+	Drwl *drw;
+	Buffer *pool[2];
+	int lrpad;
+};
+
+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 {
+	struct wlr_pointer_constraint_v1 *constraint;
+	struct wl_listener destroy;
+} PointerConstraint;
+
+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 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 bool baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy);
+static void bufdestroy(struct wlr_buffer *buffer);
+static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags,
+		void **data, uint32_t *format, size_t *stride);
+static void bufdataend(struct wlr_buffer *buffer);
+static Buffer *bufmon(Monitor *m);
+static void bufrelease(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 cleanupmon(struct wl_listener *listener, void *data);
+static void cleanuplisteners(void);
+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 commitpopup(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 KeyboardGroup *createkeyboardgroup(void);
+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 createpointerconstraint(struct wl_listener *listener, void *data);
+static void createpopup(struct wl_listener *listener, void *data);
+static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
+static void cursorframe(struct wl_listener *listener, void *data);
+static void cursorwarptohint(void);
+static void destroydecoration(struct wl_listener *listener, void *data);
+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 destroypointerconstraint(struct wl_listener *listener, void *data);
+static void destroysessionlock(struct wl_listener *listener, void *data);
+static void destroykeyboardgroup(struct wl_listener *listener, void *data);
+static Monitor *dirtomon(enum wlr_direction dir);
+static void drawbar(Monitor *m);
+static void drawbars(void);
+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 gpureset(struct wl_listener *listener, void *data);
+static void handlesig(int signo);
+static void incnmaster(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 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, struct wlr_input_device *device, double sx,
+		double sy, double sx_unaccel, double sy_unaccel);
+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 powermgrsetmode(struct wl_listener *listener, void *data);
+static void quit(const Arg *arg);
+static void rendermon(struct wl_listener *listener, void *data);
+static void requestdecorationmode(struct wl_listener *listener, void *data);
+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 setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void setmon(Client *c, Monitor *m, uint32_t newtags);
+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 int statusin(int fd, unsigned int mask, void *data);
+static void tag(const Arg *arg);
+static void tagmon(const Arg *arg);
+static void tile(Monitor *m);
+static void togglebar(const Arg *arg);
+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 updatebar(Monitor *m);
+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 void virtualpointer(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 pid_t child_pid = -1;
+static int locked;
+static void *exclusive_focus;
+static struct wl_display *dpy;
+static struct wl_event_loop *event_loop;
+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_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
+static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
+static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
+static struct wlr_output_power_manager_v1 *power_mgr;
+
+static struct wlr_pointer_constraints_v1 *pointer_constraints;
+static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
+static struct wlr_pointer_constraint_v1 *active_constraint;
+
+static struct wlr_cursor *cursor;
+static struct wlr_xcursor_manager *cursor_mgr;
+
+static struct wlr_scene_rect *root_bg;
+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 wlr_seat *seat;
+static KeyboardGroup *kb_group;
+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;
+
+static char stext[256];
+static struct wl_event_source *status_event_source;
+
+static const struct wlr_buffer_impl buffer_impl = {
+    .destroy = bufdestroy,
+    .begin_data_ptr_access = bufdatabegin,
+    .end_data_ptr_access = bufdataend,
+};
+
+/* global event handlers */
+static struct wl_listener cursor_axis = {.notify = axisnotify};
+static struct wl_listener cursor_button = {.notify = buttonpress};
+static struct wl_listener cursor_frame = {.notify = cursorframe};
+static struct wl_listener cursor_motion = {.notify = motionrelative};
+static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute};
+static struct wl_listener gpu_reset = {.notify = gpureset};
+static struct wl_listener layout_change = {.notify = updatemons};
+static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor};
+static struct wl_listener new_input_device = {.notify = inputdevice};
+static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard};
+static struct wl_listener new_virtual_pointer = {.notify = virtualpointer};
+static struct wl_listener new_pointer_constraint = {.notify = createpointerconstraint};
+static struct wl_listener new_output = {.notify = createmon};
+static struct wl_listener new_xdg_toplevel = {.notify = createnotify};
+static struct wl_listener new_xdg_popup = {.notify = createpopup};
+static struct wl_listener new_xdg_decoration = {.notify = createdecoration};
+static struct wl_listener new_layer_surface = {.notify = createlayersurface};
+static struct wl_listener output_mgr_apply = {.notify = outputmgrapply};
+static struct wl_listener output_mgr_test = {.notify = outputmgrtest};
+static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode};
+static struct wl_listener request_activate = {.notify = urgent};
+static struct wl_listener request_cursor = {.notify = setcursor};
+static struct wl_listener request_set_psel = {.notify = setpsel};
+static struct wl_listener request_set_sel = {.notify = setsel};
+static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
+static struct wl_listener request_start_drag = {.notify = requeststartdrag};
+static struct wl_listener start_drag = {.notify = startdrag};
+static struct wl_listener new_session_lock = {.notify = locksession};
+
+#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 void sethints(struct wl_listener *listener, void *data);
+static void xwaylandready(struct wl_listener *listener, void *data);
+static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11};
+static struct wl_listener xwayland_ready = {.notify = xwaylandready};
+static struct wlr_xwayland *xwayland;
+#endif
+
+/* configuration, allows nested code to access above variables */
+#include "config.h"
+
+/* attempt to encapsulate suck into one file */
+#include "client.h"
+
+/* function implementations */
+void
+applybounds(Client *c, struct wlr_box *bbox)
+{
+	/* set minimum possible */
+	c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
+	c->geom.height = MAX(1 + 2 * (int)c->bw, 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 <= bbox->x)
+		c->geom.x = bbox->x;
+	if (c->geom.y + c->geom.height <= bbox->y)
+		c->geom.y = bbox->y;
+}
+
+void
+applyrules(Client *c)
+{
+	/* rule matching */
+	const char *appid, *title;
+	uint32_t newtags = 0;
+	int i;
+	const Rule *r;
+	Monitor *mon = selmon, *m;
+
+	appid = client_get_appid(c);
+	title = client_get_title(c);
+
+	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;
+			}
+		}
+	}
+
+	c->isfloating |= client_is_float_type(c);
+	setmon(c, mon, newtags);
+}
+
+static int
+textw_no_ansi(Monitor *m, const char *text)
+{
+    char buffer[256];
+    int wr = 0, rd = 0;
+    
+    while (text[rd] && wr < sizeof(buffer) - 1) {
+        if (text[rd] == '\033' && text[rd + 1] == '[') {
+            size_t alen = strspn(text + rd + 2, "0123456789;");
+            if (text[rd + alen + 2] == 'm') {
+                rd += alen + 3;
+                continue;
+            }
+        }
+        buffer[wr++] = text[rd++];
+    }
+    buffer[wr] = '\0';
+
+    return TEXTW(m, buffer) - m->lrpad;
+}
+
+
+static void 
+draw_ansi_segment(Monitor *m, const char *text, int *x, int height, int lrpad)
+{
+    int fg = 7, bg = 0, fmt = 0;
+    uint32_t pw;
+    int left_padding = 0; 
+    char buffer[256];
+    int wr = 0, rd = 0;
+
+    while (text[rd] && wr < sizeof(buffer) - 1) {
+        if (text[rd] == '\033' && text[rd + 1] == '[') {
+            // Find the end of the escape sequence
+            int seq_start = rd;
+            rd += 2; // skip \033[
+
+            // Parse the sequence more carefully
+            char *seq_end = strchr(text + rd, 'm');
+            if (seq_end) {
+                // Draw any accumulated text first
+                if (wr > 0) {
+                    buffer[wr] = '\0';
+                    pw = TEXTW(m, buffer) - lrpad;
+                    drwl_setscheme(m->drw, (uint32_t[]){
+                        barcolors[fg],
+                        barcolors[bg],
+                        barcolors[bg]
+                    });
+                    drwl_text(m->drw, *x, 0, pw + left_padding, height, left_padding, buffer, fmt & 1);
+
+                    if (fmt & 2) { 
+                        drwl_setscheme(m->drw, (uint32_t[]){
+                            barcolors[fg],
+                            barcolors[fg],
+                            barcolors[fg]
+                        });
+                        drwl_rect(m->drw, *x + left_padding, height - 2, pw, 1, 1, 0);
+                    }
+
+                    *x += pw;
+                    left_padding = 0;
+                    wr = 0;
+                }
+
+                char *p = (char*)text + seq_start + 2;
+                while (p < seq_end) {
+                    unsigned v = strtoul(p, &p, 10);
+                    if (v == 0) {
+                        fg = 7; bg = 0; fmt = 0;
+                    } else if (v == 1) {
+                        fg |= 8;
+                    } else if (v == 4) {
+                        fmt |= 2; 
+                    } else if (v == 7) {
+                        fmt |= 1;
+                    } else if (v == 24) {
+                        fmt &= ~2; 
+                    } else if (v == 27) {
+                        fmt &= ~1;
+                    } else if (v >= 30 && v <= 37) {
+                        fg = (v % 10) | (fg & 8);
+                    } else if (v >= 40 && v <= 47) {
+                        bg = v % 10;
+                    }
+
+                    if (*p == ';') p++;
+                }
+
+                rd = seq_end - text + 1;
+                continue;
+            }
+        }
+        buffer[wr++] = text[rd++];
+    }
+
+    if (wr > 0) {
+        buffer[wr] = '\0';
+        pw = TEXTW(m, buffer) - lrpad;
+        drwl_setscheme(m->drw, (uint32_t[]){
+            barcolors[fg],
+            barcolors[bg],
+            barcolors[bg]
+        });
+        drwl_text(m->drw, *x, 0, pw + left_padding, height, left_padding, buffer, fmt & 1);
+
+        if (fmt & 2) { 
+            drwl_setscheme(m->drw, (uint32_t[]){
+                barcolors[fg],
+                barcolors[fg],
+                barcolors[fg]
+            });
+            drwl_rect(m->drw, *x + left_padding, height - 2, pw, 1, 1, 0);
+        }
+
+        *x += pw;
+    }
+}
+
+void
+arrange(Monitor *m)
+{
+	Client *c;
+
+	if (!m->wlr_output->enabled)
+		return;
+
+	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, sizeof(m->ltsymbol));
+
+	/* We move all clients (except fullscreen and unmanaged) to LyrTile while
+	 * in floating layout to avoid "real" floating clients be always on top */
+	wl_list_for_each(c, &clients, link) {
+		if (c->mon != m || c->scene->node.parent == layers[LyrFS])
+			continue;
+
+		wlr_scene_node_reparent(&c->scene->node,
+				(!m->lt[m->sellt]->arrange && c->isfloating)
+						? layers[LyrTile]
+						: (m->lt[m->sellt]->arrange && c->isfloating)
+								? layers[LyrFloat]
+								: c->scene->node.parent);
+	}
+
+	if (m->lt[m->sellt]->arrange)
+		m->lt[m->sellt]->arrange(m);
+	motionnotify(0, NULL, 0, 0, 0, 0);
+	checkidleinhibitor(NULL);
+}
+
+void
+arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive)
+{
+	LayerSurface *l;
+	struct wlr_box full_area = m->m;
+
+	wl_list_for_each(l, list, link) {
+		struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
+
+		if (!layer_surface->initialized)
+			continue;
+
+		if (exclusive != (layer_surface->current.exclusive_zone > 0))
+			continue;
+
+		wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area);
+		wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y);
+	}
+}
+
+void
+arrangelayers(Monitor *m)
+{
+	int i;
+	struct wlr_box usable_area = m->m;
+	LayerSurface *l;
+	uint32_t layers_above_shell[] = {
+		ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+		ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+	};
+	if (!m->wlr_output->enabled)
+		return;
+
+	if (m->scene_buffer->node.enabled) {
+		usable_area.height -= m->b.real_height;
+		usable_area.y += topbar ? m->b.real_height : 0;
+	}
+
+	/* Arrange exclusive surfaces from top->bottom */
+	for (i = 3; i >= 0; i--)
+		arrangelayer(m, &m->layers[i], &usable_area, 1);
+
+	if (!wlr_box_equal(&usable_area, &m->w)) {
+		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 < (int)LENGTH(layers_above_shell); i++) {
+		wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
+			if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped)
+				continue;
+			/* Deactivate the focused client. */
+			focusclient(NULL, 0);
+			exclusive_focus = l;
+			client_notify_enter(l->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 wheel for mousebindings, it can be implemented
+	 * by 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, event->relative_direction);
+}
+
+bool
+baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy)
+{
+	return true;
+}
+
+void
+bufdestroy(struct wlr_buffer *wlr_buffer)
+{
+	Buffer *buf = wl_container_of(wlr_buffer, buf, base);
+	if (buf->busy)
+		wl_list_remove(&buf->release.link);
+	drwl_image_destroy(buf->image);
+	free(buf);
+}
+
+bool
+bufdatabegin(struct wlr_buffer *wlr_buffer, uint32_t flags,
+		void **data, uint32_t *format, size_t *stride)
+{
+	Buffer *buf = wl_container_of(wlr_buffer, buf, base);
+
+	if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false;
+
+	*data   = buf->data;
+	*stride = wlr_buffer->width * 4;
+	*format = DRM_FORMAT_ARGB8888;
+
+	return true;
+}
+
+void
+bufdataend(struct wlr_buffer *wlr_buffer)
+{
+}
+
+Buffer *
+bufmon(Monitor *m)
+{
+	size_t i;
+	Buffer *buf = NULL;
+
+	for (i = 0; i < LENGTH(m->pool); i++) {
+		if (m->pool[i]) {
+			if (m->pool[i]->busy)
+				continue;
+			buf = m->pool[i];
+			break;
+		}
+
+		buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height));
+		buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data);
+		wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height);
+		m->pool[i] = buf;
+		break;
+	}
+	if (!buf)
+		return NULL;
+
+	buf->busy = true;
+	LISTEN(&buf->base.events.release, &buf->release, bufrelease);
+	wlr_buffer_lock(&buf->base);
+	drwl_setimage(m->drw, buf->image);
+	return buf;
+}
+
+void
+bufrelease(struct wl_listener *listener, void *data)
+{
+	Buffer *buf = wl_container_of(listener, buf, release);
+	buf->busy = false;
+	wl_list_remove(&buf->release.link);
+}
+
+void
+buttonpress(struct wl_listener *listener, void *data)
+{
+	unsigned int i = 0, x = 0;
+	double cx;
+	unsigned int click;
+	struct wlr_pointer_button_event *event = data;
+	struct wlr_keyboard *keyboard;
+	struct wlr_scene_node *node;
+	struct wlr_scene_buffer *buffer;
+	uint32_t mods;
+	Arg arg = {0};
+	Client *c;
+	const Button *b;
+
+	wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+
+	click = ClkRoot;
+	xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
+	if (c)
+		click = ClkClient;
+
+	switch (event->state) {
+	case WL_POINTER_BUTTON_STATE_PRESSED:
+		cursor_mode = CurPressed;
+		selmon = xytomon(cursor->x, cursor->y);
+		if (locked)
+			break;
+
+		if (!c && !exclusive_focus &&
+			(node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) &&
+			(buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) {
+			cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale;
+			do
+				x += TEXTW(selmon, tags[i]);
+			while (cx >= x && ++i < LENGTH(tags));
+			if (i < LENGTH(tags)) {
+				click = ClkTagBar;
+				arg.ui = 1 << i;
+			} else if (cx < x + TEXTW(selmon, selmon->ltsymbol))
+				click = ClkLtSymbol;
+			else if (cx > selmon->b.width - (TEXTW(selmon, stext) - selmon->lrpad + 2)) {
+				click = ClkStatus;
+			} else
+				click = ClkTitle;
+		}
+
+		/* Change focus if the button was _pressed_ over a client */
+		xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
+		if (click == ClkClient && (!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 && click == b->click && b->func) {
+				b->func(click == ClkTagBar && b->arg.i == 0 ? &arg : &b->arg);
+				return;
+			}
+		}
+		break;
+	case WL_POINTER_BUTTON_STATE_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);
+			grabc = NULL;
+			return;
+		}
+		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)
+{
+	cleanuplisteners();
+#ifdef XWAYLAND
+	wlr_xwayland_destroy(xwayland);
+	xwayland = NULL;
+#endif
+	wl_display_destroy_clients(dpy);
+	if (child_pid > 0) {
+		kill(-child_pid, SIGTERM);
+		waitpid(child_pid, NULL, 0);
+	}
+	wlr_xcursor_manager_destroy(cursor_mgr);
+
+	destroykeyboardgroup(&kb_group->destroy, NULL);
+
+	/* If it's not destroyed manually, it will cause a use-after-free of wlr_seat.
+	 * Destroy it until it's fixed on the wlroots side */
+	wlr_backend_destroy(backend);
+
+	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);
+
+	drwl_fini();
+}
+
+void
+cleanupmon(struct wl_listener *listener, void *data)
+{
+	Monitor *m = wl_container_of(listener, m, destroy);
+	LayerSurface *l, *tmp;
+	size_t i;
+
+	/* m->layers[i] are intentionally not unlinked */
+	for (i = 0; i < LENGTH(m->layers); i++) {
+		wl_list_for_each_safe(l, tmp, &m->layers[i], link)
+			wlr_layer_surface_v1_destroy(l->layer_surface);
+	}
+
+	for (i = 0; i < LENGTH(m->pool); i++)
+		wlr_buffer_drop(&m->pool[i]->base);
+
+	drwl_setimage(m->drw, NULL);
+	drwl_destroy(m->drw);
+
+	wl_list_remove(&m->destroy.link);
+	wl_list_remove(&m->frame.link);
+	wl_list_remove(&m->link);
+	wl_list_remove(&m->request_state.link);
+	if (m->lock_surface)
+		destroylocksurface(&m->destroy_lock_surface, NULL);
+	m->wlr_output->data = NULL;
+	wlr_output_layout_remove(output_layout, m->wlr_output);
+	wlr_scene_output_destroy(m->scene_output);
+
+	closemon(m);
+	wlr_scene_node_destroy(&m->fullscreen_bg->node);
+	wlr_scene_node_destroy(&m->scene_buffer->node);
+	free(m);
+}
+
+void
+cleanuplisteners(void)
+{
+	wl_list_remove(&cursor_axis.link);
+	wl_list_remove(&cursor_button.link);
+	wl_list_remove(&cursor_frame.link);
+	wl_list_remove(&cursor_motion.link);
+	wl_list_remove(&cursor_motion_absolute.link);
+	wl_list_remove(&gpu_reset.link);
+	wl_list_remove(&new_idle_inhibitor.link);
+	wl_list_remove(&layout_change.link);
+	wl_list_remove(&new_input_device.link);
+	wl_list_remove(&new_virtual_keyboard.link);
+	wl_list_remove(&new_virtual_pointer.link);
+	wl_list_remove(&new_pointer_constraint.link);
+	wl_list_remove(&new_output.link);
+	wl_list_remove(&new_xdg_toplevel.link);
+	wl_list_remove(&new_xdg_decoration.link);
+	wl_list_remove(&new_xdg_popup.link);
+	wl_list_remove(&new_layer_surface.link);
+	wl_list_remove(&output_mgr_apply.link);
+	wl_list_remove(&output_mgr_test.link);
+	wl_list_remove(&output_power_mgr_set_mode.link);
+	wl_list_remove(&request_activate.link);
+	wl_list_remove(&request_cursor.link);
+	wl_list_remove(&request_set_psel.link);
+	wl_list_remove(&request_set_sel.link);
+	wl_list_remove(&request_set_cursor_shape.link);
+	wl_list_remove(&request_start_drag.link);
+	wl_list_remove(&start_drag.link);
+	wl_list_remove(&new_session_lock.link);
+#ifdef XWAYLAND
+	wl_list_remove(&new_xwayland_surface.link);
+	wl_list_remove(&xwayland_ready.link);
+#endif
+}
+
+void
+closemon(Monitor *m)
+{
+	/* update selmon if needed and
+	 * move closed monitor's clients to the focused one */
+	Client *c;
+	int i = 0, nmons = wl_list_length(&mons);
+	if (!nmons) {
+		selmon = NULL;
+	} else if (m == selmon) {
+		do /* don't switch to disabled mons */
+			selmon = wl_container_of(mons.next, selmon, link);
+		while (!selmon->wlr_output->enabled && i++ < nmons);
+
+		if (!selmon->wlr_output->enabled)
+			selmon = NULL;
+	}
+
+	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);
+	drawbars();
+}
+
+void
+commitlayersurfacenotify(struct wl_listener *listener, void *data)
+{
+	LayerSurface *l = wl_container_of(listener, l, surface_commit);
+	struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
+	struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]];
+	struct wlr_layer_surface_v1_state old_state;
+
+	if (l->layer_surface->initial_commit) {
+		client_set_scale(layer_surface->surface, l->mon->wlr_output->scale);
+
+		/* Temporarily set the layer's current state to pending
+		 * so that we can easily arrange it */
+		old_state = l->layer_surface->current;
+		l->layer_surface->current = l->layer_surface->pending;
+		arrangelayers(l->mon);
+		l->layer_surface->current = old_state;
+		return;
+	}
+
+	if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
+		return;
+	l->mapped = layer_surface->surface->mapped;
+
+	if (scene_layer != l->scene->node.parent) {
+		wlr_scene_node_reparent(&l->scene->node, scene_layer);
+		wl_list_remove(&l->link);
+		wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link);
+		wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer
+				< ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer));
+	}
+
+	arrangelayers(l->mon);
+}
+
+void
+commitnotify(struct wl_listener *listener, void *data)
+{
+	Client *c = wl_container_of(listener, c, commit);
+
+	if (c->surface.xdg->initial_commit) {
+		/*
+		 * Get the monitor this client will be rendered on
+		 * Note that if the user set a rule in which the client is placed on
+		 * a different monitor based on its title, this will likely select
+		 * a wrong monitor.
+		 */
+		applyrules(c);
+		if (c->mon) {
+			client_set_scale(client_surface(c), c->mon->wlr_output->scale);
+		}
+		setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */
+
+		wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel,
+				WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
+		if (c->decoration)
+			requestdecorationmode(&c->set_decoration_mode, c->decoration);
+		wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
+		return;
+	}
+
+	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
+commitpopup(struct wl_listener *listener, void *data)
+{
+	struct wlr_surface *surface = data;
+	struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface);
+	LayerSurface *l = NULL;
+	Client *c = NULL;
+	struct wlr_box box;
+	int type = -1;
+
+	if (!popup->base->initial_commit)
+		return;
+
+	type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
+	if (!popup->parent || type < 0)
+		return;
+	popup->base->surface->data = wlr_scene_xdg_surface_create(
+			popup->parent->data, popup->base);
+	if ((l && !l->mon) || (c && !c->mon)) {
+		wlr_xdg_popup_destroy(popup);
+		return;
+	}
+	box = type == LayerShell ? l->mon->m : c->mon->w;
+	box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
+	box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
+	wlr_xdg_popup_unconstrain_from_box(popup, &box);
+	wl_list_remove(&listener->link);
+	free(listener);
+}
+
+void
+createdecoration(struct wl_listener *listener, void *data)
+{
+	struct wlr_xdg_toplevel_decoration_v1 *deco = data;
+	Client *c = deco->toplevel->base->data;
+	c->decoration = deco;
+
+	LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode);
+	LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration);
+
+	requestdecorationmode(&c->set_decoration_mode, deco);
+}
+
+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)
+{
+	/* Set the keymap to match the group keymap */
+	wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap);
+
+	/* Add the new keyboard to the group */
+	wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
+}
+
+KeyboardGroup *
+createkeyboardgroup(void)
+{
+	KeyboardGroup *group = ecalloc(1, sizeof(*group));
+	struct xkb_context *context;
+	struct xkb_keymap *keymap;
+
+	group->wlr_group = wlr_keyboard_group_create();
+	group->wlr_group->data = group;
+
+	/* Prepare an XKB keymap and assign it to the keyboard group. */
+	context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+	if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules,
+				XKB_KEYMAP_COMPILE_NO_FLAGS)))
+		die("failed to compile keymap");
+
+	wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap);
+	xkb_keymap_unref(keymap);
+	xkb_context_unref(context);
+
+	wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay);
+
+	/* Set up listeners for keyboard events */
+	LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress);
+	LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod);
+
+	group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group);
+
+	/* 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 wlr_keyboard_group, which provides a single wlr_keyboard interface for
+	 * all of them. Set this combined wlr_keyboard as the seat keyboard.
+	 */
+	wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
+	return group;
+}
+
+void
+createlayersurface(struct wl_listener *listener, void *data)
+{
+	struct wlr_layer_surface_v1 *layer_surface = data;
+	LayerSurface *l;
+	struct wlr_surface *surface = layer_surface->surface;
+	struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]];
+
+	if (!layer_surface->output
+			&& !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) {
+		wlr_layer_surface_v1_destroy(layer_surface);
+		return;
+	}
+
+	l = layer_surface->data = ecalloc(1, sizeof(*l));
+	l->type = LayerShell;
+	LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify);
+	LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify);
+	LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify);
+
+	l->layer_surface = layer_surface;
+	l->mon = layer_surface->output->data;
+	l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface);
+	l->scene = l->scene_layer->tree;
+	l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer
+			< ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer);
+	l->scene->node.data = l->popups->node.data = l;
+
+	wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link);
+	wlr_surface_send_enter(surface, layer_surface->output);
+}
+
+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;
+	struct wlr_output_state state;
+	Monitor *m;
+
+	if (!wlr_output_init_render(wlr_output, alloc, drw))
+		return;
+
+	m = wlr_output->data = ecalloc(1, sizeof(*m));
+	m->wlr_output = wlr_output;
+
+	for (i = 0; i < LENGTH(m->layers); i++)
+		wl_list_init(&m->layers[i]);
+
+	wlr_output_state_init(&state);
+	/* Initialize monitor state using configured rules */
+	m->gaps = gaps;
+
+	m->tagset[0] = m->tagset[1] = 1;
+	for (r = monrules; r < END(monrules); r++) {
+		if (!r->name || strstr(wlr_output->name, r->name)) {
+			m->m.x = r->x;
+			m->m.y = r->y;
+			m->mfact = r->mfact;
+			m->nmaster = r->nmaster;
+			m->lt[0] = r->lt;
+			m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
+			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
+			wlr_output_state_set_scale(&state, r->scale);
+			wlr_output_state_set_transform(&state, r->rr);
+			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_state_set_mode(&state, 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_state_set_enabled(&state, 1);
+	wlr_output_commit_state(wlr_output, &state);
+	wlr_output_state_finish(&state);
+
+	if (!(m->drw = drwl_create()))
+		die("failed to create drwl context");
+
+	m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL);
+	m->scene_buffer->point_accepts_input = baracceptsinput;
+	updatebar(m);
+
+	wl_list_insert(&mons, &m->link);
+	drawbars();
+
+	/* 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.
+	 *
+	 * 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 == -1 && m->m.y == -1)
+		wlr_output_layout_add_auto(output_layout, wlr_output);
+	else
+		wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
+}
+
+void
+createnotify(struct wl_listener *listener, void *data)
+{
+	/* This event is raised when a client creates a new toplevel (application window). */
+	struct wlr_xdg_toplevel *toplevel = data;
+	Client *c = NULL;
+
+	/* Allocate a Client for this surface */
+	c = toplevel->base->data = ecalloc(1, sizeof(*c));
+	c->surface.xdg = toplevel->base;
+	c->bw = borderpx;
+
+	LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify);
+	LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify);
+	LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmapnotify);
+	LISTEN(&toplevel->events.destroy, &c->destroy, destroynotify);
+	LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify);
+	LISTEN(&toplevel->events.request_maximize, &c->maximize, maximizenotify);
+	LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle);
+}
+
+void
+createpointer(struct wlr_pointer *pointer)
+{
+	struct libinput_device *device;
+	if (wlr_input_device_is_libinput(&pointer->base)
+			&& (device = wlr_libinput_get_device_handle(&pointer->base))) {
+
+		if (libinput_device_config_tap_get_finger_count(device)) {
+			libinput_device_config_tap_set_enabled(device, tap_to_click);
+			libinput_device_config_tap_set_drag_enabled(device, tap_and_drag);
+			libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock);
+			libinput_device_config_tap_set_button_map(device, button_map);
+		}
+
+		if (libinput_device_config_scroll_has_natural_scroll(device))
+			libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling);
+
+		if (libinput_device_config_dwt_is_available(device))
+			libinput_device_config_dwt_set_enabled(device, disable_while_typing);
+
+		if (libinput_device_config_left_handed_is_available(device))
+			libinput_device_config_left_handed_set(device, left_handed);
+
+		if (libinput_device_config_middle_emulation_is_available(device))
+			libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation);
+
+		if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
+			libinput_device_config_scroll_set_method(device, scroll_method);
+
+		if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
+			libinput_device_config_click_set_method(device, click_method);
+
+		if (libinput_device_config_send_events_get_modes(device))
+			libinput_device_config_send_events_set_mode(device, send_events_mode);
+
+		if (libinput_device_config_accel_is_available(device)) {
+			libinput_device_config_accel_set_profile(device, accel_profile);
+			libinput_device_config_accel_set_speed(device, accel_speed);
+		}
+	}
+
+	wlr_cursor_attach_input_device(cursor, &pointer->base);
+}
+
+void
+createpointerconstraint(struct wl_listener *listener, void *data)
+{
+	PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint));
+	pointer_constraint->constraint = data;
+	LISTEN(&pointer_constraint->constraint->events.destroy,
+			&pointer_constraint->destroy, destroypointerconstraint);
+}
+
+void
+createpopup(struct wl_listener *listener, void *data)
+{
+	/* This event is raised when a client (either xdg-shell or layer-shell)
+	 * creates a new popup. */
+	struct wlr_xdg_popup *popup = data;
+	LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
+}
+
+void
+cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
+{
+	if (active_constraint == constraint)
+		return;
+
+	if (active_constraint)
+		wlr_pointer_constraint_v1_send_deactivated(active_constraint);
+
+	active_constraint = constraint;
+	wlr_pointer_constraint_v1_send_activated(constraint);
+}
+
+void
+cursorframe(struct wl_listener *listener, void *data)
+{
+	/* This event is forwarded by the cursor when a pointer emits a 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
+cursorwarptohint(void)
+{
+	Client *c = NULL;
+	double sx = active_constraint->current.cursor_hint.x;
+	double sy = active_constraint->current.cursor_hint.y;
+
+	toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
+	if (c && active_constraint->current.cursor_hint.enabled) {
+		wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw);
+		wlr_seat_pointer_warp(active_constraint->seat, sx, sy);
+	}
+}
+
+void
+destroydecoration(struct wl_listener *listener, void *data)
+{
+	Client *c = wl_container_of(listener, c, destroy_decoration);
+
+	wl_list_remove(&c->destroy_decoration.link);
+	wl_list_remove(&c->set_decoration_mode.link);
+}
+
+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, NULL, 0, 0, 0, 0);
+	wl_list_remove(&listener->link);
+	free(listener);
+}
+
+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));
+	wl_list_remove(&listener->link);
+	free(listener);
+}
+
+void
+destroylayersurfacenotify(struct wl_listener *listener, void *data)
+{
+	LayerSurface *l = wl_container_of(listener, l, destroy);
+
+	wl_list_remove(&l->link);
+	wl_list_remove(&l->destroy.link);
+	wl_list_remove(&l->unmap.link);
+	wl_list_remove(&l->surface_commit.link);
+	wlr_scene_node_destroy(&l->scene->node);
+	wlr_scene_node_destroy(&l->popups->node);
+	free(l);
+}
+
+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, NULL, 0, 0, 0, 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);
+		wl_list_remove(&c->maximize.link);
+	}
+	free(c);
+}
+
+void
+destroypointerconstraint(struct wl_listener *listener, void *data)
+{
+	PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy);
+
+	if (active_constraint == pointer_constraint->constraint) {
+		cursorwarptohint();
+		active_constraint = NULL;
+	}
+
+	wl_list_remove(&pointer_constraint->destroy.link);
+	free(pointer_constraint);
+}
+
+void
+destroysessionlock(struct wl_listener *listener, void *data)
+{
+	SessionLock *lock = wl_container_of(listener, lock, destroy);
+	destroylock(lock, 0);
+}
+
+void
+destroykeyboardgroup(struct wl_listener *listener, void *data)
+{
+	KeyboardGroup *group = wl_container_of(listener, group, destroy);
+	wl_event_source_remove(group->key_repeat_source);
+	wl_list_remove(&group->key.link);
+	wl_list_remove(&group->modifiers.link);
+	wl_list_remove(&group->destroy.link);
+	wlr_keyboard_group_destroy(group->wlr_group);
+	free(group);
+}
+
+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
+drawbar(Monitor *m)
+{
+    int x, w, tw = 0;
+    int boxs = m->drw->font->height / 9;
+    int boxw = m->drw->font->height / 6 + 2;
+    uint32_t i, occ = 0, urg = 0;
+    Client *c;
+    Buffer *buf;
+    
+    if (!m->scene_buffer->node.enabled)
+        return;
+    if (!(buf = bufmon(m)))
+        return;
+
+    // Always clear the entire bar with background color first
+    drwl_setscheme(m->drw, colors[SchemeBg]); // or any scheme that uses col_bg
+    drwl_rect(m->drw, 0, 0, m->b.width, m->b.height, 1, 0);
+    
+    if (m == selmon) {
+        char stext_copy[sizeof(stext)];
+        memset(stext_copy, 0, sizeof(stext_copy));
+        strncpy(stext_copy, stext, sizeof(stext_copy) - 1);
+        char *segment = strtok(stext_copy, "|");
+        int x_pos;
+
+        /* first calculate total width WITHOUT ansi codes */
+        tw = 0;
+        while (segment) {
+            tw += textw_no_ansi(m, segment);  // Use the new function here
+            segment = strtok(NULL, "|");
+        }
+        x_pos = m->b.width - tw;
+
+        /* draw each segment with color reset */
+        memset(stext_copy, 0, sizeof(stext_copy));
+        strncpy(stext_copy, stext, sizeof(stext_copy) - 1);
+        segment = strtok(stext_copy, "|");
+        while (segment) {
+            draw_ansi_segment(m, segment, &x_pos, m->b.height, m->lrpad);
+            segment = strtok(NULL, "|");
+        }
+    }
+
+    wl_list_for_each(c, &clients, link) {
+        if (c->mon != m)
+            continue;
+        occ |= c->tags;
+        if (c->isurgent)
+            urg |= c->tags;
+    }
+    x = 0;
+    c = focustop(m);
+    for (i = 0; i < LENGTH(tags); i++) {
+        const int selected = (m->tagset[m->seltags] & (1 << i)) != 0;
+        const int occupied = (occ & (1 << i)) != 0;
+        w = TEXTW(m, tags[i]);
+        if (selected) {
+            drwl_setscheme(m->drw, colors[SchemeSel]);
+        } else if (occupied) {
+            drwl_setscheme(m->drw, colors[SchemeOcc]);
+        } else {
+            drwl_setscheme(m->drw, colors[SchemeNorm]);
+        }
+        drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & (1 << i));
+        if(selected){
+            const int ulh = 2; /* underline height (px) */
+            drwl_setscheme(m->drw, colors[SchemeUnder]);
+            drwl_rect(m->drw, x, m->b.height - ulh, w, ulh, 1, 0);
+        }
+		x += w;
+	}
+	w = TEXTW(m, m->ltsymbol);
+	drwl_setscheme(m->drw, colors[SchemeNorm]);
+	x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0);
+
+	if ((w = m->b.width - tw - x) > m->b.height) {
+		if (c) {
+			drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);
+			drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0);
+		} else {
+			drwl_setscheme(m->drw, colors[SchemeNorm]);
+		}
+	}
+
+	wlr_scene_buffer_set_dest_size(m->scene_buffer,
+		m->b.real_width, m->b.real_height);
+	wlr_scene_node_set_position(&m->scene_buffer->node, m->m.x,
+		m->m.y + (topbar ? 0 : m->m.height - m->b.real_height));
+	wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base);
+	wlr_buffer_unlock(&buf->base);
+}
+
+void
+drawbars(void)
+{
+	Monitor *m = NULL;
+
+	wl_list_for_each(m, &mons, link)
+		drawbar(m);
+}
+
+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;
+
+		/* 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, (float[])COLOR(colors[SchemeSel][ColBorder]));
+	}
+
+	/* 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 its color
+		 * 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, (float[])COLOR(colors[SchemeNorm][ColBorder]));
+			client_activate_surface(old, 0);
+		}
+	}
+	drawbars();
+
+	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, NULL, 0, 0, 0, 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 && !client_has_children(sel)))
+		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 it
+ * 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
+gpureset(struct wl_listener *listener, void *data)
+{
+	struct wlr_renderer *old_drw = drw;
+	struct wlr_allocator *old_alloc = alloc;
+	struct Monitor *m;
+	if (!(drw = wlr_renderer_autocreate(backend)))
+		die("couldn't recreate renderer");
+
+	if (!(alloc = wlr_allocator_autocreate(backend, drw)))
+		die("couldn't recreate allocator");
+
+	wl_list_remove(&gpu_reset.link);
+	wl_signal_add(&drw->events.lost, &gpu_reset);
+
+	wlr_compositor_set_renderer(compositor, drw);
+
+	wl_list_for_each(m, &mons, link) {
+		wlr_output_init_render(m->wlr_output, alloc, drw);
+	}
+
+	wlr_allocator_destroy(old_alloc);
+	wlr_renderer_destroy(old_drw);
+}
+
+void
+handlesig(int signo)
+{
+	if (signo == SIGCHLD)
+		while (waitpid(-1, NULL, WNOHANG) > 0);
+	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
+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(&kb_group->wlr_group->devices))
+		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.
+	 */
+	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);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+void
+keypress(struct wl_listener *listener, void *data)
+{
+	int i;
+	/* This event is raised when a key is pressed or released. */
+	KeyboardGroup *group = wl_container_of(listener, group, 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(
+			group->wlr_group->keyboard.xkb_state, keycode, &syms);
+
+	int handled = 0;
+	uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->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 && group->wlr_group->keyboard.repeat_info.delay > 0) {
+		group->mods = mods;
+		group->keysyms = syms;
+		group->nsyms = nsyms;
+		wl_event_source_timer_update(group->key_repeat_source,
+				group->wlr_group->keyboard.repeat_info.delay);
+	} else {
+		group->nsyms = 0;
+		wl_event_source_timer_update(group->key_repeat_source, 0);
+	}
+
+	if (handled)
+		return;
+
+	wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
+	/* Pass unhandled keycodes along to the client. */
+	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. */
+	KeyboardGroup *group = wl_container_of(listener, group, modifiers);
+
+	wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
+	/* Send modifiers to the client. */
+	wlr_seat_keyboard_notify_modifiers(seat,
+			&group->wlr_group->keyboard.modifiers);
+}
+
+int
+keyrepeat(void *data)
+{
+	KeyboardGroup *group = data;
+	int i;
+	if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
+		return 0;
+
+	wl_event_source_timer_update(group->key_repeat_source,
+			1000 / group->wlr_group->keyboard.repeat_info.rate);
+
+	for (i = 0; i < group->nsyms; i++)
+		keybinding(group->mods, group->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);
+}
+
+static Client *
+nexttagged(Client *c) {
+	Client *walked;
+	wl_list_for_each(walked, &clients, link) {
+		if (walked->isfloating || !VISIBLEON(walked, c->mon))
+			continue;
+		return walked;
+	}
+	return NULL;
+}
+
+static void
+attachaside(Client *c) {
+	Client *at = nexttagged(c);
+	if (!at) {
+		/* No tiled clients, insert at head */
+		wl_list_insert(&clients, &c->link);
+		return;
+	}
+	/* Insert after the first tiled client */
+	wl_list_insert(&at->link, &c->link);
+}
+
+void
+mapnotify(struct wl_listener *listener, void *data)
+{
+	/* Called when the surface is mapped, or ready to display on-screen. */
+	Client *p = NULL;
+	Client *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]);
+	/* Enabled later by a call to arrange() */
+	wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c));
+	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;
+
+	client_get_geometry(c, &c->geom);
+
+	/* Handle unmanaged clients first so we can return prior create borders */
+	if (client_is_unmanaged(c)) {
+		/* 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, c->geom.y);
+		client_set_size(c, c->geom.width, c->geom.height);
+		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,
+			(float[])COLOR(colors[c->isurgent ? SchemeUrg : SchemeNorm][ColBorder]));
+		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);
+	c->geom.width += 2 * c->bw;
+	c->geom.height += 2 * c->bw;
+
+	/* Insert this client into client lists. */
+	// wl_list_insert(&clients, &c->link);
+    if (clients.prev)
+		// tile at the bottom
+		wl_list_insert(clients.prev, &c->link);
+	else
+		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 as its parent.
+	 * If there is no parent, apply rules */
+	if ((p = client_get_parent(c))) {
+		c->isfloating = 1;
+		setmon(c, p->mon, p->tags);
+	} else {
+		applyrules(c);
+	}
+	drawbars();
+
+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 != p && 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 (c->surface.xdg->initialized
+			&& wl_resource_get_version(c->surface.xdg->toplevel->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;
+		resize(c, m->w, 0);
+		n++;
+	}
+	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. Also, some hardware emits these events. */
+	struct wlr_pointer_motion_absolute_event *event = data;
+	double lx, ly, dx, dy;
+
+	if (!event->time_msec) /* this is 0 with virtual pointers */
+		wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
+
+	wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly);
+	dx = lx - cursor->x;
+	dy = ly - cursor->y;
+	motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy);
+}
+
+void
+motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
+		double dx_unaccel, double dy_unaccel)
+{
+	double sx = 0, sy = 0, sx_confined, sy_confined;
+	Client *c = NULL, *w = NULL;
+	LayerSurface *l = NULL;
+	struct wlr_surface *surface = NULL;
+	struct wlr_pointer_constraint_v1 *constraint;
+
+	/* 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
+			&& surface != seat->pointer_state.focused_surface
+			&& toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
+		c = w;
+		surface = seat->pointer_state.focused_surface;
+		sx = cursor->x - (l ? l->scene->node.x : w->geom.x);
+		sy = cursor->y - (l ? l->scene->node.y : w->geom.y);
+	}
+
+	/* time is 0 in internal calls meant to restore pointer focus. */
+	if (time) {
+		wlr_relative_pointer_manager_v1_send_relative_motion(
+				relative_pointer_mgr, seat, (uint64_t)time * 1000,
+				dx, dy, dx_unaccel, dy_unaccel);
+
+		wl_list_for_each(constraint, &pointer_constraints->constraints, link)
+			cursorconstrain(constraint);
+
+		if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
+			toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
+			if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
+				sx = cursor->x - c->geom.x - c->bw;
+				sy = cursor->y - c->geom.y - c->bw;
+				if (wlr_region_confine(&active_constraint->region, sx, sy,
+						sx + dx, sy + dy, &sx_confined, &sy_confined)) {
+					dx = sx_confined - sx;
+					dy = sy_confined - sy;
+				}
+
+				if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
+					return;
+			}
+		}
+
+		wlr_cursor_move(cursor, device, dx, dy);
+		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, (int)round(cursor->x), (int)round(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 = (int)round(cursor->x) - grabcx, .y = (int)round(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 = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1);
+		return;
+	}
+
+	/* 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. */
+	motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y,
+			event->unaccel_dx, event->unaccel_dy);
+}
+
+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 = (int)round(cursor->x) - grabc->geom.x;
+		grabcy = (int)round(cursor->y) - grabc->geom.y;
+		wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
+		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;
+		struct wlr_output_state state;
+
+		/* Ensure displays previously disabled by wlr-output-power-management-v1
+		 * are properly handled*/
+		m->asleep = 0;
+
+		wlr_output_state_init(&state);
+		wlr_output_state_set_enabled(&state, config_head->state.enabled);
+		if (!config_head->state.enabled)
+			goto apply_or_test;
+
+		if (config_head->state.mode)
+			wlr_output_state_set_mode(&state, config_head->state.mode);
+		else
+			wlr_output_state_set_custom_mode(&state,
+					config_head->state.custom_mode.width,
+					config_head->state.custom_mode.height,
+					config_head->state.custom_mode.refresh);
+
+		wlr_output_state_set_transform(&state, config_head->state.transform);
+		wlr_output_state_set_scale(&state, config_head->state.scale);
+		wlr_output_state_set_adaptive_sync_enabled(&state,
+				config_head->state.adaptive_sync_enabled);
+
+apply_or_test:
+		ok &= test ? wlr_output_test_state(wlr_output, &state)
+				: wlr_output_commit_state(wlr_output, &state);
+
+		/* Don't move monitors if position wouldn't change. This avoids
+		 * wlroots marking the output as manually configured.
+		 * wlr_output_layout_add does not like disabled outputs */
+		if (!test && wlr_output->enabled && (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_state_finish(&state);
+	}
+
+	if (ok)
+		wlr_output_configuration_v1_send_succeeded(config);
+	else
+		wlr_output_configuration_v1_send_failed(config);
+	wlr_output_configuration_v1_destroy(config);
+
+	/* https://codeberg.org/dwl/dwl/issues/577 */
+	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;
+
+	if (surface != seat->pointer_state.focused_surface &&
+			sloppyfocus && time && 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 (!time) {
+		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
+powermgrsetmode(struct wl_listener *listener, void *data)
+{
+	struct wlr_output_power_v1_set_mode_event *event = data;
+	struct wlr_output_state state = {0};
+	Monitor *m = event->output->data;
+
+	if (!m)
+		return;
+
+	m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */
+	wlr_output_state_set_enabled(&state, event->mode);
+	wlr_output_commit_state(m->wlr_output, &state);
+
+	m->asleep = !event->mode;
+	updatemons(NULL, NULL);
+}
+
+void
+quit(const Arg *arg)
+{
+	wl_display_terminate(dpy);
+}
+
+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 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;
+	}
+
+	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
+requestdecorationmode(struct wl_listener *listener, void *data)
+{
+	Client *c = wl_container_of(listener, c, set_decoration_mode);
+	if (c->surface.xdg->initialized)
+		wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration,
+				WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
+}
+
+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;
+	struct wlr_box clip;
+
+	if (!c->mon || !client_surface(c)->mapped)
+		return;
+
+	bbox = interact ? &sgeom : &c->mon->w;
+
+	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 */
+	if (startup_cmd) {
+		if ((child_pid = fork()) < 0)
+			die("startup: fork:");
+		if (child_pid == 0) {
+			close(STDIN_FILENO);
+			setsid();
+			execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
+			die("startup: execl:");
+		}
+	}
+
+	/* Mark stdout as non-blocking to avoid the startup script
+	 * causing dwl to freeze when a user neither closes stdin
+	 * nor consumes standard input in his startup script */
+
+	if (fd_set_nonblock(STDOUT_FILENO) < 0)
+		close(STDOUT_FILENO);
+
+	drawbars();
+
+	/* 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
+	 * 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
+	 * 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)
+{
+	Client *p = client_get_parent(c);
+	c->isfloating = floating;
+	/* If in floating layout do not change the client's layer */
+	if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
+		return;
+	wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
+			(p && p->isfullscreen) ? LyrFS
+			: c->isfloating ? LyrFloat : LyrTile]);
+	arrange(c->mon);
+	drawbars();
+}
+
+void
+setfullscreen(Client *c, int fullscreen)
+{
+	c->isfullscreen = fullscreen;
+	if (!c->mon || !client_surface(c)->mapped)
+		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);
+	drawbars();
+}
+
+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, sizeof(selmon->ltsymbol));
+	arrange(selmon);
+	drawbar(selmon);
+}
+
+/* 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.0f ? arg->f + selmon->mfact : arg->f - 1.0f;
+	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 them
+	 */
+	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 them
+	 */
+	struct wlr_seat_request_set_selection_event *event = data;
+	wlr_seat_set_selection(seat, event->source, event->serial);
+}
+
+
+void
+setup(void)
+{
+    int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
+    struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
+    sigemptyset(&sa.sa_mask);
+
+	for (i = 0; i < (int)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();
+	event_loop = wl_display_get_event_loop(dpy);
+
+	/* 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(event_loop, &session)))
+		die("couldn't create backend");
+
+	/* Initialize the scene graph used to lay out windows */
+	scene = wlr_scene_create();
+	root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor);
+	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");
+	wl_signal_add(&drw->events.lost, &gpu_reset);
+
+	/* Create shm, drm and linux_dmabuf interfaces by ourselves.
+	 * The simplest way is to call:
+	 *      wlr_renderer_init_wl_display(drw);
+	 * but we need to create the linux_dmabuf interface manually to integrate it
+	 * with wlr_scene. */
+	wlr_renderer_init_wl_shm(drw, dpy);
+
+	if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
+		wlr_drm_create(dpy, drw);
+		wlr_scene_set_linux_dmabuf_v1(scene,
+				wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw));
+	}
+
+	if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline
+			&& backend->features.timeline)
+		wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd);
+
+	/* 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);
+	wlr_presentation_create(dpy, backend, 2);
+	wlr_alpha_modifier_v1_create(dpy);
+
+	/* Initializes the interface used to implement urgency hints */
+	activation = wlr_xdg_activation_v1_create(dpy);
+	wl_signal_add(&activation->events.request_activate, &request_activate);
+
+	wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
+
+	power_mgr = wlr_output_power_manager_v1_create(dpy);
+	wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
+
+	/* Creates an output layout, which is a wlroots utility for working with an
+	 * arrangement of screens in a physical layout. */
+	output_layout = wlr_output_layout_create(dpy);
+	wl_signal_add(&output_layout->events.change, &layout_change);
+
+    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);
+	wl_signal_add(&backend->events.new_output, &new_output);
+
+	/* 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);
+	wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
+	wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
+
+	layer_shell = wlr_layer_shell_v1_create(dpy, 3);
+	wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
+
+	idle_notifier = wlr_idle_notifier_v1_create(dpy);
+
+	idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
+	wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
+
+	session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
+	wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock);
+	locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
+			(float [4]){0.1f, 0.1f, 0.1f, 1.0f});
+	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);
+	wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
+
+	pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
+	wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
+
+	relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
+
+	/*
+	 * 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.
+	 */
+	wl_signal_add(&cursor->events.motion, &cursor_motion);
+	wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
+	wl_signal_add(&cursor->events.button, &cursor_button);
+	wl_signal_add(&cursor->events.axis, &cursor_axis);
+	wl_signal_add(&cursor->events.frame, &cursor_frame);
+
+	cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
+	wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
+
+	/*
+	 * 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_signal_add(&backend->events.new_input, &new_input_device);
+	virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
+	wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard,
+			&new_virtual_keyboard);
+	virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
+    wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
+            &new_virtual_pointer);
+
+	seat = wlr_seat_create(dpy, "seat0");
+	wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
+	wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
+	wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel);
+	wl_signal_add(&seat->events.request_start_drag, &request_start_drag);
+	wl_signal_add(&seat->events.start_drag, &start_drag);
+
+	kb_group = createkeyboardgroup();
+	wl_list_init(&kb_group->destroy.link);
+
+	output_mgr = wlr_output_manager_v1_create(dpy);
+	wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
+	wl_signal_add(&output_mgr->events.test, &output_mgr_test);
+
+	drwl_init();
+
+	status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy),
+		STDIN_FILENO, WL_EVENT_READABLE, statusin, NULL);
+
+	/* Make sure XWayland clients don't connect to the parent X server,
+	 * e.g when running in the x11 backend or the wayland backend and the
+	 * compositor has Xwayland support */
+	unsetenv("DISPLAY");
+#ifdef XWAYLAND
+	/*
+	 * Initialise the XWayland X server.
+	 * It will be started when the first X client is started.
+	 */
+	if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
+		wl_signal_add(&xwayland->events.ready, &xwayland_ready);
+		wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);
+
+		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) {
+		close(STDIN_FILENO);
+		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);
+}
+
+int
+statusin(int fd, unsigned int mask, void *data)
+{
+	char status[256];
+	ssize_t n;
+
+	if (mask & WL_EVENT_ERROR)
+		die("status in event error");
+	if (mask & WL_EVENT_HANGUP)
+		wl_event_source_remove(status_event_source);
+
+	n = read(fd, status, sizeof(status) - 1);
+	if (n < 0 && errno != EWOULDBLOCK)
+		die("read:");
+
+	status[n] = '\0';
+	status[strcspn(status, "\n")] = '\0';
+
+	strncpy(stext, status, sizeof(stext));
+	drawbars();
+
+	return 0;
+}
+
+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);
+	drawbars();
+}
+
+void
+tagmon(const Arg *arg)
+{
+	Client *sel = focustop(selmon);
+	if (sel)
+		setmon(sel, dirtomon(arg->i), 0);
+}
+
+void
+tile(Monitor *m)
+{
+	unsigned int h, r, e = m->gaps, mw, my, ty;
+	int i, n = 0;
+	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)
+		e = 0;
+
+	if (n > m->nmaster)
+		mw = m->nmaster ? (int)roundf((m->w.width + gappx*e) * m->mfact) : 0;
+	else
+		mw = m->w.width;
+	i = 0;
+	my = ty = gappx*e;
+	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 - gappx*e - gappx*e * (r - 1)) / r;
+			resize(c, (struct wlr_box){.x = m->w.x + gappx*e, .y = m->w.y + my,
+				.width = mw - 2*gappx*e, .height = h}, 0);
+			my += c->geom.height + gappx*e;
+		} else {
+			r = n - i;
+			h = (m->w.height - ty - gappx*e - gappx*e * (r - 1)) / r;
+			resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
+				.width = m->w.width - mw - gappx*e, .height = h}, 0);
+			ty += c->geom.height + gappx*e;
+		}
+		i++;
+	}
+}
+
+void
+togglebar(const Arg *arg)
+{
+	wlr_scene_node_set_enabled(&selmon->scene_buffer->node,
+		!selmon->scene_buffer->node.enabled);
+	arrangelayers(selmon);
+}
+
+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)
+{
+	selmon->gaps = !selmon->gaps;
+	arrange(selmon);
+}
+
+void
+toggletag(const Arg *arg)
+{
+	uint32_t newtags;
+	Client *sel = focustop(selmon);
+	if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK)))
+		return;
+
+	sel->tags = newtags;
+	focusclient(focustop(selmon), 1);
+	arrange(selmon);
+	drawbars();
+}
+
+void
+toggleview(const Arg *arg)
+{
+	uint32_t newtagset;
+	if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0))
+		return;
+
+	selmon->tagset[selmon->seltags] = newtagset;
+	focusclient(focustop(selmon), 1);
+	arrange(selmon);
+	drawbars();
+}
+
+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 *l = wl_container_of(listener, l, unmap);
+
+	l->mapped = 0;
+	wlr_scene_node_set_enabled(&l->scene->node, 0);
+	if (l == exclusive_focus)
+		exclusive_focus = NULL;
+	if (l->layer_surface->output && (l->mon = l->layer_surface->output->data))
+		arrangelayers(l->mon);
+	if (l->layer_surface->surface == seat->keyboard_state.focused_surface)
+		focusclient(focustop(selmon), 1);
+	motionnotify(0, NULL, 0, 0, 0, 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;
+			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);
+	drawbars();
+	motionnotify(0, NULL, 0, 0, 0, 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 || m->asleep)
+			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);
+		m->m = m->w = (struct wlr_box){0};
+	}
+	/* 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);
+
+	wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y);
+	wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height);
+
+	/* 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);
+
+		/* Try to re-set the gamma LUT when updating monitors,
+		 * it's only really needed when enabling a disabled output, but meh. */
+		m->gamma_lut_changed = 1;
+
+		config_head->state.x = m->m.x;
+		config_head->state.y = m->m.y;
+
+		if (!selmon) {
+			selmon = m;
+		}
+	}
+
+	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);
+		}
+	}
+
+	wl_list_for_each(m, &mons, link) {
+		updatebar(m);
+		drawbar(m);
+	}
+
+	/* 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
+updatebar(Monitor *m)
+{
+	size_t i;
+	int rw, rh;
+	char fontattrs[12];
+
+	wlr_output_transformed_resolution(m->wlr_output, &rw, &rh);
+	m->b.width = rw;
+	m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale);
+
+	wlr_scene_node_set_enabled(&m->scene_buffer->node, m->wlr_output->enabled ? showbar : 0);
+
+	for (i = 0; i < LENGTH(m->pool); i++)
+		if (m->pool[i]) {
+			wlr_buffer_drop(&m->pool[i]->base);
+			m->pool[i] = NULL;
+		}
+
+	if (m->b.scale == m->wlr_output->scale && m->drw)
+		return;
+
+	drwl_font_destroy(m->drw->font);
+	snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale);
+	if (!(drwl_font_create(m->drw, LENGTH(fonts), fonts, fontattrs)))
+		die("Could not load font");
+
+	m->b.scale = m->wlr_output->scale;
+	m->lrpad = m->drw->font->height;
+	m->b.height = m->drw->font->height + 2;
+	m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale);
+}
+
+void
+updatetitle(struct wl_listener *listener, void *data)
+{
+	Client *c = wl_container_of(listener, c, set_title);
+	if (c == focustop(c->mon))
+		drawbars();
+}
+
+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;
+
+	c->isurgent = 1;
+	drawbars();
+
+	if (client_surface(c)->mapped)
+		client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
+}
+
+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);
+	drawbars();
+}
+
+void
+virtualkeyboard(struct wl_listener *listener, void *data)
+{
+	struct wlr_virtual_keyboard_v1 *kb = data;
+	/* virtual keyboards shouldn't share keyboard group */
+	KeyboardGroup *group = createkeyboardgroup();
+	/* Set the keymap to match the group keymap */
+	wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
+	LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup);
+
+	/* Add the new keyboard to the group */
+	wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
+}
+
+void
+virtualpointer(struct wl_listener *listener, void *data)
+{
+	struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
+	struct wlr_input_device *device = &event->new_pointer->pointer.base;
+
+	wlr_cursor_attach_input_device(cursor, device);
+	if (event->suggested_output)
+		wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
+}
+
+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;
+	struct wlr_scene_surface *scene_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) {
+			scene_surface = wlr_scene_surface_try_from_buffer(
+					wlr_scene_buffer_from_node(node));
+			if (!scene_surface) continue;
+			surface = scene_surface->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 (!client_is_unmanaged(c))
+		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 (!client_surface(c) || !client_surface(c)->mapped) {
+		wlr_xwayland_surface_configure(c->surface.xwayland,
+				event->x, event->y, event->width, event->height);
+		return;
+	}
+	if (client_is_unmanaged(c)) {
+		wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
+		wlr_xwayland_surface_configure(c->surface.xwayland,
+				event->x, event->y, event->width, event->height);
+		return;
+	}
+	if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) {
+		resize(c, (struct wlr_box){.x = event->x - c->bw,
+				.y = event->y - c->bw, .width = event->width + c->bw * 2,
+				.height = event->height + c->bw * 2}, 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 = X11;
+	c->bw = client_is_unmanaged(c) ? 0 : borderpx;
+
+	/* Listen to the various events it can emit */
+	LISTEN(&xsurface->events.associate, &c->associate, associatex11);
+	LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
+	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.request_fullscreen, &c->fullscreen, fullscreennotify);
+	LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
+	LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle);
+}
+
+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);
+}
+
+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) || !c->surface.xwayland->hints)
+		return;
+
+	c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
+	drawbars();
+
+	if (c->isurgent && surface && surface->mapped)
+		client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
+}
+
+void
+xwaylandready(struct wl_listener *listener, void *data)
+{
+	struct wlr_xcursor *xcursor;
+
+	/* 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);
+}
+#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.desktop b/dwl.desktop
new file mode 100644
index 0000000..e1380f7
--- /dev/null
+++ b/dwl.desktop
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Name=dwl
+Comment=dwm for Wayland
+Exec=dwl
+Type=Application
diff --git a/patches/bar.patch b/patches/bar.patch
new file mode 100644
index 0000000..c9527c1
--- /dev/null
+++ b/patches/bar.patch
@@ -0,0 +1,1266 @@
+From 8906a73dbc8996dd1bfff15f5b26aaee6d45fd61 Mon Sep 17 00:00:00 2001
+From: sewn <sewn@disroot.org>
+Date: Tue, 29 Jul 2025 15:21:19 +0300
+Subject: [PATCH] Implement dwm bar clone
+
+Signed-off-by: sewn <sewn@disroot.org>
+---
+ Makefile     |   2 +-
+ config.def.h |  33 ++--
+ drwl.h       | 311 ++++++++++++++++++++++++++++++++++++
+ dwl.c        | 441 +++++++++++++++++++++++++++++++++++++++++----------
+ 4 files changed, 695 insertions(+), 92 deletions(-)
+ create mode 100644 drwl.h
+
+diff --git a/Makefile b/Makefile
+index 578194f..279b1c0 100644
+--- a/Makefile
++++ b/Makefile
+@@ -12,7 +12,7 @@ DWLDEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
+ 	-Wfloat-conversion
+ 
+ # CFLAGS / LDFLAGS
+-PKGS      = wayland-server xkbcommon libinput $(XLIBS)
++PKGS      = wayland-server xkbcommon libinput pixman-1 fcft $(XLIBS)
+ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEVCFLAGS) $(CFLAGS)
+ LDLIBS    = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
+ 
+diff --git a/config.def.h b/config.def.h
+index 95c2afa..1b7472d 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -7,15 +7,21 @@
+ 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 rootcolor[]             = COLOR(0x222222ff);
+-static const float bordercolor[]           = COLOR(0x444444ff);
+-static const float focuscolor[]            = COLOR(0x005577ff);
+-static const float urgentcolor[]           = COLOR(0xff0000ff);
++static const int showbar                   = 1; /* 0 means no bar */
++static const int topbar                    = 1; /* 0 means bottom bar */
++static const char *fonts[]                 = {"monospace:size=10"};
++static const float rootcolor[]             = COLOR(0x000000ff);
+ /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
+ static const float fullscreen_bg[]         = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
++static uint32_t colors[][3]                = {
++	/*               fg          bg          border    */
++	[SchemeNorm] = { 0xbbbbbbff, 0x222222ff, 0x444444ff },
++	[SchemeSel]  = { 0xeeeeeeff, 0x005577ff, 0x005577ff },
++	[SchemeUrg]  = { 0,          0,          0x770000ff },
++};
+ 
+-/* tagging - TAGCOUNT must be no greater than 31 */
+-#define TAGCOUNT (9)
++/* tagging */
++static char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+ 
+ /* logging */
+ static int log_level = WLR_ERROR;
+@@ -127,6 +133,7 @@ static const Key keys[] = {
+ 	/* modifier                  key                 function        argument */
+ 	{ MODKEY,                    XKB_KEY_p,          spawn,          {.v = menucmd} },
+ 	{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return,     spawn,          {.v = termcmd} },
++	{ MODKEY,                    XKB_KEY_b,          togglebar,      {0} },
+ 	{ MODKEY,                    XKB_KEY_j,          focusstack,     {.i = +1} },
+ 	{ MODKEY,                    XKB_KEY_k,          focusstack,     {.i = -1} },
+ 	{ MODKEY,                    XKB_KEY_i,          incnmaster,     {.i = +1} },
+@@ -170,7 +177,15 @@ static const Key keys[] = {
+ };
+ 
+ static const Button buttons[] = {
+-	{ MODKEY, BTN_LEFT,   moveresize,     {.ui = CurMove} },
+-	{ MODKEY, BTN_MIDDLE, togglefloating, {0} },
+-	{ MODKEY, BTN_RIGHT,  moveresize,     {.ui = CurResize} },
++	{ ClkLtSymbol, 0,      BTN_LEFT,   setlayout,      {.v = &layouts[0]} },
++	{ ClkLtSymbol, 0,      BTN_RIGHT,  setlayout,      {.v = &layouts[2]} },
++	{ ClkTitle,    0,      BTN_MIDDLE, zoom,           {0} },
++	{ ClkStatus,   0,      BTN_MIDDLE, spawn,          {.v = termcmd} },
++	{ ClkClient,   MODKEY, BTN_LEFT,   moveresize,     {.ui = CurMove} },
++	{ ClkClient,   MODKEY, BTN_MIDDLE, togglefloating, {0} },
++	{ ClkClient,   MODKEY, BTN_RIGHT,  moveresize,     {.ui = CurResize} },
++	{ ClkTagBar,   0,      BTN_LEFT,   view,           {0} },
++	{ ClkTagBar,   0,      BTN_RIGHT,  toggleview,     {0} },
++	{ ClkTagBar,   MODKEY, BTN_LEFT,   tag,            {0} },
++	{ ClkTagBar,   MODKEY, BTN_RIGHT,  toggletag,      {0} },
+ };
+diff --git a/drwl.h b/drwl.h
+new file mode 100644
+index 0000000..90cc35b
+--- /dev/null
++++ b/drwl.h
+@@ -0,0 +1,311 @@
++/*
++ * drwl - https://codeberg.org/sewn/drwl
++ *
++ * Copyright (c) 2023-2025 sewn <sewn@disroot.org>
++ * Copyright (c) 2024 notchoc <notchoc@disroot.org>
++ * 
++ * 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.
++ *
++ * The UTF-8 Decoder included is from Bjoern Hoehrmann:
++ * Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
++ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
++ */
++#pragma once
++
++#include <stdlib.h>
++#include <fcft/fcft.h>
++#include <pixman-1/pixman.h>
++
++enum { ColFg, ColBg, ColBorder }; /* colorscheme index */
++
++typedef struct fcft_font Fnt;
++typedef pixman_image_t Img;
++
++typedef struct {
++	Img *image;
++	Fnt *font;
++	uint32_t *scheme;
++} Drwl;
++
++#define UTF8_ACCEPT 0
++#define UTF8_REJECT 12
++#define UTF8_INVALID 0xFFFD
++
++static const uint8_t utf8d[] = {
++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++	 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
++	 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,  9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
++	 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,  7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
++	 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,  2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
++	10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
++
++	 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
++	12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
++	12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
++	12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
++	12,36,12,12,12,12,12,12,12,12,12,12,
++};
++
++static inline uint32_t
++utf8decode(uint32_t *state, uint32_t *codep, uint8_t byte)
++{
++	uint32_t type = utf8d[byte];
++
++	*codep = (*state != UTF8_ACCEPT) ?
++		(byte & 0x3fu) | (*codep << 6) :
++		(0xff >> type) & (byte);
++
++	*state = utf8d[256 + *state + type];
++	return *state;
++}
++
++static int
++drwl_init(void)
++{
++	fcft_set_scaling_filter(FCFT_SCALING_FILTER_LANCZOS3);
++	return fcft_init(FCFT_LOG_COLORIZE_AUTO, 0, FCFT_LOG_CLASS_ERROR);
++}
++
++static Drwl *
++drwl_create(void)
++{
++	Drwl *drwl;
++	
++	if (!(drwl = calloc(1, sizeof(Drwl))))
++		return NULL;
++
++	return drwl;
++}
++
++static void
++drwl_setfont(Drwl *drwl, Fnt *font)
++{
++	if (drwl)
++		drwl->font = font;
++}
++
++static void
++drwl_setimage(Drwl *drwl, Img *image)
++{
++	if (drwl)
++		drwl->image = image;
++}
++
++static Fnt *
++drwl_font_create(Drwl *drwl, size_t count,
++		const char *names[static count], const char *attributes)
++{
++	Fnt *font = fcft_from_name(count, names, attributes);
++	if (drwl)
++		drwl_setfont(drwl, font);
++	return font;
++}
++
++static void
++drwl_font_destroy(Fnt *font)
++{
++	fcft_destroy(font);
++}
++
++static inline pixman_color_t
++convert_color(uint32_t clr)
++{
++	return (pixman_color_t){
++		((clr >> 24) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
++		((clr >> 16) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
++		((clr >> 8) & 0xFF) * 0x101 * (clr & 0xFF) / 0xFF,
++		(clr & 0xFF) * 0x101
++	};
++}
++
++static void
++drwl_setscheme(Drwl *drwl, uint32_t *scm)
++{
++	if (drwl)
++		drwl->scheme = scm;
++}
++
++static Img *
++drwl_image_create(Drwl *drwl, unsigned int w, unsigned int h, uint32_t *bits)
++{
++	Img *image;
++	pixman_region32_t clip;
++
++	image = pixman_image_create_bits_no_clear(
++		PIXMAN_a8r8g8b8, w, h, bits, w * 4);
++	if (!image)
++		return NULL;
++	pixman_region32_init_rect(&clip, 0, 0, w, h);
++	pixman_image_set_clip_region32(image, &clip);
++	pixman_region32_fini(&clip);
++
++	if (drwl)
++		drwl_setimage(drwl, image);
++	return image;
++}
++
++static void
++drwl_rect(Drwl *drwl,
++		int x, int y, unsigned int w, unsigned int h,
++		int filled, int invert)
++{
++	pixman_color_t clr;
++	if (!drwl || !drwl->scheme || !drwl->image)
++		return;
++
++	clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
++	if (filled)
++		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 1,
++			&(pixman_rectangle16_t){x, y, w, h});
++	else
++		pixman_image_fill_rectangles(PIXMAN_OP_SRC, drwl->image, &clr, 4,
++			(pixman_rectangle16_t[4]){
++				{ x,         y,         w, 1 },
++				{ x,         y + h - 1, w, 1 },
++				{ x,         y,         1, h },
++				{ x + w - 1, y,         1, h }});
++}
++
++static int
++drwl_text(Drwl *drwl,
++		int x, int y, unsigned int w, unsigned int h,
++		unsigned int lpad, const char *text, int invert)
++{
++	int ty;
++	int render = x || y || w || h;
++	long x_kern;
++	uint32_t cp = 0, last_cp = 0, state;
++	pixman_color_t clr;
++	pixman_image_t *fg_pix = NULL;
++	int noellipsis = 0;
++	const struct fcft_glyph *glyph, *eg = NULL;
++	int fcft_subpixel_mode = FCFT_SUBPIXEL_DEFAULT;
++
++	if (!drwl || (render && (!drwl->scheme || !w || !drwl->image)) || !text || !drwl->font)
++		return 0;
++
++	if (!render) {
++		w = invert ? invert : ~invert;
++	} else {
++		clr = convert_color(drwl->scheme[invert ? ColBg : ColFg]);
++		fg_pix = pixman_image_create_solid_fill(&clr);
++
++		drwl_rect(drwl, x, y, w, h, 1, !invert);
++
++		x += lpad;
++		w -= lpad;
++	}
++
++	if (render && (drwl->scheme[ColBg] & 0xFF) != 0xFF)
++		fcft_subpixel_mode = FCFT_SUBPIXEL_NONE;
++
++	if (render)
++		eg = fcft_rasterize_char_utf32(drwl->font, 0x2026 /* … */, fcft_subpixel_mode);
++
++	for (const char *p = text, *pp; pp = p, *p; p++) {
++		for (state = UTF8_ACCEPT; *p &&
++		     utf8decode(&state, &cp, *p) > UTF8_REJECT; p++)
++			;
++		if (!*p || state == UTF8_REJECT) {
++			cp = UTF8_INVALID;
++			if (p > pp)
++				p--;
++		}
++
++		glyph = fcft_rasterize_char_utf32(drwl->font, cp, fcft_subpixel_mode);
++		if (!glyph)
++			continue;
++
++		x_kern = 0;
++		if (last_cp)
++			fcft_kerning(drwl->font, last_cp, cp, &x_kern, NULL);
++		last_cp = cp;
++
++		ty = y + (h - drwl->font->height) / 2 + drwl->font->ascent;
++
++		if (render && !noellipsis && x_kern + glyph->advance.x + eg->advance.x > w &&
++		    *(p + 1) != '\0') {
++			/* cannot fit ellipsis after current codepoint */
++			if (drwl_text(drwl, 0, 0, 0, 0, 0, pp, 0) + x_kern <= w) {
++				noellipsis = 1;
++			} else {
++				w -= eg->advance.x;
++				pixman_image_composite32(
++					PIXMAN_OP_OVER, fg_pix, eg->pix, drwl->image, 0, 0, 0, 0,
++					x + eg->x, ty - eg->y, eg->width, eg->height);
++			}
++		}
++
++		if ((x_kern + glyph->advance.x) > w)
++			break;
++
++		x += x_kern;
++
++		if (render && pixman_image_get_format(glyph->pix) == PIXMAN_a8r8g8b8)
++			/* pre-rendered glyphs (eg. emoji) */
++			pixman_image_composite32(
++				PIXMAN_OP_OVER, glyph->pix, NULL, drwl->image, 0, 0, 0, 0,
++				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
++		else if (render)
++			pixman_image_composite32(
++				PIXMAN_OP_OVER, fg_pix, glyph->pix, drwl->image, 0, 0, 0, 0,
++				x + glyph->x, ty - glyph->y, glyph->width, glyph->height);
++
++		x += glyph->advance.x;
++		w -= glyph->advance.x;
++	}
++
++	if (render)
++		pixman_image_unref(fg_pix);
++
++	return x + (render ? w : 0);
++}
++
++static unsigned int
++drwl_font_getwidth(Drwl *drwl, const char *text)
++{
++	if (!drwl || !drwl->font || !text)
++		return 0;
++	return drwl_text(drwl, 0, 0, 0, 0, 0, text, 0);
++}
++
++static void
++drwl_image_destroy(Img *image)
++{
++	pixman_image_unref(image);
++}
++
++static void
++drwl_destroy(Drwl *drwl)
++{
++	if (drwl->font)
++		drwl_font_destroy(drwl->font);
++	if (drwl->image)
++		drwl_image_destroy(drwl->image);
++	free(drwl);
++}
++
++static void
++drwl_fini(void)
++{
++	fcft_fini();
++}
+diff --git a/dwl.c b/dwl.c
+index 12f441e..bf340d8 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -5,6 +5,7 @@
+ #include <libinput.h>
+ #include <linux/input-event-codes.h>
+ #include <math.h>
++#include <libdrm/drm_fourcc.h>
+ #include <signal.h>
+ #include <stdio.h>
+ #include <stdlib.h>
+@@ -59,6 +60,7 @@
+ #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/interfaces/wlr_buffer.h>
+ #include <wlr/util/log.h>
+ #include <wlr/util/region.h>
+ #include <xkbcommon/xkbcommon.h>
+@@ -69,6 +71,7 @@
+ #endif
+ 
+ #include "util.h"
++#include "drwl.h"
+ 
+ /* macros */
+ #define MAX(A, B)               ((A) > (B) ? (A) : (B))
+@@ -77,14 +80,17 @@
+ #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 TAGMASK                 ((1u << LENGTH(tags)) - 1)
+ #define LISTEN(E, L, H)         wl_signal_add((E), ((L)->notify = (H), (L)))
+ #define LISTEN_STATIC(E, H)     do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
++#define TEXTW(mon, text)        (drwl_font_getwidth(mon->drw, text) + mon->lrpad)
+ 
+ /* enums */
++enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */
+ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
+ enum { XDGShell, LayerShell, X11 }; /* client types */
+ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
++enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */
+ 
+ typedef union {
+ 	int i;
+@@ -94,6 +100,7 @@ typedef union {
+ } Arg;
+ 
+ typedef struct {
++	unsigned int click;
+ 	unsigned int mod;
+ 	unsigned int button;
+ 	void (*func)(const Arg *);
+@@ -183,10 +190,19 @@ typedef struct {
+ 	void (*arrange)(Monitor *);
+ } Layout;
+ 
++typedef struct {
++	struct wlr_buffer base;
++	struct wl_listener release;
++	bool busy;
++	Img *image;
++	uint32_t data[];
++} Buffer;
++
+ struct Monitor {
+ 	struct wl_list link;
+ 	struct wlr_output *wlr_output;
+ 	struct wlr_scene_output *scene_output;
++	struct wlr_scene_buffer *scene_buffer; /* bar buffer */
+ 	struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
+ 	struct wl_listener frame;
+ 	struct wl_listener destroy;
+@@ -194,6 +210,11 @@ struct Monitor {
+ 	struct wl_listener destroy_lock_surface;
+ 	struct wlr_session_lock_surface_v1 *lock_surface;
+ 	struct wlr_box m; /* monitor area, layout-relative */
++	struct {
++		int width, height;
++		int real_width, real_height; /* non-scaled */
++		float scale;
++	} b; /* bar area */
+ 	struct wlr_box w; /* window area, layout-relative */
+ 	struct wl_list layers[4]; /* LayerSurface.link */
+ 	const Layout *lt[2];
+@@ -205,6 +226,9 @@ struct Monitor {
+ 	int nmaster;
+ 	char ltsymbol[16];
+ 	int asleep;
++	Drwl *drw;
++	Buffer *pool[2];
++	int lrpad;
+ };
+ 
+ typedef struct {
+@@ -247,6 +271,13 @@ 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 bool baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy);
++static void bufdestroy(struct wlr_buffer *buffer);
++static bool bufdatabegin(struct wlr_buffer *buffer, uint32_t flags,
++		void **data, uint32_t *format, size_t *stride);
++static void bufdataend(struct wlr_buffer *buffer);
++static Buffer *bufmon(Monitor *m);
++static void bufrelease(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);
+@@ -282,6 +313,8 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data);
+ static void destroysessionlock(struct wl_listener *listener, void *data);
+ static void destroykeyboardgroup(struct wl_listener *listener, void *data);
+ static Monitor *dirtomon(enum wlr_direction dir);
++static void drawbar(Monitor *m);
++static void drawbars(void);
+ static void focusclient(Client *c, int lift);
+ static void focusmon(const Arg *arg);
+ static void focusstack(const Arg *arg);
+@@ -310,7 +343,6 @@ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int
+ 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 powermgrsetmode(struct wl_listener *listener, void *data);
+ static void quit(const Arg *arg);
+ static void rendermon(struct wl_listener *listener, void *data);
+@@ -331,9 +363,11 @@ 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 int statusin(int fd, unsigned int mask, void *data);
+ static void tag(const Arg *arg);
+ static void tagmon(const Arg *arg);
+ static void tile(Monitor *m);
++static void togglebar(const Arg *arg);
+ static void togglefloating(const Arg *arg);
+ static void togglefullscreen(const Arg *arg);
+ static void toggletag(const Arg *arg);
+@@ -342,6 +376,7 @@ 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 updatebar(Monitor *m);
+ static void updatetitle(struct wl_listener *listener, void *data);
+ static void urgent(struct wl_listener *listener, void *data);
+ static void view(const Arg *arg);
+@@ -406,6 +441,15 @@ static struct wlr_box sgeom;
+ static struct wl_list mons;
+ static Monitor *selmon;
+ 
++static char stext[256];
++static struct wl_event_source *status_event_source;
++
++static const struct wlr_buffer_impl buffer_impl = {
++    .destroy = bufdestroy,
++    .begin_data_ptr_access = bufdatabegin,
++    .end_data_ptr_access = bufdataend,
++};
++
+ /* global event handlers */
+ static struct wl_listener cursor_axis = {.notify = axisnotify};
+ static struct wl_listener cursor_button = {.notify = buttonpress};
+@@ -521,7 +565,7 @@ arrange(Monitor *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));
++	strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
+ 
+ 	/* We move all clients (except fullscreen and unmanaged) to LyrTile while
+ 	 * in floating layout to avoid "real" floating clients be always on top */
+@@ -576,6 +620,11 @@ arrangelayers(Monitor *m)
+ 	if (!m->wlr_output->enabled)
+ 		return;
+ 
++	if (m->scene_buffer->node.enabled) {
++		usable_area.height -= m->b.real_height;
++		usable_area.y += topbar ? m->b.real_height : 0;
++	}
++
+ 	/* Arrange exclusive surfaces from top->bottom */
+ 	for (i = 3; i >= 0; i--)
+ 		arrangelayer(m, &m->layers[i], &usable_area, 1);
+@@ -618,17 +667,102 @@ axisnotify(struct wl_listener *listener, void *data)
+ 			event->delta_discrete, event->source, event->relative_direction);
+ }
+ 
++bool
++baracceptsinput(struct wlr_scene_buffer *buffer, double *sx, double *sy)
++{
++	return true;
++}
++
++void
++bufdestroy(struct wlr_buffer *wlr_buffer)
++{
++	Buffer *buf = wl_container_of(wlr_buffer, buf, base);
++	if (buf->busy)
++		wl_list_remove(&buf->release.link);
++	drwl_image_destroy(buf->image);
++	free(buf);
++}
++
++bool
++bufdatabegin(struct wlr_buffer *wlr_buffer, uint32_t flags,
++		void **data, uint32_t *format, size_t *stride)
++{
++	Buffer *buf = wl_container_of(wlr_buffer, buf, base);
++
++	if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false;
++
++	*data   = buf->data;
++	*stride = wlr_buffer->width * 4;
++	*format = DRM_FORMAT_ARGB8888;
++
++	return true;
++}
++
++void
++bufdataend(struct wlr_buffer *wlr_buffer)
++{
++}
++
++Buffer *
++bufmon(Monitor *m)
++{
++	size_t i;
++	Buffer *buf = NULL;
++
++	for (i = 0; i < LENGTH(m->pool); i++) {
++		if (m->pool[i]) {
++			if (m->pool[i]->busy)
++				continue;
++			buf = m->pool[i];
++			break;
++		}
++
++		buf = ecalloc(1, sizeof(Buffer) + (m->b.width * 4 * m->b.height));
++		buf->image = drwl_image_create(NULL, m->b.width, m->b.height, buf->data);
++		wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height);
++		m->pool[i] = buf;
++		break;
++	}
++	if (!buf)
++		return NULL;
++
++	buf->busy = true;
++	LISTEN(&buf->base.events.release, &buf->release, bufrelease);
++	wlr_buffer_lock(&buf->base);
++	drwl_setimage(m->drw, buf->image);
++	return buf;
++}
++
++void
++bufrelease(struct wl_listener *listener, void *data)
++{
++	Buffer *buf = wl_container_of(listener, buf, release);
++	buf->busy = false;
++	wl_list_remove(&buf->release.link);
++}
++
+ void
+ buttonpress(struct wl_listener *listener, void *data)
+ {
++	unsigned int i = 0, x = 0;
++	double cx;
++	unsigned int click;
+ 	struct wlr_pointer_button_event *event = data;
+ 	struct wlr_keyboard *keyboard;
++	struct wlr_scene_node *node;
++	struct wlr_scene_buffer *buffer;
+ 	uint32_t mods;
++	Arg arg = {0};
+ 	Client *c;
+ 	const Button *b;
+ 
+ 	wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
+ 
++	click = ClkRoot;
++	xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
++	if (c)
++		click = ClkClient;
++
+ 	switch (event->state) {
+ 	case WL_POINTER_BUTTON_STATE_PRESSED:
+ 		cursor_mode = CurPressed;
+@@ -636,17 +770,34 @@ buttonpress(struct wl_listener *listener, void *data)
+ 		if (locked)
+ 			break;
+ 
++		if (!c && !exclusive_focus &&
++			(node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) &&
++			(buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) {
++			cx = (cursor->x - selmon->m.x) * selmon->wlr_output->scale;
++			do
++				x += TEXTW(selmon, tags[i]);
++			while (cx >= x && ++i < LENGTH(tags));
++			if (i < LENGTH(tags)) {
++				click = ClkTagBar;
++				arg.ui = 1 << i;
++			} else if (cx < x + TEXTW(selmon, selmon->ltsymbol))
++				click = ClkLtSymbol;
++			else if (cx > selmon->b.width - (TEXTW(selmon, stext) - selmon->lrpad + 2)) {
++				click = ClkStatus;
++			} else
++				click = ClkTitle;
++		}
++
+ 		/* 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)))
++		if (click == ClkClient && (!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);
++			if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) {
++				b->func(click == ClkTagBar && b->arg.i == 0 ? &arg : &b->arg);
+ 				return;
+ 			}
+ 		}
+@@ -721,6 +872,8 @@ cleanup(void)
+ 	/* 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);
++
++	drwl_fini();
+ }
+ 
+ void
+@@ -736,6 +889,12 @@ cleanupmon(struct wl_listener *listener, void *data)
+ 			wlr_layer_surface_v1_destroy(l->layer_surface);
+ 	}
+ 
++	for (i = 0; i < LENGTH(m->pool); i++)
++		wlr_buffer_drop(&m->pool[i]->base);
++
++	drwl_setimage(m->drw, NULL);
++	drwl_destroy(m->drw);
++
+ 	wl_list_remove(&m->destroy.link);
+ 	wl_list_remove(&m->frame.link);
+ 	wl_list_remove(&m->link);
+@@ -748,6 +907,7 @@ cleanupmon(struct wl_listener *listener, void *data)
+ 
+ 	closemon(m);
+ 	wlr_scene_node_destroy(&m->fullscreen_bg->node);
++	wlr_scene_node_destroy(&m->scene_buffer->node);
+ 	free(m);
+ }
+ 
+@@ -814,7 +974,7 @@ closemon(Monitor *m)
+ 			setmon(c, selmon, c->tags);
+ 	}
+ 	focusclient(focustop(selmon), 1);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -1066,7 +1226,7 @@ createmon(struct wl_listener *listener, void *data)
+ 			m->nmaster = r->nmaster;
+ 			m->lt[0] = r->lt;
+ 			m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
+-			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
++			strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, sizeof(m->ltsymbol));
+ 			wlr_output_state_set_scale(&state, r->scale);
+ 			wlr_output_state_set_transform(&state, r->rr);
+ 			break;
+@@ -1088,8 +1248,15 @@ createmon(struct wl_listener *listener, void *data)
+ 	wlr_output_commit_state(wlr_output, &state);
+ 	wlr_output_state_finish(&state);
+ 
++	if (!(m->drw = drwl_create()))
++		die("failed to create drwl context");
++
++	m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL);
++	m->scene_buffer->point_accepts_input = baracceptsinput;
++	updatebar(m);
++
+ 	wl_list_insert(&mons, &m->link);
+-	printstatus();
++	drawbars();
+ 
+ 	/* The xdg-protocol specifies:
+ 	 *
+@@ -1399,6 +1566,80 @@ dirtomon(enum wlr_direction dir)
+ 	return selmon;
+ }
+ 
++void
++drawbar(Monitor *m)
++{
++	int x, w, tw = 0;
++	int boxs = m->drw->font->height / 9;
++	int boxw = m->drw->font->height / 6 + 2;
++	uint32_t i, occ = 0, urg = 0;
++	Client *c;
++	Buffer *buf;
++
++	if (!m->scene_buffer->node.enabled)
++		return;
++	if (!(buf = bufmon(m)))
++		return;
++
++	/* draw status first so it can be overdrawn by tags later */
++	if (m == selmon) { /* status is only drawn on selected monitor */
++		drwl_setscheme(m->drw, colors[SchemeNorm]);
++		tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */
++		drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0);
++	}
++
++	wl_list_for_each(c, &clients, link) {
++		if (c->mon != m)
++			continue;
++		occ |= c->tags;
++		if (c->isurgent)
++			urg |= c->tags;
++	}
++	x = 0;
++	c = focustop(m);
++	for (i = 0; i < LENGTH(tags); i++) {
++		w = TEXTW(m, tags[i]);
++		drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]);
++		drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & 1 << i);
++		if (occ & 1 << i)
++			drwl_rect(m->drw, x + boxs, boxs, boxw, boxw,
++				m == selmon && c && c->tags & 1 << i,
++				urg & 1 << i);
++		x += w;
++	}
++	w = TEXTW(m, m->ltsymbol);
++	drwl_setscheme(m->drw, colors[SchemeNorm]);
++	x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0);
++
++	if ((w = m->b.width - tw - x) > m->b.height) {
++		if (c) {
++			drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]);
++			drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0);
++			if (c && c->isfloating)
++				drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0);
++		} else {
++			drwl_setscheme(m->drw, colors[SchemeNorm]);
++			drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1);
++		}
++	}
++
++	wlr_scene_buffer_set_dest_size(m->scene_buffer,
++		m->b.real_width, m->b.real_height);
++	wlr_scene_node_set_position(&m->scene_buffer->node, m->m.x,
++		m->m.y + (topbar ? 0 : m->m.height - m->b.real_height));
++	wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base);
++	wlr_buffer_unlock(&buf->base);
++}
++
++void
++drawbars(void)
++{
++	Monitor *m = NULL;
++
++	wl_list_for_each(m, &mons, link)
++		drawbar(m);
++}
++
+ void
+ focusclient(Client *c, int lift)
+ {
+@@ -1433,13 +1674,13 @@ focusclient(Client *c, int lift)
+ 		/* 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);
++			client_set_border_color(c, (float[])COLOR(colors[SchemeSel][ColBorder]));
+ 	}
+ 
+ 	/* 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
++		 * but only update its position in fstack to render its border with its color
+ 		 * 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)
+@@ -1450,12 +1691,11 @@ focusclient(Client *c, int lift)
+ 		/* 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_set_border_color(old_c, (float[])COLOR(colors[SchemeNorm][ColBorder]));
+ 			client_activate_surface(old, 0);
+ 		}
+ 	}
+-	printstatus();
++	drawbars();
+ 
+ 	if (!c) {
+ 		/* With no client, all we have left is to clear focus */
+@@ -1768,7 +2008,7 @@ mapnotify(struct wl_listener *listener, void *data)
+ 
+ 	for (i = 0; i < 4; i++) {
+ 		c->border[i] = wlr_scene_rect_create(c->scene, 0, 0,
+-				c->isurgent ? urgentcolor : bordercolor);
++			(float[])COLOR(colors[c->isurgent ? SchemeUrg : SchemeNorm][ColBorder]));
+ 		c->border[i]->node.data = c;
+ 	}
+ 
+@@ -1791,7 +2031,7 @@ mapnotify(struct wl_listener *listener, void *data)
+ 	} else {
+ 		applyrules(c);
+ 	}
+-	printstatus();
++	drawbars();
+ 
+ unset_fullscreen:
+ 	m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
+@@ -2084,44 +2324,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
+ 	wlr_seat_pointer_notify_motion(seat, time, sx, sy);
+ }
+ 
+-void
+-printstatus(void)
+-{
+-	Monitor *m = NULL;
+-	Client *c;
+-	uint32_t occ, urg, sel;
+-
+-	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))) {
+-			printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
+-			printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
+-			printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
+-			printf("%s floating %d\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 %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\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
+ powermgrsetmode(struct wl_listener *listener, void *data)
+ {
+@@ -2250,22 +2452,14 @@ run(char *startup_cmd)
+ 
+ 	/* Now that the socket exists and the backend is started, run the startup command */
+ 	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) {
++			close(STDIN_FILENO);
+ 			setsid();
+-			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]);
+ 	}
+ 
+ 	/* Mark stdout as non-blocking to avoid the startup script
+@@ -2275,7 +2469,7 @@ run(char *startup_cmd)
+ 	if (fd_set_nonblock(STDOUT_FILENO) < 0)
+ 		close(STDOUT_FILENO);
+ 
+-	printstatus();
++	drawbars();
+ 
+ 	/* At this point the outputs are initialized, choose initial selmon based on
+ 	 * cursor position, and set default cursor image */
+@@ -2341,7 +2535,7 @@ setfloating(Client *c, int floating)
+ 			(p && p->isfullscreen) ? LyrFS
+ 			: c->isfloating ? LyrFloat : LyrTile]);
+ 	arrange(c->mon);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -2364,7 +2558,7 @@ setfullscreen(Client *c, int fullscreen)
+ 		resize(c, c->prev, 0);
+ 	}
+ 	arrange(c->mon);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -2376,9 +2570,9 @@ setlayout(const Arg *arg)
+ 		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));
++	strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, sizeof(selmon->ltsymbol));
+ 	arrange(selmon);
+-	printstatus();
++	drawbar(selmon);
+ }
+ 
+ /* arg > 1.0 will set mfact absolutely */
+@@ -2451,6 +2645,7 @@ setup(void)
+ 	for (i = 0; i < (int)LENGTH(sig); i++)
+ 		sigaction(sig[i], &sa, NULL);
+ 
++
+ 	wlr_log_init(log_level, NULL);
+ 
+ 	/* The Wayland display is managed by libwayland. It handles accepting
+@@ -2645,6 +2840,11 @@ setup(void)
+ 	wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
+ 	wl_signal_add(&output_mgr->events.test, &output_mgr_test);
+ 
++	drwl_init();
++
++	status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy),
++		STDIN_FILENO, WL_EVENT_READABLE, statusin, NULL);
++
+ 	/* Make sure XWayland clients don't connect to the parent X server,
+ 	 * e.g when running in the x11 backend or the wayland backend and the
+ 	 * compositor has Xwayland support */
+@@ -2669,6 +2869,7 @@ void
+ spawn(const Arg *arg)
+ {
+ 	if (fork() == 0) {
++		close(STDIN_FILENO);
+ 		dup2(STDERR_FILENO, STDOUT_FILENO);
+ 		setsid();
+ 		execvp(((char **)arg->v)[0], (char **)arg->v);
+@@ -2687,6 +2888,30 @@ startdrag(struct wl_listener *listener, void *data)
+ 	LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
+ }
+ 
++int
++statusin(int fd, unsigned int mask, void *data)
++{
++	char status[256];
++	ssize_t n;
++
++	if (mask & WL_EVENT_ERROR)
++		die("status in event error");
++	if (mask & WL_EVENT_HANGUP)
++		wl_event_source_remove(status_event_source);
++
++	n = read(fd, status, sizeof(status) - 1);
++	if (n < 0 && errno != EWOULDBLOCK)
++		die("read:");
++
++	status[n] = '\0';
++	status[strcspn(status, "\n")] = '\0';
++
++	strncpy(stext, status, sizeof(stext));
++	drawbars();
++
++	return 0;
++}
++
+ void
+ tag(const Arg *arg)
+ {
+@@ -2697,7 +2922,7 @@ tag(const Arg *arg)
+ 	sel->tags = arg->ui & TAGMASK;
+ 	focusclient(focustop(selmon), 1);
+ 	arrange(selmon);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -2742,6 +2967,14 @@ tile(Monitor *m)
+ 	}
+ }
+ 
++void
++togglebar(const Arg *arg)
++{
++	wlr_scene_node_set_enabled(&selmon->scene_buffer->node,
++		!selmon->scene_buffer->node.enabled);
++	arrangelayers(selmon);
++}
++
+ void
+ togglefloating(const Arg *arg)
+ {
+@@ -2770,7 +3003,7 @@ toggletag(const Arg *arg)
+ 	sel->tags = newtags;
+ 	focusclient(focustop(selmon), 1);
+ 	arrange(selmon);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -2783,7 +3016,7 @@ toggleview(const Arg *arg)
+ 	selmon->tagset[selmon->seltags] = newtagset;
+ 	focusclient(focustop(selmon), 1);
+ 	arrange(selmon);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -2831,7 +3064,7 @@ unmapnotify(struct wl_listener *listener, void *data)
+ 	}
+ 
+ 	wlr_scene_node_destroy(&c->scene->node);
+-	printstatus();
++	drawbars();
+ 	motionnotify(0, NULL, 0, 0, 0, 0);
+ }
+ 
+@@ -2931,6 +3164,13 @@ updatemons(struct wl_listener *listener, void *data)
+ 		}
+ 	}
+ 
++	if (stext[0] == '\0')
++		strncpy(stext, "dwl-"VERSION, sizeof(stext));
++	wl_list_for_each(m, &mons, link) {
++		updatebar(m);
++		drawbar(m);
++	}
++
+ 	/* 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
+@@ -2941,12 +3181,45 @@ updatemons(struct wl_listener *listener, void *data)
+ 	wlr_output_manager_v1_set_configuration(output_mgr, config);
+ }
+ 
++void
++updatebar(Monitor *m)
++{
++	size_t i;
++	int rw, rh;
++	char fontattrs[12];
++
++	wlr_output_transformed_resolution(m->wlr_output, &rw, &rh);
++	m->b.width = rw;
++	m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale);
++
++	wlr_scene_node_set_enabled(&m->scene_buffer->node, m->wlr_output->enabled ? showbar : 0);
++
++	for (i = 0; i < LENGTH(m->pool); i++)
++		if (m->pool[i]) {
++			wlr_buffer_drop(&m->pool[i]->base);
++			m->pool[i] = NULL;
++		}
++
++	if (m->b.scale == m->wlr_output->scale && m->drw)
++		return;
++
++	drwl_font_destroy(m->drw->font);
++	snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale);
++	if (!(drwl_font_create(m->drw, LENGTH(fonts), fonts, fontattrs)))
++		die("Could not load font");
++
++	m->b.scale = m->wlr_output->scale;
++	m->lrpad = m->drw->font->height;
++	m->b.height = m->drw->font->height + 2;
++	m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale);
++}
++
+ void
+ updatetitle(struct wl_listener *listener, void *data)
+ {
+ 	Client *c = wl_container_of(listener, c, set_title);
+ 	if (c == focustop(c->mon))
+-		printstatus();
++		drawbars();
+ }
+ 
+ void
+@@ -2959,10 +3232,10 @@ urgent(struct wl_listener *listener, void *data)
+ 		return;
+ 
+ 	c->isurgent = 1;
+-	printstatus();
++	drawbars();
+ 
+ 	if (client_surface(c)->mapped)
+-		client_set_border_color(c, urgentcolor);
++		client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
+ }
+ 
+ void
+@@ -2975,7 +3248,7 @@ view(const Arg *arg)
+ 		selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
+ 	focusclient(focustop(selmon), 1);
+ 	arrange(selmon);
+-	printstatus();
++	drawbars();
+ }
+ 
+ void
+@@ -3016,6 +3289,7 @@ xytonode(double x, double y, struct wlr_surface **psurface,
+ {
+ 	struct wlr_scene_node *node, *pnode;
+ 	struct wlr_surface *surface = NULL;
++	struct wlr_scene_surface *scene_surface = NULL;
+ 	Client *c = NULL;
+ 	LayerSurface *l = NULL;
+ 	int layer;
+@@ -3024,9 +3298,12 @@ xytonode(double x, double y, struct wlr_surface **psurface,
+ 		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;
++		if (node->type == WLR_SCENE_NODE_BUFFER) {
++			scene_surface = wlr_scene_surface_try_from_buffer(
++					wlr_scene_buffer_from_node(node));
++			if (!scene_surface) continue;
++			surface = scene_surface->surface;
++		}
+ 		/* Walk the tree to find a node that knows the client */
+ 		for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
+ 			c = pnode->data;
+@@ -3159,10 +3436,10 @@ sethints(struct wl_listener *listener, void *data)
+ 		return;
+ 
+ 	c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
+-	printstatus();
++	drawbars();
+ 
+ 	if (c->isurgent && surface && surface->mapped)
+-		client_set_border_color(c, urgentcolor);
++		client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder]));
+ }
+ 
+ void
+-- 
+2.50.0
+
diff --git a/patches/gaps.patch b/patches/gaps.patch
new file mode 100644
index 0000000..c025baf
--- /dev/null
+++ b/patches/gaps.patch
@@ -0,0 +1,127 @@
+From 50e3dd4746b6cb719efb9f8213b94ac52a5320d9 Mon Sep 17 00:00:00 2001
+From: peesock <kcormn@gmail.com>
+Date: Mon, 24 Jun 2024 20:06:42 -0700
+Subject: [PATCH] gaps!
+
+Co-authored-by: sewn <sewn@disroot.org>
+Co-authored-by: serenevoid <ajuph9224@gmail.com>
+---
+ config.def.h |  4 ++++
+ dwl.c        | 34 ++++++++++++++++++++++++++--------
+ 2 files changed, 30 insertions(+), 8 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 22d2171..b388b4e 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -6,6 +6,9 @@
+ /* 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 int gaps                            = 1;  /* 1 means gaps between windows are added */
++static const unsigned int gappx            = 10; /* gap pixel between windows */
+ static const unsigned int borderpx         = 1;  /* border pixel of windows */
+ static const float rootcolor[]             = COLOR(0x222222ff);
+ static const float bordercolor[]           = COLOR(0x444444ff);
+@@ -135,6 +138,7 @@ static const Key keys[] = {
+ 	{ MODKEY,                    XKB_KEY_l,          setmfact,       {.f = +0.05f} },
+ 	{ MODKEY,                    XKB_KEY_Return,     zoom,           {0} },
+ 	{ MODKEY,                    XKB_KEY_Tab,        view,           {0} },
++	{ MODKEY,                    XKB_KEY_g,          togglegaps,     {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]} },
+diff --git a/dwl.c b/dwl.c
+index dc0437e..dc851df 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -199,6 +199,7 @@ struct Monitor {
+ 	struct wlr_box w; /* window area, layout-relative */
+ 	struct wl_list layers[4]; /* LayerSurface.link */
+ 	const Layout *lt[2];
++	int gaps;
+ 	unsigned int seltags;
+ 	unsigned int sellt;
+ 	uint32_t tagset[2];
+@@ -336,6 +337,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);
+@@ -949,6 +951,8 @@ createmon(struct wl_listener *listener, void *data)
+ 
+ 	wlr_output_state_init(&state);
+ 	/* Initialize monitor state using configured rules */
++	m->gaps = gaps;
++
+ 	m->tagset[0] = m->tagset[1] = 1;
+ 	for (r = monrules; r < END(monrules); r++) {
+ 		if (!r->name || strstr(wlr_output->name, r->name)) {
+@@ -2638,7 +2642,7 @@ tagmon(const Arg *arg)
+ void
+ tile(Monitor *m)
+ {
+-	unsigned int mw, my, ty;
++	unsigned int h, r, e = m->gaps, mw, my, ty;
+ 	int i, n = 0;
+ 	Client *c;
+ 
+@@ -2647,23 +2651,30 @@ tile(Monitor *m)
+ 			n++;
+ 	if (n == 0)
+ 		return;
++	if (smartgaps == n)
++		e = 0;
+ 
+ 	if (n > m->nmaster)
+-		mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
++		mw = m->nmaster ? (int)roundf((m->w.width + gappx*e) * m->mfact) : 0;
+ 	else
+ 		mw = m->w.width;
+-	i = my = ty = 0;
++	i = 0;
++	my = ty = gappx*e;
+ 	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 - gappx*e - gappx*e * (r - 1)) / r;
++			resize(c, (struct wlr_box){.x = m->w.x + gappx*e, .y = m->w.y + my,
++				.width = mw - 2*gappx*e, .height = h}, 0);
++			my += c->geom.height + gappx*e;
+ 		} else {
++			r = n - i;
++			h = (m->w.height - ty - gappx*e - gappx*e * (r - 1)) / r;
+ 			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;
++				.width = m->w.width - mw - gappx*e, .height = h}, 0);
++			ty += c->geom.height + gappx*e;
+ 		}
+ 		i++;
+ 	}
+@@ -2686,6 +2697,13 @@ togglefullscreen(const Arg *arg)
+ 		setfullscreen(sel, !sel->isfullscreen);
+ }
+ 
++void
++togglegaps(const Arg *arg)
++{
++	selmon->gaps = !selmon->gaps;
++	arrange(selmon);
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+-- 
+2.45.2
+
diff --git a/patches/vanitygaps.patch b/patches/vanitygaps.patch
new file mode 100644
index 0000000..db1ddf4
--- /dev/null
+++ b/patches/vanitygaps.patch
@@ -0,0 +1,357 @@
+From 8d29d5cace06c97917fbc26c673fd37731ac4984 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
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Leonardo Hernández Hernández <leohdz172@proton.me>
+---
+ config.def.h |  21 ++++++++
+ dwl.c        | 149 +++++++++++++++++++++++++++++++++++++++++++++++----
+ 2 files changed, 160 insertions(+), 10 deletions(-)
+
+diff --git a/config.def.h b/config.def.h
+index 22d2171d..39e528b1 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -6,7 +6,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 rootcolor[]             = COLOR(0x222222ff);
+ static const float bordercolor[]           = COLOR(0x444444ff);
+ static const float focuscolor[]            = COLOR(0x005577ff);
+@@ -133,6 +138,22 @@ static const Key keys[] = {
+ 	{ MODKEY,                    XKB_KEY_d,          incnmaster,     {.i = -1} },
+ 	{ MODKEY,                    XKB_KEY_h,          setmfact,       {.f = -0.05f} },
+ 	{ MODKEY,                    XKB_KEY_l,          setmfact,       {.f = +0.05f} },
++	{ 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 ad21e1ba..fa823957 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -197,6 +197,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];
+@@ -271,6 +275,7 @@ static void createpopup(struct wl_listener *listener, void *data);
+ static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
+ static void cursorframe(struct wl_listener *listener, void *data);
+ static void cursorwarptohint(void);
++static void defaultgaps(const Arg *arg);
+ static void destroydecoration(struct wl_listener *listener, void *data);
+ static void destroydragicon(struct wl_listener *listener, void *data);
+ static void destroyidleinhibitor(struct wl_listener *listener, void *data);
+@@ -290,6 +295,13 @@ static void fullscreennotify(struct wl_listener *listener, void *data);
+ static void gpureset(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);
+@@ -323,6 +335,7 @@ 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 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);
+@@ -336,6 +349,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);
+@@ -405,6 +419,7 @@ static struct wlr_output_layout *output_layout;
+ 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};
+@@ -1048,6 +1063,11 @@ createmon(struct wl_listener *listener, void *data)
+ 	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;
++
+ 	wlr_output_state_init(&state);
+ 	/* Initialize monitor state using configured rules */
+ 	m->tagset[0] = m->tagset[1] = 1;
+@@ -1230,6 +1250,12 @@ cursorwarptohint(void)
+ 	}
+ }
+ 
++void
++defaultgaps(const Arg *arg)
++{
++	setgaps(gappoh, gappov, gappih, gappiv);
++}
++
+ void
+ destroydecoration(struct wl_listener *listener, void *data)
+ {
+@@ -1566,6 +1592,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)
+ {
+@@ -2357,6 +2460,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)
+ {
+@@ -2701,7 +2814,7 @@ tagmon(const Arg *arg)
+ void
+ tile(Monitor *m)
+ {
+-	unsigned int mw, my, ty;
++	unsigned int mw, my, ty, h, r, oe = enablegaps, ie = enablegaps;
+ 	int i, n = 0;
+ 	Client *c;
+ 
+@@ -2711,22 +2824,31 @@ tile(Monitor *m)
+ 	if (n == 0)
+ 		return;
+ 
++	if (smartgaps == n) {
++		oe = 0; // outer gaps disabled
++	}
++
+ 	if (n > m->nmaster)
+-		mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
++		mw = m->nmaster ? (int)roundf((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++;
+ 	}
+@@ -2749,6 +2871,13 @@ togglefullscreen(const Arg *arg)
+ 		setfullscreen(sel, !sel->isfullscreen);
+ }
+ 
++void
++togglegaps(const Arg *arg)
++{
++	enablegaps = !enablegaps;
++	arrange(selmon);
++}
++
+ void
+ toggletag(const Arg *arg)
+ {
+-- 
+2.48.0
+
+
+From e3d10f01df9b7d6735ce9e43ebfdca35b8639f0e 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
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Leonardo Hernández Hernández <leohdz172@proton.me>
+---
+ 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 39e528b1..f4d4095d 100644
+--- a/config.def.h
++++ b/config.def.h
+@@ -7,6 +7,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 fa823957..59eabb54 100644
+--- a/dwl.c
++++ b/dwl.c
+@@ -1922,8 +1922,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);
+-- 
+2.48.0
+
diff --git a/protocols/wlr-layer-shell-unstable-v1.xml b/protocols/wlr-layer-shell-unstable-v1.xml
new file mode 100644
index 0000000..d62fd51
--- /dev/null
+++ b/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/protocols/wlr-output-power-management-unstable-v1.xml b/protocols/wlr-output-power-management-unstable-v1.xml
new file mode 100644
index 0000000..a977839
--- /dev/null
+++ b/protocols/wlr-output-power-management-unstable-v1.xml
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="wlr_output_power_management_unstable_v1">
+  <copyright>
+    Copyright © 2019 Purism SPC
+
+    Permission is hereby granted, free of charge, to any person obtaining a
+    copy of this software and associated documentation files (the "Software"),
+    to deal in the Software without restriction, including without limitation
+    the rights to use, copy, modify, merge, publish, distribute, sublicense,
+    and/or sell copies of the Software, and to permit persons to whom the
+    Software is furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice (including the next
+    paragraph) shall be included in all copies or substantial portions of the
+    Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+  </copyright>
+
+  <description summary="Control power management modes of outputs">
+    This protocol allows clients to control power management modes
+    of outputs that are currently part of the compositor space. The
+    intent is to allow special clients like desktop shells to power
+    down outputs when the system is idle.
+
+    To modify outputs not currently part of the compositor space see
+    wlr-output-management.
+
+    Warning! The protocol described in this file is experimental and
+    backward incompatible changes may be made. Backward compatible changes
+    may be added together with the corresponding interface version bump.
+    Backward incompatible changes are done by bumping the version number in
+    the protocol and interface names and resetting the interface version.
+    Once the protocol is to be declared stable, the 'z' prefix and the
+    version number in the protocol and interface names are removed and the
+    interface version number is reset.
+  </description>
+
+  <interface name="zwlr_output_power_manager_v1" version="1">
+    <description summary="manager to create per-output power management">
+      This interface is a manager that allows creating per-output power
+      management mode controls.
+    </description>
+
+    <request name="get_output_power">
+      <description summary="get a power management for an output">
+        Create a output power management mode control that can be used to
+        adjust the power management mode for a given output.
+      </description>
+      <arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
+      <arg name="output" type="object" interface="wl_output"/>
+    </request>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the manager">
+        All objects created by the manager will still remain valid, until their
+        appropriate destroy request has been called.
+      </description>
+    </request>
+  </interface>
+
+  <interface name="zwlr_output_power_v1" version="1">
+    <description summary="adjust power management mode for an output">
+      This object offers requests to set the power management mode of
+      an output.
+    </description>
+
+    <enum name="mode">
+      <entry name="off" value="0"
+             summary="Output is turned off."/>
+      <entry name="on" value="1"
+             summary="Output is turned on, no power saving"/>
+    </enum>
+
+    <enum name="error">
+      <entry name="invalid_mode" value="1" summary="inexistent power save mode"/>
+    </enum>
+
+    <request name="set_mode">
+      <description summary="Set an outputs power save mode">
+        Set an output's power save mode to the given mode. The mode change
+        is effective immediately. If the output does not support the given
+        mode a failed event is sent.
+      </description>
+      <arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
+    </request>
+
+    <event name="mode">
+      <description summary="Report a power management mode change">
+        Report the power management mode change of an output.
+
+        The mode event is sent after an output changed its power
+        management mode. The reason can be a client using set_mode or the
+        compositor deciding to change an output's mode.
+        This event is also sent immediately when the object is created
+        so the client is informed about the current power management mode.
+      </description>
+      <arg name="mode" type="uint" enum="mode"
+           summary="the output's new power management mode"/>
+    </event>
+
+    <event name="failed">
+      <description summary="object no longer valid">
+        This event indicates that the output power management mode control
+        is no longer valid. This can happen for a number of reasons,
+        including:
+        - The output doesn't support power management
+        - Another client already has exclusive power management mode control
+          for this output
+        - The output disappeared
+
+        Upon receiving this event, the client should destroy this object.
+      </description>
+    </event>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy this power management">
+        Destroys the output power management mode control object.
+      </description>
+    </request>
+  </interface>
+</protocol>
diff --git a/util.c b/util.c
new file mode 100644
index 0000000..b925987
--- /dev/null
+++ b/util.c
@@ -0,0 +1,51 @@
+/* See LICENSE.dwm file for copyright and license details. */
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.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;
+}
+
+int
+fd_set_nonblock(int fd) {
+	int flags = fcntl(fd, F_GETFL);
+	if (flags < 0) {
+		perror("fcntl(F_GETFL):");
+		return -1;
+	}
+	if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
+		perror("fcntl(F_SETFL):");
+		return -1;
+	}
+
+	return 0;
+}
diff --git a/util.h b/util.h
new file mode 100644
index 0000000..226980d
--- /dev/null
+++ b/util.h
@@ -0,0 +1,5 @@
+/* See LICENSE.dwm file for copyright and license details. */
+
+void die(const char *fmt, ...);
+void *ecalloc(size_t nmemb, size_t size);
+int fd_set_nonblock(int fd);