commit 540c1a5
ohmu
·
2026-10-18 23:48:03 +0300 MSK
parent f33f07a
upload
A
LICENSE
+693,
-0
1@@ -0,0 +1,693 @@
2+Leeloo - 5element for wayland
3+
4+Copyright © 2026 wh1tepearl
5+Copyright © 2026 umix11
6+
7+See also the files LICENSE.dwl, LICENSE.tinywl, LICENSE.dwm and LICENSE.sway.
8+
9+This program is free software: you can redistribute it and/or modify
10+it under the terms of the GNU General Public License as published by
11+the Free Software Foundation, either version 3 of the License, or
12+(at your option) any later version.
13+
14+This program is distributed in the hope that it will be useful,
15+but WITHOUT ANY WARRANTY; without even the implied warranty of
16+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17+GNU General Public License for more details.
18+
19+----
20+
21+ GNU GENERAL PUBLIC LICENSE
22+ Version 3, 29 June 2007
23+
24+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
25+ Everyone is permitted to copy and distribute verbatim copies
26+ of this license document, but changing it is not allowed.
27+
28+ Preamble
29+
30+ The GNU General Public License is a free, copyleft license for
31+software and other kinds of works.
32+
33+ The licenses for most software and other practical works are designed
34+to take away your freedom to share and change the works. By contrast,
35+the GNU General Public License is intended to guarantee your freedom to
36+share and change all versions of a program--to make sure it remains free
37+software for all its users. We, the Free Software Foundation, use the
38+GNU General Public License for most of our software; it applies also to
39+any other work released this way by its authors. You can apply it to
40+your programs, too.
41+
42+ When we speak of free software, we are referring to freedom, not
43+price. Our General Public Licenses are designed to make sure that you
44+have the freedom to distribute copies of free software (and charge for
45+them if you wish), that you receive source code or can get it if you
46+want it, that you can change the software or use pieces of it in new
47+free programs, and that you know you can do these things.
48+
49+ To protect your rights, we need to prevent others from denying you
50+these rights or asking you to surrender the rights. Therefore, you have
51+certain responsibilities if you distribute copies of the software, or if
52+you modify it: responsibilities to respect the freedom of others.
53+
54+ For example, if you distribute copies of such a program, whether
55+gratis or for a fee, you must pass on to the recipients the same
56+freedoms that you received. You must make sure that they, too, receive
57+or can get the source code. And you must show them these terms so they
58+know their rights.
59+
60+ Developers that use the GNU GPL protect your rights with two steps:
61+(1) assert copyright on the software, and (2) offer you this License
62+giving you legal permission to copy, distribute and/or modify it.
63+
64+ For the developers' and authors' protection, the GPL clearly explains
65+that there is no warranty for this free software. For both users' and
66+authors' sake, the GPL requires that modified versions be marked as
67+changed, so that their problems will not be attributed erroneously to
68+authors of previous versions.
69+
70+ Some devices are designed to deny users access to install or run
71+modified versions of the software inside them, although the manufacturer
72+can do so. This is fundamentally incompatible with the aim of
73+protecting users' freedom to change the software. The systematic
74+pattern of such abuse occurs in the area of products for individuals to
75+use, which is precisely where it is most unacceptable. Therefore, we
76+have designed this version of the GPL to prohibit the practice for those
77+products. If such problems arise substantially in other domains, we
78+stand ready to extend this provision to those domains in future versions
79+of the GPL, as needed to protect the freedom of users.
80+
81+ Finally, every program is threatened constantly by software patents.
82+States should not allow patents to restrict development and use of
83+software on general-purpose computers, but in those that do, we wish to
84+avoid the special danger that patents applied to a free program could
85+make it effectively proprietary. To prevent this, the GPL assures that
86+patents cannot be used to render the program non-free.
87+
88+ The precise terms and conditions for copying, distribution and
89+modification follow.
90+
91+ TERMS AND CONDITIONS
92+
93+ 0. Definitions.
94+
95+ "This License" refers to version 3 of the GNU General Public License.
96+
97+ "Copyright" also means copyright-like laws that apply to other kinds of
98+works, such as semiconductor masks.
99+
100+ "The Program" refers to any copyrightable work licensed under this
101+License. Each licensee is addressed as "you". "Licensees" and
102+"recipients" may be individuals or organizations.
103+
104+ To "modify" a work means to copy from or adapt all or part of the work
105+in a fashion requiring copyright permission, other than the making of an
106+exact copy. The resulting work is called a "modified version" of the
107+earlier work or a work "based on" the earlier work.
108+
109+ A "covered work" means either the unmodified Program or a work based
110+on the Program.
111+
112+ To "propagate" a work means to do anything with it that, without
113+permission, would make you directly or secondarily liable for
114+infringement under applicable copyright law, except executing it on a
115+computer or modifying a private copy. Propagation includes copying,
116+distribution (with or without modification), making available to the
117+public, and in some countries other activities as well.
118+
119+ To "convey" a work means any kind of propagation that enables other
120+parties to make or receive copies. Mere interaction with a user through
121+a computer network, with no transfer of a copy, is not conveying.
122+
123+ An interactive user interface displays "Appropriate Legal Notices"
124+to the extent that it includes a convenient and prominently visible
125+feature that (1) displays an appropriate copyright notice, and (2)
126+tells the user that there is no warranty for the work (except to the
127+extent that warranties are provided), that licensees may convey the
128+work under this License, and how to view a copy of this License. If
129+the interface presents a list of user commands or options, such as a
130+menu, a prominent item in the list meets this criterion.
131+
132+ 1. Source Code.
133+
134+ The "source code" for a work means the preferred form of the work
135+for making modifications to it. "Object code" means any non-source
136+form of a work.
137+
138+ A "Standard Interface" means an interface that either is an official
139+standard defined by a recognized standards body, or, in the case of
140+interfaces specified for a particular programming language, one that
141+is widely used among developers working in that language.
142+
143+ The "System Libraries" of an executable work include anything, other
144+than the work as a whole, that (a) is included in the normal form of
145+packaging a Major Component, but which is not part of that Major
146+Component, and (b) serves only to enable use of the work with that
147+Major Component, or to implement a Standard Interface for which an
148+implementation is available to the public in source code form. A
149+"Major Component", in this context, means a major essential component
150+(kernel, window system, and so on) of the specific operating system
151+(if any) on which the executable work runs, or a compiler used to
152+produce the work, or an object code interpreter used to run it.
153+
154+ The "Corresponding Source" for a work in object code form means all
155+the source code needed to generate, install, and (for an executable
156+work) run the object code and to modify the work, including scripts to
157+control those activities. However, it does not include the work's
158+System Libraries, or general-purpose tools or generally available free
159+programs which are used unmodified in performing those activities but
160+which are not part of the work. For example, Corresponding Source
161+includes interface definition files associated with source files for
162+the work, and the source code for shared libraries and dynamically
163+linked subprograms that the work is specifically designed to require,
164+such as by intimate data communication or control flow between those
165+subprograms and other parts of the work.
166+
167+ The Corresponding Source need not include anything that users
168+can regenerate automatically from other parts of the Corresponding
169+Source.
170+
171+ The Corresponding Source for a work in source code form is that
172+same work.
173+
174+ 2. Basic Permissions.
175+
176+ All rights granted under this License are granted for the term of
177+copyright on the Program, and are irrevocable provided the stated
178+conditions are met. This License explicitly affirms your unlimited
179+permission to run the unmodified Program. The output from running a
180+covered work is covered by this License only if the output, given its
181+content, constitutes a covered work. This License acknowledges your
182+rights of fair use or other equivalent, as provided by copyright law.
183+
184+ You may make, run and propagate covered works that you do not
185+convey, without conditions so long as your license otherwise remains
186+in force. You may convey covered works to others for the sole purpose
187+of having them make modifications exclusively for you, or provide you
188+with facilities for running those works, provided that you comply with
189+the terms of this License in conveying all material for which you do
190+not control copyright. Those thus making or running the covered works
191+for you must do so exclusively on your behalf, under your direction
192+and control, on terms that prohibit them from making any copies of
193+your copyrighted material outside their relationship with you.
194+
195+ Conveying under any other circumstances is permitted solely under
196+the conditions stated below. Sublicensing is not allowed; section 10
197+makes it unnecessary.
198+
199+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
200+
201+ No covered work shall be deemed part of an effective technological
202+measure under any applicable law fulfilling obligations under article
203+11 of the WIPO copyright treaty adopted on 20 December 1996, or
204+similar laws prohibiting or restricting circumvention of such
205+measures.
206+
207+ When you convey a covered work, you waive any legal power to forbid
208+circumvention of technological measures to the extent such circumvention
209+is effected by exercising rights under this License with respect to
210+the covered work, and you disclaim any intention to limit operation or
211+modification of the work as a means of enforcing, against the work's
212+users, your or third parties' legal rights to forbid circumvention of
213+technological measures.
214+
215+ 4. Conveying Verbatim Copies.
216+
217+ You may convey verbatim copies of the Program's source code as you
218+receive it, in any medium, provided that you conspicuously and
219+appropriately publish on each copy an appropriate copyright notice;
220+keep intact all notices stating that this License and any
221+non-permissive terms added in accord with section 7 apply to the code;
222+keep intact all notices of the absence of any warranty; and give all
223+recipients a copy of this License along with the Program.
224+
225+ You may charge any price or no price for each copy that you convey,
226+and you may offer support or warranty protection for a fee.
227+
228+ 5. Conveying Modified Source Versions.
229+
230+ You may convey a work based on the Program, or the modifications to
231+produce it from the Program, in the form of source code under the
232+terms of section 4, provided that you also meet all of these conditions:
233+
234+ a) The work must carry prominent notices stating that you modified
235+ it, and giving a relevant date.
236+
237+ b) The work must carry prominent notices stating that it is
238+ released under this License and any conditions added under section
239+ 7. This requirement modifies the requirement in section 4 to
240+ "keep intact all notices".
241+
242+ c) You must license the entire work, as a whole, under this
243+ License to anyone who comes into possession of a copy. This
244+ License will therefore apply, along with any applicable section 7
245+ additional terms, to the whole of the work, and all its parts,
246+ regardless of how they are packaged. This License gives no
247+ permission to license the work in any other way, but it does not
248+ invalidate such permission if you have separately received it.
249+
250+ d) If the work has interactive user interfaces, each must display
251+ Appropriate Legal Notices; however, if the Program has interactive
252+ interfaces that do not display Appropriate Legal Notices, your
253+ work need not make them do so.
254+
255+ A compilation of a covered work with other separate and independent
256+works, which are not by their nature extensions of the covered work,
257+and which are not combined with it such as to form a larger program,
258+in or on a volume of a storage or distribution medium, is called an
259+"aggregate" if the compilation and its resulting copyright are not
260+used to limit the access or legal rights of the compilation's users
261+beyond what the individual works permit. Inclusion of a covered work
262+in an aggregate does not cause this License to apply to the other
263+parts of the aggregate.
264+
265+ 6. Conveying Non-Source Forms.
266+
267+ You may convey a covered work in object code form under the terms
268+of sections 4 and 5, provided that you also convey the
269+machine-readable Corresponding Source under the terms of this License,
270+in one of these ways:
271+
272+ a) Convey the object code in, or embodied in, a physical product
273+ (including a physical distribution medium), accompanied by the
274+ Corresponding Source fixed on a durable physical medium
275+ customarily used for software interchange.
276+
277+ b) Convey the object code in, or embodied in, a physical product
278+ (including a physical distribution medium), accompanied by a
279+ written offer, valid for at least three years and valid for as
280+ long as you offer spare parts or customer support for that product
281+ model, to give anyone who possesses the object code either (1) a
282+ copy of the Corresponding Source for all the software in the
283+ product that is covered by this License, on a durable physical
284+ medium customarily used for software interchange, for a price no
285+ more than your reasonable cost of physically performing this
286+ conveying of source, or (2) access to copy the
287+ Corresponding Source from a network server at no charge.
288+
289+ c) Convey individual copies of the object code with a copy of the
290+ written offer to provide the Corresponding Source. This
291+ alternative is allowed only occasionally and noncommercially, and
292+ only if you received the object code with such an offer, in accord
293+ with subsection 6b.
294+
295+ d) Convey the object code by offering access from a designated
296+ place (gratis or for a charge), and offer equivalent access to the
297+ Corresponding Source in the same way through the same place at no
298+ further charge. You need not require recipients to copy the
299+ Corresponding Source along with the object code. If the place to
300+ copy the object code is a network server, the Corresponding Source
301+ may be on a different server (operated by you or a third party)
302+ that supports equivalent copying facilities, provided you maintain
303+ clear directions next to the object code saying where to find the
304+ Corresponding Source. Regardless of what server hosts the
305+ Corresponding Source, you remain obligated to ensure that it is
306+ available for as long as needed to satisfy these requirements.
307+
308+ e) Convey the object code using peer-to-peer transmission, provided
309+ you inform other peers where the object code and Corresponding
310+ Source of the work are being offered to the general public at no
311+ charge under subsection 6d.
312+
313+ A separable portion of the object code, whose source code is excluded
314+from the Corresponding Source as a System Library, need not be
315+included in conveying the object code work.
316+
317+ A "User Product" is either (1) a "consumer product", which means any
318+tangible personal property which is normally used for personal, family,
319+or household purposes, or (2) anything designed or sold for incorporation
320+into a dwelling. In determining whether a product is a consumer product,
321+doubtful cases shall be resolved in favor of coverage. For a particular
322+product received by a particular user, "normally used" refers to a
323+typical or common use of that class of product, regardless of the status
324+of the particular user or of the way in which the particular user
325+actually uses, or expects or is expected to use, the product. A product
326+is a consumer product regardless of whether the product has substantial
327+commercial, industrial or non-consumer uses, unless such uses represent
328+the only significant mode of use of the product.
329+
330+ "Installation Information" for a User Product means any methods,
331+procedures, authorization keys, or other information required to install
332+and execute modified versions of a covered work in that User Product from
333+a modified version of its Corresponding Source. The information must
334+suffice to ensure that the continued functioning of the modified object
335+code is in no case prevented or interfered with solely because
336+modification has been made.
337+
338+ If you convey an object code work under this section in, or with, or
339+specifically for use in, a User Product, and the conveying occurs as
340+part of a transaction in which the right of possession and use of the
341+User Product is transferred to the recipient in perpetuity or for a
342+fixed term (regardless of how the transaction is characterized), the
343+Corresponding Source conveyed under this section must be accompanied
344+by the Installation Information. But this requirement does not apply
345+if neither you nor any third party retains the ability to install
346+modified object code on the User Product (for example, the work has
347+been installed in ROM).
348+
349+ The requirement to provide Installation Information does not include a
350+requirement to continue to provide support service, warranty, or updates
351+for a work that has been modified or installed by the recipient, or for
352+the User Product in which it has been modified or installed. Access to a
353+network may be denied when the modification itself materially and
354+adversely affects the operation of the network or violates the rules and
355+protocols for communication across the network.
356+
357+ Corresponding Source conveyed, and Installation Information provided,
358+in accord with this section must be in a format that is publicly
359+documented (and with an implementation available to the public in
360+source code form), and must require no special password or key for
361+unpacking, reading or copying.
362+
363+ 7. Additional Terms.
364+
365+ "Additional permissions" are terms that supplement the terms of this
366+License by making exceptions from one or more of its conditions.
367+Additional permissions that are applicable to the entire Program shall
368+be treated as though they were included in this License, to the extent
369+that they are valid under applicable law. If additional permissions
370+apply only to part of the Program, that part may be used separately
371+under those permissions, but the entire Program remains governed by
372+this License without regard to the additional permissions.
373+
374+ When you convey a copy of a covered work, you may at your option
375+remove any additional permissions from that copy, or from any part of
376+it. (Additional permissions may be written to require their own
377+removal in certain cases when you modify the work.) You may place
378+additional permissions on material, added by you to a covered work,
379+for which you have or can give appropriate copyright permission.
380+
381+ Notwithstanding any other provision of this License, for material you
382+add to a covered work, you may (if authorized by the copyright holders of
383+that material) supplement the terms of this License with terms:
384+
385+ a) Disclaiming warranty or limiting liability differently from the
386+ terms of sections 15 and 16 of this License; or
387+
388+ b) Requiring preservation of specified reasonable legal notices or
389+ author attributions in that material or in the Appropriate Legal
390+ Notices displayed by works containing it; or
391+
392+ c) Prohibiting misrepresentation of the origin of that material, or
393+ requiring that modified versions of such material be marked in
394+ reasonable ways as different from the original version; or
395+
396+ d) Limiting the use for publicity purposes of names of licensors or
397+ authors of the material; or
398+
399+ e) Declining to grant rights under trademark law for use of some
400+ trade names, trademarks, or service marks; or
401+
402+ f) Requiring indemnification of licensors and authors of that
403+ material by anyone who conveys the material (or modified versions of
404+ it) with contractual assumptions of liability to the recipient, for
405+ any liability that these contractual assumptions directly impose on
406+ those licensors and authors.
407+
408+ All other non-permissive additional terms are considered "further
409+restrictions" within the meaning of section 10. If the Program as you
410+received it, or any part of it, contains a notice stating that it is
411+governed by this License along with a term that is a further
412+restriction, you may remove that term. If a license document contains
413+a further restriction but permits relicensing or conveying under this
414+License, you may add to a covered work material governed by the terms
415+of that license document, provided that the further restriction does
416+not survive such relicensing or conveying.
417+
418+ If you add terms to a covered work in accord with this section, you
419+must place, in the relevant source files, a statement of the
420+additional terms that apply to those files, or a notice indicating
421+where to find the applicable terms.
422+
423+ Additional terms, permissive or non-permissive, may be stated in the
424+form of a separately written license, or stated as exceptions;
425+the above requirements apply either way.
426+
427+ 8. Termination.
428+
429+ You may not propagate or modify a covered work except as expressly
430+provided under this License. Any attempt otherwise to propagate or
431+modify it is void, and will automatically terminate your rights under
432+this License (including any patent licenses granted under the third
433+paragraph of section 11).
434+
435+ However, if you cease all violation of this License, then your
436+license from a particular copyright holder is reinstated (a)
437+provisionally, unless and until the copyright holder explicitly and
438+finally terminates your license, and (b) permanently, if the copyright
439+holder fails to notify you of the violation by some reasonable means
440+prior to 60 days after the cessation.
441+
442+ Moreover, your license from a particular copyright holder is
443+reinstated permanently if the copyright holder notifies you of the
444+violation by some reasonable means, this is the first time you have
445+received notice of violation of this License (for any work) from that
446+copyright holder, and you cure the violation prior to 30 days after
447+your receipt of the notice.
448+
449+ Termination of your rights under this section does not terminate the
450+licenses of parties who have received copies or rights from you under
451+this License. If your rights have been terminated and not permanently
452+reinstated, you do not qualify to receive new licenses for the same
453+material under section 10.
454+
455+ 9. Acceptance Not Required for Having Copies.
456+
457+ You are not required to accept this License in order to receive or
458+run a copy of the Program. Ancillary propagation of a covered work
459+occurring solely as a consequence of using peer-to-peer transmission
460+to receive a copy likewise does not require acceptance. However,
461+nothing other than this License grants you permission to propagate or
462+modify any covered work. These actions infringe copyright if you do
463+not accept this License. Therefore, by modifying or propagating a
464+covered work, you indicate your acceptance of this License to do so.
465+
466+ 10. Automatic Licensing of Downstream Recipients.
467+
468+ Each time you convey a covered work, the recipient automatically
469+receives a license from the original licensors, to run, modify and
470+propagate that work, subject to this License. You are not responsible
471+for enforcing compliance by third parties with this License.
472+
473+ An "entity transaction" is a transaction transferring control of an
474+organization, or substantially all assets of one, or subdividing an
475+organization, or merging organizations. If propagation of a covered
476+work results from an entity transaction, each party to that
477+transaction who receives a copy of the work also receives whatever
478+licenses to the work the party's predecessor in interest had or could
479+give under the previous paragraph, plus a right to possession of the
480+Corresponding Source of the work from the predecessor in interest, if
481+the predecessor has it or can get it with reasonable efforts.
482+
483+ You may not impose any further restrictions on the exercise of the
484+rights granted or affirmed under this License. For example, you may
485+not impose a license fee, royalty, or other charge for exercise of
486+rights granted under this License, and you may not initiate litigation
487+(including a cross-claim or counterclaim in a lawsuit) alleging that
488+any patent claim is infringed by making, using, selling, offering for
489+sale, or importing the Program or any portion of it.
490+
491+ 11. Patents.
492+
493+ A "contributor" is a copyright holder who authorizes use under this
494+License of the Program or a work on which the Program is based. The
495+work thus licensed is called the contributor's "contributor version".
496+
497+ A contributor's "essential patent claims" are all patent claims
498+owned or controlled by the contributor, whether already acquired or
499+hereafter acquired, that would be infringed by some manner, permitted
500+by this License, of making, using, or selling its contributor version,
501+but do not include claims that would be infringed only as a
502+consequence of further modification of the contributor version. For
503+purposes of this definition, "control" includes the right to grant
504+patent sublicenses in a manner consistent with the requirements of
505+this License.
506+
507+ Each contributor grants you a non-exclusive, worldwide, royalty-free
508+patent license under the contributor's essential patent claims, to
509+make, use, sell, offer for sale, import and otherwise run, modify and
510+propagate the contents of its contributor version.
511+
512+ In the following three paragraphs, a "patent license" is any express
513+agreement or commitment, however denominated, not to enforce a patent
514+(such as an express permission to practice a patent or covenant not to
515+sue for patent infringement). To "grant" such a patent license to a
516+party means to make such an agreement or commitment not to enforce a
517+patent against the party.
518+
519+ If you convey a covered work, knowingly relying on a patent license,
520+and the Corresponding Source of the work is not available for anyone
521+to copy, free of charge and under the terms of this License, through a
522+publicly available network server or other readily accessible means,
523+then you must either (1) cause the Corresponding Source to be so
524+available, or (2) arrange to deprive yourself of the benefit of the
525+patent license for this particular work, or (3) arrange, in a manner
526+consistent with the requirements of this License, to extend the patent
527+license to downstream recipients. "Knowingly relying" means you have
528+actual knowledge that, but for the patent license, your conveying the
529+covered work in a country, or your recipient's use of the covered work
530+in a country, would infringe one or more identifiable patents in that
531+country that you have reason to believe are valid.
532+
533+ If, pursuant to or in connection with a single transaction or
534+arrangement, you convey, or propagate by procuring conveyance of, a
535+covered work, and grant a patent license to some of the parties
536+receiving the covered work authorizing them to use, propagate, modify
537+or convey a specific copy of the covered work, then the patent license
538+you grant is automatically extended to all recipients of the covered
539+work and works based on it.
540+
541+ A patent license is "discriminatory" if it does not include within
542+the scope of its coverage, prohibits the exercise of, or is
543+conditioned on the non-exercise of one or more of the rights that are
544+specifically granted under this License. You may not convey a covered
545+work if you are a party to an arrangement with a third party that is
546+in the business of distributing software, under which you make payment
547+to the third party based on the extent of your activity of conveying
548+the work, and under which the third party grants, to any of the
549+parties who would receive the covered work from you, a discriminatory
550+patent license (a) in connection with copies of the covered work
551+conveyed by you (or copies made from those copies), or (b) primarily
552+for and in connection with specific products or compilations that
553+contain the covered work, unless you entered into that arrangement,
554+or that patent license was granted, prior to 28 March 2007.
555+
556+ Nothing in this License shall be construed as excluding or limiting
557+any implied license or other defenses to infringement that may
558+otherwise be available to you under applicable patent law.
559+
560+ 12. No Surrender of Others' Freedom.
561+
562+ If conditions are imposed on you (whether by court order, agreement or
563+otherwise) that contradict the conditions of this License, they do not
564+excuse you from the conditions of this License. If you cannot convey a
565+covered work so as to satisfy simultaneously your obligations under this
566+License and any other pertinent obligations, then as a consequence you may
567+not convey it at all. For example, if you agree to terms that obligate you
568+to collect a royalty for further conveying from those to whom you convey
569+the Program, the only way you could satisfy both those terms and this
570+License would be to refrain entirely from conveying the Program.
571+
572+ 13. Use with the GNU Affero General Public License.
573+
574+ Notwithstanding any other provision of this License, you have
575+permission to link or combine any covered work with a work licensed
576+under version 3 of the GNU Affero General Public License into a single
577+combined work, and to convey the resulting work. The terms of this
578+License will continue to apply to the part which is the covered work,
579+but the special requirements of the GNU Affero General Public License,
580+section 13, concerning interaction through a network will apply to the
581+combination as such.
582+
583+ 14. Revised Versions of this License.
584+
585+ The Free Software Foundation may publish revised and/or new versions of
586+the GNU General Public License from time to time. Such new versions will
587+be similar in spirit to the present version, but may differ in detail to
588+address new problems or concerns.
589+
590+ Each version is given a distinguishing version number. If the
591+Program specifies that a certain numbered version of the GNU General
592+Public License "or any later version" applies to it, you have the
593+option of following the terms and conditions either of that numbered
594+version or of any later version published by the Free Software
595+Foundation. If the Program does not specify a version number of the
596+GNU General Public License, you may choose any version ever published
597+by the Free Software Foundation.
598+
599+ If the Program specifies that a proxy can decide which future
600+versions of the GNU General Public License can be used, that proxy's
601+public statement of acceptance of a version permanently authorizes you
602+to choose that version for the Program.
603+
604+ Later license versions may give you additional or different
605+permissions. However, no additional obligations are imposed on any
606+author or copyright holder as a result of your choosing to follow a
607+later version.
608+
609+ 15. Disclaimer of Warranty.
610+
611+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
612+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
613+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
614+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
615+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
616+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
617+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
618+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
619+
620+ 16. Limitation of Liability.
621+
622+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
623+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
624+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
625+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
626+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
627+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
628+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
629+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
630+SUCH DAMAGES.
631+
632+ 17. Interpretation of Sections 15 and 16.
633+
634+ If the disclaimer of warranty and limitation of liability provided
635+above cannot be given local legal effect according to their terms,
636+reviewing courts shall apply local law that most closely approximates
637+an absolute waiver of all civil liability in connection with the
638+Program, unless a warranty or assumption of liability accompanies a
639+copy of the Program in return for a fee.
640+
641+ END OF TERMS AND CONDITIONS
642+
643+ How to Apply These Terms to Your New Programs
644+
645+ If you develop a new program, and you want it to be of the greatest
646+possible use to the public, the best way to achieve this is to make it
647+free software which everyone can redistribute and change under these terms.
648+
649+ To do so, attach the following notices to the program. It is safest
650+to attach them to the start of each source file to most effectively
651+state the exclusion of warranty; and each file should have at least
652+the "copyright" line and a pointer to where the full notice is found.
653+
654+ <one line to give the program's name and a brief idea of what it does.>
655+ Copyright (C) <year> <name of author>
656+
657+ This program is free software: you can redistribute it and/or modify
658+ it under the terms of the GNU General Public License as published by
659+ the Free Software Foundation, either version 3 of the License, or
660+ (at your option) any later version.
661+
662+ This program is distributed in the hope that it will be useful,
663+ but WITHOUT ANY WARRANTY; without even the implied warranty of
664+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
665+ GNU General Public License for more details.
666+
667+ You should have received a copy of the GNU General Public License
668+ along with this program. If not, see <https://www.gnu.org/licenses/>.
669+
670+Also add information on how to contact you by electronic and paper mail.
671+
672+ If the program does terminal interaction, make it output a short
673+notice like this when it starts in an interactive mode:
674+
675+ <program> Copyright (C) <year> <name of author>
676+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
677+ This is free software, and you are welcome to redistribute it
678+ under certain conditions; type `show c' for details.
679+
680+The hypothetical commands `show w' and `show c' should show the appropriate
681+parts of the General Public License. Of course, your program's commands
682+might be different; for a GUI interface, you would use an "about box".
683+
684+ You should also get your employer (if you work as a programmer) or school,
685+if any, to sign a "copyright disclaimer" for the program, if necessary.
686+For more information on this, and how to apply and follow the GNU GPL, see
687+<https://www.gnu.org/licenses/>.
688+
689+ The GNU General Public License does not permit incorporating your program
690+into proprietary programs. If your program is a subroutine library, you
691+may consider it more useful to permit linking proprietary applications with
692+the library. If this is what you want to do, use the GNU Lesser General
693+Public License instead of this License. But first, please read
694+<https://www.gnu.org/licenses/why-not-lgpl.html>.
+692,
-0
1@@ -0,0 +1,692 @@
2+dwl - dwm for Wayland
3+
4+Copyright © 2020 dwl team
5+
6+See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway.
7+
8+This program is free software: you can redistribute it and/or modify
9+it under the terms of the GNU General Public License as published by
10+the Free Software Foundation, either version 3 of the License, or
11+(at your option) any later version.
12+
13+This program is distributed in the hope that it will be useful,
14+but WITHOUT ANY WARRANTY; without even the implied warranty of
15+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+GNU General Public License for more details.
17+
18+----
19+
20+ GNU GENERAL PUBLIC LICENSE
21+ Version 3, 29 June 2007
22+
23+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
24+ Everyone is permitted to copy and distribute verbatim copies
25+ of this license document, but changing it is not allowed.
26+
27+ Preamble
28+
29+ The GNU General Public License is a free, copyleft license for
30+software and other kinds of works.
31+
32+ The licenses for most software and other practical works are designed
33+to take away your freedom to share and change the works. By contrast,
34+the GNU General Public License is intended to guarantee your freedom to
35+share and change all versions of a program--to make sure it remains free
36+software for all its users. We, the Free Software Foundation, use the
37+GNU General Public License for most of our software; it applies also to
38+any other work released this way by its authors. You can apply it to
39+your programs, too.
40+
41+ When we speak of free software, we are referring to freedom, not
42+price. Our General Public Licenses are designed to make sure that you
43+have the freedom to distribute copies of free software (and charge for
44+them if you wish), that you receive source code or can get it if you
45+want it, that you can change the software or use pieces of it in new
46+free programs, and that you know you can do these things.
47+
48+ To protect your rights, we need to prevent others from denying you
49+these rights or asking you to surrender the rights. Therefore, you have
50+certain responsibilities if you distribute copies of the software, or if
51+you modify it: responsibilities to respect the freedom of others.
52+
53+ For example, if you distribute copies of such a program, whether
54+gratis or for a fee, you must pass on to the recipients the same
55+freedoms that you received. You must make sure that they, too, receive
56+or can get the source code. And you must show them these terms so they
57+know their rights.
58+
59+ Developers that use the GNU GPL protect your rights with two steps:
60+(1) assert copyright on the software, and (2) offer you this License
61+giving you legal permission to copy, distribute and/or modify it.
62+
63+ For the developers' and authors' protection, the GPL clearly explains
64+that there is no warranty for this free software. For both users' and
65+authors' sake, the GPL requires that modified versions be marked as
66+changed, so that their problems will not be attributed erroneously to
67+authors of previous versions.
68+
69+ Some devices are designed to deny users access to install or run
70+modified versions of the software inside them, although the manufacturer
71+can do so. This is fundamentally incompatible with the aim of
72+protecting users' freedom to change the software. The systematic
73+pattern of such abuse occurs in the area of products for individuals to
74+use, which is precisely where it is most unacceptable. Therefore, we
75+have designed this version of the GPL to prohibit the practice for those
76+products. If such problems arise substantially in other domains, we
77+stand ready to extend this provision to those domains in future versions
78+of the GPL, as needed to protect the freedom of users.
79+
80+ Finally, every program is threatened constantly by software patents.
81+States should not allow patents to restrict development and use of
82+software on general-purpose computers, but in those that do, we wish to
83+avoid the special danger that patents applied to a free program could
84+make it effectively proprietary. To prevent this, the GPL assures that
85+patents cannot be used to render the program non-free.
86+
87+ The precise terms and conditions for copying, distribution and
88+modification follow.
89+
90+ TERMS AND CONDITIONS
91+
92+ 0. Definitions.
93+
94+ "This License" refers to version 3 of the GNU General Public License.
95+
96+ "Copyright" also means copyright-like laws that apply to other kinds of
97+works, such as semiconductor masks.
98+
99+ "The Program" refers to any copyrightable work licensed under this
100+License. Each licensee is addressed as "you". "Licensees" and
101+"recipients" may be individuals or organizations.
102+
103+ To "modify" a work means to copy from or adapt all or part of the work
104+in a fashion requiring copyright permission, other than the making of an
105+exact copy. The resulting work is called a "modified version" of the
106+earlier work or a work "based on" the earlier work.
107+
108+ A "covered work" means either the unmodified Program or a work based
109+on the Program.
110+
111+ To "propagate" a work means to do anything with it that, without
112+permission, would make you directly or secondarily liable for
113+infringement under applicable copyright law, except executing it on a
114+computer or modifying a private copy. Propagation includes copying,
115+distribution (with or without modification), making available to the
116+public, and in some countries other activities as well.
117+
118+ To "convey" a work means any kind of propagation that enables other
119+parties to make or receive copies. Mere interaction with a user through
120+a computer network, with no transfer of a copy, is not conveying.
121+
122+ An interactive user interface displays "Appropriate Legal Notices"
123+to the extent that it includes a convenient and prominently visible
124+feature that (1) displays an appropriate copyright notice, and (2)
125+tells the user that there is no warranty for the work (except to the
126+extent that warranties are provided), that licensees may convey the
127+work under this License, and how to view a copy of this License. If
128+the interface presents a list of user commands or options, such as a
129+menu, a prominent item in the list meets this criterion.
130+
131+ 1. Source Code.
132+
133+ The "source code" for a work means the preferred form of the work
134+for making modifications to it. "Object code" means any non-source
135+form of a work.
136+
137+ A "Standard Interface" means an interface that either is an official
138+standard defined by a recognized standards body, or, in the case of
139+interfaces specified for a particular programming language, one that
140+is widely used among developers working in that language.
141+
142+ The "System Libraries" of an executable work include anything, other
143+than the work as a whole, that (a) is included in the normal form of
144+packaging a Major Component, but which is not part of that Major
145+Component, and (b) serves only to enable use of the work with that
146+Major Component, or to implement a Standard Interface for which an
147+implementation is available to the public in source code form. A
148+"Major Component", in this context, means a major essential component
149+(kernel, window system, and so on) of the specific operating system
150+(if any) on which the executable work runs, or a compiler used to
151+produce the work, or an object code interpreter used to run it.
152+
153+ The "Corresponding Source" for a work in object code form means all
154+the source code needed to generate, install, and (for an executable
155+work) run the object code and to modify the work, including scripts to
156+control those activities. However, it does not include the work's
157+System Libraries, or general-purpose tools or generally available free
158+programs which are used unmodified in performing those activities but
159+which are not part of the work. For example, Corresponding Source
160+includes interface definition files associated with source files for
161+the work, and the source code for shared libraries and dynamically
162+linked subprograms that the work is specifically designed to require,
163+such as by intimate data communication or control flow between those
164+subprograms and other parts of the work.
165+
166+ The Corresponding Source need not include anything that users
167+can regenerate automatically from other parts of the Corresponding
168+Source.
169+
170+ The Corresponding Source for a work in source code form is that
171+same work.
172+
173+ 2. Basic Permissions.
174+
175+ All rights granted under this License are granted for the term of
176+copyright on the Program, and are irrevocable provided the stated
177+conditions are met. This License explicitly affirms your unlimited
178+permission to run the unmodified Program. The output from running a
179+covered work is covered by this License only if the output, given its
180+content, constitutes a covered work. This License acknowledges your
181+rights of fair use or other equivalent, as provided by copyright law.
182+
183+ You may make, run and propagate covered works that you do not
184+convey, without conditions so long as your license otherwise remains
185+in force. You may convey covered works to others for the sole purpose
186+of having them make modifications exclusively for you, or provide you
187+with facilities for running those works, provided that you comply with
188+the terms of this License in conveying all material for which you do
189+not control copyright. Those thus making or running the covered works
190+for you must do so exclusively on your behalf, under your direction
191+and control, on terms that prohibit them from making any copies of
192+your copyrighted material outside their relationship with you.
193+
194+ Conveying under any other circumstances is permitted solely under
195+the conditions stated below. Sublicensing is not allowed; section 10
196+makes it unnecessary.
197+
198+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
199+
200+ No covered work shall be deemed part of an effective technological
201+measure under any applicable law fulfilling obligations under article
202+11 of the WIPO copyright treaty adopted on 20 December 1996, or
203+similar laws prohibiting or restricting circumvention of such
204+measures.
205+
206+ When you convey a covered work, you waive any legal power to forbid
207+circumvention of technological measures to the extent such circumvention
208+is effected by exercising rights under this License with respect to
209+the covered work, and you disclaim any intention to limit operation or
210+modification of the work as a means of enforcing, against the work's
211+users, your or third parties' legal rights to forbid circumvention of
212+technological measures.
213+
214+ 4. Conveying Verbatim Copies.
215+
216+ You may convey verbatim copies of the Program's source code as you
217+receive it, in any medium, provided that you conspicuously and
218+appropriately publish on each copy an appropriate copyright notice;
219+keep intact all notices stating that this License and any
220+non-permissive terms added in accord with section 7 apply to the code;
221+keep intact all notices of the absence of any warranty; and give all
222+recipients a copy of this License along with the Program.
223+
224+ You may charge any price or no price for each copy that you convey,
225+and you may offer support or warranty protection for a fee.
226+
227+ 5. Conveying Modified Source Versions.
228+
229+ You may convey a work based on the Program, or the modifications to
230+produce it from the Program, in the form of source code under the
231+terms of section 4, provided that you also meet all of these conditions:
232+
233+ a) The work must carry prominent notices stating that you modified
234+ it, and giving a relevant date.
235+
236+ b) The work must carry prominent notices stating that it is
237+ released under this License and any conditions added under section
238+ 7. This requirement modifies the requirement in section 4 to
239+ "keep intact all notices".
240+
241+ c) You must license the entire work, as a whole, under this
242+ License to anyone who comes into possession of a copy. This
243+ License will therefore apply, along with any applicable section 7
244+ additional terms, to the whole of the work, and all its parts,
245+ regardless of how they are packaged. This License gives no
246+ permission to license the work in any other way, but it does not
247+ invalidate such permission if you have separately received it.
248+
249+ d) If the work has interactive user interfaces, each must display
250+ Appropriate Legal Notices; however, if the Program has interactive
251+ interfaces that do not display Appropriate Legal Notices, your
252+ work need not make them do so.
253+
254+ A compilation of a covered work with other separate and independent
255+works, which are not by their nature extensions of the covered work,
256+and which are not combined with it such as to form a larger program,
257+in or on a volume of a storage or distribution medium, is called an
258+"aggregate" if the compilation and its resulting copyright are not
259+used to limit the access or legal rights of the compilation's users
260+beyond what the individual works permit. Inclusion of a covered work
261+in an aggregate does not cause this License to apply to the other
262+parts of the aggregate.
263+
264+ 6. Conveying Non-Source Forms.
265+
266+ You may convey a covered work in object code form under the terms
267+of sections 4 and 5, provided that you also convey the
268+machine-readable Corresponding Source under the terms of this License,
269+in one of these ways:
270+
271+ a) Convey the object code in, or embodied in, a physical product
272+ (including a physical distribution medium), accompanied by the
273+ Corresponding Source fixed on a durable physical medium
274+ customarily used for software interchange.
275+
276+ b) Convey the object code in, or embodied in, a physical product
277+ (including a physical distribution medium), accompanied by a
278+ written offer, valid for at least three years and valid for as
279+ long as you offer spare parts or customer support for that product
280+ model, to give anyone who possesses the object code either (1) a
281+ copy of the Corresponding Source for all the software in the
282+ product that is covered by this License, on a durable physical
283+ medium customarily used for software interchange, for a price no
284+ more than your reasonable cost of physically performing this
285+ conveying of source, or (2) access to copy the
286+ Corresponding Source from a network server at no charge.
287+
288+ c) Convey individual copies of the object code with a copy of the
289+ written offer to provide the Corresponding Source. This
290+ alternative is allowed only occasionally and noncommercially, and
291+ only if you received the object code with such an offer, in accord
292+ with subsection 6b.
293+
294+ d) Convey the object code by offering access from a designated
295+ place (gratis or for a charge), and offer equivalent access to the
296+ Corresponding Source in the same way through the same place at no
297+ further charge. You need not require recipients to copy the
298+ Corresponding Source along with the object code. If the place to
299+ copy the object code is a network server, the Corresponding Source
300+ may be on a different server (operated by you or a third party)
301+ that supports equivalent copying facilities, provided you maintain
302+ clear directions next to the object code saying where to find the
303+ Corresponding Source. Regardless of what server hosts the
304+ Corresponding Source, you remain obligated to ensure that it is
305+ available for as long as needed to satisfy these requirements.
306+
307+ e) Convey the object code using peer-to-peer transmission, provided
308+ you inform other peers where the object code and Corresponding
309+ Source of the work are being offered to the general public at no
310+ charge under subsection 6d.
311+
312+ A separable portion of the object code, whose source code is excluded
313+from the Corresponding Source as a System Library, need not be
314+included in conveying the object code work.
315+
316+ A "User Product" is either (1) a "consumer product", which means any
317+tangible personal property which is normally used for personal, family,
318+or household purposes, or (2) anything designed or sold for incorporation
319+into a dwelling. In determining whether a product is a consumer product,
320+doubtful cases shall be resolved in favor of coverage. For a particular
321+product received by a particular user, "normally used" refers to a
322+typical or common use of that class of product, regardless of the status
323+of the particular user or of the way in which the particular user
324+actually uses, or expects or is expected to use, the product. A product
325+is a consumer product regardless of whether the product has substantial
326+commercial, industrial or non-consumer uses, unless such uses represent
327+the only significant mode of use of the product.
328+
329+ "Installation Information" for a User Product means any methods,
330+procedures, authorization keys, or other information required to install
331+and execute modified versions of a covered work in that User Product from
332+a modified version of its Corresponding Source. The information must
333+suffice to ensure that the continued functioning of the modified object
334+code is in no case prevented or interfered with solely because
335+modification has been made.
336+
337+ If you convey an object code work under this section in, or with, or
338+specifically for use in, a User Product, and the conveying occurs as
339+part of a transaction in which the right of possession and use of the
340+User Product is transferred to the recipient in perpetuity or for a
341+fixed term (regardless of how the transaction is characterized), the
342+Corresponding Source conveyed under this section must be accompanied
343+by the Installation Information. But this requirement does not apply
344+if neither you nor any third party retains the ability to install
345+modified object code on the User Product (for example, the work has
346+been installed in ROM).
347+
348+ The requirement to provide Installation Information does not include a
349+requirement to continue to provide support service, warranty, or updates
350+for a work that has been modified or installed by the recipient, or for
351+the User Product in which it has been modified or installed. Access to a
352+network may be denied when the modification itself materially and
353+adversely affects the operation of the network or violates the rules and
354+protocols for communication across the network.
355+
356+ Corresponding Source conveyed, and Installation Information provided,
357+in accord with this section must be in a format that is publicly
358+documented (and with an implementation available to the public in
359+source code form), and must require no special password or key for
360+unpacking, reading or copying.
361+
362+ 7. Additional Terms.
363+
364+ "Additional permissions" are terms that supplement the terms of this
365+License by making exceptions from one or more of its conditions.
366+Additional permissions that are applicable to the entire Program shall
367+be treated as though they were included in this License, to the extent
368+that they are valid under applicable law. If additional permissions
369+apply only to part of the Program, that part may be used separately
370+under those permissions, but the entire Program remains governed by
371+this License without regard to the additional permissions.
372+
373+ When you convey a copy of a covered work, you may at your option
374+remove any additional permissions from that copy, or from any part of
375+it. (Additional permissions may be written to require their own
376+removal in certain cases when you modify the work.) You may place
377+additional permissions on material, added by you to a covered work,
378+for which you have or can give appropriate copyright permission.
379+
380+ Notwithstanding any other provision of this License, for material you
381+add to a covered work, you may (if authorized by the copyright holders of
382+that material) supplement the terms of this License with terms:
383+
384+ a) Disclaiming warranty or limiting liability differently from the
385+ terms of sections 15 and 16 of this License; or
386+
387+ b) Requiring preservation of specified reasonable legal notices or
388+ author attributions in that material or in the Appropriate Legal
389+ Notices displayed by works containing it; or
390+
391+ c) Prohibiting misrepresentation of the origin of that material, or
392+ requiring that modified versions of such material be marked in
393+ reasonable ways as different from the original version; or
394+
395+ d) Limiting the use for publicity purposes of names of licensors or
396+ authors of the material; or
397+
398+ e) Declining to grant rights under trademark law for use of some
399+ trade names, trademarks, or service marks; or
400+
401+ f) Requiring indemnification of licensors and authors of that
402+ material by anyone who conveys the material (or modified versions of
403+ it) with contractual assumptions of liability to the recipient, for
404+ any liability that these contractual assumptions directly impose on
405+ those licensors and authors.
406+
407+ All other non-permissive additional terms are considered "further
408+restrictions" within the meaning of section 10. If the Program as you
409+received it, or any part of it, contains a notice stating that it is
410+governed by this License along with a term that is a further
411+restriction, you may remove that term. If a license document contains
412+a further restriction but permits relicensing or conveying under this
413+License, you may add to a covered work material governed by the terms
414+of that license document, provided that the further restriction does
415+not survive such relicensing or conveying.
416+
417+ If you add terms to a covered work in accord with this section, you
418+must place, in the relevant source files, a statement of the
419+additional terms that apply to those files, or a notice indicating
420+where to find the applicable terms.
421+
422+ Additional terms, permissive or non-permissive, may be stated in the
423+form of a separately written license, or stated as exceptions;
424+the above requirements apply either way.
425+
426+ 8. Termination.
427+
428+ You may not propagate or modify a covered work except as expressly
429+provided under this License. Any attempt otherwise to propagate or
430+modify it is void, and will automatically terminate your rights under
431+this License (including any patent licenses granted under the third
432+paragraph of section 11).
433+
434+ However, if you cease all violation of this License, then your
435+license from a particular copyright holder is reinstated (a)
436+provisionally, unless and until the copyright holder explicitly and
437+finally terminates your license, and (b) permanently, if the copyright
438+holder fails to notify you of the violation by some reasonable means
439+prior to 60 days after the cessation.
440+
441+ Moreover, your license from a particular copyright holder is
442+reinstated permanently if the copyright holder notifies you of the
443+violation by some reasonable means, this is the first time you have
444+received notice of violation of this License (for any work) from that
445+copyright holder, and you cure the violation prior to 30 days after
446+your receipt of the notice.
447+
448+ Termination of your rights under this section does not terminate the
449+licenses of parties who have received copies or rights from you under
450+this License. If your rights have been terminated and not permanently
451+reinstated, you do not qualify to receive new licenses for the same
452+material under section 10.
453+
454+ 9. Acceptance Not Required for Having Copies.
455+
456+ You are not required to accept this License in order to receive or
457+run a copy of the Program. Ancillary propagation of a covered work
458+occurring solely as a consequence of using peer-to-peer transmission
459+to receive a copy likewise does not require acceptance. However,
460+nothing other than this License grants you permission to propagate or
461+modify any covered work. These actions infringe copyright if you do
462+not accept this License. Therefore, by modifying or propagating a
463+covered work, you indicate your acceptance of this License to do so.
464+
465+ 10. Automatic Licensing of Downstream Recipients.
466+
467+ Each time you convey a covered work, the recipient automatically
468+receives a license from the original licensors, to run, modify and
469+propagate that work, subject to this License. You are not responsible
470+for enforcing compliance by third parties with this License.
471+
472+ An "entity transaction" is a transaction transferring control of an
473+organization, or substantially all assets of one, or subdividing an
474+organization, or merging organizations. If propagation of a covered
475+work results from an entity transaction, each party to that
476+transaction who receives a copy of the work also receives whatever
477+licenses to the work the party's predecessor in interest had or could
478+give under the previous paragraph, plus a right to possession of the
479+Corresponding Source of the work from the predecessor in interest, if
480+the predecessor has it or can get it with reasonable efforts.
481+
482+ You may not impose any further restrictions on the exercise of the
483+rights granted or affirmed under this License. For example, you may
484+not impose a license fee, royalty, or other charge for exercise of
485+rights granted under this License, and you may not initiate litigation
486+(including a cross-claim or counterclaim in a lawsuit) alleging that
487+any patent claim is infringed by making, using, selling, offering for
488+sale, or importing the Program or any portion of it.
489+
490+ 11. Patents.
491+
492+ A "contributor" is a copyright holder who authorizes use under this
493+License of the Program or a work on which the Program is based. The
494+work thus licensed is called the contributor's "contributor version".
495+
496+ A contributor's "essential patent claims" are all patent claims
497+owned or controlled by the contributor, whether already acquired or
498+hereafter acquired, that would be infringed by some manner, permitted
499+by this License, of making, using, or selling its contributor version,
500+but do not include claims that would be infringed only as a
501+consequence of further modification of the contributor version. For
502+purposes of this definition, "control" includes the right to grant
503+patent sublicenses in a manner consistent with the requirements of
504+this License.
505+
506+ Each contributor grants you a non-exclusive, worldwide, royalty-free
507+patent license under the contributor's essential patent claims, to
508+make, use, sell, offer for sale, import and otherwise run, modify and
509+propagate the contents of its contributor version.
510+
511+ In the following three paragraphs, a "patent license" is any express
512+agreement or commitment, however denominated, not to enforce a patent
513+(such as an express permission to practice a patent or covenant not to
514+sue for patent infringement). To "grant" such a patent license to a
515+party means to make such an agreement or commitment not to enforce a
516+patent against the party.
517+
518+ If you convey a covered work, knowingly relying on a patent license,
519+and the Corresponding Source of the work is not available for anyone
520+to copy, free of charge and under the terms of this License, through a
521+publicly available network server or other readily accessible means,
522+then you must either (1) cause the Corresponding Source to be so
523+available, or (2) arrange to deprive yourself of the benefit of the
524+patent license for this particular work, or (3) arrange, in a manner
525+consistent with the requirements of this License, to extend the patent
526+license to downstream recipients. "Knowingly relying" means you have
527+actual knowledge that, but for the patent license, your conveying the
528+covered work in a country, or your recipient's use of the covered work
529+in a country, would infringe one or more identifiable patents in that
530+country that you have reason to believe are valid.
531+
532+ If, pursuant to or in connection with a single transaction or
533+arrangement, you convey, or propagate by procuring conveyance of, a
534+covered work, and grant a patent license to some of the parties
535+receiving the covered work authorizing them to use, propagate, modify
536+or convey a specific copy of the covered work, then the patent license
537+you grant is automatically extended to all recipients of the covered
538+work and works based on it.
539+
540+ A patent license is "discriminatory" if it does not include within
541+the scope of its coverage, prohibits the exercise of, or is
542+conditioned on the non-exercise of one or more of the rights that are
543+specifically granted under this License. You may not convey a covered
544+work if you are a party to an arrangement with a third party that is
545+in the business of distributing software, under which you make payment
546+to the third party based on the extent of your activity of conveying
547+the work, and under which the third party grants, to any of the
548+parties who would receive the covered work from you, a discriminatory
549+patent license (a) in connection with copies of the covered work
550+conveyed by you (or copies made from those copies), or (b) primarily
551+for and in connection with specific products or compilations that
552+contain the covered work, unless you entered into that arrangement,
553+or that patent license was granted, prior to 28 March 2007.
554+
555+ Nothing in this License shall be construed as excluding or limiting
556+any implied license or other defenses to infringement that may
557+otherwise be available to you under applicable patent law.
558+
559+ 12. No Surrender of Others' Freedom.
560+
561+ If conditions are imposed on you (whether by court order, agreement or
562+otherwise) that contradict the conditions of this License, they do not
563+excuse you from the conditions of this License. If you cannot convey a
564+covered work so as to satisfy simultaneously your obligations under this
565+License and any other pertinent obligations, then as a consequence you may
566+not convey it at all. For example, if you agree to terms that obligate you
567+to collect a royalty for further conveying from those to whom you convey
568+the Program, the only way you could satisfy both those terms and this
569+License would be to refrain entirely from conveying the Program.
570+
571+ 13. Use with the GNU Affero General Public License.
572+
573+ Notwithstanding any other provision of this License, you have
574+permission to link or combine any covered work with a work licensed
575+under version 3 of the GNU Affero General Public License into a single
576+combined work, and to convey the resulting work. The terms of this
577+License will continue to apply to the part which is the covered work,
578+but the special requirements of the GNU Affero General Public License,
579+section 13, concerning interaction through a network will apply to the
580+combination as such.
581+
582+ 14. Revised Versions of this License.
583+
584+ The Free Software Foundation may publish revised and/or new versions of
585+the GNU General Public License from time to time. Such new versions will
586+be similar in spirit to the present version, but may differ in detail to
587+address new problems or concerns.
588+
589+ Each version is given a distinguishing version number. If the
590+Program specifies that a certain numbered version of the GNU General
591+Public License "or any later version" applies to it, you have the
592+option of following the terms and conditions either of that numbered
593+version or of any later version published by the Free Software
594+Foundation. If the Program does not specify a version number of the
595+GNU General Public License, you may choose any version ever published
596+by the Free Software Foundation.
597+
598+ If the Program specifies that a proxy can decide which future
599+versions of the GNU General Public License can be used, that proxy's
600+public statement of acceptance of a version permanently authorizes you
601+to choose that version for the Program.
602+
603+ Later license versions may give you additional or different
604+permissions. However, no additional obligations are imposed on any
605+author or copyright holder as a result of your choosing to follow a
606+later version.
607+
608+ 15. Disclaimer of Warranty.
609+
610+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
611+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
612+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
613+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
614+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
615+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
616+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
617+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
618+
619+ 16. Limitation of Liability.
620+
621+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
622+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
623+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
624+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
625+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
626+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
627+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
628+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
629+SUCH DAMAGES.
630+
631+ 17. Interpretation of Sections 15 and 16.
632+
633+ If the disclaimer of warranty and limitation of liability provided
634+above cannot be given local legal effect according to their terms,
635+reviewing courts shall apply local law that most closely approximates
636+an absolute waiver of all civil liability in connection with the
637+Program, unless a warranty or assumption of liability accompanies a
638+copy of the Program in return for a fee.
639+
640+ END OF TERMS AND CONDITIONS
641+
642+ How to Apply These Terms to Your New Programs
643+
644+ If you develop a new program, and you want it to be of the greatest
645+possible use to the public, the best way to achieve this is to make it
646+free software which everyone can redistribute and change under these terms.
647+
648+ To do so, attach the following notices to the program. It is safest
649+to attach them to the start of each source file to most effectively
650+state the exclusion of warranty; and each file should have at least
651+the "copyright" line and a pointer to where the full notice is found.
652+
653+ <one line to give the program's name and a brief idea of what it does.>
654+ Copyright (C) <year> <name of author>
655+
656+ This program is free software: you can redistribute it and/or modify
657+ it under the terms of the GNU General Public License as published by
658+ the Free Software Foundation, either version 3 of the License, or
659+ (at your option) any later version.
660+
661+ This program is distributed in the hope that it will be useful,
662+ but WITHOUT ANY WARRANTY; without even the implied warranty of
663+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
664+ GNU General Public License for more details.
665+
666+ You should have received a copy of the GNU General Public License
667+ along with this program. If not, see <https://www.gnu.org/licenses/>.
668+
669+Also add information on how to contact you by electronic and paper mail.
670+
671+ If the program does terminal interaction, make it output a short
672+notice like this when it starts in an interactive mode:
673+
674+ <program> Copyright (C) <year> <name of author>
675+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
676+ This is free software, and you are welcome to redistribute it
677+ under certain conditions; type `show c' for details.
678+
679+The hypothetical commands `show w' and `show c' should show the appropriate
680+parts of the General Public License. Of course, your program's commands
681+might be different; for a GUI interface, you would use an "about box".
682+
683+ You should also get your employer (if you work as a programmer) or school,
684+if any, to sign a "copyright disclaimer" for the program, if necessary.
685+For more information on this, and how to apply and follow the GNU GPL, see
686+<https://www.gnu.org/licenses/>.
687+
688+ The GNU General Public License does not permit incorporating your program
689+into proprietary programs. If your program is a subroutine library, you
690+may consider it more useful to permit linking proprietary applications with
691+the library. If this is what you want to do, use the GNU Lesser General
692+Public License instead of this License. But first, please read
693+<https://www.gnu.org/licenses/why-not-lgpl.html>.
+39,
-0
1@@ -0,0 +1,39 @@
2+Portions of dwl based on dwm code are used under the following license:
3+
4+MIT/X Consortium License
5+
6+© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
7+© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
8+© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
9+© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
10+© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
11+© 2007-2009 Christof Musik <christof at sendfax dot de>
12+© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
13+© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
14+© 2008 Martin Hurton <martin dot hurton at gmail dot com>
15+© 2008 Neale Pickett <neale dot woozle dot org>
16+© 2009 Mate Nagy <mnagy at port70 dot net>
17+© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
18+© 2010-2012 Connor Lane Smith <cls@lubutu.com>
19+© 2011 Christoph Lohmann <20h@r-36.net>
20+© 2015-2016 Quentin Rameau <quinq@fifth.space>
21+© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
22+© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
23+
24+Permission is hereby granted, free of charge, to any person obtaining a
25+copy of this software and associated documentation files (the "Software"),
26+to deal in the Software without restriction, including without limitation
27+the rights to use, copy, modify, merge, publish, distribute, sublicense,
28+and/or sell copies of the Software, and to permit persons to whom the
29+Software is furnished to do so, subject to the following conditions:
30+
31+The above copyright notice and this permission notice shall be included in
32+all copies or substantial portions of the Software.
33+
34+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
39+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
40+DEALINGS IN THE SOFTWARE.
+19,
-0
1@@ -0,0 +1,19 @@
2+Copyright (c) 2016-2017 Drew DeVault
3+
4+Permission is hereby granted, free of charge, to any person obtaining a copy of
5+this software and associated documentation files (the "Software"), to deal in
6+the Software without restriction, including without limitation the rights to
7+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
8+of the Software, and to permit persons to whom the Software is furnished to do
9+so, subject to the following conditions:
10+
11+The above copyright notice and this permission notice shall be included in all
12+copies or substantial portions of the Software.
13+
14+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20+SOFTWARE.
+127,
-0
1@@ -0,0 +1,127 @@
2+dwl is originally based on TinyWL, which is used under the following license:
3+
4+This work is licensed under CC0, which effectively puts it in the public domain.
5+
6+---
7+
8+Creative Commons Legal Code
9+
10+CC0 1.0 Universal
11+
12+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
13+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
14+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
15+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
16+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
17+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
18+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
19+ HEREUNDER.
20+
21+Statement of Purpose
22+
23+The laws of most jurisdictions throughout the world automatically confer
24+exclusive Copyright and Related Rights (defined below) upon the creator
25+and subsequent owner(s) (each and all, an "owner") of an original work of
26+authorship and/or a database (each, a "Work").
27+
28+Certain owners wish to permanently relinquish those rights to a Work for
29+the purpose of contributing to a commons of creative, cultural and
30+scientific works ("Commons") that the public can reliably and without fear
31+of later claims of infringement build upon, modify, incorporate in other
32+works, reuse and redistribute as freely as possible in any form whatsoever
33+and for any purposes, including without limitation commercial purposes.
34+These owners may contribute to the Commons to promote the ideal of a free
35+culture and the further production of creative, cultural and scientific
36+works, or to gain reputation or greater distribution for their Work in
37+part through the use and efforts of others.
38+
39+For these and/or other purposes and motivations, and without any
40+expectation of additional consideration or compensation, the person
41+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
42+is an owner of Copyright and Related Rights in the Work, voluntarily
43+elects to apply CC0 to the Work and publicly distribute the Work under its
44+terms, with knowledge of his or her Copyright and Related Rights in the
45+Work and the meaning and intended legal effect of CC0 on those rights.
46+
47+1. Copyright and Related Rights. A Work made available under CC0 may be
48+protected by copyright and related or neighboring rights ("Copyright and
49+Related Rights"). Copyright and Related Rights include, but are not
50+limited to, the following:
51+
52+ i. the right to reproduce, adapt, distribute, perform, display,
53+ communicate, and translate a Work;
54+ ii. moral rights retained by the original author(s) and/or performer(s);
55+iii. publicity and privacy rights pertaining to a person's image or
56+ likeness depicted in a Work;
57+ iv. rights protecting against unfair competition in regards to a Work,
58+ subject to the limitations in paragraph 4(a), below;
59+ v. rights protecting the extraction, dissemination, use and reuse of data
60+ in a Work;
61+ vi. database rights (such as those arising under Directive 96/9/EC of the
62+ European Parliament and of the Council of 11 March 1996 on the legal
63+ protection of databases, and under any national implementation
64+ thereof, including any amended or successor version of such
65+ directive); and
66+vii. other similar, equivalent or corresponding rights throughout the
67+ world based on applicable law or treaty, and any national
68+ implementations thereof.
69+
70+2. Waiver. To the greatest extent permitted by, but not in contravention
71+of, applicable law, Affirmer hereby overtly, fully, permanently,
72+irrevocably and unconditionally waives, abandons, and surrenders all of
73+Affirmer's Copyright and Related Rights and associated claims and causes
74+of action, whether now known or unknown (including existing as well as
75+future claims and causes of action), in the Work (i) in all territories
76+worldwide, (ii) for the maximum duration provided by applicable law or
77+treaty (including future time extensions), (iii) in any current or future
78+medium and for any number of copies, and (iv) for any purpose whatsoever,
79+including without limitation commercial, advertising or promotional
80+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
81+member of the public at large and to the detriment of Affirmer's heirs and
82+successors, fully intending that such Waiver shall not be subject to
83+revocation, rescission, cancellation, termination, or any other legal or
84+equitable action to disrupt the quiet enjoyment of the Work by the public
85+as contemplated by Affirmer's express Statement of Purpose.
86+
87+3. Public License Fallback. Should any part of the Waiver for any reason
88+be judged legally invalid or ineffective under applicable law, then the
89+Waiver shall be preserved to the maximum extent permitted taking into
90+account Affirmer's express Statement of Purpose. In addition, to the
91+extent the Waiver is so judged Affirmer hereby grants to each affected
92+person a royalty-free, non transferable, non sublicensable, non exclusive,
93+irrevocable and unconditional license to exercise Affirmer's Copyright and
94+Related Rights in the Work (i) in all territories worldwide, (ii) for the
95+maximum duration provided by applicable law or treaty (including future
96+time extensions), (iii) in any current or future medium and for any number
97+of copies, and (iv) for any purpose whatsoever, including without
98+limitation commercial, advertising or promotional purposes (the
99+"License"). The License shall be deemed effective as of the date CC0 was
100+applied by Affirmer to the Work. Should any part of the License for any
101+reason be judged legally invalid or ineffective under applicable law, such
102+partial invalidity or ineffectiveness shall not invalidate the remainder
103+of the License, and in such case Affirmer hereby affirms that he or she
104+will not (i) exercise any of his or her remaining Copyright and Related
105+Rights in the Work or (ii) assert any associated claims and causes of
106+action with respect to the Work, in either case contrary to Affirmer's
107+express Statement of Purpose.
108+
109+4. Limitations and Disclaimers.
110+
111+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
112+ surrendered, licensed or otherwise affected by this document.
113+ b. Affirmer offers the Work as-is and makes no representations or
114+ warranties of any kind concerning the Work, express, implied,
115+ statutory or otherwise, including without limitation warranties of
116+ title, merchantability, fitness for a particular purpose, non
117+ infringement, or the absence of latent or other defects, accuracy, or
118+ the present or absence of errors, whether or not discoverable, all to
119+ the greatest extent permissible under applicable law.
120+ c. Affirmer disclaims responsibility for clearing rights of other persons
121+ that may apply to the Work or any use thereof, including without
122+ limitation any person's Copyright and Related Rights in the Work.
123+ Further, Affirmer disclaims responsibility for obtaining any necessary
124+ consents, permissions or other rights required for any use of the
125+ Work.
126+ d. Affirmer understands and acknowledges that Creative Commons is not a
127+ party to this document and has no duty or obligation with respect to
128+ this CC0 or use of the Work.
A
Makefile
+89,
-0
1@@ -0,0 +1,89 @@
2+.POSIX:
3+.SUFFIXES:
4+
5+include config.mk
6+include modules.mk
7+
8+VPATH = src
9+
10+# flags for compiling
11+LEELOOCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \
12+ -DVERSION=\"$(VERSION)\" $(XWAYLAND)
13+LEELOODEVCFLAGS = -g -Wpedantic -Wall -Wextra -Wdeclaration-after-statement \
14+ -Wno-unused-parameter -Wshadow -Wunused-macros -Werror=strict-prototypes \
15+ -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types \
16+ -Wfloat-conversion
17+
18+# CFLAGS / LDFLAGS
19+PKGS = wayland-server xkbcommon libinput $(XLIBS)
20+LEELOOCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(LEELOOMODULES) $(LEELOOCPPFLAGS) $(LEELOODEVCFLAGS) $(CFLAGS)
21+LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
22+
23+all: leeloo
24+leeloo: leeloo.o util.o
25+ $(CC) leeloo.o util.o $(LEELOOCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
26+leeloo.o: leeloo.c client.h config.h config.mk cursor-shape-v1-protocol.h \
27+ pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
28+ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \
29+ tablet-v2-protocol.h
30+util.o: util.c util.h
31+
32+# wayland-scanner is a tool which generates C headers and rigging for Wayland
33+# protocols, which are specified in XML. wlroots requires you to rig these up
34+# to your build system yourself and provide them in the include path.
35+WAYLAND_SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner`
36+WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols`
37+
38+cursor-shape-v1-protocol.h:
39+ $(WAYLAND_SCANNER) enum-header \
40+ $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@
41+pointer-constraints-unstable-v1-protocol.h:
42+ $(WAYLAND_SCANNER) enum-header \
43+ $(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
44+wlr-layer-shell-unstable-v1-protocol.h:
45+ $(WAYLAND_SCANNER) enum-header \
46+ protocols/wlr-layer-shell-unstable-v1.xml $@
47+wlr-output-power-management-unstable-v1-protocol.h:
48+ $(WAYLAND_SCANNER) server-header \
49+ protocols/wlr-output-power-management-unstable-v1.xml $@
50+xdg-shell-protocol.h:
51+ $(WAYLAND_SCANNER) server-header \
52+ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@
53+# tablet-input
54+tablet-v2-protocol.h:
55+ $(WAYLAND_SCANNER) server-header \
56+ $(WAYLAND_PROTOCOLS)/unstable/tablet/tablet-unstable-v2.xml $@
57+# tablet-input end
58+config.h:
59+ cp config.def.h $@
60+clean:
61+ rm -f leeloo *.o *-protocol.h leeloo-*.tar.gz
62+
63+dist: clean
64+ mkdir -p leeloo-$(VERSION)
65+ mkdir -p leeloo-$(VERSION)/src
66+ cp -R LICENSE* Makefile config.def.h \
67+ config.mk modules.mk protocols leeloo.desktop \
68+ leeloo-$(VERSION)
69+ cp -R src/leeloo.c src/client.h src/util.c src/util.h leeloo-$(VERSION)/src/
70+ tar -caf leeloo-$(VERSION).tar.gz leeloo-$(VERSION)
71+
72+ rm -rf leeloo-$(VERSION)
73+
74+install: leeloo
75+
76+ mkdir -p $(DESTDIR)$(PREFIX)/bin
77+ rm -f $(DESTDIR)$(PREFIX)/bin/leeloo
78+ cp -f leeloo $(DESTDIR)$(PREFIX)/bin
79+ chmod 755 $(DESTDIR)$(PREFIX)/bin/leeloo
80+ mkdir -p $(DESTDIR)$(MANDIR)/man1
81+ mkdir -p $(DESTDIR)$(DATADIR)/wayland-sessions
82+ cp -f leeloo.desktop $(DESTDIR)$(DATADIR)/wayland-sessions/leeloo.desktop
83+ chmod 644 $(DESTDIR)$(DATADIR)/wayland-sessions/leeloo.desktop
84+uninstall:
85+ rm -f $(DESTDIR)$(PREFIX)/bin/leeloo \
86+ $(DESTDIR)$(DATADIR)/wayland-sessions/leeloo.desktop
87+
88+.SUFFIXES: .c .o
89+.c.o:
90+ $(CC) $(CPPFLAGS) $(LEELOOCFLAGS) -o $@ -c $<
+6,
-0
1@@ -1,3 +1,9 @@
2 # leeloo
3 leeloo by wh1tepearl and ohmu(me)
4
5+thanks tsukasa for archive
6+
7+THIS IS REUPLOAD BY OFFICIAL MAINTAINER
8+
9+For everything see manual
10+
+284,
-0
1@@ -0,0 +1,284 @@
2+/* Taken from https://github.com/djpohly/dwl/issues/466 */
3+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \
4+ ((hex >> 16) & 0xFF) / 255.0f, \
5+ ((hex >> 8) & 0xFF) / 255.0f, \
6+ (hex & 0xFF) / 255.0f }
7+/* appearance */
8+static const int tabletmaptosurface = 1; /* map tablet input to surface(1) or monitor(0) */
9+static const int sloppyfocus = 1; /* focus follows mouse */
10+static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
11+
12+#define ZOOMMIN 0.05
13+#define ZOOMMAX 3
14+#define animatezoom 1
15+#define zoomduration 0.33
16+#define zoombezier "0.22,1.0,0.36,1.0"
17+#define movecanvasmouse_multiplier_is_affected_by_zoom 1
18+#define ZOOM_IN_TOWARDS_CURSOR 1
19+#define ZOOM_OUT_TOWARDS_CURSOR 0
20+
21+#define SPAWNREGION_STYLE 2 /* 0 = None
22+ 1 = Full rectangle.
23+ 2 = outline
24+ */
25+
26+/* gaps */
27+static const int enablegaps = 1; /* 1 means gaps are enabled */
28+static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */
29+static const int monoclegaps = 0; /* 1 means outer gaps in monocle layout */
30+static const unsigned int gappih = 10; /* horizontal inner gap between windows */
31+static const unsigned int gappiv = 10; /* vertical inner gap between windows */
32+static const unsigned int gappoh = 10; /* horizontal outer gap between windows and screen edge */
33+static const unsigned int gappov = 10; /* vertical outer gap between windows and screen edge */
34+
35+/* colors */
36+static const float rootcolor[] = COLOR(0x22222267); /* Background color, only visible without any wallpaper setter */
37+
38+/* inner border */
39+static const unsigned int innerborderpx = 4; /* inner border pixel width */
40+
41+static const float focused_inner_border_color[] = COLOR(0xFDD9C6ff);
42+static const float urgent_inner_border_color[] = COLOR(0xFDD9C6ff);
43+static const float unfocused_inner_border_color[] = COLOR(0x7B565600);
44+
45+/* outer border */
46+static const unsigned int outerborderpx = 6; /* outer border pixel width */
47+
48+static const float focused_outer_border_color[] = COLOR(0x0a0100ff);
49+static const float urgent_outer_border_color[] = COLOR(0x0a0100ff);
50+static const float unfocused_outer_border_color[] = COLOR(0x1e1c1c00);
51+
52+#define SPAWNREGION_COLOR COLOR(0xFDD9C667)
53+
54+static const unsigned int spawnregion_innerborderpx = 3; /* spawnregion inner border pixel width */
55+static const unsigned int spawnregion_outerborderpx = 7; /* spawnregion outer border pixel width */
56+static const float spawnregion_inner_border_color[] = COLOR(0xC9B8B6ff);
57+static const float spawnregion_outer_border_color[] = COLOR(0x430a03ff);
58+
59+/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
60+static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
61+/* Pen active area: value 0.0 => 1.0 { x, y, width, height } */
62+static const double tablet_area[4] = { 0.4, 0.4, 0.4, 0.4 };
63+
64+/* tagging - TAGCOUNT must be no greater than 31 */
65+#define TAGCOUNT (9)
66+
67+/* logging */
68+static int log_level = WLR_ERROR;
69+
70+
71+
72+static const Rule rules[] = {
73+ /* app_id title tags mask isfloating monitor */
74+ { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
75+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
76+ /* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */
77+};
78+
79+#define CENTER_NEW_WINDOWS 1
80+
81+/* layout(s) */
82+static const Layout layouts[] = {
83+ /* symbol arrange function */
84+ { "><>", NULL }, /* no layout function means floating behavior */
85+ { "[]=", tile },
86+ { "[M]", monocle },
87+};
88+
89+/* monitors */
90+/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
91+ * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients due to
92+ * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 */
93+static const MonitorRule monrules[] = {
94+ /* name mfact nmaster scale layout rotate/reflect x y resx resy rate mode adaptive */
95+ /*
96+ * mode lets the user decide how dwl should implement the modes:
97+ * -1 sets a custom mode following the user's choice
98+ * All other numbers set the mode at the index n; 0 is the standard mode; see wlr-randr
99+ */
100+ { "HDMI-A-1", 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1, 0, 0, 180.003006f, 1, 0 },
101+ /* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */
102+};
103+
104+/* keyboard */
105+static const struct xkb_rule_names xkb_rules = {
106+ /* can specify fields: rules, model, layout, variant, options */
107+ /* example:
108+ .options = "ctrl:nocaps",
109+ */
110+ .options = NULL,
111+};
112+
113+static const int repeat_rate = 40;
114+static const int repeat_delay = 250;
115+
116+/* Trackpad */
117+static const int tap_to_click = 1;
118+static const int tap_and_drag = 1;
119+static const int drag_lock = 1;
120+static const int natural_scrolling = 0;
121+static const int disable_while_typing = 1;
122+static const int left_handed = 0;
123+static const int middle_button_emulation = 0;
124+/* You can choose between:
125+LIBINPUT_CONFIG_SCROLL_NO_SCROLL
126+LIBINPUT_CONFIG_SCROLL_2FG
127+LIBINPUT_CONFIG_SCROLL_EDGE
128+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
129+*/
130+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
131+
132+/* You can choose between:
133+LIBINPUT_CONFIG_CLICK_METHOD_NONE
134+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
135+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
136+*/
137+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
138+
139+/* You can choose between:
140+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
141+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
142+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
143+*/
144+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
145+
146+/* You can choose between:
147+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
148+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
149+*/
150+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
151+static const double accel_speed = -1.0;
152+
153+/* You can choose between:
154+LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
155+LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
156+*/
157+static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
158+
159+/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
160+#define MODKEY WLR_MODIFIER_LOGO
161+#define ALT_MODKEY WLR_MODIFIER_ALT
162+
163+#define TAGKEYS(KEY,SKEY,TAG) \
164+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
165+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
166+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
167+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
168+
169+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
170+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
171+
172+/* commands */
173+static const char *termcmd[] = { "foot", NULL };
174+static const char *menucmd[] = { "wmenu_run", NULL };
175+
176+static const Key keys[] = {
177+ /* Note that Shift changes certain key codes: 2 -> at, etc. */
178+ /* modifier key function argument */
179+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
180+ { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
181+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
182+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
183+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
184+ { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
185+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
186+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
187+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, swapmaster, {0} },
188+ { MODKEY, XKB_KEY_Tab, view, {0} },
189+ { MODKEY, XKB_KEY_c, killclient, {0} },
190+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[1]} },
191+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[0]} },
192+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
193+ { MODKEY, XKB_KEY_space, setlayout, {0} },
194+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
195+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_f, togglefullscreen, {0} },
196+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
197+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
198+ { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
199+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
200+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
201+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
202+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
203+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
204+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
205+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
206+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
207+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
208+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
209+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
210+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
211+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
212+
213+ /* infinite_canvas */
214+ { MODKEY, XKB_KEY_r, homecanvas, {0} },
215+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, movecanvas, {.i = 0} },
216+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, movecanvas, {.i = 1} },
217+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, movecanvas, {.i = 2} },
218+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, movecanvas, {.i = 3} },
219+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_d, centerwindow, {0} },
220+ /* infinite_canvas_end */
221+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
222+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
223+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
224+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
225+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
226+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
227+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
228+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
229+ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
230+ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
231+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
232+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
233+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
234+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
235+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
236+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
237+ { WLR_MODIFIER_ALT, XKB_KEY_R, zoomreset, {0} },
238+ { MODKEY, XKB_KEY_equal, zoom, {.f = +0.1f} },
239+ { MODKEY, XKB_KEY_minus, zoom, {.f = -0.1f} },
240+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
241+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
242+ /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
243+ * do not remove them.
244+ */
245+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
246+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
247+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
248+};
249+
250+static const Button buttons[] = {
251+ { ClkClient, MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
252+ { ClkRoot, 0,BTN_MIDDLE, spawn, {.v = menucmd} },
253+ { ClkClient, MODKEY, BTN_MIDDLE, togglefloating, {0} },
254+ { ClkClient, MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
255+ /* infinite_canvas */
256+ { ClkClient, MODKEY|WLR_MODIFIER_SHIFT, BTN_LEFT, movecanvasmouse, {.f = 2} },
257+ { ClkRoot, MODKEY|WLR_MODIFIER_SHIFT, BTN_LEFT, movecanvasmouse, {.f = 2} },
258+ { ClkRoot, 0, BTN_LEFT, movecanvasmouse, {.f = 2} },
259+ /* infinite_canvas_end */
260+ { ClkRoot, 0, BTN_RIGHT, spawn, {.v = termcmd} },
261+ { ClkClient, MODKEY, SCROLL_UP, zoom, {.f = +0.25} },
262+ { ClkClient, MODKEY, SCROLL_DOWN, zoom, {.f = -0.25f} },
263+
264+ { ClkRoot, MODKEY, SCROLL_UP, zoom, {.f = +0.25f} },
265+ { ClkRoot, MODKEY, SCROLL_DOWN, zoom, {.f = -0.25f} },
266+
267+ { ClkRoot, 0, SCROLL_UP, zoom, {.f = +0.25f} },
268+ { ClkRoot, 0, SCROLL_DOWN, zoom, {.f = -0.25f} },
269+};
270+
271+static const TabletButton tabletbuttons[] = {
272+ /* button state function argument */
273+/* { BTN_STYLUS, WLR_BUTTON_PRESSED, spawn, {.v = termcmd} }, */
274+/* { BTN_STYLUS2, WLR_BUTTON_PRESSED, killclient, {0} }, */
275+ { 0, 0, NULL, {0} }
276+};
277+
278+static const TabletPadButton tabletpadbuttons[] = {
279+ /* button state function argument */
280+/* { 0, WLR_BUTTON_PRESSED, spawn, {.v = termcmd} }, */
281+/* { 1, WLR_BUTTON_PRESSED, killclient, {0} },*/
282+/* { 2, WLR_BUTTON_PRESSED, togglefloating, {0} },*/
283+/* { 3, WLR_BUTTON_PRESSED, zoom, {0} },*/
284+ { 0, 0, NULL, {0} }
285+};
A
config.h
+284,
-0
1@@ -0,0 +1,284 @@
2+/* Taken from https://github.com/djpohly/dwl/issues/466 */
3+#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \
4+ ((hex >> 16) & 0xFF) / 255.0f, \
5+ ((hex >> 8) & 0xFF) / 255.0f, \
6+ (hex & 0xFF) / 255.0f }
7+/* appearance */
8+static const int tabletmaptosurface = 1; /* map tablet input to surface(1) or monitor(0) */
9+static const int sloppyfocus = 1; /* focus follows mouse */
10+static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */
11+
12+#define ZOOMMIN 0.05
13+#define ZOOMMAX 3
14+#define animatezoom 1
15+#define zoomduration 0.33
16+#define zoombezier "0.22,1.0,0.36,1.0"
17+#define movecanvasmouse_multiplier_is_affected_by_zoom 1
18+#define ZOOM_IN_TOWARDS_CURSOR 1
19+#define ZOOM_OUT_TOWARDS_CURSOR 0
20+
21+#define SPAWNREGION_STYLE 2 /* 0 = None
22+ 1 = Full rectangle.
23+ 2 = outline
24+ */
25+
26+/* gaps */
27+static const int enablegaps = 1; /* 1 means gaps are enabled */
28+static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */
29+static const int monoclegaps = 0; /* 1 means outer gaps in monocle layout */
30+static const unsigned int gappih = 10; /* horizontal inner gap between windows */
31+static const unsigned int gappiv = 10; /* vertical inner gap between windows */
32+static const unsigned int gappoh = 10; /* horizontal outer gap between windows and screen edge */
33+static const unsigned int gappov = 10; /* vertical outer gap between windows and screen edge */
34+
35+/* colors */
36+static const float rootcolor[] = COLOR(0x22222267); /* Background color, only visible without any wallpaper setter */
37+
38+/* inner border */
39+static const unsigned int innerborderpx = 4; /* inner border pixel width */
40+
41+static const float focused_inner_border_color[] = COLOR(0xFDD9C6ff);
42+static const float urgent_inner_border_color[] = COLOR(0xFDD9C6ff);
43+static const float unfocused_inner_border_color[] = COLOR(0x7B565600);
44+
45+/* outer border */
46+static const unsigned int outerborderpx = 6; /* outer border pixel width */
47+
48+static const float focused_outer_border_color[] = COLOR(0x0a0100ff);
49+static const float urgent_outer_border_color[] = COLOR(0x0a0100ff);
50+static const float unfocused_outer_border_color[] = COLOR(0x1e1c1c00);
51+
52+#define SPAWNREGION_COLOR COLOR(0xFDD9C667)
53+
54+static const unsigned int spawnregion_innerborderpx = 3; /* spawnregion inner border pixel width */
55+static const unsigned int spawnregion_outerborderpx = 7; /* spawnregion outer border pixel width */
56+static const float spawnregion_inner_border_color[] = COLOR(0xC9B8B6ff);
57+static const float spawnregion_outer_border_color[] = COLOR(0x430a03ff);
58+
59+/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */
60+static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */
61+/* Pen active area: value 0.0 => 1.0 { x, y, width, height } */
62+static const double tablet_area[4] = { 0.4, 0.4, 0.4, 0.4 };
63+
64+/* tagging - TAGCOUNT must be no greater than 31 */
65+#define TAGCOUNT (9)
66+
67+/* logging */
68+static int log_level = WLR_ERROR;
69+
70+
71+
72+static const Rule rules[] = {
73+ /* app_id title tags mask isfloating monitor */
74+ { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
75+ { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
76+ /* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */
77+};
78+
79+#define CENTER_NEW_WINDOWS 1
80+
81+/* layout(s) */
82+static const Layout layouts[] = {
83+ /* symbol arrange function */
84+ { "><>", NULL }, /* no layout function means floating behavior */
85+ { "[]=", tile },
86+ { "[M]", monocle },
87+};
88+
89+/* monitors */
90+/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
91+ * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients due to
92+ * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 */
93+static const MonitorRule monrules[] = {
94+ /* name mfact nmaster scale layout rotate/reflect x y resx resy rate mode adaptive */
95+ /*
96+ * mode lets the user decide how dwl should implement the modes:
97+ * -1 sets a custom mode following the user's choice
98+ * All other numbers set the mode at the index n; 0 is the standard mode; see wlr-randr
99+ */
100+ { "HDMI-A-1", 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1, 0, 0, 180.003006f, 1, 0 },
101+ /* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */
102+};
103+
104+/* keyboard */
105+static const struct xkb_rule_names xkb_rules = {
106+ /* can specify fields: rules, model, layout, variant, options */
107+ /* example:
108+ .options = "ctrl:nocaps",
109+ */
110+ .options = NULL,
111+};
112+
113+static const int repeat_rate = 40;
114+static const int repeat_delay = 250;
115+
116+/* Trackpad */
117+static const int tap_to_click = 1;
118+static const int tap_and_drag = 1;
119+static const int drag_lock = 1;
120+static const int natural_scrolling = 0;
121+static const int disable_while_typing = 1;
122+static const int left_handed = 0;
123+static const int middle_button_emulation = 0;
124+/* You can choose between:
125+LIBINPUT_CONFIG_SCROLL_NO_SCROLL
126+LIBINPUT_CONFIG_SCROLL_2FG
127+LIBINPUT_CONFIG_SCROLL_EDGE
128+LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN
129+*/
130+static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG;
131+
132+/* You can choose between:
133+LIBINPUT_CONFIG_CLICK_METHOD_NONE
134+LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS
135+LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER
136+*/
137+static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
138+
139+/* You can choose between:
140+LIBINPUT_CONFIG_SEND_EVENTS_ENABLED
141+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED
142+LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE
143+*/
144+static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
145+
146+/* You can choose between:
147+LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT
148+LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE
149+*/
150+static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
151+static const double accel_speed = -1.0;
152+
153+/* You can choose between:
154+LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle
155+LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right
156+*/
157+static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM;
158+
159+/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */
160+#define MODKEY WLR_MODIFIER_LOGO
161+#define ALT_MODKEY WLR_MODIFIER_ALT
162+
163+#define TAGKEYS(KEY,SKEY,TAG) \
164+ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \
165+ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \
166+ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \
167+ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }
168+
169+/* helper for spawning shell commands in the pre dwm-5.0 fashion */
170+#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } }
171+
172+/* commands */
173+static const char *termcmd[] = { "foot", NULL };
174+static const char *menucmd[] = { "wmenu_run", NULL };
175+
176+static const Key keys[] = {
177+ /* Note that Shift changes certain key codes: 2 -> at, etc. */
178+ /* modifier key function argument */
179+ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
180+ { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} },
181+ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} },
182+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
183+ { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} },
184+ { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} },
185+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} },
186+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
187+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, swapmaster, {0} },
188+ { MODKEY, XKB_KEY_Tab, view, {0} },
189+ { MODKEY, XKB_KEY_c, killclient, {0} },
190+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[1]} },
191+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[0]} },
192+ { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
193+ { MODKEY, XKB_KEY_space, setlayout, {0} },
194+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
195+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_f, togglefullscreen, {0} },
196+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
197+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
198+ { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} },
199+ { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} },
200+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} },
201+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} },
202+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
203+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
204+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
205+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
206+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
207+ TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5),
208+ TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
209+ TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
210+ TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
211+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
212+
213+ /* infinite_canvas */
214+ { MODKEY, XKB_KEY_r, homecanvas, {0} },
215+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Left, movecanvas, {.i = 0} },
216+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Right, movecanvas, {.i = 1} },
217+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Up, movecanvas, {.i = 2} },
218+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Down, movecanvas, {.i = 3} },
219+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_d, centerwindow, {0} },
220+ /* infinite_canvas_end */
221+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } },
222+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } },
223+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } },
224+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } },
225+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } },
226+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } },
227+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} },
228+ { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} },
229+ { MODKEY, XKB_KEY_y, incihgaps, {.i = +1 } },
230+ { MODKEY, XKB_KEY_o, incihgaps, {.i = -1 } },
231+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_y, incivgaps, {.i = +1 } },
232+ { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_o, incivgaps, {.i = -1 } },
233+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_y, incohgaps, {.i = +1 } },
234+ { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_o, incohgaps, {.i = -1 } },
235+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Y, incovgaps, {.i = +1 } },
236+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, incovgaps, {.i = -1 } },
237+ { WLR_MODIFIER_ALT, XKB_KEY_R, zoomreset, {0} },
238+ { MODKEY, XKB_KEY_equal, zoom, {.f = +0.1f} },
239+ { MODKEY, XKB_KEY_minus, zoom, {.f = -0.1f} },
240+ /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
241+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
242+ /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is
243+ * do not remove them.
244+ */
245+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
246+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
247+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
248+};
249+
250+static const Button buttons[] = {
251+ { ClkClient, MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} },
252+ { ClkRoot, 0,BTN_MIDDLE, spawn, {.v = menucmd} },
253+ { ClkClient, MODKEY, BTN_MIDDLE, togglefloating, {0} },
254+ { ClkClient, MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} },
255+ /* infinite_canvas */
256+ { ClkClient, MODKEY|WLR_MODIFIER_SHIFT, BTN_LEFT, movecanvasmouse, {.f = 2} },
257+ { ClkRoot, MODKEY|WLR_MODIFIER_SHIFT, BTN_LEFT, movecanvasmouse, {.f = 2} },
258+ { ClkRoot, 0, BTN_LEFT, movecanvasmouse, {.f = 2} },
259+ /* infinite_canvas_end */
260+ { ClkRoot, 0, BTN_RIGHT, spawn, {.v = termcmd} },
261+ { ClkClient, MODKEY, SCROLL_UP, zoom, {.f = +0.25} },
262+ { ClkClient, MODKEY, SCROLL_DOWN, zoom, {.f = -0.25f} },
263+
264+ { ClkRoot, MODKEY, SCROLL_UP, zoom, {.f = +0.25f} },
265+ { ClkRoot, MODKEY, SCROLL_DOWN, zoom, {.f = -0.25f} },
266+
267+ { ClkRoot, 0, SCROLL_UP, zoom, {.f = +0.25f} },
268+ { ClkRoot, 0, SCROLL_DOWN, zoom, {.f = -0.25f} },
269+};
270+
271+static const TabletButton tabletbuttons[] = {
272+ /* button state function argument */
273+/* { BTN_STYLUS, WLR_BUTTON_PRESSED, spawn, {.v = termcmd} }, */
274+/* { BTN_STYLUS2, WLR_BUTTON_PRESSED, killclient, {0} }, */
275+ { 0, 0, NULL, {0} }
276+};
277+
278+static const TabletPadButton tabletpadbuttons[] = {
279+ /* button state function argument */
280+/* { 0, WLR_BUTTON_PRESSED, spawn, {.v = termcmd} }, */
281+/* { 1, WLR_BUTTON_PRESSED, killclient, {0} },*/
282+/* { 2, WLR_BUTTON_PRESSED, togglefloating, {0} },*/
283+/* { 3, WLR_BUTTON_PRESSED, zoom, {0} },*/
284+ { 0, 0, NULL, {0} }
285+};
+32,
-0
1@@ -0,0 +1,32 @@
2+VERSION = 1
3+
4+PKG_CONFIG = pkg-config
5+
6+# paths
7+PREFIX = /usr/local
8+MANDIR = $(PREFIX)/share/man
9+DATADIR = $(PREFIX)/share
10+
11+WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19`
12+WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19`
13+
14+# Allow using an alternative wlroots installation
15+# This has to have all the includes required by wlroots, e.g:
16+# Assuming wlroots git repo is "${PWD}/wlroots" and you only ran "meson setup build && ninja -C build"
17+#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
18+# -I$(PWD)/wlroots/include
19+# Set -rpath to avoid using the wrong library.
20+#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19
21+
22+# Assuming you ran "meson setup --prefix ${PWD}/0.19 build && ninja -C build install"
23+#WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \
24+# -I$(PWD)/wlroots/0.19/include/wlroots-0.19
25+#WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/0.19/lib64 -L$(PWD)/wlroots/0.19/lib64 -lwlroots-0.19
26+
27+XWAYLAND =
28+XLIBS =
29+# Uncomment to build XWayland support
30+XWAYLAND = -DXWAYLAND
31+XLIBS = xcb xcb-icccm
32+
33+CC = cc
A
leeloo.1
+99,
-0
1@@ -0,0 +1,99 @@
2+.Dd $Mdocdate$
3+.Dt LEELOO 1
4+.Os
5+.Sh NAME
6+.Nm leeloo
7+.Nd Wayland compositor with an infinite canvas
8+.Sh SYNOPSIS
9+.Nm
10+.Op Fl dv
11+.Op Fl s Ar command
12+.Sh DESCRIPTION
13+.Nm
14+is a Wayland compositor based on
15+.Xr dwl 1 .
16+Windows are placed on an unbounded canvas
17+which can be panned and zoomed.
18+.Pp
19+The options are as follows:
20+.Bl -tag -width Ds
21+.It Fl d
22+Log debugging messages to standard error.
23+.It Fl s Ar command
24+Run
25+.Ar command
26+with
27+.Xr sh 1
28+once the compositor is up.
29+.Nm
30+exits when it terminates.
31+.It Fl v
32+Print the version and exit.
33+.El
34+.Pp
35+Configuration is done at compile time by editing
36+.Pa config.h
37+and rebuilding.
38+.Sh DEFAULT BINDINGS
39+.Bl -tag -width "Mod+Shift+Arrow" -compact
40+.It Mod+Return
41+Spawn
42+.Xr foot 1 .
43+.It Mod+p
44+Spawn
45+.Xr wmenu_run 1 .
46+.It Mod+c
47+Close window.
48+.It Mod+Shift+q
49+Quit.
50+.It Mod+1\(en9
51+View tag.
52+.It Mod+Shift+1\(en9
53+Move window to tag.
54+.It Mod+t , f , m
55+Tile, float, monocle.
56+.It Mod+Shift+f
57+Toggle fullscreen.
58+.It Mod+Shift+Arrow
59+Pan canvas.
60+.It Mod+r
61+Return canvas to origin.
62+.It Mod+Shift+d
63+Center window.
64+.It Mod+= , \-
65+Zoom in, out.
66+.It Alt+Shift+r
67+Reset zoom.
68+.It Mod+Button1
69+Move window.
70+.It Mod+Button3
71+Resize window.
72+.It Button1
73+Drag canvas.
74+.It Scroll
75+Zoom.
76+.El
77+.Pp
78+.Dq Mod
79+is the logo key.
80+.Sh ENVIRONMENT
81+.Bl -tag -width XDG_RUNTIME_DIR
82+.It Ev XDG_RUNTIME_DIR
83+Directory for the Wayland socket.
84+Must be set.
85+.It Ev WAYLAND_DISPLAY
86+Set for child processes.
87+.El
88+.Sh EXIT STATUS
89+.Ex -std
90+.Sh SEE ALSO
91+.Xr foot 1 ,
92+.Xr sh 1
93+.Sh AUTHORS
94+.An -nosplit
95+Based on
96+.Xr dwl 1
97+by
98+.An Devin J. Pohly .
99+.Sh CAVEATS
100+Changing the configuration requires recompilation.
+5,
-0
1@@ -0,0 +1,5 @@
2+[Desktop Entry]
3+Name=Leeloo
4+Comment=Leeloo Wayland Compositor
5+Exec=leeloo
6+Type=Application
+8,
-0
1@@ -0,0 +1,8 @@
2+# Additional modules cuz yeah
3+
4+# Tiling, i've made it a module because
5+# i feel it is unneeded by many people
6+# but it could be wanted by many others too.
7+# (comment/uncomment to enable/disable support for tiling)
8+
9+LEELOOMODULES += -DTILING
+390,
-0
1@@ -0,0 +1,390 @@
2+<?xml version="1.0" encoding="UTF-8"?>
3+<protocol name="wlr_layer_shell_unstable_v1">
4+ <copyright>
5+ Copyright © 2017 Drew DeVault
6+
7+ Permission to use, copy, modify, distribute, and sell this
8+ software and its documentation for any purpose is hereby granted
9+ without fee, provided that the above copyright notice appear in
10+ all copies and that both that copyright notice and this permission
11+ notice appear in supporting documentation, and that the name of
12+ the copyright holders not be used in advertising or publicity
13+ pertaining to distribution of the software without specific,
14+ written prior permission. The copyright holders make no
15+ representations about the suitability of this software for any
16+ purpose. It is provided "as is" without express or implied
17+ warranty.
18+
19+ THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
20+ SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
21+ FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
22+ SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
24+ AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25+ ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
26+ THIS SOFTWARE.
27+ </copyright>
28+
29+ <interface name="zwlr_layer_shell_v1" version="4">
30+ <description summary="create surfaces that are layers of the desktop">
31+ Clients can use this interface to assign the surface_layer role to
32+ wl_surfaces. Such surfaces are assigned to a "layer" of the output and
33+ rendered with a defined z-depth respective to each other. They may also be
34+ anchored to the edges and corners of a screen and specify input handling
35+ semantics. This interface should be suitable for the implementation of
36+ many desktop shell components, and a broad number of other applications
37+ that interact with the desktop.
38+ </description>
39+
40+ <request name="get_layer_surface">
41+ <description summary="create a layer_surface from a surface">
42+ Create a layer surface for an existing surface. This assigns the role of
43+ layer_surface, or raises a protocol error if another role is already
44+ assigned.
45+
46+ Creating a layer surface from a wl_surface which has a buffer attached
47+ or committed is a client error, and any attempts by a client to attach
48+ or manipulate a buffer prior to the first layer_surface.configure call
49+ must also be treated as errors.
50+
51+ After creating a layer_surface object and setting it up, the client
52+ must perform an initial commit without any buffer attached.
53+ The compositor will reply with a layer_surface.configure event.
54+ The client must acknowledge it and is then allowed to attach a buffer
55+ to map the surface.
56+
57+ You may pass NULL for output to allow the compositor to decide which
58+ output to use. Generally this will be the one that the user most
59+ recently interacted with.
60+
61+ Clients can specify a namespace that defines the purpose of the layer
62+ surface.
63+ </description>
64+ <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
65+ <arg name="surface" type="object" interface="wl_surface"/>
66+ <arg name="output" type="object" interface="wl_output" allow-null="true"/>
67+ <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
68+ <arg name="namespace" type="string" summary="namespace for the layer surface"/>
69+ </request>
70+
71+ <enum name="error">
72+ <entry name="role" value="0" summary="wl_surface has another role"/>
73+ <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
74+ <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
75+ </enum>
76+
77+ <enum name="layer">
78+ <description summary="available layers for surfaces">
79+ These values indicate which layers a surface can be rendered in. They
80+ are ordered by z depth, bottom-most first. Traditional shell surfaces
81+ will typically be rendered between the bottom and top layers.
82+ Fullscreen shell surfaces are typically rendered at the top layer.
83+ Multiple surfaces can share a single layer, and ordering within a
84+ single layer is undefined.
85+ </description>
86+
87+ <entry name="background" value="0"/>
88+ <entry name="bottom" value="1"/>
89+ <entry name="top" value="2"/>
90+ <entry name="overlay" value="3"/>
91+ </enum>
92+
93+ <!-- Version 3 additions -->
94+
95+ <request name="destroy" type="destructor" since="3">
96+ <description summary="destroy the layer_shell object">
97+ This request indicates that the client will not use the layer_shell
98+ object any more. Objects that have been created through this instance
99+ are not affected.
100+ </description>
101+ </request>
102+ </interface>
103+
104+ <interface name="zwlr_layer_surface_v1" version="4">
105+ <description summary="layer metadata interface">
106+ An interface that may be implemented by a wl_surface, for surfaces that
107+ are designed to be rendered as a layer of a stacked desktop-like
108+ environment.
109+
110+ Layer surface state (layer, size, anchor, exclusive zone,
111+ margin, interactivity) is double-buffered, and will be applied at the
112+ time wl_surface.commit of the corresponding wl_surface is called.
113+
114+ Attaching a null buffer to a layer surface unmaps it.
115+
116+ Unmapping a layer_surface means that the surface cannot be shown by the
117+ compositor until it is explicitly mapped again. The layer_surface
118+ returns to the state it had right after layer_shell.get_layer_surface.
119+ The client can re-map the surface by performing a commit without any
120+ buffer attached, waiting for a configure event and handling it as usual.
121+ </description>
122+
123+ <request name="set_size">
124+ <description summary="sets the size of the surface">
125+ Sets the size of the surface in surface-local coordinates. The
126+ compositor will display the surface centered with respect to its
127+ anchors.
128+
129+ If you pass 0 for either value, the compositor will assign it and
130+ inform you of the assignment in the configure event. You must set your
131+ anchor to opposite edges in the dimensions you omit; not doing so is a
132+ protocol error. Both values are 0 by default.
133+
134+ Size is double-buffered, see wl_surface.commit.
135+ </description>
136+ <arg name="width" type="uint"/>
137+ <arg name="height" type="uint"/>
138+ </request>
139+
140+ <request name="set_anchor">
141+ <description summary="configures the anchor point of the surface">
142+ Requests that the compositor anchor the surface to the specified edges
143+ and corners. If two orthogonal edges are specified (e.g. 'top' and
144+ 'left'), then the anchor point will be the intersection of the edges
145+ (e.g. the top left corner of the output); otherwise the anchor point
146+ will be centered on that edge, or in the center if none is specified.
147+
148+ Anchor is double-buffered, see wl_surface.commit.
149+ </description>
150+ <arg name="anchor" type="uint" enum="anchor"/>
151+ </request>
152+
153+ <request name="set_exclusive_zone">
154+ <description summary="configures the exclusive geometry of this surface">
155+ Requests that the compositor avoids occluding an area with other
156+ surfaces. The compositor's use of this information is
157+ implementation-dependent - do not assume that this region will not
158+ actually be occluded.
159+
160+ A positive value is only meaningful if the surface is anchored to one
161+ edge or an edge and both perpendicular edges. If the surface is not
162+ anchored, anchored to only two perpendicular edges (a corner), anchored
163+ to only two parallel edges or anchored to all edges, a positive value
164+ will be treated the same as zero.
165+
166+ A positive zone is the distance from the edge in surface-local
167+ coordinates to consider exclusive.
168+
169+ Surfaces that do not wish to have an exclusive zone may instead specify
170+ how they should interact with surfaces that do. If set to zero, the
171+ surface indicates that it would like to be moved to avoid occluding
172+ surfaces with a positive exclusive zone. If set to -1, the surface
173+ indicates that it would not like to be moved to accommodate for other
174+ surfaces, and the compositor should extend it all the way to the edges
175+ it is anchored to.
176+
177+ For example, a panel might set its exclusive zone to 10, so that
178+ maximized shell surfaces are not shown on top of it. A notification
179+ might set its exclusive zone to 0, so that it is moved to avoid
180+ occluding the panel, but shell surfaces are shown underneath it. A
181+ wallpaper or lock screen might set their exclusive zone to -1, so that
182+ they stretch below or over the panel.
183+
184+ The default value is 0.
185+
186+ Exclusive zone is double-buffered, see wl_surface.commit.
187+ </description>
188+ <arg name="zone" type="int"/>
189+ </request>
190+
191+ <request name="set_margin">
192+ <description summary="sets a margin from the anchor point">
193+ Requests that the surface be placed some distance away from the anchor
194+ point on the output, in surface-local coordinates. Setting this value
195+ for edges you are not anchored to has no effect.
196+
197+ The exclusive zone includes the margin.
198+
199+ Margin is double-buffered, see wl_surface.commit.
200+ </description>
201+ <arg name="top" type="int"/>
202+ <arg name="right" type="int"/>
203+ <arg name="bottom" type="int"/>
204+ <arg name="left" type="int"/>
205+ </request>
206+
207+ <enum name="keyboard_interactivity">
208+ <description summary="types of keyboard interaction possible for a layer shell surface">
209+ Types of keyboard interaction possible for layer shell surfaces. The
210+ rationale for this is twofold: (1) some applications are not interested
211+ in keyboard events and not allowing them to be focused can improve the
212+ desktop experience; (2) some applications will want to take exclusive
213+ keyboard focus.
214+ </description>
215+
216+ <entry name="none" value="0">
217+ <description summary="no keyboard focus is possible">
218+ This value indicates that this surface is not interested in keyboard
219+ events and the compositor should never assign it the keyboard focus.
220+
221+ This is the default value, set for newly created layer shell surfaces.
222+
223+ This is useful for e.g. desktop widgets that display information or
224+ only have interaction with non-keyboard input devices.
225+ </description>
226+ </entry>
227+ <entry name="exclusive" value="1">
228+ <description summary="request exclusive keyboard focus">
229+ Request exclusive keyboard focus if this surface is above the shell surface layer.
230+
231+ For the top and overlay layers, the seat will always give
232+ exclusive keyboard focus to the top-most layer which has keyboard
233+ interactivity set to exclusive. If this layer contains multiple
234+ surfaces with keyboard interactivity set to exclusive, the compositor
235+ determines the one receiving keyboard events in an implementation-
236+ defined manner. In this case, no guarantee is made when this surface
237+ will receive keyboard focus (if ever).
238+
239+ For the bottom and background layers, the compositor is allowed to use
240+ normal focus semantics.
241+
242+ This setting is mainly intended for applications that need to ensure
243+ they receive all keyboard events, such as a lock screen or a password
244+ prompt.
245+ </description>
246+ </entry>
247+ <entry name="on_demand" value="2" since="4">
248+ <description summary="request regular keyboard focus semantics">
249+ This requests the compositor to allow this surface to be focused and
250+ unfocused by the user in an implementation-defined manner. The user
251+ should be able to unfocus this surface even regardless of the layer
252+ it is on.
253+
254+ Typically, the compositor will want to use its normal mechanism to
255+ manage keyboard focus between layer shell surfaces with this setting
256+ and regular toplevels on the desktop layer (e.g. click to focus).
257+ Nevertheless, it is possible for a compositor to require a special
258+ interaction to focus or unfocus layer shell surfaces (e.g. requiring
259+ a click even if focus follows the mouse normally, or providing a
260+ keybinding to switch focus between layers).
261+
262+ This setting is mainly intended for desktop shell components (e.g.
263+ panels) that allow keyboard interaction. Using this option can allow
264+ implementing a desktop shell that can be fully usable without the
265+ mouse.
266+ </description>
267+ </entry>
268+ </enum>
269+
270+ <request name="set_keyboard_interactivity">
271+ <description summary="requests keyboard events">
272+ Set how keyboard events are delivered to this surface. By default,
273+ layer shell surfaces do not receive keyboard events; this request can
274+ be used to change this.
275+
276+ This setting is inherited by child surfaces set by the get_popup
277+ request.
278+
279+ Layer surfaces receive pointer, touch, and tablet events normally. If
280+ you do not want to receive them, set the input region on your surface
281+ to an empty region.
282+
283+ Keyboard interactivity is double-buffered, see wl_surface.commit.
284+ </description>
285+ <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
286+ </request>
287+
288+ <request name="get_popup">
289+ <description summary="assign this layer_surface as an xdg_popup parent">
290+ This assigns an xdg_popup's parent to this layer_surface. This popup
291+ should have been created via xdg_surface::get_popup with the parent set
292+ to NULL, and this request must be invoked before committing the popup's
293+ initial state.
294+
295+ See the documentation of xdg_popup for more details about what an
296+ xdg_popup is and how it is used.
297+ </description>
298+ <arg name="popup" type="object" interface="xdg_popup"/>
299+ </request>
300+
301+ <request name="ack_configure">
302+ <description summary="ack a configure event">
303+ When a configure event is received, if a client commits the
304+ surface in response to the configure event, then the client
305+ must make an ack_configure request sometime before the commit
306+ request, passing along the serial of the configure event.
307+
308+ If the client receives multiple configure events before it
309+ can respond to one, it only has to ack the last configure event.
310+
311+ A client is not required to commit immediately after sending
312+ an ack_configure request - it may even ack_configure several times
313+ before its next surface commit.
314+
315+ A client may send multiple ack_configure requests before committing, but
316+ only the last request sent before a commit indicates which configure
317+ event the client really is responding to.
318+ </description>
319+ <arg name="serial" type="uint" summary="the serial from the configure event"/>
320+ </request>
321+
322+ <request name="destroy" type="destructor">
323+ <description summary="destroy the layer_surface">
324+ This request destroys the layer surface.
325+ </description>
326+ </request>
327+
328+ <event name="configure">
329+ <description summary="suggest a surface change">
330+ The configure event asks the client to resize its surface.
331+
332+ Clients should arrange their surface for the new states, and then send
333+ an ack_configure request with the serial sent in this configure event at
334+ some point before committing the new surface.
335+
336+ The client is free to dismiss all but the last configure event it
337+ received.
338+
339+ The width and height arguments specify the size of the window in
340+ surface-local coordinates.
341+
342+ The size is a hint, in the sense that the client is free to ignore it if
343+ it doesn't resize, pick a smaller size (to satisfy aspect ratio or
344+ resize in steps of NxM pixels). If the client picks a smaller size and
345+ is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
346+ surface will be centered on this axis.
347+
348+ If the width or height arguments are zero, it means the client should
349+ decide its own window dimension.
350+ </description>
351+ <arg name="serial" type="uint"/>
352+ <arg name="width" type="uint"/>
353+ <arg name="height" type="uint"/>
354+ </event>
355+
356+ <event name="closed">
357+ <description summary="surface should be closed">
358+ The closed event is sent by the compositor when the surface will no
359+ longer be shown. The output may have been destroyed or the user may
360+ have asked for it to be removed. Further changes to the surface will be
361+ ignored. The client should destroy the resource after receiving this
362+ event, and create a new surface if they so choose.
363+ </description>
364+ </event>
365+
366+ <enum name="error">
367+ <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
368+ <entry name="invalid_size" value="1" summary="size is invalid"/>
369+ <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
370+ <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
371+ </enum>
372+
373+ <enum name="anchor" bitfield="true">
374+ <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
375+ <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
376+ <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
377+ <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
378+ </enum>
379+
380+ <!-- Version 2 additions -->
381+
382+ <request name="set_layer" since="2">
383+ <description summary="change the layer of the surface">
384+ Change the layer that the surface is rendered on.
385+
386+ Layer is double-buffered, see wl_surface.commit.
387+ </description>
388+ <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
389+ </request>
390+ </interface>
391+</protocol>
1@@ -0,0 +1,128 @@
2+<?xml version="1.0" encoding="UTF-8"?>
3+<protocol name="wlr_output_power_management_unstable_v1">
4+ <copyright>
5+ Copyright © 2019 Purism SPC
6+
7+ Permission is hereby granted, free of charge, to any person obtaining a
8+ copy of this software and associated documentation files (the "Software"),
9+ to deal in the Software without restriction, including without limitation
10+ the rights to use, copy, modify, merge, publish, distribute, sublicense,
11+ and/or sell copies of the Software, and to permit persons to whom the
12+ Software is furnished to do so, subject to the following conditions:
13+
14+ The above copyright notice and this permission notice (including the next
15+ paragraph) shall be included in all copies or substantial portions of the
16+ Software.
17+
18+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21+ THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24+ DEALINGS IN THE SOFTWARE.
25+ </copyright>
26+
27+ <description summary="Control power management modes of outputs">
28+ This protocol allows clients to control power management modes
29+ of outputs that are currently part of the compositor space. The
30+ intent is to allow special clients like desktop shells to power
31+ down outputs when the system is idle.
32+
33+ To modify outputs not currently part of the compositor space see
34+ wlr-output-management.
35+
36+ Warning! The protocol described in this file is experimental and
37+ backward incompatible changes may be made. Backward compatible changes
38+ may be added together with the corresponding interface version bump.
39+ Backward incompatible changes are done by bumping the version number in
40+ the protocol and interface names and resetting the interface version.
41+ Once the protocol is to be declared stable, the 'z' prefix and the
42+ version number in the protocol and interface names are removed and the
43+ interface version number is reset.
44+ </description>
45+
46+ <interface name="zwlr_output_power_manager_v1" version="1">
47+ <description summary="manager to create per-output power management">
48+ This interface is a manager that allows creating per-output power
49+ management mode controls.
50+ </description>
51+
52+ <request name="get_output_power">
53+ <description summary="get a power management for an output">
54+ Create a output power management mode control that can be used to
55+ adjust the power management mode for a given output.
56+ </description>
57+ <arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
58+ <arg name="output" type="object" interface="wl_output"/>
59+ </request>
60+
61+ <request name="destroy" type="destructor">
62+ <description summary="destroy the manager">
63+ All objects created by the manager will still remain valid, until their
64+ appropriate destroy request has been called.
65+ </description>
66+ </request>
67+ </interface>
68+
69+ <interface name="zwlr_output_power_v1" version="1">
70+ <description summary="adjust power management mode for an output">
71+ This object offers requests to set the power management mode of
72+ an output.
73+ </description>
74+
75+ <enum name="mode">
76+ <entry name="off" value="0"
77+ summary="Output is turned off."/>
78+ <entry name="on" value="1"
79+ summary="Output is turned on, no power saving"/>
80+ </enum>
81+
82+ <enum name="error">
83+ <entry name="invalid_mode" value="1" summary="inexistent power save mode"/>
84+ </enum>
85+
86+ <request name="set_mode">
87+ <description summary="Set an outputs power save mode">
88+ Set an output's power save mode to the given mode. The mode change
89+ is effective immediately. If the output does not support the given
90+ mode a failed event is sent.
91+ </description>
92+ <arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
93+ </request>
94+
95+ <event name="mode">
96+ <description summary="Report a power management mode change">
97+ Report the power management mode change of an output.
98+
99+ The mode event is sent after an output changed its power
100+ management mode. The reason can be a client using set_mode or the
101+ compositor deciding to change an output's mode.
102+ This event is also sent immediately when the object is created
103+ so the client is informed about the current power management mode.
104+ </description>
105+ <arg name="mode" type="uint" enum="mode"
106+ summary="the output's new power management mode"/>
107+ </event>
108+
109+ <event name="failed">
110+ <description summary="object no longer valid">
111+ This event indicates that the output power management mode control
112+ is no longer valid. This can happen for a number of reasons,
113+ including:
114+ - The output doesn't support power management
115+ - Another client already has exclusive power management mode control
116+ for this output
117+ - The output disappeared
118+
119+ Upon receiving this event, the client should destroy this object.
120+ </description>
121+ </event>
122+
123+ <request name="destroy" type="destructor">
124+ <description summary="destroy this power management">
125+ Destroys the output power management mode control object.
126+ </description>
127+ </request>
128+ </interface>
129+</protocol>
+406,
-0
1@@ -0,0 +1,406 @@
2+/*
3+ * Attempt to consolidate unavoidable suck into one file, away from leeloo.c. This
4+ * file is not meant to be pretty. We use a .h file with static inline
5+ * functions instead of a separate .c module, or function pointers like sway, so
6+ * that they will simply compile out if the chosen #defines leave them unused.
7+ */
8+
9+/* Leave these functions first; they're used in the others */
10+static inline int
11+client_is_x11(Client *c)
12+{
13+#ifdef XWAYLAND
14+ return c->type == X11;
15+#endif
16+ return 0;
17+}
18+
19+static inline struct wlr_surface *
20+client_surface(Client *c)
21+{
22+#ifdef XWAYLAND
23+ if (client_is_x11(c))
24+ return c->surface.xwayland->surface;
25+#endif
26+ return c->surface.xdg->surface;
27+}
28+
29+static inline int
30+toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl)
31+{
32+ struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface;
33+ struct wlr_surface *root_surface;
34+ struct wlr_layer_surface_v1 *layer_surface;
35+ Client *c = NULL;
36+ LayerSurface *l = NULL;
37+ int type = -1;
38+#ifdef XWAYLAND
39+ struct wlr_xwayland_surface *xsurface;
40+#endif
41+
42+ if (!s)
43+ return -1;
44+ root_surface = wlr_surface_get_root_surface(s);
45+
46+#ifdef XWAYLAND
47+ if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) {
48+ c = xsurface->data;
49+ type = c->type;
50+ goto end;
51+ }
52+#endif
53+
54+ if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) {
55+ l = layer_surface->data;
56+ type = LayerShell;
57+ goto end;
58+ }
59+
60+ xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
61+ while (xdg_surface) {
62+ tmp_xdg_surface = NULL;
63+ switch (xdg_surface->role) {
64+ case WLR_XDG_SURFACE_ROLE_POPUP:
65+ if (!xdg_surface->popup || !xdg_surface->popup->parent)
66+ return -1;
67+
68+ tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
69+
70+ if (!tmp_xdg_surface)
71+ return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl);
72+
73+ xdg_surface = tmp_xdg_surface;
74+ break;
75+ case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
76+ c = xdg_surface->data;
77+ type = c->type;
78+ goto end;
79+ case WLR_XDG_SURFACE_ROLE_NONE:
80+ return -1;
81+ }
82+ }
83+
84+end:
85+ if (pl)
86+ *pl = l;
87+ if (pc)
88+ *pc = c;
89+ return type;
90+}
91+
92+/* The others */
93+static inline void
94+client_activate_surface(struct wlr_surface *s, int activated)
95+{
96+ struct wlr_xdg_toplevel *toplevel;
97+#ifdef XWAYLAND
98+ struct wlr_xwayland_surface *xsurface;
99+ if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
100+ wlr_xwayland_surface_activate(xsurface, activated);
101+ return;
102+ }
103+#endif
104+ if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s)))
105+ wlr_xdg_toplevel_set_activated(toplevel, activated);
106+}
107+
108+static inline uint32_t
109+client_set_bounds(Client *c, int32_t width, int32_t height)
110+{
111+#ifdef XWAYLAND
112+ if (client_is_x11(c))
113+ return 0;
114+#endif
115+ if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
116+ XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0
117+ && (c->bounds.width != width || c->bounds.height != height)) {
118+ c->bounds.width = width;
119+ c->bounds.height = height;
120+ return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height);
121+ }
122+ return 0;
123+}
124+
125+static inline const char *
126+client_get_appid(Client *c)
127+{
128+#ifdef XWAYLAND
129+ if (client_is_x11(c))
130+ return c->surface.xwayland->class ? c->surface.xwayland->class : "broken";
131+#endif
132+ return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken";
133+}
134+
135+static inline void
136+client_get_clip(Client *c, struct wlr_box *clip)
137+{
138+ *clip = (struct wlr_box){
139+ .x = 0,
140+ .y = 0,
141+ .width = c->geom.width - c->bw,
142+ .height = c->geom.height - c->bw,
143+ };
144+
145+#ifdef XWAYLAND
146+ if (client_is_x11(c))
147+ return;
148+#endif
149+
150+ clip->x = c->surface.xdg->geometry.x;
151+ clip->y = c->surface.xdg->geometry.y;
152+}
153+
154+static inline void
155+client_get_geometry(Client *c, struct wlr_box *geom)
156+{
157+#ifdef XWAYLAND
158+ if (client_is_x11(c)) {
159+ geom->x = c->surface.xwayland->x;
160+ geom->y = c->surface.xwayland->y;
161+ geom->width = c->surface.xwayland->width;
162+ geom->height = c->surface.xwayland->height;
163+ return;
164+ }
165+#endif
166+ *geom = c->surface.xdg->geometry;
167+}
168+
169+static inline Client *
170+client_get_parent(Client *c)
171+{
172+ Client *p = NULL;
173+#ifdef XWAYLAND
174+ if (client_is_x11(c)) {
175+ if (c->surface.xwayland->parent)
176+ toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL);
177+ return p;
178+ }
179+#endif
180+ if (c->surface.xdg->toplevel->parent)
181+ toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL);
182+ return p;
183+}
184+
185+static inline int
186+client_has_children(Client *c)
187+{
188+#ifdef XWAYLAND
189+ if (client_is_x11(c))
190+ return !wl_list_empty(&c->surface.xwayland->children);
191+#endif
192+ /* surface.xdg->link is never empty because it always contains at least the
193+ * surface itself. */
194+ return wl_list_length(&c->surface.xdg->link) > 1;
195+}
196+
197+static inline const char *
198+client_get_title(Client *c)
199+{
200+#ifdef XWAYLAND
201+ if (client_is_x11(c))
202+ return c->surface.xwayland->title ? c->surface.xwayland->title : "broken";
203+#endif
204+ return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken";
205+}
206+
207+static inline int
208+client_is_float_type(Client *c)
209+{
210+ struct wlr_xdg_toplevel *toplevel;
211+ struct wlr_xdg_toplevel_state state;
212+
213+#ifdef XWAYLAND
214+ if (client_is_x11(c)) {
215+ struct wlr_xwayland_surface *surface = c->surface.xwayland;
216+ xcb_size_hints_t *size_hints = surface->size_hints;
217+ if (surface->modal)
218+ return 1;
219+
220+ if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG)
221+ || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH)
222+ || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR)
223+ || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) {
224+ return 1;
225+ }
226+
227+ return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0
228+ && (size_hints->max_width == size_hints->min_width
229+ || size_hints->max_height == size_hints->min_height);
230+ }
231+#endif
232+
233+ toplevel = c->surface.xdg->toplevel;
234+ state = toplevel->current;
235+ return toplevel->parent || (state.min_width != 0 && state.min_height != 0
236+ && (state.min_width == state.max_width
237+ || state.min_height == state.max_height));
238+}
239+
240+static inline int
241+client_is_rendered_on_mon(Client *c, Monitor *m)
242+{
243+ /* This is needed for when you don't want to check formal assignment,
244+ * but rather actual displaying of the pixels.
245+ * Usually VISIBLEON suffices and is also faster. */
246+ struct wlr_surface_output *s;
247+ int unused_lx, unused_ly;
248+ if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
249+ return 0;
250+ wl_list_for_each(s, &client_surface(c)->current_outputs, link)
251+ if (s->output == m->wlr_output)
252+ return 1;
253+ return 0;
254+}
255+
256+static inline int
257+client_is_stopped(Client *c)
258+{
259+ int pid;
260+ siginfo_t in = {0};
261+#ifdef XWAYLAND
262+ if (client_is_x11(c))
263+ return 0;
264+#endif
265+
266+ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL);
267+ if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) {
268+ /* This process is not our child process, while is very unlikely that
269+ * it is stopped, in order to do not skip frames, assume that it is. */
270+ if (errno == ECHILD)
271+ return 1;
272+ } else if (in.si_pid) {
273+ if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
274+ return 1;
275+ if (in.si_code == CLD_CONTINUED)
276+ return 0;
277+ }
278+
279+ return 0;
280+}
281+
282+static inline int
283+client_is_unmanaged(Client *c)
284+{
285+#ifdef XWAYLAND
286+ if (client_is_x11(c))
287+ return c->surface.xwayland->override_redirect;
288+#endif
289+ return 0;
290+}
291+
292+static inline void
293+client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb)
294+{
295+ if (kb)
296+ wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes,
297+ kb->num_keycodes, &kb->modifiers);
298+ else
299+ wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL);
300+}
301+
302+static inline void
303+client_send_close(Client *c)
304+{
305+#ifdef XWAYLAND
306+ if (client_is_x11(c)) {
307+ wlr_xwayland_surface_close(c->surface.xwayland);
308+ return;
309+ }
310+#endif
311+ wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel);
312+}
313+
314+static inline void
315+client_set_border_color(Client *c, const float inner[static 4], const float outer[static 4])
316+{
317+ int i;
318+ for (i = 0; i < 4; i++) {
319+ wlr_scene_rect_set_color(c->inner_border[i], inner);
320+ wlr_scene_rect_set_color(c->outer_border[i], outer);
321+ }
322+}
323+
324+static inline void
325+client_set_fullscreen(Client *c, int fullscreen)
326+{
327+#ifdef XWAYLAND
328+ if (client_is_x11(c)) {
329+ wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
330+ return;
331+ }
332+#endif
333+ wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen);
334+}
335+
336+static inline void
337+client_set_scale(struct wlr_surface *s, float scale)
338+{
339+ wlr_fractional_scale_v1_notify_scale(s, scale);
340+ wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
341+}
342+
343+static inline uint32_t
344+client_set_size(Client *c, uint32_t width, uint32_t height)
345+{
346+#ifdef XWAYLAND
347+ if (client_is_x11(c)) {
348+ wlr_xwayland_surface_configure(c->surface.xwayland,
349+ c->geom.x + c->bw, c->geom.y + c->bw, width, height);
350+ return 0;
351+ }
352+#endif
353+ if ((int32_t)width == c->surface.xdg->toplevel->current.width
354+ && (int32_t)height == c->surface.xdg->toplevel->current.height)
355+ return 0;
356+ return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
357+}
358+
359+static inline void
360+client_set_tiled(Client *c, uint32_t edges)
361+{
362+#ifdef XWAYLAND
363+ if (client_is_x11(c)) {
364+ wlr_xwayland_surface_set_maximized(c->surface.xwayland,
365+ edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE);
366+ return;
367+ }
368+#endif
369+ if (wl_resource_get_version(c->surface.xdg->toplevel->resource)
370+ >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
371+ wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
372+ } else {
373+ wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE);
374+ }
375+}
376+
377+static inline void
378+client_set_suspended(Client *c, int suspended)
379+{
380+#ifdef XWAYLAND
381+ if (client_is_x11(c))
382+ return;
383+#endif
384+
385+ wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
386+}
387+
388+static inline int
389+client_wants_focus(Client *c)
390+{
391+#ifdef XWAYLAND
392+ return client_is_unmanaged(c)
393+ && wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland)
394+ && wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE;
395+#endif
396+ return 0;
397+}
398+
399+static inline int
400+client_wants_fullscreen(Client *c)
401+{
402+#ifdef XWAYLAND
403+ if (client_is_x11(c))
404+ return c->surface.xwayland->fullscreen;
405+#endif
406+ return c->surface.xdg->toplevel->requested.fullscreen;
407+}
+5969,
-0
1@@ -0,0 +1,5969 @@
2+/*
3+ * See LICENSE file for copyright and license details.
4+ */
5+#include <getopt.h>
6+#include <libinput.h>
7+#include <linux/input-event-codes.h>
8+#include <math.h>
9+#include <signal.h>
10+#include <stdio.h>
11+#include <stdlib.h>
12+#include <sys/wait.h>
13+#include <time.h>
14+#include <unistd.h>
15+#include <wayland-server-core.h>
16+#include <wlr/backend.h>
17+#include <wlr/backend/libinput.h>
18+#include <wlr/render/allocator.h>
19+#include <wlr/render/wlr_renderer.h>
20+#include <wlr/types/wlr_alpha_modifier_v1.h>
21+#include <wlr/types/wlr_compositor.h>
22+#include <wlr/types/wlr_cursor.h>
23+#include <wlr/types/wlr_cursor_shape_v1.h>
24+#include <wlr/types/wlr_data_control_v1.h>
25+#include <wlr/types/wlr_data_device.h>
26+#include <wlr/types/wlr_drm.h>
27+#include <wlr/types/wlr_export_dmabuf_v1.h>
28+#include <wlr/types/wlr_ext_data_control_v1.h>
29+#include <wlr/types/wlr_fractional_scale_v1.h>
30+#include <wlr/types/wlr_gamma_control_v1.h>
31+#include <wlr/types/wlr_idle_inhibit_v1.h>
32+#include <wlr/types/wlr_idle_notify_v1.h>
33+#include <wlr/types/wlr_input_device.h>
34+#include <wlr/types/wlr_keyboard.h>
35+#include <wlr/types/wlr_keyboard_group.h>
36+#include <wlr/types/wlr_layer_shell_v1.h>
37+#include <wlr/types/wlr_linux_dmabuf_v1.h>
38+#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
39+#include <wlr/types/wlr_output.h>
40+#include <wlr/types/wlr_output_layout.h>
41+#include <wlr/types/wlr_output_management_v1.h>
42+#include <wlr/types/wlr_output_power_management_v1.h>
43+#include <wlr/types/wlr_pointer.h>
44+#include <wlr/types/wlr_pointer_constraints_v1.h>
45+#include <wlr/types/wlr_pointer_gestures_v1.h>
46+#include <wlr/types/wlr_presentation_time.h>
47+#include <wlr/types/wlr_primary_selection.h>
48+#include <wlr/types/wlr_primary_selection_v1.h>
49+#include <wlr/types/wlr_relative_pointer_v1.h>
50+#include <wlr/types/wlr_scene.h>
51+#include <wlr/types/wlr_screencopy_v1.h>
52+#include <wlr/types/wlr_seat.h>
53+#include <wlr/types/wlr_server_decoration.h>
54+#include <wlr/types/wlr_session_lock_v1.h>
55+#include <wlr/types/wlr_single_pixel_buffer_v1.h>
56+#include <wlr/types/wlr_subcompositor.h>
57+#include <wlr/types/wlr_viewporter.h>
58+#include <wlr/types/wlr_virtual_keyboard_v1.h>
59+#include <wlr/types/wlr_virtual_pointer_v1.h>
60+#include <wlr/types/wlr_xcursor_manager.h>
61+#include <wlr/types/wlr_xdg_activation_v1.h>
62+#include <wlr/types/wlr_xdg_decoration_v1.h>
63+#include <wlr/types/wlr_xdg_output_v1.h>
64+#include <wlr/types/wlr_xdg_shell.h>
65+#include <wlr/util/log.h>
66+#include <wlr/util/region.h>
67+#include <xkbcommon/xkbcommon.h>
68+#ifdef XWAYLAND
69+#include <wlr/xwayland.h>
70+#include <xcb/xcb.h>
71+#include <xcb/xcb_icccm.h>
72+#endif
73+/* tablet-input */
74+#include <wlr/types/wlr_tablet_pad.h>
75+#include <wlr/types/wlr_tablet_tool.h>
76+#include <wlr/types/wlr_tablet_v2.h>
77+/* tablet-input end */
78+
79+#include "util.h"
80+
81+/* macros */
82+#define MAX(A, B) ((A) > (B) ? (A) : (B))
83+#define MIN(A, B) ((A) < (B) ? (A) : (B))
84+#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
85+#define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
86+#define LENGTH(X) (sizeof X / sizeof X[0])
87+#define END(A) ((A) + LENGTH(A))
88+#define TAGMASK ((1u << TAGCOUNT) - 1)
89+#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L)))
90+#define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0)
91+
92+/* enums */
93+enum { CurNormal, CurPressed, CurMove, CurResize, CurSpawnRegion }; /* cursor */
94+enum { SnapLeft, SnapRight, SnapTop, SnapBottom,
95+ SnapTopLeft, SnapTopRight, SnapBottomLeft, SnapBottomRight }; /* labwc-style keybind snap positions */
96+enum { XDGShell, LayerShell, X11 }; /* client types */
97+enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */
98+/* click */
99+enum { ClkClient, ClkLayerShell, ClkRoot }; /* clicks */
100+/* click end */
101+typedef union {
102+ int i;
103+ uint32_t ui;
104+ float f;
105+ const void *v;
106+} Arg;
107+
108+typedef struct {
109+ /* click */
110+ unsigned int click;
111+ /* click end */
112+ unsigned int mod;
113+ unsigned int button;
114+ void (*func)(const Arg *);
115+ const Arg arg;
116+} Button;
117+
118+typedef struct Monitor Monitor;
119+typedef struct {
120+ /* Must keep this field first */
121+ unsigned int type; /* XDGShell or X11* */
122+
123+ Monitor *mon;
124+ struct wlr_scene_tree *scene;
125+
126+ struct wlr_scene_rect *inner_border[4]; /* inner top, bottom, left, right */
127+ struct wlr_scene_rect *outer_border[4]; /* outer top, bottom, left, right */
128+
129+ struct wlr_scene_tree *scene_surface;
130+ struct wl_list link;
131+ struct wl_list flink;
132+ struct wlr_box geom; /* layout-relative, includes border */
133+ struct wlr_box prev; /* layout-relative, includes border */
134+ struct wlr_box bounds; /* only width and height are used */
135+ union {
136+ struct wlr_xdg_surface *xdg;
137+ struct wlr_xwayland_surface *xwayland;
138+ } surface;
139+ struct wlr_xdg_toplevel_decoration_v1 *decoration;
140+ struct wl_listener commit;
141+ struct wl_listener map;
142+ struct wl_listener maximize;
143+ struct wl_listener unmap;
144+ struct wl_listener destroy;
145+ struct wl_listener set_title;
146+ struct wl_listener fullscreen;
147+ struct wl_listener set_decoration_mode;
148+ struct wl_listener destroy_decoration;
149+#ifdef XWAYLAND
150+ struct wl_listener activate;
151+ struct wl_listener associate;
152+ struct wl_listener dissociate;
153+ struct wl_listener configure;
154+ struct wl_listener set_hints;
155+#endif
156+ unsigned int bw;
157+ unsigned int ibw; // inner border width
158+ unsigned int obw; // outer border width
159+ uint32_t tags;
160+ int isfloating, isurgent, isfullscreen, isfakefullscreen;
161+ uint32_t resize; /* configure serial of a pending resize */
162+ /* infinite_canvas */
163+ int saved_cx, saved_cy;
164+ int saved_cw, saved_ch;
165+ int was_on_canvas;
166+ int home_cx, home_cy; /* pan value in effect when this window was placed */
167+ /* infinite_canvas_end */
168+} Client;
169+
170+/* infinite_canvas */
171+typedef struct {
172+ int cx, cy;
173+ int saved_cx, saved_cy;
174+} CanvasOffset;
175+/* infinite_canvas_end */
176+
177+typedef struct {
178+ uint32_t mod;
179+ xkb_keysym_t keysym;
180+ void (*func)(const Arg *);
181+ const Arg arg;
182+} Key;
183+
184+typedef struct {
185+ struct wlr_keyboard_group *wlr_group;
186+
187+ int nsyms;
188+ const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */
189+ uint32_t mods; /* invalid if nsyms == 0 */
190+ struct wl_event_source *key_repeat_source;
191+
192+ struct wl_listener modifiers;
193+ struct wl_listener key;
194+ struct wl_listener destroy;
195+} KeyboardGroup;
196+
197+typedef struct {
198+ /* Must keep this field first */
199+ unsigned int type; /* LayerShell */
200+
201+ Monitor *mon;
202+ struct wlr_scene_tree *scene;
203+ struct wlr_scene_tree *popups;
204+ struct wlr_scene_layer_surface_v1 *scene_layer;
205+ struct wl_list link;
206+ int mapped;
207+ struct wlr_layer_surface_v1 *layer_surface;
208+
209+ struct wl_listener destroy;
210+ struct wl_listener unmap;
211+ struct wl_listener surface_commit;
212+} LayerSurface;
213+
214+typedef struct {
215+ const char *symbol;
216+ void (*arrange)(Monitor *);
217+} Layout;
218+
219+typedef struct {
220+ struct wl_list link;
221+ char name[64];
222+ unsigned int seltags;
223+ unsigned int sellt;
224+ uint32_t tagset[2];
225+ float mfact;
226+ int nmaster;
227+ char ltsymbol[16];
228+ const Layout *lt[2];
229+ CanvasOffset *canvas;
230+ double zoom_scale;
231+ double zoom_target;
232+ double zoom_pan_x;
233+ double zoom_pan_y;
234+} MonitorState;
235+
236+struct Monitor {
237+ struct wl_list link;
238+ struct wlr_output *wlr_output;
239+ struct wlr_scene_output *scene_output;
240+ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
241+ struct wl_listener frame;
242+ struct wl_listener destroy;
243+ struct wl_listener request_state;
244+ struct wl_listener destroy_lock_surface;
245+ struct wlr_session_lock_surface_v1 *lock_surface;
246+ struct wlr_box m; /* monitor area, layout-relative */
247+ struct wlr_box w; /* window area, layout-relative */
248+ struct wl_list layers[4]; /* LayerSurface.link */
249+ const Layout *lt[2];
250+ unsigned int seltags;
251+ unsigned int sellt;
252+ uint32_t tagset[2];
253+ float mfact;
254+ int gamma_lut_changed;
255+ int nmaster;
256+ char ltsymbol[16];
257+ int asleep;
258+ /* infinite_canvas */
259+ CanvasOffset *canvas;
260+ /* infinite_canvas_end */
261+ /* zoom */
262+ double zoom_scale;
263+ double zoom_target;
264+ double zoom_pan_x, zoom_pan_y;
265+ double zoom_anchor_fx, zoom_anchor_fy;
266+ double zoom_anchor_lx, zoom_anchor_ly;
267+ double zoom_anim_t;
268+ double zoom_start_scale;
269+ double zoom_start_pan_x, zoom_start_pan_y;
270+ int zoom_popups_dirty;
271+ /* zoom end */
272+#ifdef TILING
273+ int enablegaps; /* enable gaps, used by togglegaps */
274+ int gappih; /* horizontal gap between windows */
275+ int gappiv; /* vertical gap between windows */
276+ int gappoh; /* horizontal outer gaps */
277+ int gappov; /* vertical outer gaps */
278+#endif
279+};
280+
281+typedef struct {
282+ const char *name;
283+ float mfact;
284+ int nmaster;
285+ float scale;
286+ const Layout *lt;
287+ enum wl_output_transform rr;
288+ int x, y;
289+ int resx;
290+ int resy;
291+ float rate;
292+ int mode;
293+ int adaptive;
294+} MonitorRule;
295+
296+typedef struct {
297+ struct wlr_pointer_constraint_v1 *constraint;
298+ struct wl_listener destroy;
299+} PointerConstraint;
300+
301+typedef struct {
302+ const char *id;
303+ const char *title;
304+ uint32_t tags;
305+ int isfloating;
306+ int monitor;
307+} Rule;
308+
309+typedef struct {
310+ struct wlr_scene_tree *scene;
311+
312+ struct wlr_session_lock_v1 *lock;
313+ struct wl_listener new_surface;
314+ struct wl_listener unlock;
315+ struct wl_listener destroy;
316+} SessionLock;
317+
318+/* tablet-input */
319+typedef struct {
320+ uint32_t button;
321+ uint32_t state;
322+ void (*func)(const Arg *);
323+ const Arg arg;
324+} TabletPadButton;
325+
326+typedef struct {
327+ uint32_t button;
328+ uint32_t state;
329+ void (*func)(const Arg *);
330+ const Arg arg;
331+} TabletButton;
332+/* tablet-input end */
333+
334+/* function declarations */
335+static void applybounds(Client *c, struct wlr_box *bbox);
336+static void applyrules(Client *c);
337+static void arrange(Monitor *m);
338+static void arrangelayer(Monitor *m, struct wl_list *list,
339+ struct wlr_box *usable_area, int exclusive);
340+static void arrangelayers(Monitor *m);
341+static void axisnotify(struct wl_listener *listener, void *data);
342+static void buttonpress(struct wl_listener *listener, void *data);
343+static void chvt(const Arg *arg);
344+static void checkidleinhibitor(struct wlr_surface *exclude);
345+static void cleanup(void);
346+static void cleanupmon(struct wl_listener *listener, void *data);
347+static void cleanuplisteners(void);
348+static void closemon(Monitor *m);
349+static void commitlayersurfacenotify(struct wl_listener *listener, void *data);
350+static void commitnotify(struct wl_listener *listener, void *data);
351+static void commitpopup(struct wl_listener *listener, void *data);
352+static void createdecoration(struct wl_listener *listener, void *data);
353+static void createidleinhibitor(struct wl_listener *listener, void *data);
354+static void createkeyboard(struct wlr_keyboard *keyboard);
355+static KeyboardGroup *createkeyboardgroup(void);
356+static void createlayersurface(struct wl_listener *listener, void *data);
357+static void createlocksurface(struct wl_listener *listener, void *data);
358+static void createmon(struct wl_listener *listener, void *data);
359+static void createnotify(struct wl_listener *listener, void *data);
360+static void createpointer(struct wlr_pointer *pointer);
361+static void createpointerconstraint(struct wl_listener *listener, void *data);
362+static void createpopup(struct wl_listener *listener, void *data);
363+static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint);
364+static void cursorframe(struct wl_listener *listener, void *data);
365+static void cursorwarptohint(void);
366+static void destroydecoration(struct wl_listener *listener, void *data);
367+static void destroydragicon(struct wl_listener *listener, void *data);
368+static void destroyidleinhibitor(struct wl_listener *listener, void *data);
369+static void destroylayersurfacenotify(struct wl_listener *listener, void *data);
370+static void destroylock(SessionLock *lock, int unlocked);
371+static void destroylocksurface(struct wl_listener *listener, void *data);
372+static void destroynotify(struct wl_listener *listener, void *data);
373+static void destroypointerconstraint(struct wl_listener *listener, void *data);
374+static void destroysessionlock(struct wl_listener *listener, void *data);
375+static void destroykeyboardgroup(struct wl_listener *listener, void *data);
376+static Monitor *dirtomon(enum wlr_direction dir);
377+static void focusclient(Client *c, int lift);
378+static void focusmon(const Arg *arg);
379+static void focusstack(const Arg *arg);
380+static Client *focustop(Monitor *m);
381+static void fullscreennotify(struct wl_listener *listener, void *data);
382+static void gpureset(struct wl_listener *listener, void *data);
383+static void handlesig(int signo);
384+static void incnmaster(const Arg *arg);
385+static void inputdevice(struct wl_listener *listener, void *data);
386+static int keybinding(uint32_t mods, xkb_keysym_t sym);
387+static void keypress(struct wl_listener *listener, void *data);
388+static void keypressmod(struct wl_listener *listener, void *data);
389+static int keyrepeat(void *data);
390+static void killclient(const Arg *arg);
391+static void locksession(struct wl_listener *listener, void *data);
392+static void mapnotify(struct wl_listener *listener, void *data);
393+static void maximizenotify(struct wl_listener *listener, void *data);
394+static void monocle(Monitor *m);
395+static void motionabsolute(struct wl_listener *listener, void *data);
396+static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx,
397+ double sy, double sx_unaccel, double sy_unaccel);
398+static void motionrelative(struct wl_listener *listener, void *data);
399+static void moveresize(const Arg *arg);
400+static void outputmgrapply(struct wl_listener *listener, void *data);
401+static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test);
402+static void outputmgrtest(struct wl_listener *listener, void *data);
403+static void pointerfocus(Client *c, struct wlr_surface *surface,
404+ double sx, double sy, uint32_t time);
405+static void printstatus(void);
406+static void powermgrsetmode(struct wl_listener *listener, void *data);
407+static void quit(const Arg *arg);
408+static void rendermon(struct wl_listener *listener, void *data);
409+static void requestdecorationmode(struct wl_listener *listener, void *data);
410+static void requeststartdrag(struct wl_listener *listener, void *data);
411+static void requestmonstate(struct wl_listener *listener, void *data);
412+static void resize(Client *c, struct wlr_box geo, int interact);
413+static void run(char *startup_cmd);
414+static void setcursor(struct wl_listener *listener, void *data);
415+static void setcursorshape(struct wl_listener *listener, void *data);
416+static void setfloating(Client *c, int floating);
417+static void setfullscreen(Client *c, int fullscreen);
418+static void setlayout(const Arg *arg);
419+static void setmfact(const Arg *arg);
420+static void setmon(Client *c, Monitor *m, uint32_t newtags);
421+static void setpsel(struct wl_listener *listener, void *data);
422+static void setsel(struct wl_listener *listener, void *data);
423+static void setup(void);
424+static void spawn(const Arg *arg);
425+static void startdrag(struct wl_listener *listener, void *data);
426+static void tag(const Arg *arg);
427+static void tagmon(const Arg *arg);
428+#ifdef TILING
429+static void tile(Monitor *m);
430+static void togglefloating(const Arg *arg);
431+#endif // togglefloating is in #ifdef too because why would you need it without tiling?
432+static void togglefullscreen(const Arg *arg);
433+static void togglefakefullscreen(const Arg *arg);
434+static void snaptoposition(const Arg *arg);
435+static void toggletag(const Arg *arg);
436+static void toggleview(const Arg *arg);
437+static void unlocksession(struct wl_listener *listener, void *data);
438+static void unmaplayersurfacenotify(struct wl_listener *listener, void *data);
439+static void unmapnotify(struct wl_listener *listener, void *data);
440+static void updatemons(struct wl_listener *listener, void *data);
441+static void updatetitle(struct wl_listener *listener, void *data);
442+static void urgent(struct wl_listener *listener, void *data);
443+static void view(const Arg *arg);
444+static void virtualkeyboard(struct wl_listener *listener, void *data);
445+static void virtualpointer(struct wl_listener *listener, void *data);
446+static Monitor *xytomon(double x, double y);
447+static void xytonode(double x, double y, struct wlr_surface **psurface,
448+ Client **pc, LayerSurface **pl, double *nx, double *ny);
449+static void swapmaster(const Arg *arg);
450+/* infinite_canvas */
451+static int getcurrenttag(Monitor *m);
452+static void canvas_move_window(Client *c, int x, int y);
453+static void snap_edges(Client *c, Monitor *m, int *nx, int *ny);
454+static void resize_magnetically(Client *c, Monitor *m, struct wlr_box *geo);
455+static void homecanvas(const Arg *arg);
456+static void movecanvas(const Arg *arg);
457+static void movewinbykeys(const Arg *arg);
458+static void resizewinbykeys(const Arg *arg);
459+static void movecanvasmouse(const Arg *arg);
460+static void centerwindow(const Arg *arg);
461+static void bringintoview(Client *c);
462+static void save_canvas_positions(Monitor *m);
463+static void restore_canvas_positions(Monitor *m);
464+/* infinite_canvas_end */
465+/* zoom */
466+static void zoom(const Arg *arg);
467+static void zoomreset(const Arg *arg);
468+static void zoom_apply(Monitor *m);
469+static void zoom_update_scene_client(Client *c);
470+static void zoom_scale_buffers(struct wlr_scene_buffer *buf, int sx, int sy, void *data);
471+static void zoom_frame(Monitor *m);
472+static void popup_zoom_commit(struct wl_listener *listener, void *data);
473+static void popup_zoom_destroy(struct wl_listener *listener, void *data);
474+/* zoom end */
475+/* spawnregion */
476+static void spawnregion(const Arg *arg);
477+static void spawnregion_update_rect(void);
478+static void spawnregion_finish(void);
479+/* spawnregion_end */
480+#ifdef TILING
481+void arrangegaps(Monitor *m);
482+static void defaultgaps(const Arg *arg);
483+static void incgaps(const Arg *arg);
484+static void incigaps(const Arg *arg);
485+static void incihgaps(const Arg *arg);
486+static void incivgaps(const Arg *arg);
487+static void incogaps(const Arg *arg);
488+static void incohgaps(const Arg *arg);
489+static void incovgaps(const Arg *arg);
490+static void preparegaps(Monitor *m);
491+static void setgaps(int oh, int ov, int ih, int iv);
492+static void togglegaps(const Arg *arg);
493+#endif
494+/* tablet-input */
495+static void createtablet(struct wlr_input_device *device);
496+static void destroytablet(struct wl_listener *listener, void *data);
497+static void destroytabletpad(struct wl_listener *listener, void *data);
498+static void destroytabletsurfacenotify(struct wl_listener *listener, void *data);
499+static void destroytablettool(struct wl_listener *listener, void *data);
500+static void tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y, double x, double y, double dx, double dy);
501+static void tablettoolproximity(struct wl_listener *listener, void *data);
502+static void tabletpadbutton(struct wl_listener *listener, void *data);
503+static void tablettoolaxis(struct wl_listener *listener, void *data);
504+static void tablettoolbutton(struct wl_listener *listener, void *data);
505+static void tablettooltip(struct wl_listener *listener, void *data);
506+/* tablet-input end */
507+
508+/* variables */
509+static pid_t child_pid = -1;
510+static int locked;
511+static void *exclusive_focus;
512+static struct wl_display *dpy;
513+static struct wl_event_loop *event_loop;
514+static struct wlr_backend *backend;
515+static struct wlr_scene *scene;
516+static struct wlr_scene_tree *layers[NUM_LAYERS];
517+static struct wlr_scene_tree *drag_icon;
518+/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */
519+static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay };
520+static struct wlr_renderer *drw;
521+static struct wlr_allocator *alloc;
522+static struct wlr_compositor *compositor;
523+static struct wlr_session *session;
524+
525+/* tablet-input */
526+static struct wlr_tablet_manager_v2 *tablet_mgr;
527+static struct wlr_tablet_v2_tablet *tablet = NULL;
528+static struct wlr_tablet_v2_tablet_tool *tablet_tool = NULL;
529+static struct wlr_tablet_v2_tablet_pad *tablet_pad = NULL;
530+static struct wlr_surface *tablet_curr_surface = NULL;
531+static struct wl_listener destroy_tablet_surface_listener = {.notify = destroytabletsurfacenotify};
532+
533+static struct wl_listener tablet_device_destroy = {.notify = destroytablet};
534+static struct wl_listener tablet_pad_destroy = {.notify = destroytabletpad};
535+static struct wl_listener tablet_pad_button = {.notify = tabletpadbutton};
536+static struct wl_listener tablet_tool_axis = {.notify = tablettoolaxis};
537+static struct wl_listener tablet_tool_button = {.notify = tablettoolbutton};
538+static struct wl_listener tablet_tool_destroy = {.notify = destroytablettool};
539+static struct wl_listener tablet_tool_proximity = {.notify = tablettoolproximity};
540+static struct wl_listener tablet_tool_tip = {.notify = tablettooltip};
541+/* tablet-input end */
542+
543+static struct wlr_xdg_shell *xdg_shell;
544+static struct wlr_xdg_activation_v1 *activation;
545+static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
546+static struct wl_list clients; /* tiling order */
547+static struct wl_list fstack; /* focus order */
548+static struct wlr_idle_notifier_v1 *idle_notifier;
549+static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr;
550+static struct wlr_layer_shell_v1 *layer_shell;
551+static struct wlr_output_manager_v1 *output_mgr;
552+static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
553+static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
554+static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
555+static struct wlr_output_power_manager_v1 *power_mgr;
556+
557+static struct wlr_pointer_constraints_v1 *pointer_constraints;
558+static struct wlr_pointer_gestures_v1 *pointer_gestures;
559+static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
560+static struct wlr_pointer_constraint_v1 *active_constraint;
561+
562+static struct wlr_cursor *cursor;
563+static struct wlr_xcursor_manager *cursor_mgr;
564+
565+static struct wlr_scene_rect *root_bg;
566+static struct wlr_session_lock_manager_v1 *session_lock_mgr;
567+static struct wlr_scene_rect *locked_bg;
568+static struct wlr_session_lock_v1 *cur_lock;
569+
570+static struct wlr_seat *seat;
571+static KeyboardGroup *kb_group;
572+static unsigned int cursor_mode;
573+static Client *grabc;
574+static int grabcx, grabcy; /* client-relative */
575+static struct wl_event_source *openanim_timer = NULL;
576+static Client *openanim_client = NULL;
577+static uint64_t openanim_start_ms = 0;
578+static int openanim_running = 0;
579+/* zoom */
580+static double resize_grab_world_x = 0.0;
581+static double resize_grab_world_y = 0.0;
582+static int resize_snap_x_target = 0;
583+static int resize_snap_y_target = 0;
584+typedef struct {
585+ struct wl_list link;
586+ struct wl_listener commit;
587+ struct wl_listener destroy;
588+ Client *client;
589+ struct wlr_xdg_popup *popup;
590+ struct wlr_scene_node *node;
591+} PopupZoomData;
592+static struct wl_list popup_zoom_list;
593+/* zoom end */
594+
595+/* infinite_canvas */
596+static int canvas_panning = 0;
597+static double canvas_pan_last_x = 0, canvas_pan_last_y = 0;
598+static float canvas_pan_multiplier = 1.0f;
599+static float canvas_pan_accum_x = 0.0f, canvas_pan_accum_y = 0.0f;
600+/* infinite_canvas_end */
601+/* spawnregion */
602+static int spawnregion_active = 0;
603+static double spawnregion_x1 = 0, spawnregion_y1 = 0;
604+static double spawnregion_x2 = 0, spawnregion_y2 = 0;
605+static struct wlr_scene_rect *spawnregion_rect = NULL;
606+static struct wlr_box pending_spawn_geo = {0};
607+static int has_pending_spawn_geo = 0;
608+Client *waiting_geo_client = NULL;
609+/* spawnregion_end */
610+
611+static struct wlr_output_layout *output_layout;
612+static struct wlr_box sgeom;
613+static struct wl_list mons;
614+static Monitor *selmon;
615+static struct wl_list savedstates;
616+
617+#ifdef TILING
618+static int resizelock = 0; /* do not actually resize during arrange */
619+#endif
620+
621+/* global event handlers */
622+static struct wl_listener cursor_axis = {.notify = axisnotify};
623+static struct wl_listener cursor_button = {.notify = buttonpress};
624+static struct wl_listener cursor_frame = {.notify = cursorframe};
625+static struct wl_listener cursor_motion = {.notify = motionrelative};
626+static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute};
627+static struct wl_listener gpu_reset = {.notify = gpureset};
628+static struct wl_listener layout_change = {.notify = updatemons};
629+static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor};
630+static struct wl_listener new_input_device = {.notify = inputdevice};
631+static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard};
632+static struct wl_listener new_virtual_pointer = {.notify = virtualpointer};
633+static struct wl_listener new_pointer_constraint = {.notify = createpointerconstraint};
634+static struct wl_listener new_output = {.notify = createmon};
635+static struct wl_listener new_xdg_toplevel = {.notify = createnotify};
636+static struct wl_listener new_xdg_popup = {.notify = createpopup};
637+static struct wl_listener new_xdg_decoration = {.notify = createdecoration};
638+static struct wl_listener new_layer_surface = {.notify = createlayersurface};
639+static struct wl_listener output_mgr_apply = {.notify = outputmgrapply};
640+static struct wl_listener output_mgr_test = {.notify = outputmgrtest};
641+static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode};
642+static struct wl_listener request_activate = {.notify = urgent};
643+static struct wl_listener request_cursor = {.notify = setcursor};
644+static struct wl_listener request_set_psel = {.notify = setpsel};
645+static struct wl_listener request_set_sel = {.notify = setsel};
646+static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape};
647+static struct wl_listener request_start_drag = {.notify = requeststartdrag};
648+static struct wl_listener start_drag = {.notify = startdrag};
649+static struct wl_listener new_session_lock = {.notify = locksession};
650+
651+#ifdef XWAYLAND
652+static void activatex11(struct wl_listener *listener, void *data);
653+static void associatex11(struct wl_listener *listener, void *data);
654+static void configurex11(struct wl_listener *listener, void *data);
655+static void createnotifyx11(struct wl_listener *listener, void *data);
656+static void dissociatex11(struct wl_listener *listener, void *data);
657+static void sethints(struct wl_listener *listener, void *data);
658+static void xwaylandready(struct wl_listener *listener, void *data);
659+static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11};
660+static struct wl_listener xwayland_ready = {.notify = xwaylandready};
661+static struct wlr_xwayland *xwayland;
662+#endif
663+
664+/* zoom */
665+#define SCROLL_UP 0x14b
666+#define SCROLL_DOWN 0x14c
667+/* zoom end */
668+
669+/* configuration, allows nested code to access above variables */
670+#include "config.h"
671+
672+/* spawnregion, define it after config.h was included so SPAWNREGION_STYLE is defined */
673+#if SPAWNREGION_STYLE == 2
674+static struct wlr_scene_rect *spawnregion_border[8] = {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
675+#endif
676+/* spawnregion end */
677+
678+#ifndef SPAWNREGION_COLOR
679+#define SPAWNREGION_COLOR { 0.2f, 0.5f, 1.0f, 0.35f }
680+#endif
681+
682+/* zoom */
683+#ifndef ZOOMMIN
684+#define ZOOMMIN 0.1
685+#endif
686+#ifndef ZOOMMAX
687+#define ZOOMMAX 5.0
688+#endif
689+#ifndef animatezoom
690+#define animatezoom 1
691+#endif
692+#ifndef zoomduration
693+#define zoomduration 0.18
694+#endif
695+#ifndef zoombezier
696+#define zoombezier "0.22,1.0,0.36,1.0"
697+#endif
698+#ifndef movecanvasmouse_multiplier_is_affected_by_zoom
699+#define movecanvasmouse_multiplier_is_affected_by_zoom 1
700+#endif
701+/* zoom end */
702+
703+/* attempt to encapsulate suck into one file */
704+#include "client.h"
705+
706+/* function implementations */
707+void
708+applybounds(Client *c, struct wlr_box *bbox)
709+{
710+ /* set minimum possible */
711+ c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
712+ c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height);
713+
714+ if (c->geom.x >= bbox->x + bbox->width)
715+ c->geom.x = bbox->x + bbox->width - c->geom.width;
716+ if (c->geom.y >= bbox->y + bbox->height)
717+ c->geom.y = bbox->y + bbox->height - c->geom.height;
718+ if (c->geom.x + c->geom.width <= bbox->x)
719+ c->geom.x = bbox->x;
720+ if (c->geom.y + c->geom.height <= bbox->y)
721+ c->geom.y = bbox->y;
722+}
723+
724+void
725+applyrules(Client *c)
726+{
727+ /* rule matching */
728+ const char *appid, *title;
729+ uint32_t newtags = 0;
730+ int i;
731+ const Rule *r;
732+ Monitor *mon = selmon, *m;
733+
734+ appid = client_get_appid(c);
735+ title = client_get_title(c);
736+
737+ for (r = rules; r < END(rules); r++) {
738+ if ((!r->title || strstr(title, r->title))
739+ && (!r->id || strstr(appid, r->id))) {
740+ c->isfloating = r->isfloating;
741+ newtags |= r->tags;
742+ i = 0;
743+ wl_list_for_each(m, &mons, link) {
744+ if (r->monitor == i++)
745+ mon = m;
746+ }
747+ }
748+ }
749+
750+ c->isfloating |= client_is_float_type(c);
751+ setmon(c, mon, newtags);
752+}
753+
754+void
755+arrange(Monitor *m)
756+{
757+ Client *c;
758+
759+ if (!m->wlr_output->enabled)
760+ return;
761+
762+ wl_list_for_each(c, &clients, link) {
763+ if (c->mon == m) {
764+ wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m) && c != waiting_geo_client);
765+ client_set_suspended(c, !VISIBLEON(c, m));
766+ }
767+ }
768+
769+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
770+ (c = focustop(m)) && c->isfullscreen);
771+
772+ strncpy(m->ltsymbol, layouts[0].symbol, LENGTH(m->ltsymbol));
773+
774+ /* We move all clients (except fullscreen and unmanaged) to LyrTile while
775+ * in floating layout to avoid "real" floating clients be always on top */
776+ wl_list_for_each(c, &clients, link) {
777+ if (c->mon != m || c->scene->node.parent == layers[LyrFS])
778+ continue;
779+
780+ wlr_scene_node_reparent(&c->scene->node,
781+ (m->lt[m->sellt] && !m->lt[m->sellt]->arrange && c->isfloating)
782+ ? layers[LyrTile]
783+ : (m->lt[m->sellt] && m->lt[m->sellt]->arrange && c->isfloating)
784+ ? layers[LyrFloat]
785+ : c->scene->node.parent);
786+ }
787+
788+ if (m->lt[m->sellt] && m->lt[m->sellt]->arrange) {
789+#ifdef TILING
790+ preparegaps(m);
791+#endif
792+ m->lt[m->sellt]->arrange(m);
793+#ifdef TILING
794+ arrangegaps(m);
795+#endif
796+ }
797+ /* zoom */
798+ if (layouts[m->sellt].arrange == NULL)
799+ zoom_apply(m);
800+ /* zoom end */
801+ motionnotify(0, NULL, 0, 0, 0, 0);
802+ checkidleinhibitor(NULL);
803+}
804+
805+void
806+arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive)
807+{
808+ LayerSurface *l;
809+ struct wlr_box full_area = m->m;
810+
811+ wl_list_for_each(l, list, link) {
812+ struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
813+
814+ if (!layer_surface->initialized)
815+ continue;
816+
817+ if (exclusive != (layer_surface->current.exclusive_zone > 0))
818+ continue;
819+
820+ wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area);
821+ wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y);
822+ }
823+}
824+
825+void
826+arrangelayers(Monitor *m)
827+{
828+ int i;
829+ struct wlr_box usable_area = m->m;
830+ LayerSurface *l;
831+ uint32_t layers_above_shell[] = {
832+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
833+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
834+ };
835+ if (!m->wlr_output->enabled)
836+ return;
837+
838+ /* Arrange exclusive surfaces from top->bottom */
839+ for (i = 3; i >= 0; i--)
840+ arrangelayer(m, &m->layers[i], &usable_area, 1);
841+
842+ if (!wlr_box_equal(&usable_area, &m->w)) {
843+ m->w = usable_area;
844+ arrange(m);
845+ }
846+
847+ /* Arrange non-exclusive surfaces from top->bottom */
848+ for (i = 3; i >= 0; i--)
849+ arrangelayer(m, &m->layers[i], &usable_area, 0);
850+
851+ /* Find topmost keyboard interactive layer, if such a layer exists */
852+ for (i = 0; i < (int)LENGTH(layers_above_shell); i++) {
853+ wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
854+ if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped)
855+ continue;
856+ /* Deactivate the focused client. */
857+ focusclient(NULL, 0);
858+ exclusive_focus = l;
859+ client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
860+ return;
861+ }
862+ }
863+}
864+
865+void
866+axisnotify(struct wl_listener *listener, void *data)
867+{
868+ /* click */
869+ unsigned int click;
870+ Client *c;
871+ LayerSurface *l;
872+ /* click end */
873+ struct wlr_pointer_axis_event *event = data;
874+ struct wlr_keyboard *keyboard;
875+ uint32_t mods;
876+ const Button *b;
877+
878+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
879+
880+ /* click */
881+ click = ClkRoot;
882+ xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
883+ if (c)
884+ click = ClkClient;
885+ else if (l)
886+ click = ClkLayerShell;
887+ /* click end */
888+
889+ keyboard = wlr_seat_get_keyboard(seat);
890+ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
891+
892+ if (event->orientation == WL_POINTER_AXIS_VERTICAL_SCROLL) {
893+ /* zoom */
894+ unsigned int btn = (event->delta > 0 || event->delta_discrete > 0)
895+ ? SCROLL_DOWN : SCROLL_UP;
896+ for (b = buttons; b < END(buttons); b++) {
897+ if (CLEANMASK(mods) == CLEANMASK(b->mod)
898+ // / click /
899+ && btn == b->button && click == b->click && b->func) {
900+ b->func(&b->arg);
901+ return;
902+ }
903+ }
904+ /* zoom end */
905+ }
906+
907+ wlr_seat_pointer_notify_axis(seat,
908+ event->time_msec, event->orientation, event->delta,
909+ event->delta_discrete, event->source, event->relative_direction);
910+}
911+
912+void
913+buttonpress(struct wl_listener *listener, void *data)
914+{
915+ /* click */
916+ unsigned int click;
917+ /* click end */
918+ struct wlr_pointer_button_event *event = data;
919+ struct wlr_keyboard *keyboard;
920+ uint32_t mods;
921+ Client *c;
922+ LayerSurface *l;
923+ const Button *b;
924+
925+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
926+
927+ /* click */
928+ click = ClkRoot;
929+ xytonode(cursor->x, cursor->y, NULL, &c, &l, NULL, NULL);
930+ if (c)
931+ click = ClkClient;
932+ else if (l)
933+ click = ClkLayerShell;
934+ wlr_log(WLR_DEBUG, "buttonpress: c=%p l=%p click=%u (0=Client,1=LayerShell,2=Root)", (void*)c, (void*)l, click);
935+ /* click end */
936+
937+ switch (event->state) {
938+ case WL_POINTER_BUTTON_STATE_PRESSED:
939+ cursor_mode = CurPressed;
940+ selmon = xytomon(cursor->x, cursor->y);
941+ if (locked)
942+ break;
943+
944+ /* spawnregion */
945+ if (spawnregion_active && event->button == BTN_LEFT) {
946+ spawnregion_x1 = cursor->x;
947+ spawnregion_y1 = cursor->y;
948+ spawnregion_x2 = cursor->x;
949+ spawnregion_y2 = cursor->y;
950+#if SPAWNREGION_STYLE == 2
951+ {
952+ int i;
953+ for (i = 0; i < 4; i++) {
954+ if (!spawnregion_border[i]) {
955+ const float *oc = spawnregion_outer_border_color;
956+ spawnregion_border[i] = wlr_scene_rect_create(layers[LyrBlock],
957+ 1, 1, (float[4]){oc[0], oc[1], oc[2], oc[3]});
958+ }
959+ }
960+ for (i = 4; i < 8; i++) {
961+ if (!spawnregion_border[i]) {
962+ const float *ic = spawnregion_inner_border_color;
963+ spawnregion_border[i] = wlr_scene_rect_create(layers[LyrBlock],
964+ 1, 1, (float[4]){ic[0], ic[1], ic[2], ic[3]});
965+ }
966+ }
967+ }
968+#else
969+ if (!spawnregion_rect)
970+# if SPAWNREGION_STYLE == 1
971+ spawnregion_rect = wlr_scene_rect_create(layers[LyrBlock],
972+ 1, 1, (float[4])SPAWNREGION_COLOR);
973+# else
974+ spawnregion_rect = wlr_scene_rect_create(layers[LyrBlock],
975+ 1, 1, (float[4]){0.2f, 0.5f, 1.0f, 0.0f});
976+# endif
977+ wlr_scene_node_set_position(&spawnregion_rect->node,
978+ (int)spawnregion_x1, (int)spawnregion_y1);
979+ wlr_scene_rect_set_size(spawnregion_rect, 1, 1);
980+ wlr_scene_node_set_enabled(&spawnregion_rect->node, 1);
981+#endif
982+ cursor_mode = CurSpawnRegion;
983+ return;
984+ }
985+ /* spawnregion_end */
986+
987+ /* Change focus if the button was _pressed_ over a client */
988+ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL);
989+ // if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) // not click
990+ if (click == ClkClient && (!client_is_unmanaged(c) || client_wants_focus(c))) // click
991+ focusclient(c, 1);
992+
993+ keyboard = wlr_seat_get_keyboard(seat);
994+ mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
995+ for (b = buttons; b < END(buttons); b++) {
996+ if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
997+ // / click /
998+ event->button == b->button && click == b->click && b->func) {
999+ b->func(&b->arg);
1000+ return;
1001+ }
1002+ }
1003+ break;
1004+ case WL_POINTER_BUTTON_STATE_RELEASED:
1005+ /* If you released any buttons, we exit interactive move/resize mode. */
1006+ /* TODO: should reset to the pointer focus's current setcursor */
1007+ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
1008+ /* infinite_canvas */
1009+ if (canvas_panning) {
1010+ canvas_panning = 0;
1011+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
1012+ cursor_mode = CurNormal;
1013+ return;
1014+ }
1015+ /* infinite_canvas_end */
1016+ /* spawnregion */
1017+ if (cursor_mode == CurSpawnRegion) {
1018+ spawnregion_finish();
1019+ return;
1020+ }
1021+ /* spawnregion_end */
1022+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
1023+ cursor_mode = CurNormal;
1024+ /* infinite_canvas: a manual move/resize just finished
1025+ * under whatever pan is currently active -- that's now
1026+ * this window's natural framing for future teleports. */
1027+ if (grabc && grabc->mon) {
1028+ int tagidx = getcurrenttag(grabc->mon);
1029+ grabc->home_cx = grabc->mon->canvas[tagidx].cx;
1030+ grabc->home_cy = grabc->mon->canvas[tagidx].cy;
1031+ }
1032+ /* infinite_canvas_end */
1033+ /* Drop the window off on its new monitor */
1034+ selmon = xytomon(cursor->x, cursor->y);
1035+ setmon(grabc, selmon, 0);
1036+ grabc = NULL;
1037+ return;
1038+ }
1039+ cursor_mode = CurNormal;
1040+ break;
1041+ }
1042+ /* If the event wasn't handled by the compositor, notify the client with
1043+ * pointer focus that a button press has occurred */
1044+ wlr_seat_pointer_notify_button(seat,
1045+ event->time_msec, event->button, event->state);
1046+}
1047+
1048+void
1049+chvt(const Arg *arg)
1050+{
1051+ wlr_session_change_vt(session, arg->ui);
1052+}
1053+
1054+void
1055+checkidleinhibitor(struct wlr_surface *exclude)
1056+{
1057+ int inhibited = 0, unused_lx, unused_ly;
1058+ struct wlr_idle_inhibitor_v1 *inhibitor;
1059+ wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) {
1060+ struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface);
1061+ struct wlr_scene_tree *tree = surface->data;
1062+ if (exclude != surface && (bypass_surface_visibility || (!tree
1063+ || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) {
1064+ inhibited = 1;
1065+ break;
1066+ }
1067+ }
1068+
1069+ wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
1070+}
1071+
1072+void
1073+cleanup(void)
1074+{
1075+ cleanuplisteners();
1076+#ifdef XWAYLAND
1077+ wlr_xwayland_destroy(xwayland);
1078+ xwayland = NULL;
1079+#endif
1080+ wl_display_destroy_clients(dpy);
1081+ if (child_pid > 0) {
1082+ kill(-child_pid, SIGTERM);
1083+ waitpid(child_pid, NULL, 0);
1084+ }
1085+ wlr_xcursor_manager_destroy(cursor_mgr);
1086+
1087+ destroykeyboardgroup(&kb_group->destroy, NULL);
1088+
1089+ /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat.
1090+ * Destroy it until it's fixed on the wlroots side */
1091+ wlr_backend_destroy(backend);
1092+
1093+ wl_display_destroy(dpy);
1094+ /* Destroy after the wayland display (when the monitors are already destroyed)
1095+ to avoid destroying them with an invalid scene output. */
1096+ wlr_scene_node_destroy(&scene->tree.node);
1097+}
1098+
1099+void
1100+cleanupmon(struct wl_listener *listener, void *data)
1101+{
1102+ Monitor *m = wl_container_of(listener, m, destroy);
1103+ LayerSurface *l, *tmp;
1104+ size_t i;
1105+
1106+ /* m->layers[i] are intentionally not unlinked */
1107+ for (i = 0; i < LENGTH(m->layers); i++) {
1108+ wl_list_for_each_safe(l, tmp, &m->layers[i], link)
1109+ wlr_layer_surface_v1_destroy(l->layer_surface);
1110+ }
1111+
1112+ wl_list_remove(&m->destroy.link);
1113+ wl_list_remove(&m->frame.link);
1114+ wl_list_remove(&m->link);
1115+ wl_list_remove(&m->request_state.link);
1116+ if (m->lock_surface)
1117+ destroylocksurface(&m->destroy_lock_surface, NULL);
1118+ m->wlr_output->data = NULL;
1119+ wlr_output_layout_remove(output_layout, m->wlr_output);
1120+ wlr_scene_output_destroy(m->scene_output);
1121+
1122+ closemon(m);
1123+ wlr_scene_node_destroy(&m->fullscreen_bg->node);
1124+ /* infinite_canvas */
1125+ {
1126+ MonitorState *ms, *tmp_ms;
1127+ wl_list_for_each_safe(ms, tmp_ms, &savedstates, link) {
1128+ if (strcmp(ms->name, m->wlr_output->name) == 0) {
1129+ wl_list_remove(&ms->link);
1130+ free(ms->canvas);
1131+ free(ms);
1132+ break;
1133+ }
1134+ }
1135+ ms = ecalloc(1, sizeof(MonitorState));
1136+ strncpy(ms->name, m->wlr_output->name, sizeof(ms->name) - 1);
1137+ ms->seltags = m->seltags;
1138+ ms->sellt = m->sellt;
1139+ ms->tagset[0] = m->tagset[0];
1140+ ms->tagset[1] = m->tagset[1];
1141+ ms->mfact = m->mfact;
1142+ ms->nmaster = m->nmaster;
1143+ strncpy(ms->ltsymbol, m->ltsymbol, sizeof(ms->ltsymbol));
1144+ ms->lt[0] = m->lt[0];
1145+ ms->lt[1] = m->lt[1];
1146+ ms->canvas = m->canvas;
1147+ m->canvas = NULL;
1148+ ms->zoom_scale = m->zoom_scale;
1149+ ms->zoom_target = m->zoom_target;
1150+ ms->zoom_pan_x = m->zoom_pan_x;
1151+ ms->zoom_pan_y = m->zoom_pan_y;
1152+ wl_list_insert(&savedstates, &ms->link);
1153+ }
1154+ /* infinite_canvas_end */
1155+ free(m);
1156+}
1157+
1158+void
1159+cleanuplisteners(void)
1160+{
1161+ wl_list_remove(&cursor_axis.link);
1162+ wl_list_remove(&cursor_button.link);
1163+ wl_list_remove(&cursor_frame.link);
1164+ wl_list_remove(&cursor_motion.link);
1165+ wl_list_remove(&cursor_motion_absolute.link);
1166+ wl_list_remove(&gpu_reset.link);
1167+ wl_list_remove(&new_idle_inhibitor.link);
1168+ wl_list_remove(&layout_change.link);
1169+ wl_list_remove(&new_input_device.link);
1170+ wl_list_remove(&new_virtual_keyboard.link);
1171+ wl_list_remove(&new_virtual_pointer.link);
1172+ wl_list_remove(&new_pointer_constraint.link);
1173+ wl_list_remove(&new_output.link);
1174+ wl_list_remove(&new_xdg_toplevel.link);
1175+ wl_list_remove(&new_xdg_decoration.link);
1176+ wl_list_remove(&new_xdg_popup.link);
1177+ wl_list_remove(&new_layer_surface.link);
1178+ wl_list_remove(&output_mgr_apply.link);
1179+ wl_list_remove(&output_mgr_test.link);
1180+ wl_list_remove(&output_power_mgr_set_mode.link);
1181+ wl_list_remove(&request_activate.link);
1182+ wl_list_remove(&request_cursor.link);
1183+ wl_list_remove(&request_set_psel.link);
1184+ wl_list_remove(&request_set_sel.link);
1185+ wl_list_remove(&request_set_cursor_shape.link);
1186+ wl_list_remove(&request_start_drag.link);
1187+ wl_list_remove(&start_drag.link);
1188+ wl_list_remove(&new_session_lock.link);
1189+#ifdef XWAYLAND
1190+ wl_list_remove(&new_xwayland_surface.link);
1191+ wl_list_remove(&xwayland_ready.link);
1192+#endif
1193+}
1194+
1195+void
1196+closemon(Monitor *m)
1197+{
1198+ /* update selmon if needed and
1199+ * move closed monitor's clients to the focused one */
1200+ Client *c;
1201+ int i = 0, nmons = wl_list_length(&mons);
1202+ if (!nmons) {
1203+ selmon = NULL;
1204+ } else if (m == selmon) {
1205+ do /* don't switch to disabled mons */
1206+ selmon = wl_container_of(mons.next, selmon, link);
1207+ while (!selmon->wlr_output->enabled && i++ < nmons);
1208+
1209+ if (!selmon->wlr_output->enabled)
1210+ selmon = NULL;
1211+ }
1212+
1213+ wl_list_for_each(c, &clients, link) {
1214+ if (c->isfloating && c->geom.x > m->m.width)
1215+ resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y,
1216+ .width = c->geom.width, .height = c->geom.height}, 0);
1217+ if (c->mon == m)
1218+ setmon(c, selmon, c->tags);
1219+ }
1220+ focusclient(focustop(selmon), 1);
1221+ printstatus();
1222+}
1223+
1224+void
1225+commitlayersurfacenotify(struct wl_listener *listener, void *data)
1226+{
1227+ LayerSurface *l = wl_container_of(listener, l, surface_commit);
1228+ struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
1229+ struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]];
1230+ struct wlr_layer_surface_v1_state old_state;
1231+
1232+ if (l->layer_surface->initial_commit) {
1233+ client_set_scale(layer_surface->surface, l->mon->wlr_output->scale);
1234+
1235+ /* Temporarily set the layer's current state to pending
1236+ * so that we can easily arrange it */
1237+ old_state = l->layer_surface->current;
1238+ l->layer_surface->current = l->layer_surface->pending;
1239+ arrangelayers(l->mon);
1240+ l->layer_surface->current = old_state;
1241+ return;
1242+ }
1243+
1244+ if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
1245+ return;
1246+ l->mapped = layer_surface->surface->mapped;
1247+
1248+ if (scene_layer != l->scene->node.parent) {
1249+ wlr_scene_node_reparent(&l->scene->node, scene_layer);
1250+ wl_list_remove(&l->link);
1251+ wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link);
1252+ wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer
1253+ < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer));
1254+ }
1255+
1256+ arrangelayers(l->mon);
1257+}
1258+
1259+void
1260+commitnotify(struct wl_listener *listener, void *data)
1261+{
1262+ Client *c = wl_container_of(listener, c, commit);
1263+
1264+ if (c->surface.xdg->initial_commit) {
1265+ /*
1266+ * Get the monitor this client will be rendered on
1267+ * Note that if the user set a rule in which the client is placed on
1268+ * a different monitor based on its title, this will likely select
1269+ * a wrong monitor.
1270+ */
1271+ applyrules(c);
1272+ if (c->mon) {
1273+ client_set_scale(client_surface(c), c->mon->wlr_output->scale);
1274+ }
1275+ setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */
1276+
1277+ wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel,
1278+ WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN);
1279+ if (c->decoration)
1280+ requestdecorationmode(&c->set_decoration_mode, c->decoration);
1281+ wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
1282+ return;
1283+ }
1284+
1285+ resize(c, c->geom, (c->isfloating && !c->isfullscreen));
1286+
1287+ /* mark a pending resize as completed */
1288+ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial)
1289+ c->resize = 0;
1290+
1291+ /* zoom */
1292+ if (c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9)
1293+ wlr_scene_node_for_each_buffer(&c->scene->node, zoom_scale_buffers, c);
1294+ /* zoom end */
1295+}
1296+
1297+void
1298+commitpopup(struct wl_listener *listener, void *data)
1299+{
1300+ struct wlr_surface *surface = data;
1301+ struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface);
1302+ LayerSurface *l = NULL;
1303+ Client *c = NULL;
1304+ struct wlr_box box;
1305+ int type = -1;
1306+
1307+ if (!popup->base->initial_commit)
1308+ return;
1309+
1310+ type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
1311+ if (!popup->parent || type < 0)
1312+ return;
1313+ popup->base->surface->data = wlr_scene_xdg_surface_create(
1314+ popup->parent->data, popup->base);
1315+ if ((l && !l->mon) || (c && !c->mon)) {
1316+ wlr_xdg_popup_destroy(popup);
1317+ return;
1318+ }
1319+ box = type == LayerShell ? l->mon->m : c->mon->w;
1320+ box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x);
1321+ box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y);
1322+ /* zoom */
1323+ if (type != LayerShell && c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9) {
1324+ Monitor *_m = c->mon;
1325+ double inv_s = 1.0 / _m->zoom_scale;
1326+ box.x = (int)llround(-_m->zoom_pan_x * inv_s) - c->geom.x;
1327+ box.y = (int)llround(-_m->zoom_pan_y * inv_s) - c->geom.y;
1328+ box.width = MAX(1, (int)llround((double)_m->m.width * inv_s));
1329+ box.height = MAX(1, (int)llround((double)_m->m.height * inv_s));
1330+ }
1331+ /* zoom end */
1332+ wlr_xdg_popup_unconstrain_from_box(popup, &box);
1333+ /* zoom */
1334+ if (c && c->mon) {
1335+ struct wlr_scene_tree *ptree = popup->parent->data;
1336+ struct wlr_scene_node *pnode;
1337+ PopupZoomData *pzd;
1338+ pnode = wl_container_of(ptree->children.prev, pnode, link);
1339+ pzd = ecalloc(1, sizeof(*pzd));
1340+ pzd->client = c;
1341+ pzd->popup = popup;
1342+ pzd->node = pnode;
1343+ pzd->commit.notify = popup_zoom_commit;
1344+ pzd->destroy.notify = popup_zoom_destroy;
1345+ wl_list_insert(&popup_zoom_list, &pzd->link);
1346+ wl_signal_add(&popup->base->surface->events.commit, &pzd->commit);
1347+ wl_signal_add(&popup->base->events.destroy, &pzd->destroy);
1348+ popup_zoom_commit(&pzd->commit, NULL);
1349+ }
1350+ /* zoom end */
1351+ wl_list_remove(&listener->link);
1352+ free(listener);
1353+}
1354+
1355+void
1356+createdecoration(struct wl_listener *listener, void *data)
1357+{
1358+ struct wlr_xdg_toplevel_decoration_v1 *deco = data;
1359+ Client *c = deco->toplevel->base->data;
1360+ c->decoration = deco;
1361+
1362+ LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode);
1363+ LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration);
1364+
1365+ requestdecorationmode(&c->set_decoration_mode, deco);
1366+}
1367+
1368+void
1369+createidleinhibitor(struct wl_listener *listener, void *data)
1370+{
1371+ struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
1372+ LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor);
1373+
1374+ checkidleinhibitor(NULL);
1375+}
1376+
1377+void
1378+createkeyboard(struct wlr_keyboard *keyboard)
1379+{
1380+ /* Set the keymap to match the group keymap */
1381+ wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap);
1382+
1383+ /* Add the new keyboard to the group */
1384+ wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
1385+}
1386+
1387+KeyboardGroup *
1388+createkeyboardgroup(void)
1389+{
1390+ KeyboardGroup *group = ecalloc(1, sizeof(*group));
1391+ struct xkb_context *context;
1392+ struct xkb_keymap *keymap;
1393+
1394+ group->wlr_group = wlr_keyboard_group_create();
1395+ group->wlr_group->data = group;
1396+
1397+ /* Prepare an XKB keymap and assign it to the keyboard group. */
1398+ context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
1399+ if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules,
1400+ XKB_KEYMAP_COMPILE_NO_FLAGS)))
1401+ die("failed to compile keymap");
1402+
1403+ wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap);
1404+ xkb_keymap_unref(keymap);
1405+ xkb_context_unref(context);
1406+
1407+ wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay);
1408+
1409+ /* Set up listeners for keyboard events */
1410+ LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress);
1411+ LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod);
1412+
1413+ group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group);
1414+
1415+ /* A seat can only have one keyboard, but this is a limitation of the
1416+ * Wayland protocol - not wlroots. We assign all connected keyboards to the
1417+ * same wlr_keyboard_group, which provides a single wlr_keyboard interface for
1418+ * all of them. Set this combined wlr_keyboard as the seat keyboard.
1419+ */
1420+ wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
1421+ return group;
1422+}
1423+
1424+void
1425+createlayersurface(struct wl_listener *listener, void *data)
1426+{
1427+ struct wlr_layer_surface_v1 *layer_surface = data;
1428+ LayerSurface *l;
1429+ struct wlr_surface *surface = layer_surface->surface;
1430+ struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]];
1431+
1432+ if (!layer_surface->output
1433+ && !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) {
1434+ wlr_layer_surface_v1_destroy(layer_surface);
1435+ return;
1436+ }
1437+
1438+ l = layer_surface->data = ecalloc(1, sizeof(*l));
1439+ l->type = LayerShell;
1440+ LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify);
1441+ LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify);
1442+ LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify);
1443+
1444+ l->layer_surface = layer_surface;
1445+ l->mon = layer_surface->output->data;
1446+ l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface);
1447+ l->scene = l->scene_layer->tree;
1448+ l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer
1449+ < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer);
1450+ l->scene->node.data = l->popups->node.data = l;
1451+
1452+ wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link);
1453+ wlr_surface_send_enter(surface, layer_surface->output);
1454+}
1455+
1456+void
1457+createlocksurface(struct wl_listener *listener, void *data)
1458+{
1459+ SessionLock *lock = wl_container_of(listener, lock, new_surface);
1460+ struct wlr_session_lock_surface_v1 *lock_surface = data;
1461+ Monitor *m = lock_surface->output->data;
1462+ struct wlr_scene_tree *scene_tree = lock_surface->surface->data
1463+ = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface);
1464+ m->lock_surface = lock_surface;
1465+
1466+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
1467+ wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height);
1468+
1469+ LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface);
1470+
1471+ if (m == selmon)
1472+ client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
1473+}
1474+
1475+void
1476+createmon(struct wl_listener *listener, void *data)
1477+{
1478+ /* This event is raised by the backend when a new output (aka a display or
1479+ * monitor) becomes available. */
1480+ struct wlr_output *wlr_output = data;
1481+ struct wlr_output_mode *mode = wl_container_of(wlr_output->modes.next, mode, link);
1482+ const MonitorRule *r;
1483+ size_t i;
1484+ struct wlr_output_state state;
1485+ Monitor *m;
1486+
1487+ if (!wlr_output_init_render(wlr_output, alloc, drw))
1488+ return;
1489+
1490+ m = wlr_output->data = ecalloc(1, sizeof(*m));
1491+ m->wlr_output = wlr_output;
1492+
1493+ for (i = 0; i < LENGTH(m->layers); i++)
1494+ wl_list_init(&m->layers[i]);
1495+
1496+#ifdef TILING
1497+ m->enablegaps = enablegaps;
1498+ m->gappih = gappih;
1499+ m->gappiv = gappiv;
1500+ m->gappoh = gappoh;
1501+ m->gappov = gappov;
1502+#endif
1503+ wlr_output_state_init(&state);
1504+ /* Initialize monitor state using configured rules */
1505+ m->tagset[0] = m->tagset[1] = 1;
1506+ for (r = monrules; r < END(monrules); r++) {
1507+ if (!r->name || strstr(wlr_output->name, r->name)) {
1508+ m->m.x = r->x;
1509+ m->m.y = r->y;
1510+ m->mfact = r->mfact;
1511+ m->nmaster = r->nmaster;
1512+ m->lt[0] = r->lt ? r->lt : &layouts[0];
1513+ m->lt[1] = &layouts[LENGTH(layouts) > 1 && r->lt != &layouts[1]];
1514+ if (m->lt[m->sellt] && m->lt[m->sellt]->symbol) { strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); } else { strncpy(m->ltsymbol, "[]=", LENGTH(m->ltsymbol)); }
1515+ wlr_output_state_set_scale(&state, r->scale);
1516+ wlr_output_state_set_transform(&state, r->rr);
1517+
1518+ wlr_output_state_set_adaptive_sync_enabled(&state, r->adaptive);
1519+
1520+ if(r->mode == -1)
1521+ wlr_output_state_set_custom_mode(&state, r->resx, r->resy,
1522+ (int) (r->rate > 0 ? r->rate * 1000 : 0));
1523+ else if (!wl_list_empty(&wlr_output->modes)) {
1524+ for (int j = 0; j < r->mode; j++) {
1525+ mode = wl_container_of(mode->link.next, mode, link);
1526+ }
1527+ wlr_output_state_set_mode(&state, mode);
1528+ }
1529+
1530+ break;
1531+ }
1532+ }
1533+
1534+ /* Safety net: if no monrules entry matched this output's name (and
1535+ * there's no wildcard fallback rule in config.h), m->lt[0]/m->lt[1]
1536+ * are left NULL by ecalloc(). Every layout dereference elsewhere in
1537+ * this file assumes they're never NULL, so guarantee that here. */
1538+ if (!m->lt[0])
1539+ m->lt[0] = &layouts[0];
1540+ if (!m->lt[1])
1541+ m->lt[1] = &layouts[LENGTH(layouts) > 1];
1542+ if (!m->ltsymbol[0]) {
1543+ if (m->lt[m->sellt] && m->lt[m->sellt]->symbol)
1544+ strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol));
1545+ else
1546+ strncpy(m->ltsymbol, "[]=", LENGTH(m->ltsymbol));
1547+ }
1548+
1549+ /* infinite_canvas */
1550+ {
1551+ int restored = 0;
1552+ MonitorState *ms = NULL, *iter;
1553+ unsigned int _i;
1554+ wl_list_for_each(iter, &savedstates, link) {
1555+ if (strcmp(iter->name, wlr_output->name) == 0) {
1556+ ms = iter;
1557+ break;
1558+ }
1559+ }
1560+ if (ms) {
1561+ m->seltags = ms->seltags;
1562+ m->sellt = ms->sellt;
1563+ m->tagset[0] = ms->tagset[0];
1564+ m->tagset[1] = ms->tagset[1];
1565+ m->mfact = ms->mfact;
1566+ m->nmaster = ms->nmaster;
1567+ strncpy(m->ltsymbol, ms->ltsymbol, sizeof(m->ltsymbol));
1568+ m->lt[0] = ms->lt[0];
1569+ m->lt[1] = ms->lt[1];
1570+ m->canvas = ms->canvas;
1571+ ms->canvas = NULL;
1572+ m->zoom_scale = ms->zoom_scale;
1573+ m->zoom_target = ms->zoom_target;
1574+ m->zoom_pan_x = ms->zoom_pan_x;
1575+ m->zoom_pan_y = ms->zoom_pan_y;
1576+ wl_list_remove(&ms->link);
1577+ free(ms);
1578+ restored = 1;
1579+ } else {
1580+ m->canvas = ecalloc(TAGCOUNT, sizeof(CanvasOffset));
1581+ for (_i = 0; _i < (unsigned int)TAGCOUNT; _i++) {
1582+ m->canvas[_i].cx = 0;
1583+ m->canvas[_i].cy = 0;
1584+ }
1585+ }
1586+ if (!restored) {
1587+ m->zoom_scale = 1.0;
1588+ m->zoom_target = 1.0;
1589+ m->zoom_pan_x = 0.0;
1590+ m->zoom_pan_y = 0.0;
1591+ }
1592+ m->zoom_anim_t = 1.0;
1593+ m->zoom_start_scale = 1.0;
1594+ m->zoom_start_pan_x = 0.0;
1595+ m->zoom_start_pan_y = 0.0;
1596+ m->zoom_popups_dirty = 0;
1597+ }
1598+ /* infinite_canvas_end */
1599+ wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output));
1600+
1601+ /* Set up event listeners */
1602+ LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
1603+ LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon);
1604+ LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate);
1605+
1606+ wlr_output_state_set_enabled(&state, 1);
1607+ wlr_output_commit_state(wlr_output, &state);
1608+ wlr_output_state_finish(&state);
1609+
1610+ wl_list_insert(&mons, &m->link);
1611+ printstatus();
1612+
1613+ /* The xdg-protocol specifies:
1614+ *
1615+ * If the fullscreened surface is not opaque, the compositor must make
1616+ * sure that other screen content not part of the same surface tree (made
1617+ * up of subsurfaces, popups or similarly coupled surfaces) are not
1618+ * visible below the fullscreened surface.
1619+ *
1620+ */
1621+ /* updatemons() will resize and set correct position */
1622+ m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, fullscreen_bg);
1623+ wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0);
1624+
1625+ /* Adds this to the output layout in the order it was configured.
1626+ *
1627+ * The output layout utility automatically adds a wl_output global to the
1628+ * display, which Wayland clients can see to find out information about the
1629+ * output (such as DPI, scale factor, manufacturer, etc).
1630+ */
1631+ m->scene_output = wlr_scene_output_create(scene, wlr_output);
1632+ if (m->m.x == -1 && m->m.y == -1)
1633+ wlr_output_layout_add_auto(output_layout, wlr_output);
1634+ else
1635+ wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
1636+}
1637+
1638+void
1639+createnotify(struct wl_listener *listener, void *data)
1640+{
1641+ /* This event is raised when a client creates a new toplevel (application window). */
1642+ struct wlr_xdg_toplevel *toplevel = data;
1643+ Client *c = NULL;
1644+
1645+ /* Allocate a Client for this surface */
1646+ c = toplevel->base->data = ecalloc(1, sizeof(*c));
1647+ c->surface.xdg = toplevel->base;
1648+
1649+ c->ibw = innerborderpx;
1650+ c->obw = outerborderpx;
1651+ c->bw = c->ibw + c->obw;
1652+
1653+ LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify);
1654+ LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify);
1655+ LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmapnotify);
1656+ LISTEN(&toplevel->events.destroy, &c->destroy, destroynotify);
1657+ LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify);
1658+ LISTEN(&toplevel->events.request_maximize, &c->maximize, maximizenotify);
1659+ LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle);
1660+}
1661+
1662+typedef struct {
1663+ struct wl_listener pinch_begin;
1664+ struct wl_listener pinch_update;
1665+ struct wl_listener pinch_end;
1666+ struct wl_listener destroy;
1667+ struct wlr_pointer *pointer;
1668+} PinchListeners;
1669+
1670+static void
1671+pinchbegin(struct wl_listener *listener, void *data)
1672+{
1673+ struct wlr_pointer_pinch_begin_event *event = data;
1674+ (void)listener;
1675+ if (!pointer_gestures || !event)
1676+ return;
1677+ wlr_pointer_gestures_v1_send_pinch_begin(pointer_gestures, seat,
1678+ event->time_msec, event->fingers);
1679+}
1680+
1681+static void
1682+pinchupdate(struct wl_listener *listener, void *data)
1683+{
1684+ struct wlr_pointer_pinch_update_event *event = data;
1685+ (void)listener;
1686+ if (!pointer_gestures || !event)
1687+ return;
1688+ wlr_pointer_gestures_v1_send_pinch_update(pointer_gestures, seat,
1689+ event->time_msec, event->dx, event->dy,
1690+ event->scale, event->rotation);
1691+}
1692+
1693+static void
1694+pinchend(struct wl_listener *listener, void *data)
1695+{
1696+ struct wlr_pointer_pinch_end_event *event = data;
1697+ (void)listener;
1698+ if (!pointer_gestures || !event)
1699+ return;
1700+ wlr_pointer_gestures_v1_send_pinch_end(pointer_gestures, seat,
1701+ event->time_msec, event->cancelled);
1702+}
1703+
1704+static void
1705+pinchdestroy(struct wl_listener *listener, void *data)
1706+{
1707+ PinchListeners *pl = wl_container_of(listener, pl, destroy);
1708+ wl_list_remove(&pl->pinch_begin.link);
1709+ wl_list_remove(&pl->pinch_update.link);
1710+ wl_list_remove(&pl->pinch_end.link);
1711+ wl_list_remove(&pl->destroy.link);
1712+ free(pl);
1713+}
1714+
1715+void
1716+createpointer(struct wlr_pointer *pointer)
1717+{
1718+ struct libinput_device *device;
1719+ PinchListeners *pl;
1720+ if (wlr_input_device_is_libinput(&pointer->base)
1721+ && (device = wlr_libinput_get_device_handle(&pointer->base))) {
1722+
1723+ if (libinput_device_config_tap_get_finger_count(device)) {
1724+ libinput_device_config_tap_set_enabled(device, tap_to_click);
1725+ libinput_device_config_tap_set_drag_enabled(device, tap_and_drag);
1726+ libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock);
1727+ libinput_device_config_tap_set_button_map(device, button_map);
1728+ }
1729+
1730+ if (libinput_device_config_scroll_has_natural_scroll(device))
1731+ libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling);
1732+
1733+ if (libinput_device_config_dwt_is_available(device))
1734+ libinput_device_config_dwt_set_enabled(device, disable_while_typing);
1735+
1736+ if (libinput_device_config_left_handed_is_available(device))
1737+ libinput_device_config_left_handed_set(device, left_handed);
1738+
1739+ if (libinput_device_config_middle_emulation_is_available(device))
1740+ libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation);
1741+
1742+ if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
1743+ libinput_device_config_scroll_set_method(device, scroll_method);
1744+
1745+ if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
1746+ libinput_device_config_click_set_method(device, click_method);
1747+
1748+ if (libinput_device_config_send_events_get_modes(device))
1749+ libinput_device_config_send_events_set_mode(device, send_events_mode);
1750+
1751+ if (libinput_device_config_accel_is_available(device)) {
1752+ libinput_device_config_accel_set_profile(device, accel_profile);
1753+ libinput_device_config_accel_set_speed(device, accel_speed);
1754+ }
1755+ }
1756+
1757+ wlr_cursor_attach_input_device(cursor, &pointer->base);
1758+
1759+ pl = ecalloc(1, sizeof(*pl));
1760+ pl->pointer = pointer;
1761+ pl->pinch_begin.notify = pinchbegin;
1762+ pl->pinch_update.notify = pinchupdate;
1763+ pl->pinch_end.notify = pinchend;
1764+ pl->destroy.notify = pinchdestroy;
1765+ wl_signal_add(&pointer->events.pinch_begin, &pl->pinch_begin);
1766+ wl_signal_add(&pointer->events.pinch_update, &pl->pinch_update);
1767+ wl_signal_add(&pointer->events.pinch_end, &pl->pinch_end);
1768+ wl_signal_add(&pointer->base.events.destroy, &pl->destroy);
1769+}
1770+
1771+void
1772+createpointerconstraint(struct wl_listener *listener, void *data)
1773+{
1774+ PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint));
1775+ pointer_constraint->constraint = data;
1776+ LISTEN(&pointer_constraint->constraint->events.destroy,
1777+ &pointer_constraint->destroy, destroypointerconstraint);
1778+}
1779+
1780+void
1781+createpopup(struct wl_listener *listener, void *data)
1782+{
1783+ /* This event is raised when a client (either xdg-shell or layer-shell)
1784+ * creates a new popup. */
1785+ struct wlr_xdg_popup *popup = data;
1786+ LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup);
1787+}
1788+
1789+void
1790+cursorconstrain(struct wlr_pointer_constraint_v1 *constraint)
1791+{
1792+ if (active_constraint == constraint)
1793+ return;
1794+
1795+ if (active_constraint)
1796+ wlr_pointer_constraint_v1_send_deactivated(active_constraint);
1797+
1798+ active_constraint = constraint;
1799+ wlr_pointer_constraint_v1_send_activated(constraint);
1800+}
1801+
1802+void
1803+cursorframe(struct wl_listener *listener, void *data)
1804+{
1805+ /* This event is forwarded by the cursor when a pointer emits a frame
1806+ * event. Frame events are sent after regular pointer events to group
1807+ * multiple events together. For instance, two axis events may happen at the
1808+ * same time, in which case a frame event won't be sent in between. */
1809+ /* Notify the client with pointer focus of the frame event. */
1810+ wlr_seat_pointer_notify_frame(seat);
1811+}
1812+
1813+void
1814+cursorwarptohint(void)
1815+{
1816+ Client *c = NULL;
1817+ double sx = active_constraint->current.cursor_hint.x;
1818+ double sy = active_constraint->current.cursor_hint.y;
1819+
1820+ toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
1821+ if (c && active_constraint->current.cursor_hint.enabled) {
1822+ wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw);
1823+ wlr_seat_pointer_warp(active_constraint->seat, sx, sy);
1824+ }
1825+}
1826+
1827+void
1828+destroydecoration(struct wl_listener *listener, void *data)
1829+{
1830+ Client *c = wl_container_of(listener, c, destroy_decoration);
1831+
1832+ wl_list_remove(&c->destroy_decoration.link);
1833+ wl_list_remove(&c->set_decoration_mode.link);
1834+}
1835+
1836+void
1837+destroydragicon(struct wl_listener *listener, void *data)
1838+{
1839+ /* Focus enter isn't sent during drag, so refocus the focused node. */
1840+ focusclient(focustop(selmon), 1);
1841+ motionnotify(0, NULL, 0, 0, 0, 0);
1842+ wl_list_remove(&listener->link);
1843+ free(listener);
1844+}
1845+
1846+void
1847+destroyidleinhibitor(struct wl_listener *listener, void *data)
1848+{
1849+ /* `data` is the wlr_surface of the idle inhibitor being destroyed,
1850+ * at this point the idle inhibitor is still in the list of the manager */
1851+ checkidleinhibitor(wlr_surface_get_root_surface(data));
1852+ wl_list_remove(&listener->link);
1853+ free(listener);
1854+}
1855+
1856+void
1857+destroylayersurfacenotify(struct wl_listener *listener, void *data)
1858+{
1859+ LayerSurface *l = wl_container_of(listener, l, destroy);
1860+
1861+ wl_list_remove(&l->link);
1862+ wl_list_remove(&l->destroy.link);
1863+ wl_list_remove(&l->unmap.link);
1864+ wl_list_remove(&l->surface_commit.link);
1865+ wlr_scene_node_destroy(&l->scene->node);
1866+ wlr_scene_node_destroy(&l->popups->node);
1867+ free(l);
1868+}
1869+
1870+void
1871+destroylock(SessionLock *lock, int unlock)
1872+{
1873+ wlr_seat_keyboard_notify_clear_focus(seat);
1874+ if ((locked = !unlock))
1875+ goto destroy;
1876+
1877+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
1878+
1879+ focusclient(focustop(selmon), 0);
1880+ motionnotify(0, NULL, 0, 0, 0, 0);
1881+
1882+destroy:
1883+ wl_list_remove(&lock->new_surface.link);
1884+ wl_list_remove(&lock->unlock.link);
1885+ wl_list_remove(&lock->destroy.link);
1886+
1887+ wlr_scene_node_destroy(&lock->scene->node);
1888+ cur_lock = NULL;
1889+ free(lock);
1890+}
1891+
1892+void
1893+destroylocksurface(struct wl_listener *listener, void *data)
1894+{
1895+ Monitor *m = wl_container_of(listener, m, destroy_lock_surface);
1896+ struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
1897+
1898+ m->lock_surface = NULL;
1899+ wl_list_remove(&m->destroy_lock_surface.link);
1900+
1901+ if (lock_surface->surface != seat->keyboard_state.focused_surface)
1902+ return;
1903+
1904+ if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {
1905+ surface = wl_container_of(cur_lock->surfaces.next, surface, link);
1906+ client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
1907+ } else if (!locked) {
1908+ focusclient(focustop(selmon), 1);
1909+ } else {
1910+ wlr_seat_keyboard_clear_focus(seat);
1911+ }
1912+}
1913+
1914+void
1915+destroynotify(struct wl_listener *listener, void *data)
1916+{
1917+ /* Called when the xdg_toplevel is destroyed. */
1918+ Client *c = wl_container_of(listener, c, destroy);
1919+ wl_list_remove(&c->destroy.link);
1920+ wl_list_remove(&c->set_title.link);
1921+ wl_list_remove(&c->fullscreen.link);
1922+#ifdef XWAYLAND
1923+ if (c->type != XDGShell) {
1924+ wl_list_remove(&c->activate.link);
1925+ wl_list_remove(&c->associate.link);
1926+ wl_list_remove(&c->configure.link);
1927+ wl_list_remove(&c->dissociate.link);
1928+ wl_list_remove(&c->set_hints.link);
1929+ } else
1930+#endif
1931+ {
1932+ wl_list_remove(&c->commit.link);
1933+ wl_list_remove(&c->map.link);
1934+ wl_list_remove(&c->unmap.link);
1935+ wl_list_remove(&c->maximize.link);
1936+ }
1937+ free(c);
1938+}
1939+
1940+void
1941+destroypointerconstraint(struct wl_listener *listener, void *data)
1942+{
1943+ PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy);
1944+
1945+ if (active_constraint == pointer_constraint->constraint) {
1946+ cursorwarptohint();
1947+ active_constraint = NULL;
1948+ }
1949+
1950+ wl_list_remove(&pointer_constraint->destroy.link);
1951+ free(pointer_constraint);
1952+}
1953+
1954+void
1955+destroysessionlock(struct wl_listener *listener, void *data)
1956+{
1957+ SessionLock *lock = wl_container_of(listener, lock, destroy);
1958+ destroylock(lock, 0);
1959+}
1960+
1961+void
1962+destroykeyboardgroup(struct wl_listener *listener, void *data)
1963+{
1964+ KeyboardGroup *group = wl_container_of(listener, group, destroy);
1965+ wl_event_source_remove(group->key_repeat_source);
1966+ wl_list_remove(&group->key.link);
1967+ wl_list_remove(&group->modifiers.link);
1968+ wl_list_remove(&group->destroy.link);
1969+ wlr_keyboard_group_destroy(group->wlr_group);
1970+ free(group);
1971+}
1972+
1973+Monitor *
1974+dirtomon(enum wlr_direction dir)
1975+{
1976+ struct wlr_output *next;
1977+ if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
1978+ return selmon;
1979+ if ((next = wlr_output_layout_adjacent_output(output_layout,
1980+ dir, selmon->wlr_output, selmon->m.x, selmon->m.y)))
1981+ return next->data;
1982+ if ((next = wlr_output_layout_farthest_output(output_layout,
1983+ dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT),
1984+ selmon->wlr_output, selmon->m.x, selmon->m.y)))
1985+ return next->data;
1986+ return selmon;
1987+}
1988+
1989+void
1990+focusclient(Client *c, int lift)
1991+{
1992+ struct wlr_surface *old = seat->keyboard_state.focused_surface;
1993+ int unused_lx, unused_ly, old_client_type;
1994+ Client *old_c = NULL;
1995+ LayerSurface *old_l = NULL;
1996+
1997+ if (locked)
1998+ return;
1999+
2000+ /* Raise client in stacking order if requested */
2001+ if (c && lift)
2002+ wlr_scene_node_raise_to_top(&c->scene->node);
2003+
2004+ if (c && client_surface(c) == old)
2005+ return;
2006+
2007+ if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) {
2008+ struct wlr_xdg_popup *popup, *tmp;
2009+ wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link)
2010+ wlr_xdg_popup_destroy(popup);
2011+ }
2012+
2013+ /* Put the new client atop the focus stack and select its monitor */
2014+ if (c && !client_is_unmanaged(c)) {
2015+ wl_list_remove(&c->flink);
2016+ wl_list_insert(&fstack, &c->flink);
2017+ selmon = c->mon;
2018+ c->isurgent = 0;
2019+
2020+ /* Don't change border color if there is an exclusive focus or we are
2021+ * handling a drag operation */
2022+ if (!exclusive_focus && !seat->drag)
2023+ client_set_border_color(c, focused_inner_border_color, focused_outer_border_color);
2024+ }
2025+
2026+ /* Deactivate old client if focus is changing */
2027+ if (old && (!c || client_surface(c) != old)) {
2028+ /* If an overlay is focused, don't focus or activate the client,
2029+ * but only update its position in fstack to render its border with focuscolor
2030+ * and focus it after the overlay is closed. */
2031+ if (old_client_type == LayerShell && wlr_scene_node_coords(
2032+ &old_l->scene->node, &unused_lx, &unused_ly)
2033+ && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
2034+ return;
2035+ } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
2036+ return;
2037+ /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg
2038+ * and probably other clients */
2039+ } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
2040+ client_set_border_color(old_c, unfocused_inner_border_color, unfocused_outer_border_color);
2041+
2042+ client_activate_surface(old, 0);
2043+ }
2044+ }
2045+ printstatus();
2046+
2047+ if (!c) {
2048+ /* With no client, all we have left is to clear focus */
2049+ wlr_seat_keyboard_notify_clear_focus(seat);
2050+ return;
2051+ }
2052+
2053+ /* Change cursor surface */
2054+ motionnotify(0, NULL, 0, 0, 0, 0);
2055+
2056+ /* Have a client, so focus its top-level wlr_surface */
2057+ client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
2058+
2059+ /* Activate the new client */
2060+ client_activate_surface(client_surface(c), 1);
2061+}
2062+
2063+void
2064+focusmon(const Arg *arg)
2065+{
2066+ int i = 0, nmons = wl_list_length(&mons);
2067+ if (nmons) {
2068+ do /* don't switch to disabled mons */
2069+ selmon = dirtomon(arg->i);
2070+ while (!selmon->wlr_output->enabled && i++ < nmons);
2071+ }
2072+ focusclient(focustop(selmon), 1);
2073+}
2074+
2075+void
2076+focusstack(const Arg *arg)
2077+{
2078+ /* Focus the next or previous client (in tiling order) on selmon */
2079+ Client *c, *sel = focustop(selmon);
2080+ if (!sel || (sel->isfullscreen && !client_has_children(sel)))
2081+ return;
2082+ if (arg->i > 0) {
2083+ wl_list_for_each(c, &sel->link, link) {
2084+ if (&c->link == &clients)
2085+ continue; /* wrap past the sentinel node */
2086+ if (VISIBLEON(c, selmon))
2087+ break; /* found it */
2088+ }
2089+ } else {
2090+ wl_list_for_each_reverse(c, &sel->link, link) {
2091+ if (&c->link == &clients)
2092+ continue; /* wrap past the sentinel node */
2093+ if (VISIBLEON(c, selmon))
2094+ break; /* found it */
2095+ }
2096+ }
2097+ /* If only one client is visible on selmon, then c == sel */
2098+ focusclient(c, 1);
2099+ /* infinite_canvas */
2100+ if (selmon && !selmon->lt[selmon->sellt]->arrange)
2101+ bringintoview(c);
2102+ /* infinite_canvas_end */
2103+}
2104+
2105+/* We probably should change the name of this: it sounds like it
2106+ * will focus the topmost client of this mon, when actually will
2107+ * only return that client */
2108+Client *
2109+focustop(Monitor *m)
2110+{
2111+ Client *c;
2112+ wl_list_for_each(c, &fstack, flink) {
2113+ if (VISIBLEON(c, m))
2114+ return c;
2115+ }
2116+ return NULL;
2117+}
2118+
2119+void
2120+fullscreennotify(struct wl_listener *listener, void *data)
2121+{
2122+ Client *c = wl_container_of(listener, c, fullscreen);
2123+ setfullscreen(c, client_wants_fullscreen(c));
2124+}
2125+
2126+void
2127+gpureset(struct wl_listener *listener, void *data)
2128+{
2129+ struct wlr_renderer *old_drw = drw;
2130+ struct wlr_allocator *old_alloc = alloc;
2131+ struct Monitor *m;
2132+ if (!(drw = wlr_renderer_autocreate(backend)))
2133+ die("couldn't recreate renderer");
2134+
2135+ if (!(alloc = wlr_allocator_autocreate(backend, drw)))
2136+ die("couldn't recreate allocator");
2137+
2138+ wl_list_remove(&gpu_reset.link);
2139+ wl_signal_add(&drw->events.lost, &gpu_reset);
2140+
2141+ wlr_compositor_set_renderer(compositor, drw);
2142+
2143+ wl_list_for_each(m, &mons, link) {
2144+ wlr_output_init_render(m->wlr_output, alloc, drw);
2145+ }
2146+
2147+ wlr_allocator_destroy(old_alloc);
2148+ wlr_renderer_destroy(old_drw);
2149+}
2150+
2151+void
2152+handlesig(int signo)
2153+{
2154+ if (signo == SIGCHLD)
2155+ while (waitpid(-1, NULL, WNOHANG) > 0);
2156+ else if (signo == SIGINT || signo == SIGTERM)
2157+ quit(NULL);
2158+}
2159+
2160+void
2161+incnmaster(const Arg *arg)
2162+{
2163+ if (!arg || !selmon)
2164+ return;
2165+ selmon->nmaster = MAX(selmon->nmaster + arg->i, 0);
2166+ arrange(selmon);
2167+}
2168+
2169+void
2170+inputdevice(struct wl_listener *listener, void *data)
2171+{
2172+ /* This event is raised by the backend when a new input device becomes
2173+ * available. */
2174+ struct wlr_input_device *device = data;
2175+ /* tablet-input */
2176+ struct wlr_tablet_pad *pad = NULL;
2177+ /* tablet-input end */
2178+ uint32_t caps;
2179+
2180+ switch (device->type) {
2181+ case WLR_INPUT_DEVICE_KEYBOARD:
2182+ createkeyboard(wlr_keyboard_from_input_device(device));
2183+ break;
2184+ case WLR_INPUT_DEVICE_POINTER:
2185+ createpointer(wlr_pointer_from_input_device(device));
2186+ break;
2187+ /* tablet-input */
2188+ case WLR_INPUT_DEVICE_TABLET:
2189+ createtablet(device);
2190+ break;
2191+ case WLR_INPUT_DEVICE_TABLET_PAD:
2192+ pad = wlr_tablet_pad_from_input_device(device);
2193+ tablet_pad = wlr_tablet_pad_create(tablet_mgr, seat, device);
2194+ wl_signal_add(&pad->events.button, &tablet_pad_button);
2195+ wl_signal_add(&device->events.destroy, &tablet_pad_destroy);
2196+ break;
2197+ /* tablet-input end */
2198+ default:
2199+ /* TODO handle other input device types */
2200+ break;
2201+ }
2202+
2203+ /* We need to let the wlr_seat know what our capabilities are, which is
2204+ * communiciated to the client. In leeloo we always have a cursor, even if
2205+ * there are no pointer devices, so we always include that capability. */
2206+ /* TODO do we actually require a cursor? */
2207+ caps = WL_SEAT_CAPABILITY_POINTER;
2208+ if (!wl_list_empty(&kb_group->wlr_group->devices))
2209+ caps |= WL_SEAT_CAPABILITY_KEYBOARD;
2210+ wlr_seat_set_capabilities(seat, caps);
2211+}
2212+
2213+int
2214+keybinding(uint32_t mods, xkb_keysym_t sym)
2215+{
2216+ /*
2217+ * Here we handle compositor keybindings. This is when the compositor is
2218+ * processing keys, rather than passing them on to the client for its own
2219+ * processing.
2220+ */
2221+ const Key *k;
2222+ for (k = keys; k < END(keys); k++) {
2223+ if (CLEANMASK(mods) == CLEANMASK(k->mod)
2224+ && xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym)
2225+ && k->func) {
2226+ k->func(&k->arg);
2227+ return 1;
2228+ }
2229+ }
2230+ return 0;
2231+}
2232+
2233+void
2234+keypress(struct wl_listener *listener, void *data)
2235+{
2236+ int i;
2237+ /* This event is raised when a key is pressed or released. */
2238+ KeyboardGroup *group = wl_container_of(listener, group, key);
2239+ struct wlr_keyboard_key_event *event = data;
2240+
2241+ /* Translate libinput keycode -> xkbcommon */
2242+ uint32_t keycode = event->keycode + 8;
2243+ /* Get a list of keysyms based on the keymap for this keyboard */
2244+ const xkb_keysym_t *syms;
2245+ int nsyms = xkb_state_key_get_syms(
2246+ group->wlr_group->keyboard.xkb_state, keycode, &syms);
2247+
2248+ int handled = 0;
2249+ uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
2250+
2251+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
2252+
2253+ /* On _press_ if there is no active screen locker,
2254+ * attempt to process a compositor keybinding. */
2255+ if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
2256+ for (i = 0; i < nsyms; i++)
2257+ handled = keybinding(mods, syms[i]) || handled;
2258+ }
2259+
2260+ if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) {
2261+ group->mods = mods;
2262+ group->keysyms = syms;
2263+ group->nsyms = nsyms;
2264+ wl_event_source_timer_update(group->key_repeat_source,
2265+ group->wlr_group->keyboard.repeat_info.delay);
2266+ } else {
2267+ group->nsyms = 0;
2268+ wl_event_source_timer_update(group->key_repeat_source, 0);
2269+ }
2270+
2271+ if (handled)
2272+ return;
2273+
2274+ wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
2275+ /* Pass unhandled keycodes along to the client. */
2276+ wlr_seat_keyboard_notify_key(seat, event->time_msec,
2277+ event->keycode, event->state);
2278+}
2279+
2280+void
2281+keypressmod(struct wl_listener *listener, void *data)
2282+{
2283+ /* This event is raised when a modifier key, such as shift or alt, is
2284+ * pressed. We simply communicate this to the client. */
2285+ KeyboardGroup *group = wl_container_of(listener, group, modifiers);
2286+
2287+ wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
2288+ /* Send modifiers to the client. */
2289+ wlr_seat_keyboard_notify_modifiers(seat,
2290+ &group->wlr_group->keyboard.modifiers);
2291+}
2292+
2293+int
2294+keyrepeat(void *data)
2295+{
2296+ KeyboardGroup *group = data;
2297+ int i;
2298+ if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
2299+ return 0;
2300+
2301+ wl_event_source_timer_update(group->key_repeat_source,
2302+ 1000 / group->wlr_group->keyboard.repeat_info.rate);
2303+
2304+ for (i = 0; i < group->nsyms; i++)
2305+ keybinding(group->mods, group->keysyms[i]);
2306+
2307+ return 0;
2308+}
2309+
2310+void
2311+killclient(const Arg *arg)
2312+{
2313+ Client *sel = focustop(selmon);
2314+ if (sel)
2315+ client_send_close(sel);
2316+}
2317+
2318+void
2319+locksession(struct wl_listener *listener, void *data)
2320+{
2321+ struct wlr_session_lock_v1 *session_lock = data;
2322+ SessionLock *lock;
2323+ wlr_scene_node_set_enabled(&locked_bg->node, 1);
2324+ if (cur_lock) {
2325+ wlr_session_lock_v1_destroy(session_lock);
2326+ return;
2327+ }
2328+ lock = session_lock->data = ecalloc(1, sizeof(*lock));
2329+ focusclient(NULL, 0);
2330+
2331+ lock->scene = wlr_scene_tree_create(layers[LyrBlock]);
2332+ cur_lock = lock->lock = session_lock;
2333+ locked = 1;
2334+
2335+ LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface);
2336+ LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock);
2337+ LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession);
2338+
2339+ wlr_session_lock_v1_send_locked(session_lock);
2340+}
2341+
2342+static uint64_t
2343+leeloo_monotonic_ms(void)
2344+{
2345+ struct timespec ts;
2346+ clock_gettime(CLOCK_MONOTONIC, &ts);
2347+ return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL;
2348+}
2349+
2350+static void
2351+openanim_buffers(struct wlr_scene_buffer *buf, int sx, int sy, void *data)
2352+{
2353+ Client *c = data;
2354+ double t, k;
2355+ int tw, th;
2356+ (void)sx;
2357+ (void)sy;
2358+ if (!c || !buf->buffer || c != openanim_client || !openanim_running)
2359+ return;
2360+
2361+ t = (double)(leeloo_monotonic_ms() - openanim_start_ms) / 140.0;
2362+ if (t < 0.0)
2363+ t = 0.0;
2364+ if (t > 1.0)
2365+ t = 1.0;
2366+
2367+ /* Subtle 94% -> 100% ease-out. */
2368+ k = 0.94 + 0.06 * (1.0 - pow(1.0 - t, 3.0));
2369+ if (t >= 1.0) {
2370+ wlr_scene_buffer_set_dest_size(buf, 0, 0);
2371+ wlr_scene_buffer_set_opacity(buf, 1.0f);
2372+ return;
2373+ }
2374+
2375+ tw = MAX(1, (int)llround((double)buf->buffer->width * k));
2376+ th = MAX(1, (int)llround((double)buf->buffer->height * k));
2377+ wlr_scene_buffer_set_dest_size(buf, tw, th);
2378+ wlr_scene_buffer_set_opacity(buf, (float)k);
2379+}
2380+
2381+static int
2382+openanim_tick(void *data)
2383+{
2384+ Client *c = data;
2385+ uint64_t now;
2386+ if (!c || c != openanim_client || !c->scene_surface) {
2387+ openanim_running = 0;
2388+ openanim_client = NULL;
2389+ return 0;
2390+ }
2391+
2392+ now = leeloo_monotonic_ms();
2393+ wlr_scene_node_for_each_buffer(&c->scene_surface->node, openanim_buffers, c);
2394+ if (now - openanim_start_ms >= 140) {
2395+ openanim_running = 0;
2396+ wlr_scene_node_for_each_buffer(&c->scene_surface->node, openanim_buffers, c);
2397+ openanim_client = NULL;
2398+ wl_event_source_timer_update(openanim_timer, 0);
2399+ return 0;
2400+ }
2401+ wl_event_source_timer_update(openanim_timer, 16);
2402+ return 0;
2403+}
2404+
2405+static void
2406+start_open_animation(Client *c)
2407+{
2408+ if (!c || !c->scene_surface)
2409+ return;
2410+ if (!openanim_timer)
2411+ openanim_timer = wl_event_loop_add_timer(event_loop, openanim_tick, c);
2412+ if (!openanim_timer)
2413+ return;
2414+ openanim_client = c;
2415+ openanim_start_ms = leeloo_monotonic_ms();
2416+ openanim_running = 1;
2417+ wl_event_source_timer_update(openanim_timer, 0);
2418+}
2419+
2420+void
2421+mapnotify(struct wl_listener *listener, void *data)
2422+{
2423+ /* Called when the surface is mapped, or ready to display on-screen. */
2424+ Client *p = NULL;
2425+ Client *w, *c = wl_container_of(listener, c, map);
2426+ Monitor *m;
2427+ int i;
2428+
2429+ /* Create scene tree for this client and its border */
2430+ c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
2431+ /* Enabled later by a call to arrange() */
2432+ wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c));
2433+ c->scene_surface = c->type == XDGShell
2434+ ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
2435+ : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
2436+ c->scene->node.data = c->scene_surface->node.data = c;
2437+
2438+ client_get_geometry(c, &c->geom);
2439+
2440+ /* Handle unmanaged clients first so we can return prior create borders */
2441+ if (client_is_unmanaged(c)) {
2442+ /* Unmanaged clients always are floating */
2443+ wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]);
2444+ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
2445+ client_set_size(c, c->geom.width, c->geom.height);
2446+ if (client_wants_focus(c)) {
2447+ focusclient(c, 1);
2448+ exclusive_focus = c;
2449+ }
2450+ goto unset_fullscreen;
2451+ }
2452+
2453+ for (i = 0; i < 4; i++) {
2454+ c->inner_border[i] = wlr_scene_rect_create(c->scene, 0, 0,
2455+ c->isurgent ? urgent_inner_border_color : unfocused_inner_border_color);
2456+ c->inner_border[i]->node.data = c;
2457+
2458+ c->outer_border[i] = wlr_scene_rect_create(c->scene, 0, 0,
2459+ c->isurgent ? urgent_outer_border_color : unfocused_outer_border_color);
2460+ c->outer_border[i]->node.data = c;
2461+ }
2462+
2463+ /* Initialize client geometry with room for border */
2464+ client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
2465+
2466+ c->geom.width += 2 * c->bw;
2467+ c->geom.height += 2 * c->bw;
2468+
2469+ /* Insert this client into client lists. */
2470+ wl_list_insert(&clients, &c->link);
2471+ wl_list_insert(&fstack, &c->flink);
2472+
2473+ /* Set initial monitor, tags, floating status, and focus:
2474+ * we always consider floating, clients that have parent and thus
2475+ * we set the same tags and monitor as its parent.
2476+ * If there is no parent, apply rules */
2477+ if ((p = client_get_parent(c))) {
2478+ c->isfloating = 1;
2479+ setmon(c, p->mon, p->tags);
2480+ } else {
2481+ applyrules(c);
2482+ }
2483+ printstatus();
2484+
2485+ /* spawnregion */
2486+ if (!client_is_unmanaged(c)) {
2487+ if (c->isfullscreen || (!c->isfloating && c->mon && c->mon->lt[c->mon->sellt]->arrange) || waiting_geo_client) {
2488+ wlr_scene_node_set_enabled(&c->scene->node, 1);
2489+ } else if (has_pending_spawn_geo) {
2490+ has_pending_spawn_geo = 0;
2491+ c->isfloating = 1;
2492+ if (!c->mon)
2493+ c->mon = selmon;
2494+ if (c->mon) {
2495+ struct wlr_box min_hint = {0};
2496+ int bw2 = 2 * (int)c->bw;
2497+ if (pending_spawn_geo.width < min_hint.width + bw2 && min_hint.width > 0)
2498+ pending_spawn_geo.width = min_hint.width + bw2;
2499+ if (pending_spawn_geo.height < min_hint.height + bw2 && min_hint.height > 0)
2500+ pending_spawn_geo.height = min_hint.height + bw2;
2501+ c->geom = pending_spawn_geo;
2502+ }
2503+ } else {
2504+ wlr_scene_node_set_enabled(&c->scene->node, 0);
2505+ waiting_geo_client = c;
2506+ spawnregion(NULL);
2507+ }
2508+ }
2509+ /* spawnregion_end */
2510+unset_fullscreen:
2511+ m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y);
2512+ wl_list_for_each(w, &clients, link) {
2513+ if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags))
2514+ setfullscreen(w, 0);
2515+ }
2516+
2517+ /* infinite_canvas: remember the pan active right now as this
2518+ * window's own natural framing, so focusing it later teleports
2519+ * back to exactly how it looked when placed -- independent of
2520+ * whatever other windows' framing happens to be. */
2521+ if (m) {
2522+ int tagidx = getcurrenttag(m);
2523+ c->home_cx = m->canvas[tagidx].cx;
2524+ c->home_cy = m->canvas[tagidx].cy;
2525+ }
2526+ start_open_animation(c);
2527+
2528+}
2529+
2530+void
2531+maximizenotify(struct wl_listener *listener, void *data)
2532+{
2533+ /* This event is raised when a client would like to maximize itself,
2534+ * typically because the user clicked on the maximize button on
2535+ * client-side decorations. leeloo doesn't support maximization, but
2536+ * to conform to xdg-shell protocol we still must send a configure.
2537+ * Since xdg-shell protocol v5 we should ignore request of unsupported
2538+ * capabilities, just schedule a empty configure when the client uses <5
2539+ * protocol version
2540+ * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
2541+ Client *c = wl_container_of(listener, c, maximize);
2542+ if (c->surface.xdg->initialized
2543+ && wl_resource_get_version(c->surface.xdg->toplevel->resource)
2544+ < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
2545+ wlr_xdg_surface_schedule_configure(c->surface.xdg);
2546+}
2547+
2548+void
2549+monocle(Monitor *m)
2550+{
2551+ Client *c;
2552+ int n = 0;
2553+
2554+ wl_list_for_each(c, &clients, link) {
2555+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
2556+ continue;
2557+ resize(c, m->w, 0);
2558+ n++;
2559+ }
2560+ if (n)
2561+ snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n);
2562+ if ((c = focustop(m)))
2563+ wlr_scene_node_raise_to_top(&c->scene->node);
2564+}
2565+
2566+void
2567+motionabsolute(struct wl_listener *listener, void *data)
2568+{
2569+ /* This event is forwarded by the cursor when a pointer emits an _absolute_
2570+ * motion event, from 0..1 on each axis. This happens, for example, when
2571+ * wlroots is running under a Wayland window rather than KMS+DRM, and you
2572+ * move the mouse over the window. You could enter the window from any edge,
2573+ * so we have to warp the mouse there. Also, some hardware emits these events. */
2574+ struct wlr_pointer_motion_absolute_event *event = data;
2575+ double lx, ly, dx, dy;
2576+
2577+ if (!event->time_msec) /* this is 0 with virtual pointers */
2578+ wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
2579+
2580+ wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly);
2581+ dx = lx - cursor->x;
2582+ dy = ly - cursor->y;
2583+ motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy);
2584+}
2585+
2586+void
2587+motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy,
2588+ double dx_unaccel, double dy_unaccel)
2589+{
2590+ double sx = 0, sy = 0, sx_confined, sy_confined;
2591+ Client *c = NULL, *w = NULL;
2592+ LayerSurface *l = NULL;
2593+ struct wlr_surface *surface = NULL;
2594+ struct wlr_pointer_constraint_v1 *constraint;
2595+
2596+ /* Find the client under the pointer and send the event along. */
2597+ xytonode(cursor->x, cursor->y, &surface, &c, &l, &sx, &sy);
2598+
2599+ if (cursor_mode == CurPressed && !seat->drag
2600+ && surface != seat->pointer_state.focused_surface
2601+ && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
2602+ c = w;
2603+ surface = seat->pointer_state.focused_surface;
2604+ /* zoom */
2605+ if (l) {
2606+ sx = cursor->x - l->scene->node.x;
2607+ sy = cursor->y - l->scene->node.y;
2608+ } else if (w && w->mon && fabs(w->mon->zoom_scale - 1.0) >= 1e-9) {
2609+ double s = w->mon->zoom_scale;
2610+ int scene_x = (int)llround((double)w->geom.x * s + w->mon->zoom_pan_x);
2611+ int scene_y = (int)llround((double)w->geom.y * s + w->mon->zoom_pan_y);
2612+ int bw_s = MAX(1, (int)llround((double)w->bw * s));
2613+ int gw_s = (int)llround((double)w->geom.width * s);
2614+ int gh_s = (int)llround((double)w->geom.height * s);
2615+ int inner_w = MAX(1, gw_s - 2 * bw_s);
2616+ int inner_h = MAX(1, gh_s - 2 * bw_s);
2617+ double cw = (double)MAX(1, w->geom.width - 2 * w->bw);
2618+ double ch = (double)MAX(1, w->geom.height - 2 * w->bw);
2619+ sx = (cursor->x - (scene_x + bw_s)) * cw / (double)inner_w;
2620+ sy = (cursor->y - (scene_y + bw_s)) * ch / (double)inner_h;
2621+ } else {
2622+ sx = cursor->x - w->geom.x;
2623+ sy = cursor->y - w->geom.y;
2624+ }
2625+ /* zoom end */
2626+ }
2627+
2628+ /* time is 0 in internal calls meant to restore pointer focus. */
2629+ if (time) {
2630+ wlr_relative_pointer_manager_v1_send_relative_motion(
2631+ relative_pointer_mgr, seat, (uint64_t)time * 1000,
2632+ dx, dy, dx_unaccel, dy_unaccel);
2633+
2634+ wl_list_for_each(constraint, &pointer_constraints->constraints, link)
2635+ cursorconstrain(constraint);
2636+
2637+ if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) {
2638+ toplevel_from_wlr_surface(active_constraint->surface, &c, NULL);
2639+ if (c && active_constraint->surface == seat->pointer_state.focused_surface) {
2640+ sx = cursor->x - c->geom.x - c->bw;
2641+ sy = cursor->y - c->geom.y - c->bw;
2642+ if (wlr_region_confine(&active_constraint->region, sx, sy,
2643+ sx + dx, sy + dy, &sx_confined, &sy_confined)) {
2644+ dx = sx_confined - sx;
2645+ dy = sy_confined - sy;
2646+ }
2647+
2648+ if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED)
2649+ return;
2650+ }
2651+ }
2652+
2653+ wlr_cursor_move(cursor, device, dx, dy);
2654+ wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
2655+
2656+ /* Update selmon (even while dragging a window) */
2657+ if (sloppyfocus)
2658+ selmon = xytomon(cursor->x, cursor->y);
2659+ }
2660+
2661+ /* Update drag icon's position */
2662+ wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
2663+
2664+ /* If we are currently grabbing the mouse, handle and return */
2665+ if (cursor_mode == CurSpawnRegion) {
2666+ spawnregion_x2 = cursor->x;
2667+ spawnregion_y2 = cursor->y;
2668+ spawnregion_update_rect();
2669+ return;
2670+ }
2671+ if (cursor_mode == CurMove) {
2672+ /* infinite_canvas */
2673+ if (canvas_panning) {
2674+ int tagidx;
2675+ double raw_dx, raw_dy;
2676+ double pdx_f, pdy_f;
2677+ int pdx, pdy;
2678+ Client *pc;
2679+ if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL) {
2680+ canvas_panning = 0;
2681+ cursor_mode = CurNormal;
2682+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
2683+ return;
2684+ }
2685+ tagidx = getcurrenttag(selmon);
2686+ raw_dx = cursor->x - canvas_pan_last_x;
2687+ raw_dy = cursor->y - canvas_pan_last_y;
2688+ canvas_pan_last_x = cursor->x;
2689+ canvas_pan_last_y = cursor->y;
2690+ /* zoom */
2691+ if (movecanvasmouse_multiplier_is_affected_by_zoom) {
2692+ double s = selmon->zoom_scale;
2693+ if (fabs(s) < 1e-9) s = 1.0;
2694+ pdx_f = raw_dx * canvas_pan_multiplier / s;
2695+ pdy_f = raw_dy * canvas_pan_multiplier / s;
2696+ } else {
2697+ pdx_f = raw_dx * canvas_pan_multiplier;
2698+ pdy_f = raw_dy * canvas_pan_multiplier;
2699+ }
2700+ /* zoom end */
2701+ canvas_pan_accum_x += (float)pdx_f;
2702+ canvas_pan_accum_y += (float)pdy_f;
2703+ pdx = (int)canvas_pan_accum_x;
2704+ pdy = (int)canvas_pan_accum_y;
2705+ canvas_pan_accum_x -= pdx;
2706+ canvas_pan_accum_y -= pdy;
2707+ selmon->canvas[tagidx].cx += pdx;
2708+ selmon->canvas[tagidx].cy += pdy;
2709+ wl_list_for_each(pc, &clients, link) {
2710+ if (!VISIBLEON(pc, selmon))
2711+ continue;
2712+ pc->geom.x += pdx;
2713+ pc->geom.y += pdy;
2714+ }
2715+ /* zoom */
2716+ zoom_apply(selmon);
2717+ /* zoom end */
2718+ } else if (grabc) {
2719+ /* zoom */
2720+ {
2721+ double zmv_s = grabc->mon ? grabc->mon->zoom_scale : 1.0;
2722+ int zmv_nx, zmv_ny;
2723+ if (fabs(zmv_s) < 1e-9) zmv_s = 1.0;
2724+ zmv_nx = (int)llround((cursor->x - grabcx - (grabc->mon ? grabc->mon->zoom_pan_x : 0.0)) / zmv_s);
2725+ zmv_ny = (int)llround((cursor->y - grabcy - (grabc->mon ? grabc->mon->zoom_pan_y : 0.0)) / zmv_s);
2726+ if (grabc->mon)
2727+ snap_edges(grabc, grabc->mon, &zmv_nx, &zmv_ny);
2728+ canvas_move_window(grabc, zmv_nx, zmv_ny);
2729+ }
2730+ /* zoom end */
2731+ }
2732+ /* infinite_canvas_end */
2733+ return;
2734+ } else if (cursor_mode == CurResize) {
2735+ /* zoom */
2736+ if (grabc && grabc->mon) {
2737+ double rzs = grabc->mon->zoom_scale;
2738+ int rzsx, rzsy, new_w, new_h;
2739+ struct wlr_box geo;
2740+ if (fabs(rzs) < 1e-9) rzs = 1.0;
2741+ rzsx = (int)llround(resize_grab_world_x * rzs + grabc->mon->zoom_pan_x);
2742+ rzsy = (int)llround(resize_grab_world_y * rzs + grabc->mon->zoom_pan_y);
2743+ new_w = (int)llround((cursor->x - rzsx) / rzs);
2744+ new_h = (int)llround((cursor->y - rzsy) / rzs);
2745+ geo = (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
2746+ .width = new_w, .height = new_h};
2747+ resize_magnetically(grabc, grabc->mon, &geo);
2748+ resize(grabc, geo, 1);
2749+ } else {
2750+ struct wlr_box geo;
2751+ geo = (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y,
2752+ .width = (int)round(cursor->x) - grabc->geom.x,
2753+ .height = (int)round(cursor->y) - grabc->geom.y};
2754+ resize_magnetically(grabc, grabc->mon, &geo);
2755+ resize(grabc, geo, 1);
2756+ }
2757+ /* zoom end */
2758+ return;
2759+ }
2760+
2761+ /* If there's no client surface under the cursor, set the cursor image to a
2762+ * default. This is what makes the cursor image appear when you move it
2763+ * off of a client or over its border.
2764+ * Don't reset the cursor while spawnregion is pending a draw. */
2765+ if (!surface && !seat->drag && !spawnregion_active)
2766+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
2767+
2768+ /* zoom */
2769+ if (c && !surface && c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9)
2770+ surface = client_surface(c);
2771+ /* zoom end */
2772+
2773+ pointerfocus(c, surface, sx, sy, time);
2774+}
2775+
2776+void
2777+motionrelative(struct wl_listener *listener, void *data)
2778+{
2779+ /* This event is forwarded by the cursor when a pointer emits a _relative_
2780+ * pointer motion event (i.e. a delta) */
2781+ struct wlr_pointer_motion_event *event = data;
2782+ /* The cursor doesn't move unless we tell it to. The cursor automatically
2783+ * handles constraining the motion to the output layout, as well as any
2784+ * special configuration applied for the specific input device which
2785+ * generated the event. You can pass NULL for the device if you want to move
2786+ * the cursor around without any input. */
2787+ motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y,
2788+ event->unaccel_dx, event->unaccel_dy);
2789+}
2790+
2791+void
2792+moveresize(const Arg *arg)
2793+{
2794+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
2795+ return;
2796+ xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL);
2797+ if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen)
2798+ return;
2799+
2800+ setfloating(grabc, 1);
2801+ resize_snap_x_target = 0;
2802+ resize_snap_y_target = 0;
2803+ switch (cursor_mode = arg->ui) {
2804+ case CurMove: {
2805+ /* zoom */
2806+ Monitor *m = grabc->mon;
2807+ double s;
2808+ int mvsx, mvsy;
2809+ s = m ? m->zoom_scale : 1.0;
2810+ if (fabs(s) < 1e-9) s = 1.0;
2811+ mvsx = m ? (int)llround((double)grabc->geom.x * s + m->zoom_pan_x) : grabc->geom.x;
2812+ mvsy = m ? (int)llround((double)grabc->geom.y * s + m->zoom_pan_y) : grabc->geom.y;
2813+ grabcx = (int)round(cursor->x) - mvsx;
2814+ grabcy = (int)round(cursor->y) - mvsy;
2815+ /* zoom end */
2816+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
2817+ break;
2818+ }
2819+ case CurResize: {
2820+ /* zoom */
2821+ Monitor *m = grabc->mon;
2822+ double s;
2823+ int ex, ey;
2824+ s = m ? m->zoom_scale : 1.0;
2825+ if (fabs(s) < 1e-9) s = 1.0;
2826+ resize_grab_world_x = grabc->geom.x;
2827+ resize_grab_world_y = grabc->geom.y;
2828+ ex = m ? (int)llround((double)(grabc->geom.x + grabc->geom.width) * s + m->zoom_pan_x)
2829+ : grabc->geom.x + grabc->geom.width;
2830+ ey = m ? (int)llround((double)(grabc->geom.y + grabc->geom.height) * s + m->zoom_pan_y)
2831+ : grabc->geom.y + grabc->geom.height;
2832+ wlr_cursor_warp_closest(cursor, NULL, ex, ey);
2833+ /* zoom end */
2834+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
2835+ break;
2836+ }
2837+ }
2838+}
2839+
2840+void
2841+outputmgrapply(struct wl_listener *listener, void *data)
2842+{
2843+ struct wlr_output_configuration_v1 *config = data;
2844+ outputmgrapplyortest(config, 0);
2845+}
2846+
2847+void
2848+outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test)
2849+{
2850+ /*
2851+ * Called when a client such as wlr-randr requests a change in output
2852+ * configuration. This is only one way that the layout can be changed,
2853+ * so any Monitor information should be updated by updatemons() after an
2854+ * output_layout.change event, not here.
2855+ */
2856+ struct wlr_output_configuration_head_v1 *config_head;
2857+ int ok = 1;
2858+
2859+ wl_list_for_each(config_head, &config->heads, link) {
2860+ struct wlr_output *wlr_output = config_head->state.output;
2861+ Monitor *m = wlr_output->data;
2862+ struct wlr_output_state state;
2863+
2864+ /* Ensure displays previously disabled by wlr-output-power-management-v1
2865+ * are properly handled*/
2866+ m->asleep = 0;
2867+
2868+ wlr_output_state_init(&state);
2869+ wlr_output_state_set_enabled(&state, config_head->state.enabled);
2870+ if (!config_head->state.enabled)
2871+ goto apply_or_test;
2872+
2873+ if (config_head->state.mode)
2874+ wlr_output_state_set_mode(&state, config_head->state.mode);
2875+ else
2876+ wlr_output_state_set_custom_mode(&state,
2877+ config_head->state.custom_mode.width,
2878+ config_head->state.custom_mode.height,
2879+ config_head->state.custom_mode.refresh);
2880+
2881+ wlr_output_state_set_transform(&state, config_head->state.transform);
2882+ wlr_output_state_set_scale(&state, config_head->state.scale);
2883+ wlr_output_state_set_adaptive_sync_enabled(&state,
2884+ config_head->state.adaptive_sync_enabled);
2885+
2886+apply_or_test:
2887+ ok &= test ? wlr_output_test_state(wlr_output, &state)
2888+ : wlr_output_commit_state(wlr_output, &state);
2889+
2890+ /* Don't move monitors if position wouldn't change. This avoids
2891+ * wlroots marking the output as manually configured.
2892+ * wlr_output_layout_add does not like disabled outputs */
2893+ if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y))
2894+ wlr_output_layout_add(output_layout, wlr_output,
2895+ config_head->state.x, config_head->state.y);
2896+
2897+ wlr_output_state_finish(&state);
2898+ }
2899+
2900+ if (ok)
2901+ wlr_output_configuration_v1_send_succeeded(config);
2902+ else
2903+ wlr_output_configuration_v1_send_failed(config);
2904+ wlr_output_configuration_v1_destroy(config);
2905+
2906+ /* https://codeberg.org/dwl/dwl/issues/577 */
2907+ updatemons(NULL, NULL);
2908+}
2909+
2910+void
2911+outputmgrtest(struct wl_listener *listener, void *data)
2912+{
2913+ struct wlr_output_configuration_v1 *config = data;
2914+ outputmgrapplyortest(config, 1);
2915+}
2916+
2917+void
2918+pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy,
2919+ uint32_t time)
2920+{
2921+ struct timespec now;
2922+
2923+ if (surface != seat->pointer_state.focused_surface &&
2924+ sloppyfocus && time && c && !client_is_unmanaged(c))
2925+ focusclient(c, 1);
2926+
2927+ /* If surface is NULL, clear pointer focus */
2928+ if (!surface) {
2929+ wlr_seat_pointer_notify_clear_focus(seat);
2930+ return;
2931+ }
2932+
2933+ if (!time) {
2934+ clock_gettime(CLOCK_MONOTONIC, &now);
2935+ time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
2936+ }
2937+
2938+ /* Let the client know that the mouse cursor has entered one
2939+ * of its surfaces, and make keyboard focus follow if desired.
2940+ * wlroots makes this a no-op if surface is already focused */
2941+ wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
2942+ wlr_seat_pointer_notify_motion(seat, time, sx, sy);
2943+}
2944+
2945+void
2946+printstatus(void)
2947+{
2948+ Monitor *m = NULL;
2949+ Client *c;
2950+ uint32_t occ, urg, sel;
2951+
2952+ wl_list_for_each(m, &mons, link) {
2953+ occ = urg = 0;
2954+ wl_list_for_each(c, &clients, link) {
2955+ if (c->mon != m)
2956+ continue;
2957+ occ |= c->tags;
2958+ if (c->isurgent)
2959+ urg |= c->tags;
2960+ }
2961+ if ((c = focustop(m))) {
2962+ printf("%s title %s\n", m->wlr_output->name, client_get_title(c));
2963+ printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c));
2964+ printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen);
2965+ printf("%s floating %d\n", m->wlr_output->name, c->isfloating);
2966+ sel = c->tags;
2967+ } else {
2968+ printf("%s title \n", m->wlr_output->name);
2969+ printf("%s appid \n", m->wlr_output->name);
2970+ printf("%s fullscreen \n", m->wlr_output->name);
2971+ printf("%s floating \n", m->wlr_output->name);
2972+ sel = 0;
2973+ }
2974+
2975+ printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
2976+ printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n",
2977+ m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg);
2978+ printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol);
2979+ }
2980+ fflush(stdout);
2981+}
2982+
2983+void
2984+powermgrsetmode(struct wl_listener *listener, void *data)
2985+{
2986+ struct wlr_output_power_v1_set_mode_event *event = data;
2987+ struct wlr_output_state state = {0};
2988+ Monitor *m = event->output->data;
2989+
2990+ if (!m)
2991+ return;
2992+
2993+ m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
2994+ wlr_output_state_set_enabled(&state, event->mode);
2995+ wlr_output_commit_state(m->wlr_output, &state);
2996+
2997+ m->asleep = !event->mode;
2998+ updatemons(NULL, NULL);
2999+}
3000+
3001+void
3002+quit(const Arg *arg)
3003+{
3004+ wl_display_terminate(dpy);
3005+}
3006+
3007+void
3008+rendermon(struct wl_listener *listener, void *data)
3009+{
3010+ Monitor *m = wl_container_of(listener, m, frame);
3011+ Client *c;
3012+ struct wlr_output_state pending = {0};
3013+ struct timespec now;
3014+
3015+ /* zoom */
3016+ zoom_frame(m);
3017+ /* zoom end */
3018+
3019+ wl_list_for_each(c, &clients, link) {
3020+ if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
3021+ goto skip;
3022+ }
3023+
3024+ /* zoom */
3025+ if (fabs(m->zoom_scale - 1.0) > 1e-5)
3026+ zoom_apply(m);
3027+ /* zoom end */
3028+
3029+ /* Build the complete scene state first, then hand that fully-rendered state
3030+ * to the output backend. This separates rendering from DRM submission and
3031+ * lets wlroots apply its explicit-sync state when the backend supports it.
3032+ * It is the closest wlroots-native equivalent to waiting for frame
3033+ * completion before queueing the next frame. */
3034+ if (!wlr_scene_output_build_state(m->scene_output, &pending, NULL))
3035+ goto skip;
3036+ if (!wlr_output_commit_state(m->wlr_output, &pending))
3037+ wlr_log(WLR_ERROR, "Failed to commit rendered frame on %s", m->wlr_output->name);
3038+
3039+skip:
3040+ clock_gettime(CLOCK_MONOTONIC, &now);
3041+ wlr_scene_output_send_frame_done(m->scene_output, &now);
3042+ wlr_output_state_finish(&pending);
3043+}
3044+
3045+void
3046+requestdecorationmode(struct wl_listener *listener, void *data)
3047+{
3048+ Client *c = wl_container_of(listener, c, set_decoration_mode);
3049+ if (c->surface.xdg->initialized)
3050+ wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration,
3051+ WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
3052+}
3053+
3054+void
3055+requeststartdrag(struct wl_listener *listener, void *data)
3056+{
3057+ struct wlr_seat_request_start_drag_event *event = data;
3058+
3059+ if (wlr_seat_validate_pointer_grab_serial(seat, event->origin,
3060+ event->serial))
3061+ wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
3062+ else
3063+ wlr_data_source_destroy(event->drag->source);
3064+}
3065+
3066+void
3067+requestmonstate(struct wl_listener *listener, void *data)
3068+{
3069+ struct wlr_output_event_request_state *event = data;
3070+ wlr_output_commit_state(event->output, event->state);
3071+ updatemons(NULL, NULL);
3072+}
3073+
3074+void
3075+resize(Client *c, struct wlr_box geo, int interact)
3076+{
3077+ struct wlr_box *bbox;
3078+ struct wlr_box clip;
3079+
3080+#ifdef TILING
3081+ if (resizelock) {
3082+ c->geom = geo;
3083+ return;
3084+ }
3085+#endif
3086+ if (!c->mon || !client_surface(c)->mapped)
3087+ return;
3088+
3089+ bbox = interact ? &sgeom : &c->mon->w;
3090+
3091+ client_set_bounds(c, geo.width, geo.height);
3092+ c->geom = geo;
3093+ /* zoom */
3094+ if (c->mon && c->mon->lt[c->mon->sellt]->arrange == NULL
3095+ && fabs(c->mon->zoom_scale - 1.0) >= 1e-9) {
3096+ c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width);
3097+ c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height);
3098+ } else
3099+ /* zoom end */
3100+ if (!(c->isfloating && c->mon->lt[c->mon->sellt]->arrange == NULL) || cursor_mode == CurResize)
3101+ applybounds(c, bbox);
3102+
3103+ /* zoom */
3104+ zoom_update_scene_client(c);
3105+ /* zoom end */
3106+
3107+ c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
3108+ c->geom.height - 2 * c->bw);
3109+ client_get_clip(c, &clip);
3110+ wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
3111+}
3112+
3113+void
3114+run(char *startup_cmd)
3115+{
3116+ /* Add a Unix socket to the Wayland display. */
3117+ const char *socket = wl_display_add_socket_auto(dpy);
3118+ if (!socket)
3119+ die("startup: display_add_socket_auto");
3120+ setenv("WAYLAND_DISPLAY", socket, 1);
3121+
3122+ /* Start the backend. This will enumerate outputs and inputs, become the DRM
3123+ * master, etc */
3124+ if (!wlr_backend_start(backend))
3125+ die("startup: backend_start");
3126+
3127+ /* Now that the socket exists and the backend is started, run the startup command */
3128+ if (startup_cmd) {
3129+ int piperw[2];
3130+ if (pipe(piperw) < 0)
3131+ die("startup: pipe:");
3132+ if ((child_pid = fork()) < 0)
3133+ die("startup: fork:");
3134+ if (child_pid == 0) {
3135+ setsid();
3136+ dup2(piperw[0], STDIN_FILENO);
3137+ close(piperw[0]);
3138+ close(piperw[1]);
3139+ execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL);
3140+ die("startup: execl:");
3141+ }
3142+ dup2(piperw[1], STDOUT_FILENO);
3143+ close(piperw[1]);
3144+ close(piperw[0]);
3145+ }
3146+
3147+ /* Mark stdout as non-blocking to avoid the startup script
3148+ * causing leeloo to freeze when a user neither closes stdin
3149+ * nor consumes standard input in his startup script */
3150+
3151+ if (fd_set_nonblock(STDOUT_FILENO) < 0)
3152+ close(STDOUT_FILENO);
3153+
3154+ printstatus();
3155+
3156+ /* At this point the outputs are initialized, choose initial selmon based on
3157+ * cursor position, and set default cursor image */
3158+ selmon = xytomon(cursor->x, cursor->y);
3159+
3160+ /* TODO hack to get cursor to display in its initial location (100, 100)
3161+ * instead of (0, 0) and then jumping. Still may not be fully
3162+ * initialized, as the image/coordinates are not transformed for the
3163+ * monitor when displayed here */
3164+ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y);
3165+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
3166+
3167+ /* Run the Wayland event loop. This does not return until you exit the
3168+ * compositor. Starting the backend rigged up all of the necessary event
3169+ * loop configuration to listen to libinput events, DRM events, generate
3170+ * frame events at the refresh rate, and so on. */
3171+ wl_display_run(dpy);
3172+}
3173+
3174+void
3175+setcursor(struct wl_listener *listener, void *data)
3176+{
3177+ /* This event is raised by the seat when a client provides a cursor image */
3178+ struct wlr_seat_pointer_request_set_cursor_event *event = data;
3179+ /* If we're "grabbing" the cursor, don't use the client's image, we will
3180+ * restore it after "grabbing" sending a leave event, followed by a enter
3181+ * event, which will result in the client requesting set the cursor surface */
3182+ if (spawnregion_active)
3183+ return;
3184+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
3185+ return;
3186+ /* This can be sent by any client, so we check to make sure this one
3187+ * actually has pointer focus first. If so, we can tell the cursor to
3188+ * use the provided surface as the cursor image. It will set the
3189+ * hardware cursor on the output that it's currently on and continue to
3190+ * do so as the cursor moves between outputs. */
3191+ if (event->seat_client == seat->pointer_state.focused_client)
3192+ wlr_cursor_set_surface(cursor, event->surface,
3193+ event->hotspot_x, event->hotspot_y);
3194+}
3195+
3196+void
3197+setcursorshape(struct wl_listener *listener, void *data)
3198+{
3199+ struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
3200+
3201+ if (spawnregion_active)
3202+ return;
3203+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
3204+ return;
3205+ /* This can be sent by any client, so we check to make sure this one
3206+ * actually has pointer focus first. If so, we can tell the cursor to
3207+ * use the provided cursor shape. */
3208+ if (event->seat_client == seat->pointer_state.focused_client)
3209+ wlr_cursor_set_xcursor(cursor, cursor_mgr,
3210+ wlr_cursor_shape_v1_name(event->shape));
3211+}
3212+
3213+void
3214+setfloating(Client *c, int floating)
3215+{
3216+ Client *p = client_get_parent(c);
3217+ c->isfloating = floating;
3218+ /* If in floating layout do not change the client's layer */
3219+ if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange)
3220+ return;
3221+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen ||
3222+ (p && p->isfullscreen) ? LyrFS
3223+ : c->isfloating ? LyrFloat : LyrTile]);
3224+ arrange(c->mon);
3225+ printstatus();
3226+}
3227+
3228+void
3229+setfullscreen(Client *c, int fullscreen)
3230+{
3231+ if (c->isfakefullscreen)
3232+ return; /* avoid clobbering c->prev while fake fullscreen owns it */
3233+ c->isfullscreen = fullscreen;
3234+ if (!c->mon || !client_surface(c)->mapped)
3235+ return;
3236+
3237+ c->bw = fullscreen ? 0 : (innerborderpx + outerborderpx);
3238+ c->ibw = fullscreen ? 0 : innerborderpx;
3239+ c->obw = fullscreen ? 0 : outerborderpx;
3240+
3241+ client_set_fullscreen(c, fullscreen);
3242+ wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen
3243+ ? LyrFS : c->isfloating ? LyrFloat : LyrTile]);
3244+
3245+ if (fullscreen) {
3246+ c->prev = c->geom;
3247+ resize(c, c->mon->m, 0);
3248+ } else {
3249+ /* restore previous size instead of arrange for floating windows since
3250+ * client positions are set by the user and cannot be recalculated */
3251+ resize(c, c->prev, 0);
3252+ }
3253+ arrange(c->mon);
3254+ printstatus();
3255+}
3256+
3257+void
3258+setlayout(const Arg *arg)
3259+{
3260+ /* infinite_canvas */
3261+ const Layout *old_layout;
3262+ const Layout *new_layout;
3263+ Client *_c;
3264+ /* infinite_canvas_end */
3265+ if (!selmon)
3266+ return;
3267+
3268+ /* infinite_canvas */
3269+ old_layout = selmon->lt[selmon->sellt];
3270+ /* infinite_canvas_end */
3271+
3272+ if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt])
3273+ selmon->sellt ^= 1;
3274+ if (arg && arg->v)
3275+ selmon->lt[selmon->sellt] = (Layout *)arg->v;
3276+
3277+ /* infinite_canvas */
3278+ new_layout = selmon->lt[selmon->sellt];
3279+ if (old_layout->arrange == NULL && new_layout->arrange != NULL) {
3280+ save_canvas_positions(selmon);
3281+ homecanvas(NULL);
3282+ wl_list_for_each(_c, &clients, link)
3283+ if (_c->mon == selmon && !_c->isfullscreen)
3284+ _c->isfloating = 0;
3285+ } else if (old_layout->arrange != NULL && new_layout->arrange == NULL) {
3286+ restore_canvas_positions(selmon);
3287+ wl_list_for_each(_c, &clients, link)
3288+ if (_c->mon == selmon)
3289+ _c->isfloating = 1;
3290+ }
3291+ /* infinite_canvas_end */
3292+
3293+ strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol));
3294+ arrange(selmon);
3295+ printstatus();
3296+}
3297+
3298+/* arg > 1.0 will set mfact absolutely */
3299+void
3300+setmfact(const Arg *arg)
3301+{
3302+ float f;
3303+
3304+ if (!arg || !selmon || !selmon->lt[selmon->sellt]->arrange)
3305+ return;
3306+ f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f;
3307+ if (f < 0.1 || f > 0.9)
3308+ return;
3309+ selmon->mfact = f;
3310+ arrange(selmon);
3311+}
3312+
3313+void
3314+setmon(Client *c, Monitor *m, uint32_t newtags)
3315+{
3316+ Monitor *oldmon = c->mon;
3317+
3318+ if (oldmon == m)
3319+ return;
3320+ c->mon = m;
3321+ c->prev = c->geom;
3322+
3323+ /* Scene graph sends surface leave/enter events on move and resize */
3324+ if (oldmon)
3325+ arrange(oldmon);
3326+ if (m) {
3327+ /* Make sure window actually overlaps with the monitor */
3328+ resize(c, c->geom, 0);
3329+ c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */
3330+#if CENTER_NEW_WINDOWS
3331+ c->prev.x = (m->w.width - c->prev.width) / 2 + m->m.x;
3332+ c->prev.y = (m->w.height - c->prev.height) / 2 + m->m.y;
3333+#endif
3334+ setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */
3335+ setfloating(c, c->isfloating);
3336+ /* infinite_canvas: this window's home pan was captured under the
3337+ * OLD monitor's independent canvas scale; that's meaningless on
3338+ * the new monitor, so re-anchor it here exactly like a freshly
3339+ * placed window (see mapnotify()), or bringintoview() will
3340+ * compute a wrong/garbage shift for this window going forward. */
3341+ {
3342+ int tagidx = getcurrenttag(m);
3343+ c->home_cx = m->canvas[tagidx].cx;
3344+ c->home_cy = m->canvas[tagidx].cy;
3345+ }
3346+ }
3347+ focusclient(focustop(selmon), 1);
3348+}
3349+
3350+void
3351+setpsel(struct wl_listener *listener, void *data)
3352+{
3353+ /* This event is raised by the seat when a client wants to set the selection,
3354+ * usually when the user copies something. wlroots allows compositors to
3355+ * ignore such requests if they so choose, but in leeloo we always honor them
3356+ */
3357+ struct wlr_seat_request_set_primary_selection_event *event = data;
3358+ wlr_seat_set_primary_selection(seat, event->source, event->serial);
3359+}
3360+
3361+void
3362+setsel(struct wl_listener *listener, void *data)
3363+{
3364+ /* This event is raised by the seat when a client wants to set the selection,
3365+ * usually when the user copies something. wlroots allows compositors to
3366+ * ignore such requests if they so choose, but in leeloo we always honor them
3367+ */
3368+ struct wlr_seat_request_set_selection_event *event = data;
3369+ wlr_seat_set_selection(seat, event->source, event->serial);
3370+}
3371+
3372+void
3373+setup(void)
3374+{
3375+ int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE};
3376+ struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig};
3377+ sigemptyset(&sa.sa_mask);
3378+
3379+ for (i = 0; i < (int)LENGTH(sig); i++)
3380+ sigaction(sig[i], &sa, NULL);
3381+
3382+ wlr_log_init(log_level, NULL);
3383+
3384+ /* The Wayland display is managed by libwayland. It handles accepting
3385+ * clients from the Unix socket, managing Wayland globals, and so on. */
3386+ dpy = wl_display_create();
3387+ event_loop = wl_display_get_event_loop(dpy);
3388+
3389+ /* The backend is a wlroots feature which abstracts the underlying input and
3390+ * output hardware. The autocreate option will choose the most suitable
3391+ * backend based on the current environment, such as opening an X11 window
3392+ * if an X11 server is running. */
3393+ if (!(backend = wlr_backend_autocreate(event_loop, &session)))
3394+ die("couldn't create backend");
3395+
3396+ /* Initialize the scene graph used to lay out windows */
3397+ scene = wlr_scene_create();
3398+ root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor);
3399+ for (i = 0; i < NUM_LAYERS; i++)
3400+ layers[i] = wlr_scene_tree_create(&scene->tree);
3401+ drag_icon = wlr_scene_tree_create(&scene->tree);
3402+ wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node);
3403+
3404+ /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user
3405+ * can also specify a renderer using the WLR_RENDERER env var.
3406+ * The renderer is responsible for defining the various pixel formats it
3407+ * supports for shared memory, this configures that for clients. */
3408+ if (!(drw = wlr_renderer_autocreate(backend)))
3409+ die("couldn't create renderer");
3410+ wl_signal_add(&drw->events.lost, &gpu_reset);
3411+
3412+ /* Create shm, drm and linux_dmabuf interfaces by ourselves.
3413+ * The simplest way is to call:
3414+ * wlr_renderer_init_wl_display(drw);
3415+ * but we need to create the linux_dmabuf interface manually to integrate it
3416+ * with wlr_scene. */
3417+ wlr_renderer_init_wl_shm(drw, dpy);
3418+
3419+ if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
3420+ wlr_drm_create(dpy, drw);
3421+ wlr_scene_set_linux_dmabuf_v1(scene,
3422+ wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw));
3423+ }
3424+
3425+ if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline
3426+ && backend->features.timeline)
3427+ wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd);
3428+
3429+ /* Autocreates an allocator for us.
3430+ * The allocator is the bridge between the renderer and the backend. It
3431+ * handles the buffer creation, allowing wlroots to render onto the
3432+ * screen */
3433+ if (!(alloc = wlr_allocator_autocreate(backend, drw)))
3434+ die("couldn't create allocator");
3435+
3436+ /* This creates some hands-off wlroots interfaces. The compositor is
3437+ * necessary for clients to allocate surfaces and the data device manager
3438+ * handles the clipboard. Each of these wlroots interfaces has room for you
3439+ * to dig your fingers in and play with their behavior if you want. Note that
3440+ * the clients cannot set the selection directly without compositor approval,
3441+ * see the setsel() function. */
3442+ compositor = wlr_compositor_create(dpy, 6, drw);
3443+ wlr_subcompositor_create(dpy);
3444+ wlr_data_device_manager_create(dpy);
3445+ wlr_export_dmabuf_manager_v1_create(dpy);
3446+ wlr_screencopy_manager_v1_create(dpy);
3447+ wlr_data_control_manager_v1_create(dpy);
3448+ wlr_ext_data_control_manager_v1_create(dpy, 1);
3449+ wlr_primary_selection_v1_device_manager_create(dpy);
3450+ wlr_viewporter_create(dpy);
3451+ wlr_single_pixel_buffer_manager_v1_create(dpy);
3452+ wlr_fractional_scale_manager_v1_create(dpy, 1);
3453+ wlr_presentation_create(dpy, backend, 2);
3454+ wlr_alpha_modifier_v1_create(dpy);
3455+
3456+ /* Initializes the interface used to implement urgency hints */
3457+ activation = wlr_xdg_activation_v1_create(dpy);
3458+ wl_signal_add(&activation->events.request_activate, &request_activate);
3459+
3460+ wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
3461+
3462+ power_mgr = wlr_output_power_manager_v1_create(dpy);
3463+ wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
3464+
3465+ /* Creates an output layout, which is a wlroots utility for working with an
3466+ * arrangement of screens in a physical layout. */
3467+ output_layout = wlr_output_layout_create(dpy);
3468+ wl_signal_add(&output_layout->events.change, &layout_change);
3469+
3470+ wlr_xdg_output_manager_v1_create(dpy, output_layout);
3471+
3472+ /* Configure a listener to be notified when new outputs are available on the
3473+ * backend. */
3474+ wl_list_init(&mons);
3475+ wl_list_init(&savedstates);
3476+ wl_signal_add(&backend->events.new_output, &new_output);
3477+
3478+ /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a
3479+ * Wayland protocol which is used for application windows. For more
3480+ * detail on shells, refer to the article:
3481+ *
3482+ * https://drewdevault.com/2018/07/29/Wayland-shells.html
3483+ */
3484+ wl_list_init(&clients);
3485+ wl_list_init(&fstack);
3486+ /* zoom */
3487+ wl_list_init(&popup_zoom_list);
3488+ /* zoom end */
3489+
3490+ xdg_shell = wlr_xdg_shell_create(dpy, 6);
3491+ wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
3492+ wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
3493+
3494+ layer_shell = wlr_layer_shell_v1_create(dpy, 3);
3495+ wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
3496+
3497+ idle_notifier = wlr_idle_notifier_v1_create(dpy);
3498+
3499+ idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
3500+ wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
3501+
3502+ session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
3503+ wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock);
3504+ locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
3505+ (float [4]){0.1f, 0.1f, 0.1f, 1.0f});
3506+ wlr_scene_node_set_enabled(&locked_bg->node, 0);
3507+
3508+ /* Use decoration protocols to negotiate server-side decorations */
3509+ wlr_server_decoration_manager_set_default_mode(
3510+ wlr_server_decoration_manager_create(dpy),
3511+ WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
3512+ xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
3513+ wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
3514+
3515+ pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
3516+ pointer_gestures = wlr_pointer_gestures_v1_create(dpy);
3517+ wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
3518+
3519+ relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
3520+
3521+ /* tablet-input */
3522+ tablet_mgr = wlr_tablet_v2_create(dpy);
3523+ /* tablet-input end */
3524+
3525+ /*
3526+ * Creates a cursor, which is a wlroots utility for tracking the cursor
3527+ * image shown on screen.
3528+ */
3529+ cursor = wlr_cursor_create();
3530+ wlr_cursor_attach_output_layout(cursor, output_layout);
3531+
3532+ /* Creates an xcursor manager, another wlroots utility which loads up
3533+ * Xcursor themes to source cursor images from and makes sure that cursor
3534+ * images are available at all scale factors on the screen (necessary for
3535+ * HiDPI support). Scaled cursors will be loaded with each output. */
3536+ cursor_mgr = wlr_xcursor_manager_create(getenv("XCURSOR_THEME"),
3537+ getenv("XCURSOR_SIZE") ? atoi(getenv("XCURSOR_SIZE")) : 24);
3538+ setenv("XCURSOR_SIZE", getenv("XCURSOR_SIZE") ? getenv("XCURSOR_SIZE") : "24", 1);
3539+
3540+ /*
3541+ * wlr_cursor *only* displays an image on screen. It does not move around
3542+ * when the pointer moves. However, we can attach input devices to it, and
3543+ * it will generate aggregate events for all of them. In these events, we
3544+ * can choose how we want to process them, forwarding them to clients and
3545+ * moving the cursor around. More detail on this process is described in
3546+ * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
3547+ *
3548+ * And more comments are sprinkled throughout the notify functions above.
3549+ */
3550+ wl_signal_add(&cursor->events.motion, &cursor_motion);
3551+ wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
3552+ wl_signal_add(&cursor->events.button, &cursor_button);
3553+ wl_signal_add(&cursor->events.axis, &cursor_axis);
3554+ wl_signal_add(&cursor->events.frame, &cursor_frame);
3555+
3556+ /* tablet-input */
3557+ wl_signal_add(&cursor->events.tablet_tool_proximity, &tablet_tool_proximity);
3558+ wl_signal_add(&cursor->events.tablet_tool_axis, &tablet_tool_axis);
3559+ wl_signal_add(&cursor->events.tablet_tool_button, &tablet_tool_button);
3560+ wl_signal_add(&cursor->events.tablet_tool_tip, &tablet_tool_tip);
3561+ /* tablet-input end */
3562+
3563+ cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
3564+ wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
3565+
3566+ /*
3567+ * Configures a seat, which is a single "seat" at which a user sits and
3568+ * operates the computer. This conceptually includes up to one keyboard,
3569+ * pointer, touch, and drawing tablet device. We also rig up a listener to
3570+ * let us know when new input devices are available on the backend.
3571+ */
3572+ wl_signal_add(&backend->events.new_input, &new_input_device);
3573+ virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
3574+ wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard,
3575+ &new_virtual_keyboard);
3576+ virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
3577+ wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer,
3578+ &new_virtual_pointer);
3579+
3580+ seat = wlr_seat_create(dpy, "seat0");
3581+ wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
3582+ wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
3583+ wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel);
3584+ wl_signal_add(&seat->events.request_start_drag, &request_start_drag);
3585+ wl_signal_add(&seat->events.start_drag, &start_drag);
3586+
3587+ kb_group = createkeyboardgroup();
3588+ wl_list_init(&kb_group->destroy.link);
3589+
3590+ output_mgr = wlr_output_manager_v1_create(dpy);
3591+ wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
3592+ wl_signal_add(&output_mgr->events.test, &output_mgr_test);
3593+
3594+ /* Make sure XWayland clients don't connect to the parent X server,
3595+ * e.g when running in the x11 backend or the wayland backend and the
3596+ * compositor has Xwayland support */
3597+ unsetenv("DISPLAY");
3598+#ifdef XWAYLAND
3599+ /*
3600+ * Initialise the XWayland X server.
3601+ * It will be started when the first X client is started.
3602+ */
3603+ if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
3604+ wl_signal_add(&xwayland->events.ready, &xwayland_ready);
3605+ wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);
3606+
3607+ setenv("DISPLAY", xwayland->display_name, 1);
3608+ } else {
3609+ fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
3610+ }
3611+#endif
3612+}
3613+
3614+void
3615+spawn(const Arg *arg)
3616+{
3617+ if (fork() == 0) {
3618+ dup2(STDERR_FILENO, STDOUT_FILENO);
3619+ setsid();
3620+ execvp(((char **)arg->v)[0], (char **)arg->v);
3621+ die("leeloo: execvp %s failed:", ((char **)arg->v)[0]);
3622+ }
3623+}
3624+
3625+void
3626+startdrag(struct wl_listener *listener, void *data)
3627+{
3628+ struct wlr_drag *drag = data;
3629+ if (!drag->icon)
3630+ return;
3631+
3632+ drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node;
3633+ LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
3634+}
3635+
3636+void
3637+tag(const Arg *arg)
3638+{
3639+ Client *sel = focustop(selmon);
3640+ if (!sel || (arg->ui & TAGMASK) == 0)
3641+ return;
3642+
3643+ sel->tags = arg->ui & TAGMASK;
3644+ /* infinite_canvas */
3645+ if (selmon && selmon->lt[selmon->sellt]->arrange == NULL) {
3646+ int _i;
3647+ for (_i = 0; _i < TAGCOUNT; _i++) {
3648+ if (arg->ui & (1u << _i)) {
3649+ sel->saved_cx = selmon->canvas[_i].cx + (selmon->w.width - sel->geom.width) / 2;
3650+ sel->saved_cy = selmon->canvas[_i].cy + (selmon->w.height - sel->geom.height) / 2;
3651+ sel->saved_cw = sel->geom.width;
3652+ sel->saved_ch = sel->geom.height;
3653+ sel->was_on_canvas = 1;
3654+ break;
3655+ }
3656+ }
3657+ }
3658+ /* infinite_canvas_end */
3659+ focusclient(focustop(selmon), 1);
3660+ arrange(selmon);
3661+ printstatus();
3662+}
3663+
3664+void
3665+tagmon(const Arg *arg)
3666+{
3667+ Client *sel = focustop(selmon);
3668+ if (sel)
3669+ setmon(sel, dirtomon(arg->i), 0);
3670+}
3671+
3672+#ifndef MAGNET_SNAP_DIST
3673+#define MAGNET_SNAP_DIST 12 /* on-screen pixels within which a dragged window's edge magnetically aligns to another edge */
3674+#endif
3675+
3676+/* Adjusts a candidate drag position (*nx, *ny) so that if any edge of the
3677+ * dragged window c is within MAGNET_SNAP_DIST screen-pixels of an edge of
3678+ * another visible window (or the monitor's own usable area), it snaps
3679+ * flush against it -- GNOME/driftwm style magnetic edge alignment. Unlike
3680+ * trysnap(), this never resizes the window or forces it into a half/quarter
3681+ * region; it only nudges position, and only when actually close to an edge. */
3682+#ifndef ALIGN_SNAP_DIST
3683+#define ALIGN_SNAP_DIST 60 /* on-screen pixels: how much top/bottom mismatch still counts as "close enough" to auto-align when snapping side-by-side against a window */
3684+#endif
3685+
3686+static void
3687+snap_edges(Client *c, Monitor *m, int *nx, int *ny)
3688+{
3689+ Client *o, *xpartner = NULL;
3690+ struct wlr_box cand;
3691+ int best_dx = 0, best_dy = 0, have_dx = 0, have_dy = 0;
3692+ int thresh, align_tol;
3693+
3694+ if (!c || !m)
3695+ return;
3696+
3697+ thresh = MAGNET_SNAP_DIST;
3698+ if (m->zoom_scale > 1e-9)
3699+ thresh = (int)llround(MAGNET_SNAP_DIST / m->zoom_scale);
3700+ align_tol = ALIGN_SNAP_DIST;
3701+ if (m->zoom_scale > 1e-9)
3702+ align_tol = (int)llround(ALIGN_SNAP_DIST / m->zoom_scale);
3703+
3704+ cand.x = *nx;
3705+ cand.y = *ny;
3706+ cand.width = c->geom.width;
3707+ cand.height = c->geom.height;
3708+
3709+ /* Snap against the monitor's own usable-area edges. */
3710+ {
3711+ struct wlr_box *w = &m->w;
3712+ int d;
3713+ d = abs(cand.x - w->x);
3714+ if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = w->x - cand.x; have_dx = 1; }
3715+ d = abs((cand.x + cand.width) - (w->x + w->width));
3716+ if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = (w->x + w->width) - (cand.x + cand.width); have_dx = 1; }
3717+ d = abs(cand.y - w->y);
3718+ if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = w->y - cand.y; have_dy = 1; }
3719+ d = abs((cand.y + cand.height) - (w->y + w->height));
3720+ if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = (w->y + w->height) - (cand.y + cand.height); have_dy = 1; }
3721+ }
3722+
3723+ /* Snap against every other visible window's edges, at whatever
3724+ * size each window actually is -- no resizing involved. */
3725+ wl_list_for_each(o, &clients, link) {
3726+ int d;
3727+ if (o == c || !VISIBLEON(o, m))
3728+ continue;
3729+
3730+ /* Only snap horizontally if the two windows vertically overlap
3731+ * (and vice versa) -- otherwise aligning to a window nowhere
3732+ * near it vertically would look wrong, not like a real snap. */
3733+ if (cand.y < o->geom.y + o->geom.height && o->geom.y < cand.y + cand.height) {
3734+ d = abs(cand.x - o->geom.x);
3735+ if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = o->geom.x - cand.x; have_dx = 1; xpartner = o; }
3736+ d = abs(cand.x - (o->geom.x + o->geom.width));
3737+ if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = (o->geom.x + o->geom.width) - cand.x; have_dx = 1; xpartner = o; }
3738+ d = abs((cand.x + cand.width) - (o->geom.x + o->geom.width));
3739+ if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = (o->geom.x + o->geom.width) - (cand.x + cand.width); have_dx = 1; xpartner = o; }
3740+ d = abs((cand.x + cand.width) - o->geom.x);
3741+ if (d <= thresh && (!have_dx || d < abs(best_dx))) { best_dx = o->geom.x - (cand.x + cand.width); have_dx = 1; xpartner = o; }
3742+ }
3743+
3744+ if (cand.x < o->geom.x + o->geom.width && o->geom.x < cand.x + cand.width) {
3745+ d = abs(cand.y - o->geom.y);
3746+ if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = o->geom.y - cand.y; have_dy = 1; }
3747+ d = abs(cand.y - (o->geom.y + o->geom.height));
3748+ if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = (o->geom.y + o->geom.height) - cand.y; have_dy = 1; }
3749+ d = abs((cand.y + cand.height) - (o->geom.y + o->geom.height));
3750+ if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = (o->geom.y + o->geom.height) - (cand.y + cand.height); have_dy = 1; }
3751+ d = abs((cand.y + cand.height) - o->geom.y);
3752+ if (d <= thresh && (!have_dy || d < abs(best_dy))) { best_dy = o->geom.y - (cand.y + cand.height); have_dy = 1; }
3753+ }
3754+ }
3755+
3756+ /* Compound alignment: if we're snapping side-by-side against a
3757+ * specific window (xpartner) and didn't already find a tight,
3758+ * independent Y-edge snap, check whether that partner's top or
3759+ * bottom edge is within a moderate tolerance -- close enough that
3760+ * it looks like it was "meant" to line up (a bit higher/lower is
3761+ * fine). If the mismatch is large (genuinely far apart vertically,
3762+ * not just a small offset), leave the vertical position untouched. */
3763+ if (have_dx && xpartner && !have_dy) {
3764+ int top_d = abs(cand.y - xpartner->geom.y);
3765+ int bot_d = abs((cand.y + cand.height) - (xpartner->geom.y + xpartner->geom.height));
3766+ if (top_d <= align_tol || bot_d <= align_tol) {
3767+ if (top_d <= bot_d)
3768+ best_dy = xpartner->geom.y - cand.y;
3769+ else
3770+ best_dy = (xpartner->geom.y + xpartner->geom.height) - (cand.y + cand.height);
3771+ have_dy = 1;
3772+ }
3773+ }
3774+
3775+ *nx += best_dx;
3776+ *ny += best_dy;
3777+}
3778+
3779+#ifndef RESIZE_MAGNET_SNAP_DIST
3780+#define RESIZE_MAGNET_SNAP_DIST 48
3781+#endif
3782+#ifndef RESIZE_MAGNET_RELEASE_DIST
3783+#define RESIZE_MAGNET_RELEASE_DIST 80
3784+#endif
3785+
3786+/* When a floating window is already magnetically touching another window,
3787+ * resizing its lower/right edge acts like a shared divider. Near the
3788+ * balanced size, the divider becomes sticky and snaps to the exact
3789+ * half-way point of the two-window cluster. */
3790+static void
3791+resize_magnetically(Client *c, Monitor *m, struct wlr_box *geo)
3792+{
3793+ Client *o, *right = NULL, *below = NULL;
3794+ int minw, minh, thresh, release;
3795+ int right_edge, bottom_edge;
3796+ int x_best = 0, y_best = 0;
3797+ int xdist = 0x3fffffff, ydist = 0x3fffffff;
3798+ int cluster_left, cluster_right, cluster_top, cluster_bottom;
3799+ int balance_x, balance_y;
3800+ int proposed_x, proposed_y;
3801+
3802+ if (!c || !m || !geo)
3803+ return;
3804+
3805+ thresh = RESIZE_MAGNET_SNAP_DIST;
3806+ release = RESIZE_MAGNET_RELEASE_DIST;
3807+ if (m->zoom_scale > 1e-9) {
3808+ thresh = (int)llround((double)RESIZE_MAGNET_SNAP_DIST / m->zoom_scale);
3809+ release = (int)llround((double)RESIZE_MAGNET_RELEASE_DIST / m->zoom_scale);
3810+ }
3811+
3812+ minw = 1 + 2 * (int)c->bw;
3813+ minh = 1 + 2 * (int)c->bw;
3814+
3815+ /* Find a window magnetically attached to the moving right edge. */
3816+ wl_list_for_each(o, &clients, link) {
3817+ int gap, overlap;
3818+ if (o == c || o->mon != m || !VISIBLEON(o, m) || o->isfullscreen)
3819+ continue;
3820+
3821+ overlap = MIN(c->geom.y + c->geom.height, o->geom.y + o->geom.height)
3822+ - MAX(c->geom.y, o->geom.y);
3823+ if (overlap <= 0)
3824+ continue;
3825+
3826+ gap = abs((c->geom.x + c->geom.width) - o->geom.x);
3827+ if (gap <= MAGNET_SNAP_DIST && o->geom.x >= c->geom.x + minw
3828+ && gap < xdist) {
3829+ right = o;
3830+ xdist = gap;
3831+ }
3832+ }
3833+
3834+ /* Find a window magnetically attached to the moving bottom edge. */
3835+ wl_list_for_each(o, &clients, link) {
3836+ int gap, overlap;
3837+ if (o == c || o->mon != m || !VISIBLEON(o, m) || o->isfullscreen)
3838+ continue;
3839+
3840+ overlap = MIN(c->geom.x + c->geom.width, o->geom.x + o->geom.width)
3841+ - MAX(c->geom.x, o->geom.x);
3842+ if (overlap <= 0)
3843+ continue;
3844+
3845+ gap = abs((c->geom.y + c->geom.height) - o->geom.y);
3846+ if (gap <= MAGNET_SNAP_DIST && o->geom.y >= c->geom.y + minh
3847+ && gap < ydist) {
3848+ below = o;
3849+ ydist = gap;
3850+ }
3851+ }
3852+
3853+ /* Horizontal cluster: c | right
3854+ * Keep the shared divider intact and snap it to the exact 50/50 point
3855+ * when the two widths are close enough. */
3856+ if (right) {
3857+ right_edge = geo->x + geo->width;
3858+ cluster_left = c->geom.x;
3859+ cluster_right = right->geom.x + right->geom.width;
3860+ balance_x = cluster_left + (cluster_right - cluster_left) / 2;
3861+ proposed_x = right_edge;
3862+
3863+ if (resize_snap_x_target) {
3864+ if (abs(proposed_x - resize_snap_x_target) <= release) {
3865+ proposed_x = resize_snap_x_target;
3866+ } else {
3867+ resize_snap_x_target = 0;
3868+ }
3869+ }
3870+
3871+ if (!resize_snap_x_target &&
3872+ abs((proposed_x - cluster_left) - right->geom.width) <= thresh) {
3873+ resize_snap_x_target = balance_x;
3874+ proposed_x = balance_x;
3875+ }
3876+
3877+ /* Respect minimum sizes on both sides. */
3878+ proposed_x = MAX(cluster_left + minw, proposed_x);
3879+ proposed_x = MIN(cluster_right - (1 + 2 * (int)right->bw), proposed_x);
3880+
3881+ if (resize_snap_x_target)
3882+ resize_snap_x_target = proposed_x;
3883+
3884+ geo->width = MAX(minw, proposed_x - c->geom.x);
3885+
3886+ /* Resize the partner so the magnetic connection stays intact. */
3887+ {
3888+ struct wlr_box other = right->geom;
3889+ other.x = proposed_x;
3890+ other.width = cluster_right - proposed_x;
3891+ if (other.width >= 1 + 2 * (int)right->bw)
3892+ resize(right, other, 1);
3893+ }
3894+ } else {
3895+ resize_snap_x_target = 0;
3896+ }
3897+
3898+ /* Vertical cluster: c
3899+ * ---
3900+ * below
3901+ *
3902+ * Same sticky 50/50 divider behavior for the bottom edge. */
3903+ if (below) {
3904+ bottom_edge = geo->y + geo->height;
3905+ cluster_top = c->geom.y;
3906+ cluster_bottom = below->geom.y + below->geom.height;
3907+ balance_y = cluster_top + (cluster_bottom - cluster_top) / 2;
3908+ proposed_y = bottom_edge;
3909+
3910+ if (resize_snap_y_target) {
3911+ if (abs(proposed_y - resize_snap_y_target) <= release) {
3912+ proposed_y = resize_snap_y_target;
3913+ } else {
3914+ resize_snap_y_target = 0;
3915+ }
3916+ }
3917+
3918+ if (!resize_snap_y_target &&
3919+ abs((proposed_y - cluster_top) - below->geom.height) <= thresh) {
3920+ resize_snap_y_target = balance_y;
3921+ proposed_y = balance_y;
3922+ }
3923+
3924+ proposed_y = MAX(cluster_top + minh, proposed_y);
3925+ proposed_y = MIN(cluster_bottom - (1 + 2 * (int)below->bw), proposed_y);
3926+
3927+ if (resize_snap_y_target)
3928+ resize_snap_y_target = proposed_y;
3929+
3930+ geo->height = MAX(minh, proposed_y - c->geom.y);
3931+
3932+ /* Resize the partner so the magnetic connection stays intact. */
3933+ {
3934+ struct wlr_box other = below->geom;
3935+ other.y = proposed_y;
3936+ other.height = cluster_bottom - proposed_y;
3937+ if (other.height >= 1 + 2 * (int)below->bw)
3938+ resize(below, other, 1);
3939+ }
3940+ } else {
3941+ resize_snap_y_target = 0;
3942+ }
3943+}
3944+
3945+
3946+#ifdef TILING
3947+void
3948+tile(Monitor *m)
3949+{
3950+ unsigned int mw, my, ty;
3951+ int i, n = 0;
3952+ Client *c;
3953+
3954+ wl_list_for_each(c, &clients, link)
3955+ if (VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen)
3956+ n++;
3957+ if (n == 0)
3958+ return;
3959+
3960+ if (n > m->nmaster)
3961+ mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0;
3962+ else
3963+ mw = m->w.width;
3964+ i = my = ty = 0;
3965+ wl_list_for_each(c, &clients, link) {
3966+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
3967+ continue;
3968+ if (i < m->nmaster) {
3969+ resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw,
3970+ .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0);
3971+ my += c->geom.height;
3972+ } else {
3973+ resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty,
3974+ .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0);
3975+ ty += c->geom.height;
3976+ }
3977+ i++;
3978+ }
3979+}
3980+
3981+void
3982+togglefloating(const Arg *arg)
3983+{
3984+ Client *sel = focustop(selmon);
3985+ /* return if fullscreen */
3986+ if (sel && !sel->isfullscreen)
3987+ setfloating(sel, !sel->isfloating);
3988+}
3989+#endif
3990+
3991+void
3992+togglefullscreen(const Arg *arg)
3993+{
3994+ Client *sel = focustop(selmon);
3995+ if (sel)
3996+ setfullscreen(sel, !sel->isfullscreen);
3997+}
3998+
3999+void
4000+togglefakefullscreen(const Arg *arg)
4001+{
4002+ Client *c = focustop(selmon);
4003+
4004+ if (!c || !c->mon || c->isfullscreen)
4005+ return; /* real fullscreen already owns the screen; don't fight it */
4006+
4007+ if (!c->isfakefullscreen) {
4008+ /* Fake fullscreen covers the whole usable monitor, but keeps Leeloo's
4009+ * normal double border visible. Remember the old geometry first so the
4010+ * exact window position and size can be restored when toggled off. */
4011+ c->prev = c->geom;
4012+ c->isfakefullscreen = 1;
4013+ if (!c->isfloating)
4014+ setfloating(c, 1);
4015+
4016+ c->bw = innerborderpx + outerborderpx;
4017+ c->ibw = innerborderpx;
4018+ c->obw = outerborderpx;
4019+ resize(c, c->mon->m, 0);
4020+ } else {
4021+ c->isfakefullscreen = 0;
4022+ c->bw = innerborderpx + outerborderpx;
4023+ c->ibw = innerborderpx;
4024+ c->obw = outerborderpx;
4025+ resize(c, c->prev, 0);
4026+ }
4027+ arrange(c->mon);
4028+ printstatus();
4029+}
4030+
4031+/* labwc-style directional snap state machine.
4032+ *
4033+ * The 8 snap states are:
4034+ *
4035+ * TL T TR
4036+ * L F R
4037+ * BL B BR
4038+ *
4039+ * Directional keys move between adjacent states along the corresponding
4040+ * axis. F is the bridge between opposite full-height/full-width states.
4041+ */
4042+void
4043+snaptoposition(const Arg *arg)
4044+{
4045+ Client *c = focustop(selmon);
4046+ struct wlr_box *w;
4047+ struct wlr_box left, right, top, bottom;
4048+ struct wlr_box tl, tr, bl, br;
4049+ struct wlr_box box, cur;
4050+ int state = -1;
4051+ int halfw, halfh, rightw, bottomh;
4052+
4053+ if (!c || !c->mon || c->isfullscreen || c->isfakefullscreen)
4054+ return;
4055+ if (!c->isfloating)
4056+ setfloating(c, 1);
4057+
4058+ w = &c->mon->w;
4059+
4060+ halfw = w->width / 2;
4061+ halfh = w->height / 2;
4062+ rightw = w->width - halfw;
4063+ bottomh = w->height - halfh;
4064+
4065+ left = (struct wlr_box){w->x, w->y, halfw, w->height};
4066+ right = (struct wlr_box){w->x + halfw, w->y, rightw, w->height};
4067+ top = (struct wlr_box){w->x, w->y, w->width, halfh};
4068+ bottom = (struct wlr_box){w->x, w->y + halfh, w->width, bottomh};
4069+ tl = (struct wlr_box){w->x, w->y, halfw, halfh};
4070+ tr = (struct wlr_box){w->x + halfw, w->y, rightw, halfh};
4071+ bl = (struct wlr_box){w->x, w->y + halfh, halfw, bottomh};
4072+ br = (struct wlr_box){w->x + halfw, w->y + halfh, rightw, bottomh};
4073+
4074+ cur = c->geom;
4075+
4076+ /* c->geom stores the exact snap box, including the compositor border.
4077+ * Use exact geometry matching here: >= would let a larger side or full-screen
4078+ * box also match a corner state and break the directional state machine. */
4079+ if (cur.x == tl.x && cur.y == tl.y &&
4080+ cur.width == tl.width &&
4081+ cur.height == tl.height)
4082+ state = 1; /* top-left */
4083+ else if (cur.x == top.x && cur.y == top.y &&
4084+ cur.width == top.width &&
4085+ cur.height == top.height)
4086+ state = 2; /* top */
4087+ else if (cur.x == tr.x && cur.y == tr.y &&
4088+ cur.width == tr.width &&
4089+ cur.height == tr.height)
4090+ state = 3; /* top-right */
4091+ else if (cur.x == right.x && cur.y == right.y &&
4092+ cur.width == right.width &&
4093+ cur.height == right.height)
4094+ state = 4; /* right */
4095+ else if (cur.x == br.x && cur.y == br.y &&
4096+ cur.width == br.width &&
4097+ cur.height == br.height)
4098+ state = 5; /* bottom-right */
4099+ else if (cur.x == bottom.x && cur.y == bottom.y &&
4100+ cur.width == bottom.width &&
4101+ cur.height == bottom.height)
4102+ state = 6; /* bottom */
4103+ else if (cur.x == bl.x && cur.y == bl.y &&
4104+ cur.width == bl.width &&
4105+ cur.height == bl.height)
4106+ state = 7; /* bottom-left */
4107+ else if (cur.x == left.x && cur.y == left.y &&
4108+ cur.width == left.width &&
4109+ cur.height == left.height)
4110+ state = 0; /* left */
4111+ else if (cur.x == w->x && cur.y == w->y &&
4112+ cur.width == w->width &&
4113+ cur.height == w->height)
4114+ state = 8; /* fake/fullscreen snap bridge */
4115+
4116+ /* Unknown geometry: a directional key simply snaps into its requested
4117+ * destination rather than trying to infer a previous state. */
4118+ if (state < 0) {
4119+ switch (arg->i) {
4120+ case SnapLeft: box = left; break;
4121+ case SnapRight: box = right; break;
4122+ case SnapTop: box = top; break;
4123+ case SnapBottom: box = bottom; break;
4124+ case SnapTopLeft: box = tl; break;
4125+ case SnapTopRight: box = tr; break;
4126+ case SnapBottomLeft: box = bl; break;
4127+ case SnapBottomRight: box = br; break;
4128+ default: return;
4129+ }
4130+ resize(c, box, 0);
4131+ return;
4132+ }
4133+
4134+ /* Explicit directional state graph. This keeps every edge deterministic
4135+ * and avoids the old fullscreen/corner fall-through bugs. */
4136+ switch (arg->i) {
4137+ case SnapLeft:
4138+ switch (state) {
4139+ case 2: box = tl; break; /* T -> TL */
4140+ case 3: box = top; break; /* TR -> T */
4141+ case 4: box = *w; break;
4142+ case 5: box = bottom; break; /* BR -> B */
4143+ case 6: box = bl; break; /* B -> BL */
4144+ case 7: box = br; break; /* BL -> BR */
4145+ case 8: box = left; break; /* F -> L */
4146+ default: return;
4147+ }
4148+ break;
4149+
4150+ case SnapRight:
4151+ switch (state) {
4152+ case 0: box = *w; break; /* L -> F */
4153+ case 1: box = top; break; /* TL -> T */
4154+ case 2: box = tr; break; /* T -> TR */
4155+ case 5: box = bl; break; /* BR -> BL */
4156+ case 6: box = br; break; /* B -> BR */
4157+ case 7: box = bottom; break; /* BL -> B */
4158+ case 8: box = right; break; /* F -> R */
4159+ default: return;
4160+ }
4161+ break;
4162+
4163+ case SnapTop:
4164+ switch (state) {
4165+ case 0: box = tl; break; /* L -> TL */
4166+ case 1: box = top; break; /* TL -> T */
4167+ case 3: box = top; break; /* TR -> T */
4168+ case 4: box = tr; break; /* R -> TR */
4169+ case 5: box = right; break; /* BR -> R */
4170+ case 6: box = *w; break; /* B -> F */
4171+ case 7: box = left; break; /* BL -> L */
4172+ case 8: box = top; break; /* F -> T */
4173+ default: return;
4174+ }
4175+ break;
4176+
4177+ case SnapBottom:
4178+ switch (state) {
4179+ case 0: box = bl; break; /* L -> BL */
4180+ case 1: box = left; break; /* TL -> L */
4181+ case 2: box = *w; break; /* T -> F */
4182+ case 3: box = right; break; /* TR -> R */
4183+ case 4: box = br; break; /* R -> BR */
4184+ case 5: box = right; break; /* BR -> R */
4185+ case 7: box = bottom; break; /* BL -> B */
4186+ case 8: box = bottom; break; /* F -> B */
4187+ default: return;
4188+ }
4189+ break;
4190+
4191+ case SnapTopLeft: box = tl; break;
4192+ case SnapTopRight: box = tr; break;
4193+ case SnapBottomLeft: box = bl; break;
4194+ case SnapBottomRight: box = br; break;
4195+ default: return;
4196+ }
4197+
4198+ resize(c, box, 0);
4199+}
4200+
4201+void
4202+toggletag(const Arg *arg)
4203+{
4204+ uint32_t newtags;
4205+ Client *sel = focustop(selmon);
4206+ if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK)))
4207+ return;
4208+
4209+ sel->tags = newtags;
4210+ focusclient(focustop(selmon), 1);
4211+ arrange(selmon);
4212+ printstatus();
4213+}
4214+
4215+void
4216+toggleview(const Arg *arg)
4217+{
4218+ uint32_t newtagset;
4219+ if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0))
4220+ return;
4221+
4222+ selmon->tagset[selmon->seltags] = newtagset;
4223+ focusclient(focustop(selmon), 1);
4224+ arrange(selmon);
4225+ printstatus();
4226+}
4227+
4228+void
4229+unlocksession(struct wl_listener *listener, void *data)
4230+{
4231+ SessionLock *lock = wl_container_of(listener, lock, unlock);
4232+ destroylock(lock, 1);
4233+}
4234+
4235+void
4236+unmaplayersurfacenotify(struct wl_listener *listener, void *data)
4237+{
4238+ LayerSurface *l = wl_container_of(listener, l, unmap);
4239+
4240+ l->mapped = 0;
4241+ wlr_scene_node_set_enabled(&l->scene->node, 0);
4242+ if (l == exclusive_focus)
4243+ exclusive_focus = NULL;
4244+ if (l->layer_surface->output && (l->mon = l->layer_surface->output->data))
4245+ arrangelayers(l->mon);
4246+ if (l->layer_surface->surface == seat->keyboard_state.focused_surface)
4247+ focusclient(focustop(selmon), 1);
4248+ motionnotify(0, NULL, 0, 0, 0, 0);
4249+}
4250+
4251+void
4252+unmapnotify(struct wl_listener *listener, void *data)
4253+{
4254+ /* Called when the surface is unmapped, and should no longer be shown. */
4255+ Client *c = wl_container_of(listener, c, unmap);
4256+ if (c == grabc) {
4257+ cursor_mode = CurNormal;
4258+ grabc = NULL;
4259+ }
4260+
4261+ if (client_is_unmanaged(c)) {
4262+ if (c == exclusive_focus) {
4263+ exclusive_focus = NULL;
4264+ focusclient(focustop(selmon), 1);
4265+ }
4266+ } else {
4267+ wl_list_remove(&c->link);
4268+ setmon(c, NULL, 0);
4269+ wl_list_remove(&c->flink);
4270+ }
4271+
4272+ if (c == waiting_geo_client)
4273+ waiting_geo_client = NULL;
4274+
4275+ wlr_scene_node_destroy(&c->scene->node);
4276+ printstatus();
4277+ motionnotify(0, NULL, 0, 0, 0, 0);
4278+}
4279+
4280+void
4281+updatemons(struct wl_listener *listener, void *data)
4282+{
4283+ /*
4284+ * Called whenever the output layout changes: adding or removing a
4285+ * monitor, changing an output's mode or position, etc. This is where
4286+ * the change officially happens and we update geometry, window
4287+ * positions, focus, and the stored configuration in wlroots'
4288+ * output-manager implementation.
4289+ */
4290+ struct wlr_output_configuration_v1 *config
4291+ = wlr_output_configuration_v1_create();
4292+ Client *c;
4293+ struct wlr_output_configuration_head_v1 *config_head;
4294+ Monitor *m;
4295+
4296+ /* First remove from the layout the disabled monitors */
4297+ wl_list_for_each(m, &mons, link) {
4298+ if (m->wlr_output->enabled || m->asleep)
4299+ continue;
4300+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
4301+ config_head->state.enabled = 0;
4302+ /* Remove this output from the layout to avoid cursor enter inside it */
4303+ wlr_output_layout_remove(output_layout, m->wlr_output);
4304+ closemon(m);
4305+ m->m = m->w = (struct wlr_box){0};
4306+ }
4307+ /* Insert outputs that need to */
4308+ wl_list_for_each(m, &mons, link) {
4309+ if (m->wlr_output->enabled
4310+ && !wlr_output_layout_get(output_layout, m->wlr_output))
4311+ wlr_output_layout_add_auto(output_layout, m->wlr_output);
4312+ }
4313+
4314+ /* Now that we update the output layout we can get its box */
4315+ wlr_output_layout_get_box(output_layout, NULL, &sgeom);
4316+
4317+ wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y);
4318+ wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height);
4319+
4320+ /* Make sure the clients are hidden when leeloo is locked */
4321+ wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y);
4322+ wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height);
4323+
4324+ wl_list_for_each(m, &mons, link) {
4325+ if (!m->wlr_output->enabled)
4326+ continue;
4327+ config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
4328+
4329+ /* Get the effective monitor geometry to use for surfaces */
4330+ wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m);
4331+ m->w = m->m;
4332+ wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y);
4333+
4334+ wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y);
4335+ wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height);
4336+
4337+ if (m->lock_surface) {
4338+ struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
4339+ wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
4340+ wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height);
4341+ }
4342+
4343+ /* Calculate the effective monitor geometry to use for clients */
4344+ arrangelayers(m);
4345+ /* Don't move clients to the left output when plugging monitors */
4346+ arrange(m);
4347+ /* make sure fullscreen clients have the right size */
4348+ if ((c = focustop(m)) && c->isfullscreen)
4349+ resize(c, m->m, 0);
4350+
4351+ /* Try to re-set the gamma LUT when updating monitors,
4352+ * it's only really needed when enabling a disabled output, but meh. */
4353+ m->gamma_lut_changed = 1;
4354+
4355+ config_head->state.x = m->m.x;
4356+ config_head->state.y = m->m.y;
4357+
4358+ if (!selmon) {
4359+ selmon = m;
4360+ }
4361+ }
4362+
4363+ if (selmon && selmon->wlr_output->enabled) {
4364+ wl_list_for_each(c, &clients, link) {
4365+ if (!c->mon && client_surface(c)->mapped)
4366+ setmon(c, selmon, c->tags);
4367+ }
4368+ focusclient(focustop(selmon), 1);
4369+ if (selmon->lock_surface) {
4370+ client_notify_enter(selmon->lock_surface->surface,
4371+ wlr_seat_get_keyboard(seat));
4372+ client_activate_surface(selmon->lock_surface->surface, 1);
4373+ }
4374+ }
4375+
4376+ /* FIXME: figure out why the cursor image is at 0,0 after turning all
4377+ * the monitors on.
4378+ * Move the cursor image where it used to be. It does not generate a
4379+ * wl_pointer.motion event for the clients, it's only the image what it's
4380+ * at the wrong position after all. */
4381+ wlr_cursor_move(cursor, NULL, 0, 0);
4382+
4383+ wlr_output_manager_v1_set_configuration(output_mgr, config);
4384+}
4385+
4386+void
4387+updatetitle(struct wl_listener *listener, void *data)
4388+{
4389+ Client *c = wl_container_of(listener, c, set_title);
4390+ if (c == focustop(c->mon))
4391+ printstatus();
4392+}
4393+
4394+void
4395+urgent(struct wl_listener *listener, void *data)
4396+{
4397+ struct wlr_xdg_activation_v1_request_activate_event *event = data;
4398+ Client *c = NULL;
4399+ toplevel_from_wlr_surface(event->surface, &c, NULL);
4400+ if (!c || c == focustop(selmon))
4401+ return;
4402+
4403+ c->isurgent = 1;
4404+ printstatus();
4405+
4406+ if (client_surface(c)->mapped)
4407+ client_set_border_color(c, urgent_inner_border_color, urgent_outer_border_color);
4408+}
4409+
4410+void
4411+view(const Arg *arg)
4412+{
4413+ if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags])
4414+ return;
4415+
4416+ /* infinite_canvas */
4417+ if (selmon->lt[selmon->sellt]->arrange == NULL)
4418+ save_canvas_positions(selmon);
4419+ /* infinite_canvas_end */
4420+
4421+ selmon->seltags ^= 1; /* toggle sel tagset */
4422+ if (arg->ui & TAGMASK)
4423+ selmon->tagset[selmon->seltags] = arg->ui & TAGMASK;
4424+
4425+ /* infinite_canvas */
4426+ {
4427+ int _newtag = getcurrenttag(selmon);
4428+ Client *_c;
4429+ if (selmon->lt[selmon->sellt]->arrange != NULL) {
4430+ selmon->canvas[_newtag].cx = 0;
4431+ selmon->canvas[_newtag].cy = 0;
4432+ } else {
4433+ restore_canvas_positions(selmon);
4434+ wl_list_for_each(_c, &clients, link)
4435+ if (VISIBLEON(_c, selmon))
4436+ _c->isfloating = 1;
4437+ }
4438+ }
4439+ /* infinite_canvas_end */
4440+
4441+ focusclient(focustop(selmon), 1);
4442+ arrange(selmon);
4443+ printstatus();
4444+}
4445+
4446+void
4447+virtualkeyboard(struct wl_listener *listener, void *data)
4448+{
4449+ struct wlr_virtual_keyboard_v1 *kb = data;
4450+ /* virtual keyboards shouldn't share keyboard group */
4451+ KeyboardGroup *group = createkeyboardgroup();
4452+ /* Set the keymap to match the group keymap */
4453+ wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
4454+ LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup);
4455+
4456+ /* Add the new keyboard to the group */
4457+ wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
4458+}
4459+
4460+void
4461+virtualpointer(struct wl_listener *listener, void *data)
4462+{
4463+ struct wlr_virtual_pointer_v1_new_pointer_event *event = data;
4464+ struct wlr_input_device *device = &event->new_pointer->pointer.base;
4465+
4466+ wlr_cursor_attach_input_device(cursor, device);
4467+ if (event->suggested_output)
4468+ wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
4469+}
4470+
4471+Monitor *
4472+xytomon(double x, double y)
4473+{
4474+ struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
4475+ return o ? o->data : NULL;
4476+}
4477+
4478+void
4479+xytonode(double x, double y, struct wlr_surface **psurface,
4480+ Client **pc, LayerSurface **pl, double *nx, double *ny)
4481+{
4482+ struct wlr_scene_node *node, *pnode;
4483+ struct wlr_surface *surface = NULL;
4484+ Client *c = NULL;
4485+ LayerSurface *l = NULL;
4486+ int layer;
4487+
4488+ for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
4489+ if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
4490+ continue;
4491+
4492+ if (node->type == WLR_SCENE_NODE_BUFFER)
4493+ surface = wlr_scene_surface_try_from_buffer(
4494+ wlr_scene_buffer_from_node(node))->surface;
4495+ for (pnode = node; pnode && !c; pnode = &pnode->parent->node)
4496+ c = pnode->data;
4497+ if (c && c->type == LayerShell) {
4498+ l = (LayerSurface *)c;
4499+ c = NULL;
4500+ }
4501+ }
4502+
4503+ /* zoom */
4504+ if (c && surface && !c->isfullscreen
4505+ && c->mon && fabs(c->mon->zoom_scale - 1.0) >= 1e-9 && nx && ny) {
4506+ Monitor *m = c->mon;
4507+ double s = m->zoom_scale;
4508+ if (surface == client_surface(c)) {
4509+ int scene_x = (int)llround((double)c->geom.x * s + m->zoom_pan_x);
4510+ int scene_y = (int)llround((double)c->geom.y * s + m->zoom_pan_y);
4511+ int bw_s = MAX(1, (int)llround((double)c->bw * s));
4512+ int gw_s = (int)llround((double)c->geom.width * s);
4513+ int gh_s = (int)llround((double)c->geom.height * s);
4514+ int inner_w = MAX(1, gw_s - 2 * bw_s);
4515+ int inner_h = MAX(1, gh_s - 2 * bw_s);
4516+ double cw = (double)MAX(1, c->geom.width - 2 * c->bw);
4517+ double ch = (double)MAX(1, c->geom.height - 2 * c->bw);
4518+ double ox = (double)(scene_x + bw_s);
4519+ double oy = (double)(scene_y + bw_s);
4520+ *nx = (x - ox) * cw / (double)inner_w;
4521+ *ny = (y - oy) * ch / (double)inner_h;
4522+ } else {
4523+ int abs_nx = 0, abs_ny = 0;
4524+ if (node && wlr_scene_node_coords(node, &abs_nx, &abs_ny)) {
4525+ double cw = (double)MAX(1, c->geom.width - 2 * c->bw);
4526+ double ch = (double)MAX(1, c->geom.height - 2 * c->bw);
4527+ int gw_s = (int)llround((double)c->geom.width * s);
4528+ int gh_s = (int)llround((double)c->geom.height * s);
4529+ int bw_s = MAX(1, (int)llround((double)c->bw * s));
4530+ int inner_w = MAX(1, gw_s - 2 * bw_s);
4531+ int inner_h = MAX(1, gh_s - 2 * bw_s);
4532+ *nx = (x - abs_nx) * cw / (double)inner_w;
4533+ *ny = (y - abs_ny) * ch / (double)inner_h;
4534+ }
4535+ }
4536+ }
4537+ /* zoom end */
4538+
4539+ if (psurface) *psurface = surface;
4540+ if (pc) *pc = c;
4541+ if (pl) *pl = l;
4542+}
4543+
4544+void
4545+swapmaster(const Arg *arg)
4546+{
4547+ Client *c, *sel = focustop(selmon);
4548+
4549+ if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating)
4550+ return;
4551+
4552+ /* Search for the first tiled window that is not sel, marking sel as
4553+ * NULL if we pass it along the way */
4554+ wl_list_for_each(c, &clients, link) {
4555+ if (VISIBLEON(c, selmon) && !c->isfloating) {
4556+ if (c != sel)
4557+ break;
4558+ sel = NULL;
4559+ }
4560+ }
4561+
4562+ /* Return if no other tiled window was found */
4563+ if (&c->link == &clients)
4564+ return;
4565+
4566+ /* If we passed sel, move c to the front; otherwise, move sel to the
4567+ * front */
4568+ if (!sel)
4569+ sel = c;
4570+ wl_list_remove(&sel->link);
4571+ wl_list_insert(&clients, &sel->link);
4572+
4573+ focusclient(sel, 1);
4574+ arrange(selmon);
4575+}
4576+
4577+#ifdef XWAYLAND
4578+void
4579+activatex11(struct wl_listener *listener, void *data)
4580+{
4581+ Client *c = wl_container_of(listener, c, activate);
4582+
4583+ /* Only "managed" windows can be activated */
4584+ if (!client_is_unmanaged(c))
4585+ wlr_xwayland_surface_activate(c->surface.xwayland, 1);
4586+}
4587+
4588+void
4589+associatex11(struct wl_listener *listener, void *data)
4590+{
4591+ Client *c = wl_container_of(listener, c, associate);
4592+
4593+ LISTEN(&client_surface(c)->events.map, &c->map, mapnotify);
4594+ LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify);
4595+}
4596+
4597+void
4598+configurex11(struct wl_listener *listener, void *data)
4599+{
4600+ Client *c = wl_container_of(listener, c, configure);
4601+ struct wlr_xwayland_surface_configure_event *event = data;
4602+ if (!client_surface(c) || !client_surface(c)->mapped) {
4603+ wlr_xwayland_surface_configure(c->surface.xwayland,
4604+ event->x, event->y, event->width, event->height);
4605+ return;
4606+ }
4607+ if (client_is_unmanaged(c)) {
4608+ wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
4609+ wlr_xwayland_surface_configure(c->surface.xwayland,
4610+ event->x, event->y, event->width, event->height);
4611+ return;
4612+ }
4613+ if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) {
4614+ resize(c, (struct wlr_box){.x = event->x - c->bw,
4615+ .y = event->y - c->bw, .width = event->width + c->bw * 2,
4616+ .height = event->height + c->bw * 2}, 0);
4617+ } else {
4618+ arrange(c->mon);
4619+ }
4620+}
4621+
4622+void
4623+createnotifyx11(struct wl_listener *listener, void *data)
4624+{
4625+ struct wlr_xwayland_surface *xsurface = data;
4626+ Client *c;
4627+
4628+ /* Allocate a Client for this surface */
4629+ c = xsurface->data = ecalloc(1, sizeof(*c));
4630+ c->surface.xwayland = xsurface;
4631+ c->type = X11;
4632+ c->bw = client_is_unmanaged(c) ? 0 : innerborderpx;
4633+
4634+ /* Listen to the various events it can emit */
4635+ LISTEN(&xsurface->events.associate, &c->associate, associatex11);
4636+ LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify);
4637+ LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
4638+ LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
4639+ LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
4640+ LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify);
4641+ LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints);
4642+ LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle);
4643+}
4644+
4645+void
4646+dissociatex11(struct wl_listener *listener, void *data)
4647+{
4648+ Client *c = wl_container_of(listener, c, dissociate);
4649+ wl_list_remove(&c->map.link);
4650+ wl_list_remove(&c->unmap.link);
4651+}
4652+
4653+void
4654+sethints(struct wl_listener *listener, void *data)
4655+{
4656+ Client *c = wl_container_of(listener, c, set_hints);
4657+ struct wlr_surface *surface = client_surface(c);
4658+ if (c == focustop(selmon) || !c->surface.xwayland->hints)
4659+ return;
4660+
4661+ c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
4662+ printstatus();
4663+
4664+ if (c->isurgent && surface && surface->mapped)
4665+ client_set_border_color(c, urgent_inner_border_color, urgent_outer_border_color);
4666+}
4667+
4668+void
4669+xwaylandready(struct wl_listener *listener, void *data)
4670+{
4671+ struct wlr_xcursor *xcursor;
4672+
4673+ /* assign the one and only seat */
4674+ wlr_xwayland_set_seat(xwayland, seat);
4675+
4676+ /* Set the default XWayland cursor to match the rest of leeloo. */
4677+ if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
4678+ wlr_xwayland_set_cursor(xwayland,
4679+ xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
4680+ xcursor->images[0]->width, xcursor->images[0]->height,
4681+ xcursor->images[0]->hotspot_x, xcursor->images[0]->hotspot_y);
4682+}
4683+#endif
4684+
4685+/* infinite_canvas */
4686+
4687+int
4688+getcurrenttag(Monitor *m)
4689+{
4690+ unsigned int i;
4691+ for (i = 0; i < (unsigned int)TAGCOUNT && !(m->tagset[m->seltags] & (1u << i)); i++);
4692+ return i < (unsigned int)TAGCOUNT ? (int)i : 0;
4693+}
4694+
4695+void
4696+canvas_move_window(Client *c, int x, int y)
4697+{
4698+ if (!c->mon || !client_surface(c)->mapped)
4699+ return;
4700+
4701+ c->geom.x = x;
4702+ c->geom.y = y;
4703+
4704+ /* zoom */
4705+ zoom_update_scene_client(c);
4706+ /* zoom end */
4707+}
4708+
4709+void
4710+homecanvas(const Arg *arg)
4711+{
4712+ Client *c;
4713+ int tagidx, cx, cy;
4714+ if (!selmon)
4715+ return;
4716+
4717+ tagidx = getcurrenttag(selmon);
4718+ cx = selmon->canvas[tagidx].cx;
4719+ cy = selmon->canvas[tagidx].cy;
4720+
4721+ wl_list_for_each(c, &clients, link) {
4722+ if (!(c->tags & (1u << tagidx)) || c->mon != selmon)
4723+ continue;
4724+ c->geom.x -= cx;
4725+ c->geom.y -= cy;
4726+ }
4727+
4728+ selmon->canvas[tagidx].cx = 0;
4729+ selmon->canvas[tagidx].cy = 0;
4730+
4731+ /* zoom */
4732+ zoom_apply(selmon);
4733+ /* zoom end */
4734+}
4735+
4736+#ifndef MOVE_CANVAS_STEP
4737+#define MOVE_CANVAS_STEP 120
4738+#endif
4739+
4740+void
4741+movecanvas(const Arg *arg)
4742+{
4743+ Client *c;
4744+ int tagidx, dx, dy;
4745+ if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
4746+ return;
4747+
4748+ tagidx = getcurrenttag(selmon);
4749+ dx = 0;
4750+ dy = 0;
4751+
4752+ switch (arg->i) {
4753+ case 0: dx = -MOVE_CANVAS_STEP; break;
4754+ case 1: dx = MOVE_CANVAS_STEP; break;
4755+ case 2: dy = -MOVE_CANVAS_STEP; break;
4756+ case 3: dy = MOVE_CANVAS_STEP; break;
4757+ }
4758+
4759+ selmon->canvas[tagidx].cx -= dx;
4760+ selmon->canvas[tagidx].cy -= dy;
4761+
4762+ wl_list_for_each(c, &clients, link) {
4763+ if (!VISIBLEON(c, selmon))
4764+ continue;
4765+ c->geom.x -= dx;
4766+ c->geom.y -= dy;
4767+ }
4768+ /* zoom */
4769+ zoom_apply(selmon);
4770+ /* zoom end */
4771+}
4772+
4773+#ifndef WINMOVE_STEP_PX
4774+#define WINMOVE_STEP_PX 5 /* pixels nudged per keypress -- niri-style Ctrl+Alt+Arrow */
4775+#endif
4776+#ifndef WINRESIZE_STEP_PX
4777+#define WINRESIZE_STEP_PX 5 /* pixels resized per keypress -- niri-style Ctrl+Shift+Arrow */
4778+#endif
4779+
4780+/* Nudges the focused window by a fixed step in one direction (0=left,
4781+ * 1=right, 2=up, 3=down -- same convention as movecanvas()). Forces the
4782+ * window floating first, matching how interactive dragging already
4783+ * behaves, so the bind has a visible effect regardless of current layout. */
4784+void
4785+movewinbykeys(const Arg *arg)
4786+{
4787+ Client *c = focustop(selmon);
4788+ struct wlr_box geo;
4789+
4790+ if (!c || !c->mon || c->isfullscreen || c->isfakefullscreen)
4791+ return;
4792+ if (!c->isfloating)
4793+ setfloating(c, 1);
4794+
4795+ geo = c->geom;
4796+ switch (arg->i) {
4797+ case 0: geo.x -= WINMOVE_STEP_PX; break; /* left */
4798+ case 1: geo.x += WINMOVE_STEP_PX; break; /* right */
4799+ case 2: geo.y -= WINMOVE_STEP_PX; break; /* up */
4800+ case 3: geo.y += WINMOVE_STEP_PX; break; /* down */
4801+ default: return;
4802+ }
4803+ resize(c, geo, 1);
4804+}
4805+
4806+/* Resizes the focused window by a fixed step (0=left, 1=right, 2=up,
4807+ * 3=down). Follows the traditional i3/sway-style resize-mode convention:
4808+ * left/right adjust width (left shrinks, right grows), up/down adjust
4809+ * height (up shrinks, down grows) -- anchored at the window's current
4810+ * top-left corner, so only the size changes, not the position. */
4811+void
4812+resizewinbykeys(const Arg *arg)
4813+{
4814+ Client *c = focustop(selmon);
4815+ struct wlr_box geo;
4816+ int minw, minh;
4817+
4818+ if (!c || !c->mon || c->isfullscreen || c->isfakefullscreen)
4819+ return;
4820+ if (!c->isfloating)
4821+ setfloating(c, 1);
4822+
4823+ minw = 1 + 2 * (int)c->bw;
4824+ minh = 1 + 2 * (int)c->bw;
4825+ geo = c->geom;
4826+ switch (arg->i) {
4827+ case 0: geo.width = MAX(minw, geo.width - WINRESIZE_STEP_PX); break; /* left: shrink width */
4828+ case 1: geo.width += WINRESIZE_STEP_PX; break; /* right: grow width */
4829+ case 2: geo.height = MAX(minh, geo.height - WINRESIZE_STEP_PX); break; /* up: shrink height */
4830+ case 3: geo.height += WINRESIZE_STEP_PX; break; /* down: grow height */
4831+ default: return;
4832+ }
4833+ resize(c, geo, 1);
4834+}
4835+
4836+void
4837+movecanvasmouse(const Arg *arg)
4838+{
4839+ if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
4840+ return;
4841+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
4842+ return;
4843+
4844+ canvas_pan_multiplier = arg ? arg->f : 1.0f;
4845+ canvas_pan_accum_x = 0.0f;
4846+ canvas_pan_accum_y = 0.0f;
4847+
4848+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
4849+ cursor_mode = CurMove;
4850+ grabc = NULL;
4851+ canvas_panning = 1;
4852+ canvas_pan_last_x = cursor->x;
4853+ canvas_pan_last_y = cursor->y;
4854+}
4855+
4856+void
4857+save_canvas_positions(Monitor *m)
4858+{
4859+ Client *c;
4860+ int tagidx = getcurrenttag(m);
4861+
4862+ m->canvas[tagidx].saved_cx = m->canvas[tagidx].cx;
4863+ m->canvas[tagidx].saved_cy = m->canvas[tagidx].cy;
4864+
4865+ wl_list_for_each(c, &clients, link) {
4866+ if (!VISIBLEON(c, m))
4867+ continue;
4868+ c->saved_cx = c->geom.x + m->canvas[tagidx].cx;
4869+ c->saved_cy = c->geom.y + m->canvas[tagidx].cy;
4870+ c->saved_cw = c->geom.width;
4871+ c->saved_ch = c->geom.height;
4872+ c->was_on_canvas = 1;
4873+ }
4874+}
4875+
4876+void
4877+restore_canvas_positions(Monitor *m)
4878+{
4879+ Client *c;
4880+ int tagidx = getcurrenttag(m);
4881+
4882+ m->canvas[tagidx].cx = m->canvas[tagidx].saved_cx;
4883+ m->canvas[tagidx].cy = m->canvas[tagidx].saved_cy;
4884+
4885+ wl_list_for_each(c, &clients, link) {
4886+ int tx, ty;
4887+
4888+ if (!VISIBLEON(c, m) || !c->was_on_canvas)
4889+ continue;
4890+ c->isfloating = 1;
4891+
4892+ tx = c->saved_cx - m->canvas[tagidx].cx;
4893+ ty = c->saved_cy - m->canvas[tagidx].cy;
4894+
4895+ c->geom.width = c->saved_cw;
4896+ c->geom.height = c->saved_ch;
4897+
4898+ c->geom.x = tx;
4899+ c->geom.y = ty;
4900+
4901+ /* zoom */
4902+ zoom_update_scene_client(c);
4903+ /* zoom end */
4904+
4905+ c->resize = client_set_size(c,
4906+ c->geom.width - 2 * c->bw,
4907+ c->geom.height - 2 * c->bw);
4908+ }
4909+}
4910+
4911+void
4912+centerwindow(const Arg *arg)
4913+{
4914+ Monitor *m;
4915+ Client *tmp;
4916+ int tagidx, screen_cx, screen_cy, win_cx, win_cy, dx, dy;
4917+ Client *c = (arg && arg->v) ? (Client *)arg->v : focustop(selmon);
4918+
4919+ if (!c || !c->mon || c->mon->lt[c->mon->sellt]->arrange != NULL)
4920+ return;
4921+
4922+ m = c->mon;
4923+ tagidx = getcurrenttag(m);
4924+
4925+ screen_cx = m->w.x + m->w.width / 2;
4926+ screen_cy = m->w.y + m->w.height / 2;
4927+ win_cx = c->geom.x + c->geom.width / 2;
4928+ win_cy = c->geom.y + c->geom.height / 2;
4929+
4930+ dx = screen_cx - win_cx;
4931+ dy = screen_cy - win_cy;
4932+
4933+ if (dx == 0 && dy == 0)
4934+ return;
4935+ wl_list_for_each(tmp, &clients, link) {
4936+ if (!VISIBLEON(tmp, m))
4937+ continue;
4938+ tmp->geom.x += dx;
4939+ tmp->geom.y += dy;
4940+ }
4941+
4942+ m->canvas[tagidx].cx += dx;
4943+ m->canvas[tagidx].cy += dy;
4944+
4945+ /* zoom */
4946+ zoom_apply(m);
4947+ /* zoom end */
4948+}
4949+
4950+void
4951+bringintoview(Client *c)
4952+{
4953+ Monitor *m;
4954+ Client *tmp;
4955+ int tagidx, dx, dy;
4956+ struct wlr_box *w, *g;
4957+
4958+ if (!c || !c->mon || c->mon->lt[c->mon->sellt]->arrange != NULL)
4959+ return;
4960+
4961+ m = c->mon;
4962+ w = &m->w;
4963+ g = &c->geom;
4964+
4965+ /* Only skip teleportation when the target window is FULLY visible.
4966+ * A partially visible window must still trigger natural teleportation,
4967+ * even if a corner/edge of it is visible on the current canvas. This
4968+ * keeps focus and teleportation independent: fully visible nearby windows
4969+ * are simply focused, while any clipped/off-canvas window is brought to
4970+ * its own natural framing. */
4971+ if (g->x >= w->x && g->y >= w->y &&
4972+ g->x + g->width <= w->x + w->width &&
4973+ g->y + g->height <= w->y + w->height)
4974+ return; /* fully visible -- focus only, no teleport */
4975+
4976+ tagidx = getcurrenttag(m);
4977+
4978+ /* Each window remembers the pan (home_cx/home_cy) that was active
4979+ * when it was placed or last manually moved -- its own natural
4980+ * framing, independent of any other window's. Prefer restoring
4981+ * exactly that pan: no viewport clamping, no edges, no padding. */
4982+ dx = c->home_cx - m->canvas[tagidx].cx;
4983+ dy = c->home_cy - m->canvas[tagidx].cy;
4984+
4985+ /* But if home-restore wouldn't actually reveal it (e.g. it was
4986+ * placed off-screen to begin with, so "home" is off-screen too),
4987+ * fall back to the minimal shift that brings its box into view. */
4988+ if (g->x + dx < w->x || g->y + dy < w->y ||
4989+ g->x + dx + g->width > w->x + w->width ||
4990+ g->y + dy + g->height > w->y + w->height) {
4991+ dx = 0;
4992+ dy = 0;
4993+ if (g->x < w->x)
4994+ dx = w->x - g->x;
4995+ else if (g->x + g->width > w->x + w->width)
4996+ dx = (w->x + w->width) - (g->x + g->width);
4997+ if (g->y < w->y)
4998+ dy = w->y - g->y;
4999+ else if (g->y + g->height > w->y + w->height)
5000+ dy = (w->y + w->height) - (g->y + g->height);
5001+ }
5002+
5003+ if (dx == 0 && dy == 0)
5004+ return; /* already framed exactly as it was */
5005+
5006+ wl_list_for_each(tmp, &clients, link) {
5007+ if (!VISIBLEON(tmp, m))
5008+ continue;
5009+ tmp->geom.x += dx;
5010+ tmp->geom.y += dy;
5011+ }
5012+
5013+ m->canvas[tagidx].cx += dx;
5014+ m->canvas[tagidx].cy += dy;
5015+
5016+ /* zoom */
5017+ zoom_apply(m);
5018+ /* zoom end */
5019+}
5020+
5021+/* infinite_canvas_end */
5022+
5023+/* zoom */
5024+
5025+void
5026+popup_zoom_destroy(struct wl_listener *listener, void *data)
5027+{
5028+ PopupZoomData *pzd = wl_container_of(listener, pzd, destroy);
5029+ wl_list_remove(&pzd->commit.link);
5030+ wl_list_remove(&pzd->destroy.link);
5031+ wl_list_remove(&pzd->link);
5032+ free(pzd);
5033+}
5034+
5035+void
5036+popup_zoom_commit(struct wl_listener *listener, void *data)
5037+{
5038+ double s, px, py;
5039+ Client *c;
5040+ Monitor *m;
5041+ PopupZoomData *pzd = wl_container_of(listener, pzd, commit);
5042+
5043+ if (!pzd->client || !pzd->client->mon || !pzd->node)
5044+ return;
5045+ if (!pzd->popup->base || !pzd->popup->base->surface->mapped)
5046+ return;
5047+
5048+ c = pzd->client;
5049+ m = c->mon;
5050+ s = m->zoom_scale;
5051+ wlr_xdg_popup_get_position(pzd->popup, &px, &py);
5052+
5053+ if (fabs(s - 1.0) < 1e-9) {
5054+ wlr_scene_node_set_position(pzd->node, (int)llround(px), (int)llround(py));
5055+ return;
5056+ }
5057+
5058+ /* zoom */
5059+ wlr_scene_node_set_position(pzd->node,
5060+ (int)llround(px * s),
5061+ (int)llround(py * s));
5062+ /* zoom end */
5063+}
5064+
5065+void
5066+zoom_scale_buffers(struct wlr_scene_buffer *buf, int sx, int sy, void *data)
5067+{
5068+ Client *c = data;
5069+ Monitor *m;
5070+ struct wlr_scene_surface *ss;
5071+ struct wlr_surface *surf;
5072+ double s;
5073+ int lw, lh;
5074+
5075+ (void)sx; (void)sy;
5076+
5077+ if (!c || !(m = c->mon) || !buf->buffer)
5078+ return;
5079+
5080+ if (c->isfullscreen) {
5081+ wlr_scene_buffer_set_dest_size(buf, 0, 0);
5082+ return;
5083+ }
5084+
5085+ s = m->zoom_scale;
5086+ if (fabs(s - 1.0) < 1e-9) {
5087+ wlr_scene_buffer_set_dest_size(buf, 0, 0);
5088+ return;
5089+ }
5090+
5091+ ss = wlr_scene_surface_try_from_buffer(buf);
5092+ if (ss && (surf = ss->surface)) {
5093+ if (surf->current.viewport.has_dst) {
5094+ lw = surf->current.viewport.dst_width;
5095+ lh = surf->current.viewport.dst_height;
5096+ } else {
5097+ lw = surf->current.width;
5098+ lh = surf->current.height;
5099+ }
5100+ if (lw <= 0 || lh <= 0) {
5101+ lw = buf->buffer->width;
5102+ lh = buf->buffer->height;
5103+ }
5104+ } else {
5105+ lw = buf->buffer->width;
5106+ lh = buf->buffer->height;
5107+ }
5108+
5109+ {
5110+ int _bw = (int)llround((double)c->bw * s);
5111+ int gw = (int)llround((double)c->geom.width * s);
5112+ int gh = (int)llround((double)c->geom.height * s);
5113+ int inner_w = MAX(1, gw - 2 * _bw);
5114+ int inner_h = MAX(1, gh - 2 * _bw);
5115+
5116+ if (ss && ss->surface == client_surface(c)) {
5117+ wlr_scene_buffer_set_dest_size(buf, inner_w, inner_h);
5118+ return;
5119+ }
5120+
5121+ {
5122+ double cw = (double)MAX(1, c->geom.width - 2 * c->bw);
5123+ double ch = (double)MAX(1, c->geom.height - 2 * c->bw);
5124+ wlr_scene_buffer_set_dest_size(buf,
5125+ MAX(1, (int)llround((double)lw * (double)inner_w / cw)),
5126+ MAX(1, (int)llround((double)lh * (double)inner_h / ch)));
5127+ }
5128+ }
5129+}
5130+
5131+void
5132+zoom_update_scene_client(Client *c)
5133+{
5134+ Monitor *m = c->mon;
5135+ double s;
5136+ int gw, gh, w, h, ib, ob, ib_s, ob_s, bw_pos;
5137+
5138+ if (!m || !client_surface(c)->mapped)
5139+ return;
5140+
5141+ s = m->zoom_scale;
5142+ w = c->geom.width;
5143+ h = c->geom.height;
5144+ ib = c->ibw;
5145+ ob = c->obw;
5146+
5147+ if (c->isfullscreen) {
5148+ wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
5149+ wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
5150+ wlr_scene_rect_set_size(c->inner_border[0], MAX(1, w - 2*ob), MAX(1, ib));
5151+ wlr_scene_node_set_position(&c->inner_border[0]->node, ob, ob);
5152+ wlr_scene_rect_set_size(c->inner_border[1], MAX(1, w - 2*ob), MAX(1, ib));
5153+ wlr_scene_node_set_position(&c->inner_border[1]->node, ob, MAX(0, h - ob - ib));
5154+ wlr_scene_rect_set_size(c->inner_border[2], MAX(1, ib), MAX(1, h - 2*ob));
5155+ wlr_scene_node_set_position(&c->inner_border[2]->node, ob, ob);
5156+ wlr_scene_rect_set_size(c->inner_border[3], MAX(1, ib), MAX(1, h - 2*ob));
5157+ wlr_scene_node_set_position(&c->inner_border[3]->node, MAX(0, w - ob - ib), ob);
5158+ wlr_scene_rect_set_size(c->outer_border[0], MAX(1, w), MAX(1, ob));
5159+ wlr_scene_node_set_position(&c->outer_border[0]->node, 0, 0);
5160+ wlr_scene_rect_set_size(c->outer_border[1], MAX(1, w), MAX(1, ob));
5161+ wlr_scene_node_set_position(&c->outer_border[1]->node, 0, MAX(0, h - ob));
5162+ wlr_scene_rect_set_size(c->outer_border[2], MAX(1, ob), MAX(1, h - 2*ob));
5163+ wlr_scene_node_set_position(&c->outer_border[2]->node, 0, ob);
5164+ wlr_scene_rect_set_size(c->outer_border[3], MAX(1, ob), MAX(1, h - 2*ob));
5165+ wlr_scene_node_set_position(&c->outer_border[3]->node, MAX(0, w - ob), ob);
5166+ return;
5167+ }
5168+
5169+ gw = MAX(1, (int)llround((double)w * s));
5170+ gh = MAX(1, (int)llround((double)h * s));
5171+ bw_pos = (int)llround((double)c->bw * s);
5172+ ib_s = (int)llround((double)ib * s);
5173+ ob_s = (int)llround((double)ob * s);
5174+
5175+ wlr_scene_node_set_position(&c->scene->node,
5176+ (int)llround((double)c->geom.x * s + m->zoom_pan_x),
5177+ (int)llround((double)c->geom.y * s + m->zoom_pan_y));
5178+ wlr_scene_node_set_position(&c->scene_surface->node, bw_pos, bw_pos);
5179+
5180+ wlr_scene_rect_set_size(c->inner_border[0], MAX(0, gw - 2*ob_s), ib_s);
5181+ wlr_scene_node_set_position(&c->inner_border[0]->node, ob_s, ob_s);
5182+ wlr_scene_rect_set_size(c->inner_border[1], MAX(1, gw - 2*ob_s), ib_s);
5183+ wlr_scene_node_set_position(&c->inner_border[1]->node, ob_s, MAX(0, gh - ob_s - ib_s));
5184+ wlr_scene_rect_set_size(c->inner_border[2], ib_s, MAX(0, gh - 2*ob_s));
5185+ wlr_scene_node_set_position(&c->inner_border[2]->node, ob_s, ob_s);
5186+ wlr_scene_rect_set_size(c->inner_border[3], ib_s, MAX(1, gh - 2*ob_s));
5187+ wlr_scene_node_set_position(&c->inner_border[3]->node, MAX(0, gw - ob_s - ib_s), ob_s);
5188+ wlr_scene_rect_set_size(c->outer_border[0], gw, ob_s);
5189+ wlr_scene_node_set_position(&c->outer_border[0]->node, 0, 0);
5190+ wlr_scene_rect_set_size(c->outer_border[1], gw, ob_s);
5191+ wlr_scene_node_set_position(&c->outer_border[1]->node, 0, MAX(0, gh - ob_s));
5192+ wlr_scene_rect_set_size(c->outer_border[2], ob_s, MAX(0, gh - 2*ob_s));
5193+ wlr_scene_node_set_position(&c->outer_border[2]->node, 0, ob_s);
5194+ wlr_scene_rect_set_size(c->outer_border[3], ob_s, MAX(1, gh - 2*ob_s));
5195+ wlr_scene_node_set_position(&c->outer_border[3]->node, MAX(0, gw - ob_s), ob_s);
5196+
5197+ /* touch dest_size only when zoom isnt 1 to avoid glitch in gpu apps */
5198+ if (fabs(s - 1.0) >= 1e-9)
5199+ wlr_scene_node_for_each_buffer(&c->scene->node, zoom_scale_buffers, c);
5200+}
5201+
5202+void
5203+zoom_apply(Monitor *m)
5204+{
5205+ Client *c;
5206+ PopupZoomData *pzd;
5207+
5208+ wl_list_for_each(c, &clients, link) {
5209+ if (c->mon == m && client_surface(c)->mapped)
5210+ zoom_update_scene_client(c);
5211+ }
5212+
5213+ wl_list_for_each(pzd, &popup_zoom_list, link) {
5214+ if (pzd->client && pzd->client->mon == m)
5215+ popup_zoom_commit(&pzd->commit, NULL);
5216+ }
5217+
5218+ /* zoom */
5219+ if (m->zoom_popups_dirty && m->zoom_anim_t >= 1.0) {
5220+ double inv_s = 1.0 / m->zoom_scale;
5221+ double vis_x = -m->zoom_pan_x * inv_s;
5222+ double vis_y = -m->zoom_pan_y * inv_s;
5223+ double vis_w = (double)m->m.width * inv_s;
5224+ double vis_h = (double)m->m.height * inv_s;
5225+ wl_list_for_each(pzd, &popup_zoom_list, link) {
5226+ Client *pc;
5227+ struct wlr_box pbox;
5228+ if (!pzd->client || pzd->client->mon != m)
5229+ continue;
5230+ pc = pzd->client;
5231+ pbox.x = (int)llround(vis_x) - pc->geom.x;
5232+ pbox.y = (int)llround(vis_y) - pc->geom.y;
5233+ pbox.width = MAX(1, (int)llround(vis_w));
5234+ pbox.height = MAX(1, (int)llround(vis_h));
5235+ wlr_xdg_popup_unconstrain_from_box(pzd->popup, &pbox);
5236+ }
5237+ m->zoom_popups_dirty = 0;
5238+ }
5239+ /* zoom end */
5240+}
5241+
5242+void
5243+zoom_frame(Monitor *m)
5244+{
5245+ double t, u, target_s, end_pan_x, end_pan_y;
5246+ double bx1, by1, bx2, by2;
5247+ double cx, bx, ax, cy, by, ay, s;
5248+ int i;
5249+
5250+ if (!m->lt[m->sellt] || m->lt[m->sellt]->arrange != NULL)
5251+ return;
5252+ if (m->zoom_anim_t >= 1.0)
5253+ return;
5254+
5255+ sscanf(zoombezier, "%lf,%lf,%lf,%lf", &bx1, &by1, &bx2, &by2);
5256+
5257+ target_s = m->zoom_target;
5258+ end_pan_x = m->zoom_anchor_fx - m->zoom_anchor_lx * target_s;
5259+ end_pan_y = m->zoom_anchor_fy - m->zoom_anchor_ly * target_s;
5260+
5261+ /* zoom */
5262+ m->zoom_anim_t += 1.0 / (zoomduration *
5263+ ((double)m->wlr_output->current_mode->refresh / 1000.0));
5264+ /* zoom end */
5265+ if (m->zoom_anim_t > 1.0)
5266+ m->zoom_anim_t = 1.0;
5267+
5268+ t = m->zoom_anim_t;
5269+
5270+ cx = 3.0 * bx1;
5271+ bx = 3.0 * (bx2 - bx1) - cx;
5272+ ax = 1.0 - cx - bx;
5273+ cy = 3.0 * by1;
5274+ by = 3.0 * (by2 - by1) - cy;
5275+ ay = 1.0 - cy - by;
5276+
5277+ s = t;
5278+ for (i = 0; i < 8; i++) {
5279+ double xs = ((ax * s + bx) * s + cx) * s;
5280+ double dxs = (3.0 * ax * s + 2.0 * bx) * s + cx;
5281+ if (fabs(dxs) < 1e-9) break;
5282+ s -= (xs - t) / dxs;
5283+ if (s < 0.0) s = 0.0;
5284+ if (s > 1.0) s = 1.0;
5285+ }
5286+ u = ((ay * s + by) * s + cy) * s;
5287+
5288+ if (m->zoom_anim_t >= 1.0) {
5289+ m->zoom_scale = target_s;
5290+ m->zoom_pan_x = end_pan_x;
5291+ m->zoom_pan_y = end_pan_y;
5292+ } else {
5293+ m->zoom_scale = m->zoom_start_scale + (target_s - m->zoom_start_scale) * u;
5294+ m->zoom_pan_x = m->zoom_start_pan_x + (end_pan_x - m->zoom_start_pan_x) * u;
5295+ m->zoom_pan_y = m->zoom_start_pan_y + (end_pan_y - m->zoom_start_pan_y) * u;
5296+ }
5297+
5298+ zoom_apply(m);
5299+
5300+ if (m->zoom_anim_t < 1.0)
5301+ wlr_output_schedule_frame(m->wlr_output);
5302+}
5303+
5304+void
5305+zoom(const Arg *arg)
5306+{
5307+ double old_s, delta, new_s, fx, fy;
5308+ if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
5309+ return;
5310+
5311+ old_s = selmon->zoom_scale;
5312+ delta = arg ? arg->f : 0.0;
5313+ new_s = old_s + delta;
5314+
5315+ if (new_s < ZOOMMIN) new_s = ZOOMMIN;
5316+ if (new_s > ZOOMMAX) new_s = ZOOMMAX;
5317+
5318+ fx = selmon->w.x + selmon->w.width / 2.0;
5319+ fy = selmon->w.y + selmon->w.height / 2.0;
5320+
5321+ selmon->zoom_anchor_fx = fx;
5322+ selmon->zoom_anchor_fy = fy;
5323+ selmon->zoom_anchor_lx = (fx - selmon->zoom_pan_x) / old_s;
5324+ selmon->zoom_anchor_ly = (fy - selmon->zoom_pan_y) / old_s;
5325+ selmon->zoom_target = new_s;
5326+
5327+ if (!animatezoom) {
5328+ selmon->zoom_scale = new_s;
5329+ selmon->zoom_pan_x = fx - selmon->zoom_anchor_lx * new_s;
5330+ selmon->zoom_pan_y = fy - selmon->zoom_anchor_ly * new_s;
5331+ /* zoom */
5332+ selmon->zoom_anim_t = 1.0;
5333+ selmon->zoom_popups_dirty = 1;
5334+ /* zoom end */
5335+ zoom_apply(selmon);
5336+ } else {
5337+ selmon->zoom_start_scale = old_s;
5338+ selmon->zoom_start_pan_x = selmon->zoom_pan_x;
5339+ selmon->zoom_start_pan_y = selmon->zoom_pan_y;
5340+ /* zoom */
5341+ selmon->zoom_anim_t = 0.0;
5342+ selmon->zoom_popups_dirty = 1;
5343+ /* zoom end */
5344+ wlr_output_schedule_frame(selmon->wlr_output);
5345+ }
5346+}
5347+
5348+void
5349+zoomreset(const Arg *arg)
5350+{
5351+ double old_s, fx, fy;
5352+
5353+ if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
5354+ return;
5355+
5356+ old_s = selmon->zoom_scale;
5357+ fx = selmon->w.x + selmon->w.width / 2.0;
5358+ fy = selmon->w.y + selmon->w.height / 2.0;
5359+
5360+ selmon->zoom_anchor_fx = fx;
5361+ selmon->zoom_anchor_fy = fy;
5362+ selmon->zoom_anchor_lx = (fx - selmon->zoom_pan_x) / old_s;
5363+ selmon->zoom_anchor_ly = (fy - selmon->zoom_pan_y) / old_s;
5364+ selmon->zoom_target = 1.0;
5365+
5366+ if (!animatezoom) {
5367+ selmon->zoom_scale = 1.0;
5368+ selmon->zoom_pan_x = fx - selmon->zoom_anchor_lx * 1.0;
5369+ selmon->zoom_pan_y = fy - selmon->zoom_anchor_ly * 1.0;
5370+ /* zoom */
5371+ selmon->zoom_anim_t = 1.0;
5372+ selmon->zoom_popups_dirty = 1;
5373+ /* zoom end */
5374+ zoom_apply(selmon);
5375+ } else {
5376+ selmon->zoom_start_scale = old_s;
5377+ selmon->zoom_start_pan_x = selmon->zoom_pan_x;
5378+ selmon->zoom_start_pan_y = selmon->zoom_pan_y;
5379+ /* zoom */
5380+ selmon->zoom_anim_t = 0.0;
5381+ selmon->zoom_popups_dirty = 1;
5382+ /* zoom end */
5383+ wlr_output_schedule_frame(selmon->wlr_output);
5384+ }
5385+}
5386+
5387+/* zoom end */
5388+
5389+void
5390+spawnregion_update_rect(void)
5391+{
5392+ int x, y, w, h;
5393+ x = (int)MIN(spawnregion_x1, spawnregion_x2);
5394+ y = (int)MIN(spawnregion_y1, spawnregion_y2);
5395+ w = (int)fabs(spawnregion_x2 - spawnregion_x1);
5396+ h = (int)fabs(spawnregion_y2 - spawnregion_y1);
5397+ if (w < 1) w = 1;
5398+ if (h < 1) h = 1;
5399+#if SPAWNREGION_STYLE == 2
5400+ {
5401+ int ob = (int)spawnregion_outerborderpx;
5402+ int ib = (int)spawnregion_innerborderpx;
5403+ int side_h = MAX(0, h - 2 * ib);
5404+ int i;
5405+ if (!spawnregion_border[0]) return;
5406+ for (i = 0; i < 8; i++)
5407+ wlr_scene_node_set_enabled(&spawnregion_border[i]->node, 1);
5408+ wlr_scene_node_set_position(&spawnregion_border[0]->node, x - ob, y - ob);
5409+ wlr_scene_rect_set_size(spawnregion_border[0], w + 2 * ob, ob);
5410+ wlr_scene_node_set_position(&spawnregion_border[1]->node, x - ob, y + h);
5411+ wlr_scene_rect_set_size(spawnregion_border[1], w + 2 * ob, ob);
5412+ wlr_scene_node_set_position(&spawnregion_border[2]->node, x - ob, y);
5413+ wlr_scene_rect_set_size(spawnregion_border[2], ob, h);
5414+ wlr_scene_node_set_position(&spawnregion_border[3]->node, x + w, y);
5415+ wlr_scene_rect_set_size(spawnregion_border[3], ob, h);
5416+ wlr_scene_node_set_position(&spawnregion_border[4]->node, x, y);
5417+ wlr_scene_rect_set_size(spawnregion_border[4], w, ib);
5418+ wlr_scene_node_set_position(&spawnregion_border[5]->node, x, y + h - ib);
5419+ wlr_scene_rect_set_size(spawnregion_border[5], w, ib);
5420+ wlr_scene_node_set_position(&spawnregion_border[6]->node, x, y + ib);
5421+ wlr_scene_rect_set_size(spawnregion_border[6], ib, side_h);
5422+ wlr_scene_node_set_position(&spawnregion_border[7]->node, x + w - ib, y + ib);
5423+ wlr_scene_rect_set_size(spawnregion_border[7], ib, side_h);
5424+ }
5425+#else
5426+ if (!spawnregion_rect)
5427+ return;
5428+ wlr_scene_node_set_position(&spawnregion_rect->node, x, y);
5429+ wlr_scene_rect_set_size(spawnregion_rect, w, h);
5430+#endif
5431+}
5432+
5433+void
5434+spawnregion_finish(void)
5435+{
5436+ int x, y, w, h;
5437+
5438+ if (spawnregion_rect)
5439+ wlr_scene_node_set_enabled(&spawnregion_rect->node, 0);
5440+#if SPAWNREGION_STYLE == 2
5441+ {
5442+ int i;
5443+ for (i = 0; i < 8; i++)
5444+ if (spawnregion_border[i])
5445+ wlr_scene_node_set_enabled(&spawnregion_border[i]->node, 0);
5446+ }
5447+#endif
5448+
5449+ spawnregion_active = 0;
5450+ cursor_mode = CurNormal;
5451+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
5452+
5453+ /* zoom */
5454+ if (selmon && selmon->lt[selmon->sellt]->arrange == NULL) {
5455+ double s = selmon->zoom_scale;
5456+ if (fabs(s) >= 1e-9 && fabs(s - 1.0) >= 1e-9) {
5457+ double lx1 = (spawnregion_x1 - selmon->zoom_pan_x) / s;
5458+ double ly1 = (spawnregion_y1 - selmon->zoom_pan_y) / s;
5459+ double lx2 = (spawnregion_x2 - selmon->zoom_pan_x) / s;
5460+ double ly2 = (spawnregion_y2 - selmon->zoom_pan_y) / s;
5461+ x = (int)MIN(lx1, lx2);
5462+ y = (int)MIN(ly1, ly2);
5463+ w = (int)fabs(lx2 - lx1);
5464+ h = (int)fabs(ly2 - ly1);
5465+ goto coords_done;
5466+ }
5467+ }
5468+ /* zoom end */
5469+ x = (int)MIN(spawnregion_x1, spawnregion_x2);
5470+ y = (int)MIN(spawnregion_y1, spawnregion_y2);
5471+ w = (int)fabs(spawnregion_x2 - spawnregion_x1);
5472+ h = (int)fabs(spawnregion_y2 - spawnregion_y1);
5473+ /* zoom */
5474+coords_done:
5475+ /* zoom end */
5476+
5477+ if (w < 10 || h < 10) {
5478+ if (waiting_geo_client) {
5479+ wlr_scene_node_set_enabled(&waiting_geo_client->scene->node, 1);
5480+ focusclient(waiting_geo_client, 1);
5481+ waiting_geo_client = NULL;
5482+ }
5483+ return;
5484+ }
5485+
5486+ pending_spawn_geo.x = x;
5487+ pending_spawn_geo.y = y;
5488+ pending_spawn_geo.width = w;
5489+ pending_spawn_geo.height = h;
5490+ has_pending_spawn_geo = 1;
5491+
5492+ if (waiting_geo_client) {
5493+ if (selmon)
5494+ waiting_geo_client->tags = selmon->tagset[selmon->seltags];
5495+ waiting_geo_client->isfloating = 1;
5496+ resize(waiting_geo_client, pending_spawn_geo, 0);
5497+ wlr_scene_node_set_enabled(&waiting_geo_client->scene->node, 1);
5498+ focusclient(waiting_geo_client, 1);
5499+
5500+ waiting_geo_client = NULL;
5501+ has_pending_spawn_geo = 0;
5502+ }
5503+}
5504+
5505+void
5506+spawnregion(const Arg *arg)
5507+{
5508+ if (!selmon || selmon->lt[selmon->sellt]->arrange != NULL)
5509+ return;
5510+ if (cursor_mode != CurNormal && cursor_mode != CurPressed)
5511+ return;
5512+ if (!waiting_geo_client)
5513+ return;
5514+
5515+ spawnregion_active = 1;
5516+ has_pending_spawn_geo = 0;
5517+ wlr_cursor_set_xcursor(cursor, cursor_mgr, "crosshair");
5518+}
5519+
5520+/* spawnregion_end */
5521+
5522+/*
5523+ gaps are from this patch:
5524+ https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/genericgaps/genericgaps-0.7.patch
5525+*/
5526+
5527+#ifdef TILING
5528+void
5529+defaultgaps(const Arg *arg)
5530+{
5531+ setgaps(gappoh, gappov, gappih, gappiv);
5532+}
5533+
5534+void
5535+incgaps(const Arg *arg)
5536+{
5537+ setgaps(
5538+ selmon->gappoh + arg->i,
5539+ selmon->gappov + arg->i,
5540+ selmon->gappih + arg->i,
5541+ selmon->gappiv + arg->i
5542+ );
5543+}
5544+
5545+void
5546+incigaps(const Arg *arg)
5547+{
5548+ setgaps(
5549+ selmon->gappoh,
5550+ selmon->gappov,
5551+ selmon->gappih + arg->i,
5552+ selmon->gappiv + arg->i
5553+ );
5554+}
5555+
5556+void
5557+incihgaps(const Arg *arg)
5558+{
5559+ setgaps(
5560+ selmon->gappoh,
5561+ selmon->gappov,
5562+ selmon->gappih + arg->i,
5563+ selmon->gappiv
5564+ );
5565+}
5566+
5567+void
5568+incivgaps(const Arg *arg)
5569+{
5570+ setgaps(
5571+ selmon->gappoh,
5572+ selmon->gappov,
5573+ selmon->gappih,
5574+ selmon->gappiv + arg->i
5575+ );
5576+}
5577+
5578+void
5579+incogaps(const Arg *arg)
5580+{
5581+ setgaps(
5582+ selmon->gappoh + arg->i,
5583+ selmon->gappov + arg->i,
5584+ selmon->gappih,
5585+ selmon->gappiv
5586+ );
5587+}
5588+
5589+void
5590+incohgaps(const Arg *arg)
5591+{
5592+ setgaps(
5593+ selmon->gappoh + arg->i,
5594+ selmon->gappov,
5595+ selmon->gappih,
5596+ selmon->gappiv
5597+ );
5598+}
5599+
5600+void
5601+incovgaps(const Arg *arg)
5602+{
5603+ setgaps(
5604+ selmon->gappoh,
5605+ selmon->gappov + arg->i,
5606+ selmon->gappih,
5607+ selmon->gappiv
5608+ );
5609+}
5610+
5611+void
5612+preparegaps(Monitor *m)
5613+{
5614+ Client *c;
5615+ int n;
5616+
5617+ if (!m->enablegaps)
5618+ return;
5619+
5620+ n = 0;
5621+ wl_list_for_each(c, &clients, link) {
5622+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
5623+ continue;
5624+ n++;
5625+ }
5626+
5627+ resizelock = 1;
5628+
5629+ if ((smartgaps && n == 1) || (!monoclegaps && m->lt[m->sellt]->arrange == monocle))
5630+ return;
5631+
5632+ m->w.width -= m->gappih + 2 * m->gappoh;
5633+ m->w.height -= m->gappiv + 2 * m->gappov;
5634+}
5635+
5636+void
5637+setgaps(int oh, int ov, int ih, int iv)
5638+{
5639+ selmon->gappoh = MAX(oh, 0);
5640+ selmon->gappov = MAX(ov, 0);
5641+ selmon->gappih = MAX(ih, 0);
5642+ selmon->gappiv = MAX(iv, 0);
5643+ arrange(selmon);
5644+}
5645+
5646+void
5647+togglegaps(const Arg *arg)
5648+{
5649+ if (!selmon)
5650+ return;
5651+ selmon->enablegaps = !selmon->enablegaps;
5652+ arrange(selmon);
5653+}
5654+
5655+void
5656+arrangegaps(Monitor *m)
5657+{
5658+ Client *c;
5659+ int n, gaps;
5660+
5661+ if (!m->enablegaps)
5662+ return;
5663+
5664+ resizelock = 0;
5665+
5666+ n = 0;
5667+ wl_list_for_each(c, &clients, link) {
5668+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
5669+ continue;
5670+ n++;
5671+ }
5672+
5673+ gaps = !(smartgaps && n == 1) &&
5674+ (monoclegaps || m->lt[m->sellt]->arrange != monocle);
5675+ if (gaps) {
5676+ m->w.width += m->gappih + 2 * m->gappoh;
5677+ m->w.height += m->gappiv + 2 * m->gappov;
5678+ }
5679+ wl_list_for_each(c, &clients, link) {
5680+ if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen)
5681+ continue;
5682+ if (gaps) {
5683+ c->geom.x += c->mon->gappih + c->mon->gappoh;
5684+ c->geom.y += c->mon->gappiv + c->mon->gappov;
5685+ c->geom.width -= c->mon->gappih;
5686+ c->geom.height -= c->mon->gappiv;
5687+ }
5688+ resize(c, c->geom, 0);
5689+ }
5690+}
5691+#endif // why would you need gaps without tiling?
5692+
5693+/* tablet-input is from this patch:
5694+ https://codeberg.org/dwl/dwl-patches/src/branch/main/patches/tablet-input/tablet-input-0.8.patch
5695+*/
5696+
5697+void
5698+createtablet(struct wlr_input_device *device)
5699+{
5700+ if (!tablet) {
5701+ struct libinput_device *device_handle = NULL;
5702+ if (!wlr_input_device_is_libinput(device) ||
5703+ !(device_handle = wlr_libinput_get_device_handle(device)))
5704+ return;
5705+ tablet = wlr_tablet_create(tablet_mgr, seat, device);
5706+ wl_signal_add(&tablet->wlr_device->events.destroy, &tablet_device_destroy);
5707+ if (libinput_device_config_send_events_get_modes(device_handle)) {
5708+ libinput_device_config_send_events_set_mode(device_handle, send_events_mode);
5709+ wlr_cursor_attach_input_device(cursor, device);
5710+ }
5711+ } else if (device == tablet->wlr_device) {
5712+ wlr_log(WLR_ERROR, "createtablet: duplicate device");
5713+ } else {
5714+ wlr_log(WLR_ERROR, "createtablet: already have one tablet");
5715+ }
5716+}
5717+
5718+void
5719+destroytablet(struct wl_listener *listener, void *data)
5720+{
5721+ wl_list_remove(&tablet_device_destroy.link);
5722+ wlr_cursor_detach_input_device(cursor, tablet->wlr_device);
5723+ tablet = NULL;
5724+}
5725+
5726+void
5727+destroytabletpad(struct wl_listener *listener, void *data)
5728+{
5729+ wl_list_remove(&tablet_pad_button.link);
5730+ wl_list_remove(&tablet_pad_destroy.link);
5731+ tablet_pad = NULL;
5732+}
5733+
5734+void
5735+destroytabletsurfacenotify(struct wl_listener *listener, void *data)
5736+{
5737+ if (tablet_curr_surface)
5738+ wl_list_remove(&destroy_tablet_surface_listener.link);
5739+ tablet_curr_surface = NULL;
5740+}
5741+
5742+void
5743+destroytablettool(struct wl_listener *listener, void *data)
5744+{
5745+ destroytabletsurfacenotify(NULL, NULL);
5746+ tablet_tool = NULL;
5747+}
5748+
5749+void
5750+tabletapplymap(double x, double y, struct wlr_input_device *dev)
5751+{
5752+ Client *p;
5753+ struct wlr_box geom = {0};
5754+ if (tabletmaptosurface && tablet_curr_surface) {
5755+ toplevel_from_wlr_surface(tablet_curr_surface, &p, NULL);
5756+ if (p) {
5757+ for (; client_get_parent(p); p = client_get_parent(p));
5758+ geom.x = p->geom.x + p->bw;
5759+ geom.y = p->geom.y + p->bw;
5760+ geom.width = p->geom.width - 2 * p->bw;
5761+ geom.height = p->geom.height - 2 * p->bw;
5762+ wlr_cursor_map_input_to_region(cursor, dev, &geom);
5763+ }
5764+ }
5765+ wlr_cursor_map_input_to_output(cursor, dev, selmon->wlr_output);
5766+}
5767+
5768+void
5769+tabletpadbutton(struct wl_listener *listener, void *data)
5770+{
5771+ struct wlr_tablet_pad_button_event *event = data;
5772+ const TabletPadButton *pb;
5773+
5774+ for (pb = tabletpadbuttons; pb < END(tabletpadbuttons); pb++) {
5775+ if (event->button == pb->button
5776+ && event->state == pb->state
5777+ && pb->func) {
5778+ pb->func(&pb->arg);
5779+ }
5780+ }
5781+
5782+ if (tablet_pad)
5783+ wlr_tablet_v2_tablet_pad_notify_button(tablet_pad,
5784+ event->button, event->time_msec,
5785+ (enum zwp_tablet_pad_v2_button_state)event->state);
5786+}
5787+
5788+void
5789+tablettoolmotion(struct wlr_tablet_v2_tablet_tool *tool, bool change_x, bool change_y,
5790+ double x, double y, double dx, double dy)
5791+{
5792+ struct wlr_surface *surface = NULL;
5793+ double sx, sy;
5794+
5795+ if (!change_x && !change_y)
5796+ return;
5797+
5798+ tabletapplymap(x, y, tablet->wlr_device);
5799+
5800+ x = (MAX(tablet_area[0], MIN(tablet_area[0] + tablet_area[2], x)) - tablet_area[0]) / tablet_area[2];
5801+ y = (MAX(tablet_area[1], MIN(tablet_area[1] + tablet_area[3], y)) - tablet_area[1]) / tablet_area[3];
5802+
5803+
5804+ // TODO: apply constraints
5805+ switch (tablet_tool->wlr_tool->type) {
5806+ case WLR_TABLET_TOOL_TYPE_LENS:
5807+ case WLR_TABLET_TOOL_TYPE_MOUSE:
5808+ wlr_cursor_move(cursor, tablet->wlr_device, dx, dy);
5809+ break;
5810+ default:
5811+ wlr_cursor_warp_absolute(cursor, tablet->wlr_device, change_x ? x : NAN, change_y ? y : NAN);
5812+ break;
5813+ }
5814+
5815+ motionnotify(0, NULL, 0, 0, 0, 0);
5816+
5817+ xytonode(cursor->x, cursor->y, &surface, NULL, NULL, &sx, &sy);
5818+ if (surface && !wlr_surface_accepts_tablet_v2(surface, tablet))
5819+ surface = NULL;
5820+
5821+ if (surface != tablet_curr_surface) {
5822+ if (tablet_curr_surface) {
5823+ // TODO: wait until all buttons released before leaving
5824+ if (tablet_tool)
5825+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
5826+ if (tablet_pad)
5827+ wlr_tablet_v2_tablet_pad_notify_leave(tablet_pad, tablet_curr_surface);
5828+ wl_list_remove(&destroy_tablet_surface_listener.link);
5829+ }
5830+ if (surface) {
5831+ if (tablet_pad)
5832+ wlr_tablet_v2_tablet_pad_notify_enter(tablet_pad, tablet, surface);
5833+ if (tablet_tool)
5834+ wlr_tablet_v2_tablet_tool_notify_proximity_in(tablet_tool, tablet, surface);
5835+ wl_signal_add(&surface->events.destroy, &destroy_tablet_surface_listener);
5836+ }
5837+ tablet_curr_surface = surface;
5838+ }
5839+
5840+ if (surface)
5841+ wlr_tablet_v2_tablet_tool_notify_motion(tablet_tool, sx, sy);
5842+}
5843+
5844+void
5845+tablettoolproximity(struct wl_listener *listener, void *data)
5846+{
5847+ struct wlr_tablet_tool_proximity_event *event = data;
5848+ struct wlr_tablet_tool *tool = event->tool;
5849+
5850+ if (!tablet_tool) {
5851+ tablet_tool = wlr_tablet_tool_create(tablet_mgr, seat, tool);
5852+ wl_signal_add(&tablet_tool->wlr_tool->events.destroy, &tablet_tool_destroy);
5853+ wl_signal_add(&tablet_tool->events.set_cursor, &request_cursor);
5854+ }
5855+
5856+ switch (event->state) {
5857+ case WLR_TABLET_TOOL_PROXIMITY_OUT:
5858+ wlr_tablet_v2_tablet_tool_notify_proximity_out(tablet_tool);
5859+ destroytabletsurfacenotify(NULL, NULL);
5860+ break;
5861+ case WLR_TABLET_TOOL_PROXIMITY_IN:
5862+ tablettoolmotion(tablet_tool, true, true, event->x, event->y, 0, 0);
5863+ break;
5864+ }
5865+}
5866+
5867+double tilt_x = 0;
5868+double tilt_y = 0;
5869+
5870+void
5871+tablettoolaxis(struct wl_listener *listener, void *data)
5872+{
5873+ struct wlr_tablet_tool_axis_event *event = data;
5874+
5875+ tablettoolmotion(tablet_tool,
5876+ event->updated_axes & WLR_TABLET_TOOL_AXIS_X,
5877+ event->updated_axes & WLR_TABLET_TOOL_AXIS_Y,
5878+ event->x, event->y, event->dx, event->dy);
5879+
5880+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_PRESSURE)
5881+ wlr_tablet_v2_tablet_tool_notify_pressure(tablet_tool, event->pressure);
5882+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_DISTANCE)
5883+ wlr_tablet_v2_tablet_tool_notify_distance(tablet_tool, event->distance);
5884+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_X)
5885+ tilt_x = event->tilt_x;
5886+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_TILT_Y)
5887+ tilt_y = event->tilt_y;
5888+ if (event->updated_axes & (WLR_TABLET_TOOL_AXIS_TILT_X | WLR_TABLET_TOOL_AXIS_TILT_Y))
5889+ wlr_tablet_v2_tablet_tool_notify_tilt(tablet_tool, tilt_x, tilt_y);
5890+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_ROTATION)
5891+ wlr_tablet_v2_tablet_tool_notify_rotation(tablet_tool, event->rotation);
5892+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_SLIDER)
5893+ wlr_tablet_v2_tablet_tool_notify_slider(tablet_tool, event->slider);
5894+ if (event->updated_axes & WLR_TABLET_TOOL_AXIS_WHEEL)
5895+ wlr_tablet_v2_tablet_tool_notify_wheel(tablet_tool, event->wheel_delta, 0);
5896+}
5897+
5898+void
5899+tablettoolbutton(struct wl_listener *listener, void *data)
5900+{
5901+ struct wlr_tablet_tool_button_event *event = data;
5902+ const TabletButton *tb;
5903+
5904+ for (tb = tabletbuttons; tb < END(tabletbuttons); tb++) {
5905+ if (event->button == tb->button
5906+ && event->state == tb->state
5907+ && tb->func) {
5908+ tb->func(&tb->arg);
5909+ }
5910+ }
5911+
5912+ wlr_tablet_v2_tablet_tool_notify_button(tablet_tool, event->button,
5913+ (enum zwp_tablet_pad_v2_button_state)event->state);
5914+}
5915+
5916+void
5917+tablettooltip(struct wl_listener *listener, void *data)
5918+{
5919+ struct wlr_tablet_tool_tip_event *event = data;
5920+
5921+ if (!tablet_curr_surface) {
5922+ struct wlr_pointer_button_event fakeptrbtnevent = {
5923+ .button = BTN_LEFT,
5924+ .state = event->state == WLR_TABLET_TOOL_TIP_UP ?
5925+ WL_POINTER_BUTTON_STATE_RELEASED : WL_POINTER_BUTTON_STATE_PRESSED,
5926+ .time_msec = event->time_msec,
5927+ };
5928+ buttonpress(NULL, (void *)&fakeptrbtnevent);
5929+ }
5930+
5931+ if (event->state == WLR_TABLET_TOOL_TIP_UP) {
5932+ wlr_tablet_v2_tablet_tool_notify_up(tablet_tool);
5933+ return;
5934+ }
5935+
5936+ wlr_tablet_v2_tablet_tool_notify_down(tablet_tool);
5937+ wlr_tablet_tool_v2_start_implicit_grab(tablet_tool);
5938+}
5939+
5940+/* tablet-input end */
5941+int
5942+main(int argc, char *argv[])
5943+{
5944+ char *startup_cmd = NULL;
5945+ int c;
5946+
5947+ while ((c = getopt(argc, argv, "s:hdv")) != -1) {
5948+ if (c == 's')
5949+ startup_cmd = optarg;
5950+ else if (c == 'd')
5951+ log_level = WLR_DEBUG;
5952+ else if (c == 'v')
5953+ die("leeloo " VERSION);
5954+ else
5955+ goto usage;
5956+ }
5957+ if (optind < argc)
5958+ goto usage;
5959+
5960+ /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */
5961+ if (!getenv("XDG_RUNTIME_DIR"))
5962+ die("XDG_RUNTIME_DIR must be set");
5963+ setup();
5964+ run(startup_cmd);
5965+ cleanup();
5966+ return EXIT_SUCCESS;
5967+
5968+usage:
5969+ die("Usage: %s [-v] [-d] [-s startup command]", argv[0]);
5970+}
+51,
-0
1@@ -0,0 +1,51 @@
2+/* See LICENSE.dwm file for copyright and license details. */
3+#include <stdarg.h>
4+#include <stdio.h>
5+#include <stdlib.h>
6+#include <string.h>
7+#include <fcntl.h>
8+
9+#include "util.h"
10+
11+void
12+die(const char *fmt, ...) {
13+ va_list ap;
14+
15+ va_start(ap, fmt);
16+ vfprintf(stderr, fmt, ap);
17+ va_end(ap);
18+
19+ if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
20+ fputc(' ', stderr);
21+ perror(NULL);
22+ } else {
23+ fputc('\n', stderr);
24+ }
25+
26+ exit(1);
27+}
28+
29+void *
30+ecalloc(size_t nmemb, size_t size)
31+{
32+ void *p;
33+
34+ if (!(p = calloc(nmemb, size)))
35+ die("calloc:");
36+ return p;
37+}
38+
39+int
40+fd_set_nonblock(int fd) {
41+ int flags = fcntl(fd, F_GETFL);
42+ if (flags < 0) {
43+ perror("fcntl(F_GETFL):");
44+ return -1;
45+ }
46+ if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
47+ perror("fcntl(F_SETFL):");
48+ return -1;
49+ }
50+
51+ return 0;
52+}
+5,
-0
1@@ -0,0 +1,5 @@
2+/* See LICENSE.dwm file for copyright and license details. */
3+
4+void die(const char *fmt, ...);
5+void *ecalloc(size_t nmemb, size_t size);
6+int fd_set_nonblock(int fd);