Initial commit

f1699f66e6807da95ad347fd4e4eddbc692bf59d
Simple window manager based on dwl and spectrwm.
Alexis Sellier committed ago
.clang-format added +19 -0
1 +
IndentWidth: 4
2 +
ColumnLimit: 100
3 +
UseTab: Never
4 +
AllowShortBlocksOnASingleLine: Always  # or use 'Empty' for only empty blocks
5 +
AllowShortFunctionsOnASingleLine: Empty
6 +
AlignConsecutiveMacros: AcrossComments
7 +
AlignAfterOpenBracket: BlockIndent
8 +
AlignConsecutiveBitFields: AcrossEmptyLinesAndComments
9 +
AlignConsecutiveDeclarations:
10 +
  Enabled: true
11 +
AlignConsecutiveAssignments:
12 +
  Enabled: true
13 +
  AlignCompound: true
14 +
  PadOperators: true
15 +
BinPackParameters: false
16 +
BinPackArguments: false
17 +
BreakAfterReturnType: Automatic
18 +
PenaltyReturnTypeOnItsOwnLine: 999
19 +
Cpp11BracedListStyle: false
.clangd added +24 -0
1 +
CompileFlags:
2 +
  Compiler: clang
3 +
  Add:
4 +
    - -std=c23
5 +
    - -I.
6 +
    - -I/usr/include/wlroots-0.20
7 +
    - -I/usr/include/pixman-1
8 +
    - -I/usr/include/libdrm
9 +
    - -DWLR_USE_UNSTABLE
10 +
    - -D_POSIX_C_SOURCE=200809L
11 +
    - -DVERSION="1.0.0"
12 +
13 +
---
14 +
If:
15 +
  PathMatch: .*\.h
16 +
CompileFlags:
17 +
  Add:
18 +
    - -xc
19 +
20 +
---
21 +
If:
22 +
  PathMatch: config(\.def)?\.h
23 +
Diagnostics:
24 +
  Suppress: "*"
.gitignore added +8 -0
1 +
swm
2 +
swm.1
3 +
config.h
4 +
*.zst
5 +
*.o
6 +
*-protocol.c
7 +
*-protocol.h
8 +
*.coverage
LICENSE added +638 -0
1 +
Copyright © 2026 Alexis Sellier <self@cloudhead.io>
2 +
3 +
See also LICENSE.dwl, LICENSE.dwm, LICENSE.sway, LICENSE.tinywl and
4 +
LICENSE.spectrwm.
5 +
6 +
This program is free software: you can redistribute it and/or modify
7 +
it under the terms of the GNU General Public License as published by
8 +
the Free Software Foundation, either version 3 of the License, or
9 +
(at your option) any later version.
10 +
11 +
This program is distributed in the hope that it will be useful,
12 +
but WITHOUT ANY WARRANTY; without even the implied warranty of
13 +
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 +
GNU General Public License for more details.
15 +
16 +
----
17 +
18 +
                    GNU GENERAL PUBLIC LICENSE
19 +
                       Version 3, 29 June 2007
20 +
21 +
 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
22 +
 Everyone is permitted to copy and distribute verbatim copies
23 +
 of this license document, but changing it is not allowed.
24 +
25 +
                            Preamble
26 +
27 +
  The GNU General Public License is a free, copyleft license for
28 +
software and other kinds of works.
29 +
30 +
  The licenses for most software and other practical works are designed
31 +
to take away your freedom to share and change the works.  By contrast,
32 +
the GNU General Public License is intended to guarantee your freedom to
33 +
share and change all versions of a program--to make sure it remains free
34 +
software for all its users.  We, the Free Software Foundation, use the
35 +
GNU General Public License for most of our software; it applies also to
36 +
any other work released this way by its authors.  You can apply it to
37 +
your programs, too.
38 +
39 +
  When we speak of free software, we are referring to freedom, not
40 +
price.  Our General Public Licenses are designed to make sure that you
41 +
have the freedom to distribute copies of free software (and charge for
42 +
them if you wish), that you receive source code or can get it if you
43 +
want it, that you can change the software or use pieces of it in new
44 +
free programs, and that you know you can do these things.
45 +
46 +
  To protect your rights, we need to prevent others from denying you
47 +
these rights or asking you to surrender the rights.  Therefore, you have
48 +
certain responsibilities if you distribute copies of the software, or if
49 +
you modify it: responsibilities to respect the freedom of others.
50 +
51 +
  For example, if you distribute copies of such a program, whether
52 +
gratis or for a fee, you must pass on to the recipients the same
53 +
freedoms that you received.  You must make sure that they, too, receive
54 +
or can get the source code.  And you must show them these terms so they
55 +
know their rights.
56 +
57 +
  Developers that use the GNU GPL protect your rights with two steps:
58 +
(1) assert copyright on the software, and (2) offer you this License
59 +
giving you legal permission to copy, distribute and/or modify it.
60 +
61 +
  For the developers' and authors' protection, the GPL clearly explains
62 +
that there is no warranty for this free software.  For both users' and
63 +
authors' sake, the GPL requires that modified versions be marked as
64 +
changed, so that their problems will not be attributed erroneously to
65 +
authors of previous versions.
66 +
67 +
  Some devices are designed to deny users access to install or run
68 +
modified versions of the software inside them, although the manufacturer
69 +
can do so.  This is fundamentally incompatible with the aim of
70 +
protecting users' freedom to change the software.  The systematic
71 +
pattern of such abuse occurs in the area of products for individuals to
72 +
use, which is precisely where it is most unacceptable.  Therefore, we
73 +
have designed this version of the GPL to prohibit the practice for those
74 +
products.  If such problems arise substantially in other domains, we
75 +
stand ready to extend this provision to those domains in future versions
76 +
of the GPL, as needed to protect the freedom of users.
77 +
78 +
  Finally, every program is threatened constantly by software patents.
79 +
States should not allow patents to restrict development and use of
80 +
software on general-purpose computers, but in those that do, we wish to
81 +
avoid the special danger that patents applied to a free program could
82 +
make it effectively proprietary.  To prevent this, the GPL assures that
83 +
patents cannot be used to render the program non-free.
84 +
85 +
  The precise terms and conditions for copying, distribution and
86 +
modification follow.
87 +
88 +
                       TERMS AND CONDITIONS
89 +
90 +
  0. Definitions.
91 +
92 +
  "This License" refers to version 3 of the GNU General Public License.
93 +
94 +
  "Copyright" also means copyright-like laws that apply to other kinds of
95 +
works, such as semiconductor masks.
96 +
97 +
  "The Program" refers to any copyrightable work licensed under this
98 +
License.  Each licensee is addressed as "you".  "Licensees" and
99 +
"recipients" may be individuals or organizations.
100 +
101 +
  To "modify" a work means to copy from or adapt all or part of the work
102 +
in a fashion requiring copyright permission, other than the making of an
103 +
exact copy.  The resulting work is called a "modified version" of the
104 +
earlier work or a work "based on" the earlier work.
105 +
106 +
  A "covered work" means either the unmodified Program or a work based
107 +
on the Program.
108 +
109 +
  To "propagate" a work means to do anything with it that, without
110 +
permission, would make you directly or secondarily liable for
111 +
infringement under applicable copyright law, except executing it on a
112 +
computer or modifying a private copy.  Propagation includes copying,
113 +
distribution (with or without modification), making available to the
114 +
public, and in some countries other activities as well.
115 +
116 +
  To "convey" a work means any kind of propagation that enables other
117 +
parties to make or receive copies.  Mere interaction with a user through
118 +
a computer network, with no transfer of a copy, is not conveying.
119 +
120 +
  An interactive user interface displays "Appropriate Legal Notices"
121 +
to the extent that it includes a convenient and prominently visible
122 +
feature that (1) displays an appropriate copyright notice, and (2)
123 +
tells the user that there is no warranty for the work (except to the
124 +
extent that warranties are provided), that licensees may convey the
125 +
work under this License, and how to view a copy of this License.  If
126 +
the interface presents a list of user commands or options, such as a
127 +
menu, a prominent item in the list meets this criterion.
128 +
129 +
  1. Source Code.
130 +
131 +
  The "source code" for a work means the preferred form of the work
132 +
for making modifications to it.  "Object code" means any non-source
133 +
form of a work.
134 +
135 +
  A "Standard Interface" means an interface that either is an official
136 +
standard defined by a recognized standards body, or, in the case of
137 +
interfaces specified for a particular programming language, one that
138 +
is widely used among developers working in that language.
139 +
140 +
  The "System Libraries" of an executable work include anything, other
141 +
than the work as a whole, that (a) is included in the normal form of
142 +
packaging a Major Component, but which is not part of that Major
143 +
Component, and (b) serves only to enable use of the work with that
144 +
Major Component, or to implement a Standard Interface for which an
145 +
implementation is available to the public in source code form.  A
146 +
"Major Component", in this context, means a major essential component
147 +
(kernel, window system, and so on) of the specific operating system
148 +
(if any) on which the executable work runs, or a compiler used to
149 +
produce the work, or an object code interpreter used to run it.
150 +
151 +
  The "Corresponding Source" for a work in object code form means all
152 +
the source code needed to generate, install, and (for an executable
153 +
work) run the object code and to modify the work, including scripts to
154 +
control those activities.  However, it does not include the work's
155 +
System Libraries, or general-purpose tools or generally available free
156 +
programs which are used unmodified in performing those activities but
157 +
which are not part of the work.  For example, Corresponding Source
158 +
includes interface definition files associated with source files for
159 +
the work, and the source code for shared libraries and dynamically
160 +
linked subprograms that the work is specifically designed to require,
161 +
such as by intimate data communication or control flow between those
162 +
subprograms and other parts of the work.
163 +
164 +
  The Corresponding Source need not include anything that users
165 +
can regenerate automatically from other parts of the Corresponding
166 +
Source.
167 +
168 +
  The Corresponding Source for a work in source code form is that
169 +
same work.
170 +
171 +
  2. Basic Permissions.
172 +
173 +
  All rights granted under this License are granted for the term of
174 +
copyright on the Program, and are irrevocable provided the stated
175 +
conditions are met.  This License explicitly affirms your unlimited
176 +
permission to run the unmodified Program.  The output from running a
177 +
covered work is covered by this License only if the output, given its
178 +
content, constitutes a covered work.  This License acknowledges your
179 +
rights of fair use or other equivalent, as provided by copyright law.
180 +
181 +
  You may make, run and propagate covered works that you do not
182 +
convey, without conditions so long as your license otherwise remains
183 +
in force.  You may convey covered works to others for the sole purpose
184 +
of having them make modifications exclusively for you, or provide you
185 +
with facilities for running those works, provided that you comply with
186 +
the terms of this License in conveying all material for which you do
187 +
not control copyright.  Those thus making or running the covered works
188 +
for you must do so exclusively on your behalf, under your direction
189 +
and control, on terms that prohibit them from making any copies of
190 +
your copyrighted material outside their relationship with you.
191 +
192 +
  Conveying under any other circumstances is permitted solely under
193 +
the conditions stated below.  Sublicensing is not allowed; section 10
194 +
makes it unnecessary.
195 +
196 +
  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
197 +
198 +
  No covered work shall be deemed part of an effective technological
199 +
measure under any applicable law fulfilling obligations under article
200 +
11 of the WIPO copyright treaty adopted on 20 December 1996, or
201 +
similar laws prohibiting or restricting circumvention of such
202 +
measures.
203 +
204 +
  When you convey a covered work, you waive any legal power to forbid
205 +
circumvention of technological measures to the extent such circumvention
206 +
is effected by exercising rights under this License with respect to
207 +
the covered work, and you disclaim any intention to limit operation or
208 +
modification of the work as a means of enforcing, against the work's
209 +
users, your or third parties' legal rights to forbid circumvention of
210 +
technological measures.
211 +
212 +
  4. Conveying Verbatim Copies.
213 +
214 +
  You may convey verbatim copies of the Program's source code as you
215 +
receive it, in any medium, provided that you conspicuously and
216 +
appropriately publish on each copy an appropriate copyright notice;
217 +
keep intact all notices stating that this License and any
218 +
non-permissive terms added in accord with section 7 apply to the code;
219 +
keep intact all notices of the absence of any warranty; and give all
220 +
recipients a copy of this License along with the Program.
221 +
222 +
  You may charge any price or no price for each copy that you convey,
223 +
and you may offer support or warranty protection for a fee.
224 +
225 +
  5. Conveying Modified Source Versions.
226 +
227 +
  You may convey a work based on the Program, or the modifications to
228 +
produce it from the Program, in the form of source code under the
229 +
terms of section 4, provided that you also meet all of these conditions:
230 +
231 +
    a) The work must carry prominent notices stating that you modified
232 +
    it, and giving a relevant date.
233 +
234 +
    b) The work must carry prominent notices stating that it is
235 +
    released under this License and any conditions added under section
236 +
    7.  This requirement modifies the requirement in section 4 to
237 +
    "keep intact all notices".
238 +
239 +
    c) You must license the entire work, as a whole, under this
240 +
    License to anyone who comes into possession of a copy.  This
241 +
    License will therefore apply, along with any applicable section 7
242 +
    additional terms, to the whole of the work, and all its parts,
243 +
    regardless of how they are packaged.  This License gives no
244 +
    permission to license the work in any other way, but it does not
245 +
    invalidate such permission if you have separately received it.
246 +
247 +
    d) If the work has interactive user interfaces, each must display
248 +
    Appropriate Legal Notices; however, if the Program has interactive
249 +
    interfaces that do not display Appropriate Legal Notices, your
250 +
    work need not make them do so.
251 +
252 +
  A compilation of a covered work with other separate and independent
253 +
works, which are not by their nature extensions of the covered work,
254 +
and which are not combined with it such as to form a larger program,
255 +
in or on a volume of a storage or distribution medium, is called an
256 +
"aggregate" if the compilation and its resulting copyright are not
257 +
used to limit the access or legal rights of the compilation's users
258 +
beyond what the individual works permit.  Inclusion of a covered work
259 +
in an aggregate does not cause this License to apply to the other
260 +
parts of the aggregate.
261 +
262 +
  6. Conveying Non-Source Forms.
263 +
264 +
  You may convey a covered work in object code form under the terms
265 +
of sections 4 and 5, provided that you also convey the
266 +
machine-readable Corresponding Source under the terms of this License,
267 +
in one of these ways:
268 +
269 +
    a) Convey the object code in, or embodied in, a physical product
270 +
    (including a physical distribution medium), accompanied by the
271 +
    Corresponding Source fixed on a durable physical medium
272 +
    customarily used for software interchange.
273 +
274 +
    b) Convey the object code in, or embodied in, a physical product
275 +
    (including a physical distribution medium), accompanied by a
276 +
    written offer, valid for at least three years and valid for as
277 +
    long as you offer spare parts or customer support for that product
278 +
    model, to give anyone who possesses the object code either (1) a
279 +
    copy of the Corresponding Source for all the software in the
280 +
    product that is covered by this License, on a durable physical
281 +
    medium customarily used for software interchange, for a price no
282 +
    more than your reasonable cost of physically performing this
283 +
    conveying of source, or (2) access to copy the
284 +
    Corresponding Source from a network server at no charge.
285 +
286 +
    c) Convey individual copies of the object code with a copy of the
287 +
    written offer to provide the Corresponding Source.  This
288 +
    alternative is allowed only occasionally and noncommercially, and
289 +
    only if you received the object code with such an offer, in accord
290 +
    with subsection 6b.
291 +
292 +
    d) Convey the object code by offering access from a designated
293 +
    place (gratis or for a charge), and offer equivalent access to the
294 +
    Corresponding Source in the same way through the same place at no
295 +
    further charge.  You need not require recipients to copy the
296 +
    Corresponding Source along with the object code.  If the place to
297 +
    copy the object code is a network server, the Corresponding Source
298 +
    may be on a different server (operated by you or a third party)
299 +
    that supports equivalent copying facilities, provided you maintain
300 +
    clear directions next to the object code saying where to find the
301 +
    Corresponding Source.  Regardless of what server hosts the
302 +
    Corresponding Source, you remain obligated to ensure that it is
303 +
    available for as long as needed to satisfy these requirements.
304 +
305 +
    e) Convey the object code using peer-to-peer transmission, provided
306 +
    you inform other peers where the object code and Corresponding
307 +
    Source of the work are being offered to the general public at no
308 +
    charge under subsection 6d.
309 +
310 +
  A separable portion of the object code, whose source code is excluded
311 +
from the Corresponding Source as a System Library, need not be
312 +
included in conveying the object code work.
313 +
314 +
  A "User Product" is either (1) a "consumer product", which means any
315 +
tangible personal property which is normally used for personal, family,
316 +
or household purposes, or (2) anything designed or sold for incorporation
317 +
into a dwelling.  In determining whether a product is a consumer product,
318 +
doubtful cases shall be resolved in favor of coverage.  For a particular
319 +
product received by a particular user, "normally used" refers to a
320 +
typical or common use of that class of product, regardless of the status
321 +
of the particular user or of the way in which the particular user
322 +
actually uses, or expects or is expected to use, the product.  A product
323 +
is a consumer product regardless of whether the product has substantial
324 +
commercial, industrial or non-consumer uses, unless such uses represent
325 +
the only significant mode of use of the product.
326 +
327 +
  "Installation Information" for a User Product means any methods,
328 +
procedures, authorization keys, or other information required to install
329 +
and execute modified versions of a covered work in that User Product from
330 +
a modified version of its Corresponding Source.  The information must
331 +
suffice to ensure that the continued functioning of the modified object
332 +
code is in no case prevented or interfered with solely because
333 +
modification has been made.
334 +
335 +
  If you convey an object code work under this section in, or with, or
336 +
specifically for use in, a User Product, and the conveying occurs as
337 +
part of a transaction in which the right of possession and use of the
338 +
User Product is transferred to the recipient in perpetuity or for a
339 +
fixed term (regardless of how the transaction is characterized), the
340 +
Corresponding Source conveyed under this section must be accompanied
341 +
by the Installation Information.  But this requirement does not apply
342 +
if neither you nor any third party retains the ability to install
343 +
modified object code on the User Product (for example, the work has
344 +
been installed in ROM).
345 +
346 +
  The requirement to provide Installation Information does not include a
347 +
requirement to continue to provide support service, warranty, or updates
348 +
for a work that has been modified or installed by the recipient, or for
349 +
the User Product in which it has been modified or installed.  Access to a
350 +
network may be denied when the modification itself materially and
351 +
adversely affects the operation of the network or violates the rules and
352 +
protocols for communication across the network.
353 +
354 +
  Corresponding Source conveyed, and Installation Information provided,
355 +
in accord with this section must be in a format that is publicly
356 +
documented (and with an implementation available to the public in
357 +
source code form), and must require no special password or key for
358 +
unpacking, reading or copying.
359 +
360 +
  7. Additional Terms.
361 +
362 +
  "Additional permissions" are terms that supplement the terms of this
363 +
License by making exceptions from one or more of its conditions.
364 +
Additional permissions that are applicable to the entire Program shall
365 +
be treated as though they were included in this License, to the extent
366 +
that they are valid under applicable law.  If additional permissions
367 +
apply only to part of the Program, that part may be used separately
368 +
under those permissions, but the entire Program remains governed by
369 +
this License without regard to the additional permissions.
370 +
371 +
  When you convey a copy of a covered work, you may at your option
372 +
remove any additional permissions from that copy, or from any part of
373 +
it.  (Additional permissions may be written to require their own
374 +
removal in certain cases when you modify the work.)  You may place
375 +
additional permissions on material, added by you to a covered work,
376 +
for which you have or can give appropriate copyright permission.
377 +
378 +
  Notwithstanding any other provision of this License, for material you
379 +
add to a covered work, you may (if authorized by the copyright holders of
380 +
that material) supplement the terms of this License with terms:
381 +
382 +
    a) Disclaiming warranty or limiting liability differently from the
383 +
    terms of sections 15 and 16 of this License; or
384 +
385 +
    b) Requiring preservation of specified reasonable legal notices or
386 +
    author attributions in that material or in the Appropriate Legal
387 +
    Notices displayed by works containing it; or
388 +
389 +
    c) Prohibiting misrepresentation of the origin of that material, or
390 +
    requiring that modified versions of such material be marked in
391 +
    reasonable ways as different from the original version; or
392 +
393 +
    d) Limiting the use for publicity purposes of names of licensors or
394 +
    authors of the material; or
395 +
396 +
    e) Declining to grant rights under trademark law for use of some
397 +
    trade names, trademarks, or service marks; or
398 +
399 +
    f) Requiring indemnification of licensors and authors of that
400 +
    material by anyone who conveys the material (or modified versions of
401 +
    it) with contractual assumptions of liability to the recipient, for
402 +
    any liability that these contractual assumptions directly impose on
403 +
    those licensors and authors.
404 +
405 +
  All other non-permissive additional terms are considered "further
406 +
restrictions" within the meaning of section 10.  If the Program as you
407 +
received it, or any part of it, contains a notice stating that it is
408 +
governed by this License along with a term that is a further
409 +
restriction, you may remove that term.  If a license document contains
410 +
a further restriction but permits relicensing or conveying under this
411 +
License, you may add to a covered work material governed by the terms
412 +
of that license document, provided that the further restriction does
413 +
not survive such relicensing or conveying.
414 +
415 +
  If you add terms to a covered work in accord with this section, you
416 +
must place, in the relevant source files, a statement of the
417 +
additional terms that apply to those files, or a notice indicating
418 +
where to find the applicable terms.
419 +
420 +
  Additional terms, permissive or non-permissive, may be stated in the
421 +
form of a separately written license, or stated as exceptions;
422 +
the above requirements apply either way.
423 +
424 +
  8. Termination.
425 +
426 +
  You may not propagate or modify a covered work except as expressly
427 +
provided under this License.  Any attempt otherwise to propagate or
428 +
modify it is void, and will automatically terminate your rights under
429 +
this License (including any patent licenses granted under the third
430 +
paragraph of section 11).
431 +
432 +
  However, if you cease all violation of this License, then your
433 +
license from a particular copyright holder is reinstated (a)
434 +
provisionally, unless and until the copyright holder explicitly and
435 +
finally terminates your license, and (b) permanently, if the copyright
436 +
holder fails to notify you of the violation by some reasonable means
437 +
prior to 60 days after the cessation.
438 +
439 +
  Moreover, your license from a particular copyright holder is
440 +
reinstated permanently if the copyright holder notifies you of the
441 +
violation by some reasonable means, this is the first time you have
442 +
received notice of violation of this License (for any work) from that
443 +
copyright holder, and you cure the violation prior to 30 days after
444 +
your receipt of the notice.
445 +
446 +
  Termination of your rights under this section does not terminate the
447 +
licenses of parties who have received copies or rights from you under
448 +
this License.  If your rights have been terminated and not permanently
449 +
reinstated, you do not qualify to receive new licenses for the same
450 +
material under section 10.
451 +
452 +
  9. Acceptance Not Required for Having Copies.
453 +
454 +
  You are not required to accept this License in order to receive or
455 +
run a copy of the Program.  Ancillary propagation of a covered work
456 +
occurring solely as a consequence of using peer-to-peer transmission
457 +
to receive a copy likewise does not require acceptance.  However,
458 +
nothing other than this License grants you permission to propagate or
459 +
modify any covered work.  These actions infringe copyright if you do
460 +
not accept this License.  Therefore, by modifying or propagating a
461 +
covered work, you indicate your acceptance of this License to do so.
462 +
463 +
  10. Automatic Licensing of Downstream Recipients.
464 +
465 +
  Each time you convey a covered work, the recipient automatically
466 +
receives a license from the original licensors, to run, modify and
467 +
propagate that work, subject to this License.  You are not responsible
468 +
for enforcing compliance by third parties with this License.
469 +
470 +
  An "entity transaction" is a transaction transferring control of an
471 +
organization, or substantially all assets of one, or subdividing an
472 +
organization, or merging organizations.  If propagation of a covered
473 +
work results from an entity transaction, each party to that
474 +
transaction who receives a copy of the work also receives whatever
475 +
licenses to the work the party's predecessor in interest had or could
476 +
give under the previous paragraph, plus a right to possession of the
477 +
Corresponding Source of the work from the predecessor in interest, if
478 +
the predecessor has it or can get it with reasonable efforts.
479 +
480 +
  You may not impose any further restrictions on the exercise of the
481 +
rights granted or affirmed under this License.  For example, you may
482 +
not impose a license fee, royalty, or other charge for exercise of
483 +
rights granted under this License, and you may not initiate litigation
484 +
(including a cross-claim or counterclaim in a lawsuit) alleging that
485 +
any patent claim is infringed by making, using, selling, offering for
486 +
sale, or importing the Program or any portion of it.
487 +
488 +
  11. Patents.
489 +
490 +
  A "contributor" is a copyright holder who authorizes use under this
491 +
License of the Program or a work on which the Program is based.  The
492 +
work thus licensed is called the contributor's "contributor version".
493 +
494 +
  A contributor's "essential patent claims" are all patent claims
495 +
owned or controlled by the contributor, whether already acquired or
496 +
hereafter acquired, that would be infringed by some manner, permitted
497 +
by this License, of making, using, or selling its contributor version,
498 +
but do not include claims that would be infringed only as a
499 +
consequence of further modification of the contributor version.  For
500 +
purposes of this definition, "control" includes the right to grant
501 +
patent sublicenses in a manner consistent with the requirements of
502 +
this License.
503 +
504 +
  Each contributor grants you a non-exclusive, worldwide, royalty-free
505 +
patent license under the contributor's essential patent claims, to
506 +
make, use, sell, offer for sale, import and otherwise run, modify and
507 +
propagate the contents of its contributor version.
508 +
509 +
  In the following three paragraphs, a "patent license" is any express
510 +
agreement or commitment, however denominated, not to enforce a patent
511 +
(such as an express permission to practice a patent or covenant not to
512 +
sue for patent infringement).  To "grant" such a patent license to a
513 +
party means to make such an agreement or commitment not to enforce a
514 +
patent against the party.
515 +
516 +
  If you convey a covered work, knowingly relying on a patent license,
517 +
and the Corresponding Source of the work is not available for anyone
518 +
to copy, free of charge and under the terms of this License, through a
519 +
publicly available network server or other readily accessible means,
520 +
then you must either (1) cause the Corresponding Source to be so
521 +
available, or (2) arrange to deprive yourself of the benefit of the
522 +
patent license for this particular work, or (3) arrange, in a manner
523 +
consistent with the requirements of this License, to extend the patent
524 +
license to downstream recipients.  "Knowingly relying" means you have
525 +
actual knowledge that, but for the patent license, your conveying the
526 +
covered work in a country, or your recipient's use of the covered work
527 +
in a country, would infringe one or more identifiable patents in that
528 +
country that you have reason to believe are valid.
529 +
530 +
  If, pursuant to or in connection with a single transaction or
531 +
arrangement, you convey, or propagate by procuring conveyance of, a
532 +
covered work, and grant a patent license to some of the parties
533 +
receiving the covered work authorizing them to use, propagate, modify
534 +
or convey a specific copy of the covered work, then the patent license
535 +
you grant is automatically extended to all recipients of the covered
536 +
work and works based on it.
537 +
538 +
  A patent license is "discriminatory" if it does not include within
539 +
the scope of its coverage, prohibits the exercise of, or is
540 +
conditioned on the non-exercise of one or more of the rights that are
541 +
specifically granted under this License.  You may not convey a covered
542 +
work if you are a party to an arrangement with a third party that is
543 +
in the business of distributing software, under which you make payment
544 +
to the third party based on the extent of your activity of conveying
545 +
the work, and under which the third party grants, to any of the
546 +
parties who would receive the covered work from you, a discriminatory
547 +
patent license (a) in connection with copies of the covered work
548 +
conveyed by you (or copies made from those copies), or (b) primarily
549 +
for and in connection with specific products or compilations that
550 +
contain the covered work, unless you entered into that arrangement,
551 +
or that patent license was granted, prior to 28 March 2007.
552 +
553 +
  Nothing in this License shall be construed as excluding or limiting
554 +
any implied license or other defenses to infringement that may
555 +
otherwise be available to you under applicable patent law.
556 +
557 +
  12. No Surrender of Others' Freedom.
558 +
559 +
  If conditions are imposed on you (whether by court order, agreement or
560 +
otherwise) that contradict the conditions of this License, they do not
561 +
excuse you from the conditions of this License.  If you cannot convey a
562 +
covered work so as to satisfy simultaneously your obligations under this
563 +
License and any other pertinent obligations, then as a consequence you may
564 +
not convey it at all.  For example, if you agree to terms that obligate you
565 +
to collect a royalty for further conveying from those to whom you convey
566 +
the Program, the only way you could satisfy both those terms and this
567 +
License would be to refrain entirely from conveying the Program.
568 +
569 +
  13. Use with the GNU Affero General Public License.
570 +
571 +
  Notwithstanding any other provision of this License, you have
572 +
permission to link or combine any covered work with a work licensed
573 +
under version 3 of the GNU Affero General Public License into a single
574 +
combined work, and to convey the resulting work.  The terms of this
575 +
License will continue to apply to the part which is the covered work,
576 +
but the special requirements of the GNU Affero General Public License,
577 +
section 13, concerning interaction through a network will apply to the
578 +
combination as such.
579 +
580 +
  14. Revised Versions of this License.
581 +
582 +
  The Free Software Foundation may publish revised and/or new versions of
583 +
the GNU General Public License from time to time.  Such new versions will
584 +
be similar in spirit to the present version, but may differ in detail to
585 +
address new problems or concerns.
586 +
587 +
  Each version is given a distinguishing version number.  If the
588 +
Program specifies that a certain numbered version of the GNU General
589 +
Public License "or any later version" applies to it, you have the
590 +
option of following the terms and conditions either of that numbered
591 +
version or of any later version published by the Free Software
592 +
Foundation.  If the Program does not specify a version number of the
593 +
GNU General Public License, you may choose any version ever published
594 +
by the Free Software Foundation.
595 +
596 +
  If the Program specifies that a proxy can decide which future
597 +
versions of the GNU General Public License can be used, that proxy's
598 +
public statement of acceptance of a version permanently authorizes you
599 +
to choose that version for the Program.
600 +
601 +
  Later license versions may give you additional or different
602 +
permissions.  However, no additional obligations are imposed on any
603 +
author or copyright holder as a result of your choosing to follow a
604 +
later version.
605 +
606 +
  15. Disclaimer of Warranty.
607 +
608 +
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
609 +
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
610 +
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
611 +
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
612 +
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
613 +
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
614 +
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
615 +
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
616 +
617 +
  16. Limitation of Liability.
618 +
619 +
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
620 +
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
621 +
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
622 +
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
623 +
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
624 +
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
625 +
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
626 +
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
627 +
SUCH DAMAGES.
628 +
629 +
  17. Interpretation of Sections 15 and 16.
630 +
631 +
  If the disclaimer of warranty and limitation of liability provided
632 +
above cannot be given local legal effect according to their terms,
633 +
reviewing courts shall apply local law that most closely approximates
634 +
an absolute waiver of all civil liability in connection with the
635 +
Program, unless a warranty or assumption of liability accompanies a
636 +
copy of the Program in return for a fee.
637 +
638 +
                     END OF TERMS AND CONDITIONS
LICENSE.dwl added +692 -0
1 +
dwl - dwm for Wayland
2 +
3 +
Copyright © 2020 dwl team
4 +
5 +
See also the files LICENSE.tinywl, LICENSE.dwm and LICENSE.sway.
6 +
7 +
This program is free software: you can redistribute it and/or modify
8 +
it under the terms of the GNU General Public License as published by
9 +
the Free Software Foundation, either version 3 of the License, or
10 +
(at your option) any later version.
11 +
12 +
This program is distributed in the hope that it will be useful,
13 +
but WITHOUT ANY WARRANTY; without even the implied warranty of
14 +
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 +
GNU General Public License for more details.
16 +
17 +
----
18 +
19 +
                    GNU GENERAL PUBLIC LICENSE
20 +
                       Version 3, 29 June 2007
21 +
22 +
 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
23 +
 Everyone is permitted to copy and distribute verbatim copies
24 +
 of this license document, but changing it is not allowed.
25 +
26 +
                            Preamble
27 +
28 +
  The GNU General Public License is a free, copyleft license for
29 +
software and other kinds of works.
30 +
31 +
  The licenses for most software and other practical works are designed
32 +
to take away your freedom to share and change the works.  By contrast,
33 +
the GNU General Public License is intended to guarantee your freedom to
34 +
share and change all versions of a program--to make sure it remains free
35 +
software for all its users.  We, the Free Software Foundation, use the
36 +
GNU General Public License for most of our software; it applies also to
37 +
any other work released this way by its authors.  You can apply it to
38 +
your programs, too.
39 +
40 +
  When we speak of free software, we are referring to freedom, not
41 +
price.  Our General Public Licenses are designed to make sure that you
42 +
have the freedom to distribute copies of free software (and charge for
43 +
them if you wish), that you receive source code or can get it if you
44 +
want it, that you can change the software or use pieces of it in new
45 +
free programs, and that you know you can do these things.
46 +
47 +
  To protect your rights, we need to prevent others from denying you
48 +
these rights or asking you to surrender the rights.  Therefore, you have
49 +
certain responsibilities if you distribute copies of the software, or if
50 +
you modify it: responsibilities to respect the freedom of others.
51 +
52 +
  For example, if you distribute copies of such a program, whether
53 +
gratis or for a fee, you must pass on to the recipients the same
54 +
freedoms that you received.  You must make sure that they, too, receive
55 +
or can get the source code.  And you must show them these terms so they
56 +
know their rights.
57 +
58 +
  Developers that use the GNU GPL protect your rights with two steps:
59 +
(1) assert copyright on the software, and (2) offer you this License
60 +
giving you legal permission to copy, distribute and/or modify it.
61 +
62 +
  For the developers' and authors' protection, the GPL clearly explains
63 +
that there is no warranty for this free software.  For both users' and
64 +
authors' sake, the GPL requires that modified versions be marked as
65 +
changed, so that their problems will not be attributed erroneously to
66 +
authors of previous versions.
67 +
68 +
  Some devices are designed to deny users access to install or run
69 +
modified versions of the software inside them, although the manufacturer
70 +
can do so.  This is fundamentally incompatible with the aim of
71 +
protecting users' freedom to change the software.  The systematic
72 +
pattern of such abuse occurs in the area of products for individuals to
73 +
use, which is precisely where it is most unacceptable.  Therefore, we
74 +
have designed this version of the GPL to prohibit the practice for those
75 +
products.  If such problems arise substantially in other domains, we
76 +
stand ready to extend this provision to those domains in future versions
77 +
of the GPL, as needed to protect the freedom of users.
78 +
79 +
  Finally, every program is threatened constantly by software patents.
80 +
States should not allow patents to restrict development and use of
81 +
software on general-purpose computers, but in those that do, we wish to
82 +
avoid the special danger that patents applied to a free program could
83 +
make it effectively proprietary.  To prevent this, the GPL assures that
84 +
patents cannot be used to render the program non-free.
85 +
86 +
  The precise terms and conditions for copying, distribution and
87 +
modification follow.
88 +
89 +
                       TERMS AND CONDITIONS
90 +
91 +
  0. Definitions.
92 +
93 +
  "This License" refers to version 3 of the GNU General Public License.
94 +
95 +
  "Copyright" also means copyright-like laws that apply to other kinds of
96 +
works, such as semiconductor masks.
97 +
98 +
  "The Program" refers to any copyrightable work licensed under this
99 +
License.  Each licensee is addressed as "you".  "Licensees" and
100 +
"recipients" may be individuals or organizations.
101 +
102 +
  To "modify" a work means to copy from or adapt all or part of the work
103 +
in a fashion requiring copyright permission, other than the making of an
104 +
exact copy.  The resulting work is called a "modified version" of the
105 +
earlier work or a work "based on" the earlier work.
106 +
107 +
  A "covered work" means either the unmodified Program or a work based
108 +
on the Program.
109 +
110 +
  To "propagate" a work means to do anything with it that, without
111 +
permission, would make you directly or secondarily liable for
112 +
infringement under applicable copyright law, except executing it on a
113 +
computer or modifying a private copy.  Propagation includes copying,
114 +
distribution (with or without modification), making available to the
115 +
public, and in some countries other activities as well.
116 +
117 +
  To "convey" a work means any kind of propagation that enables other
118 +
parties to make or receive copies.  Mere interaction with a user through
119 +
a computer network, with no transfer of a copy, is not conveying.
120 +
121 +
  An interactive user interface displays "Appropriate Legal Notices"
122 +
to the extent that it includes a convenient and prominently visible
123 +
feature that (1) displays an appropriate copyright notice, and (2)
124 +
tells the user that there is no warranty for the work (except to the
125 +
extent that warranties are provided), that licensees may convey the
126 +
work under this License, and how to view a copy of this License.  If
127 +
the interface presents a list of user commands or options, such as a
128 +
menu, a prominent item in the list meets this criterion.
129 +
130 +
  1. Source Code.
131 +
132 +
  The "source code" for a work means the preferred form of the work
133 +
for making modifications to it.  "Object code" means any non-source
134 +
form of a work.
135 +
136 +
  A "Standard Interface" means an interface that either is an official
137 +
standard defined by a recognized standards body, or, in the case of
138 +
interfaces specified for a particular programming language, one that
139 +
is widely used among developers working in that language.
140 +
141 +
  The "System Libraries" of an executable work include anything, other
142 +
than the work as a whole, that (a) is included in the normal form of
143 +
packaging a Major Component, but which is not part of that Major
144 +
Component, and (b) serves only to enable use of the work with that
145 +
Major Component, or to implement a Standard Interface for which an
146 +
implementation is available to the public in source code form.  A
147 +
"Major Component", in this context, means a major essential component
148 +
(kernel, window system, and so on) of the specific operating system
149 +
(if any) on which the executable work runs, or a compiler used to
150 +
produce the work, or an object code interpreter used to run it.
151 +
152 +
  The "Corresponding Source" for a work in object code form means all
153 +
the source code needed to generate, install, and (for an executable
154 +
work) run the object code and to modify the work, including scripts to
155 +
control those activities.  However, it does not include the work's
156 +
System Libraries, or general-purpose tools or generally available free
157 +
programs which are used unmodified in performing those activities but
158 +
which are not part of the work.  For example, Corresponding Source
159 +
includes interface definition files associated with source files for
160 +
the work, and the source code for shared libraries and dynamically
161 +
linked subprograms that the work is specifically designed to require,
162 +
such as by intimate data communication or control flow between those
163 +
subprograms and other parts of the work.
164 +
165 +
  The Corresponding Source need not include anything that users
166 +
can regenerate automatically from other parts of the Corresponding
167 +
Source.
168 +
169 +
  The Corresponding Source for a work in source code form is that
170 +
same work.
171 +
172 +
  2. Basic Permissions.
173 +
174 +
  All rights granted under this License are granted for the term of
175 +
copyright on the Program, and are irrevocable provided the stated
176 +
conditions are met.  This License explicitly affirms your unlimited
177 +
permission to run the unmodified Program.  The output from running a
178 +
covered work is covered by this License only if the output, given its
179 +
content, constitutes a covered work.  This License acknowledges your
180 +
rights of fair use or other equivalent, as provided by copyright law.
181 +
182 +
  You may make, run and propagate covered works that you do not
183 +
convey, without conditions so long as your license otherwise remains
184 +
in force.  You may convey covered works to others for the sole purpose
185 +
of having them make modifications exclusively for you, or provide you
186 +
with facilities for running those works, provided that you comply with
187 +
the terms of this License in conveying all material for which you do
188 +
not control copyright.  Those thus making or running the covered works
189 +
for you must do so exclusively on your behalf, under your direction
190 +
and control, on terms that prohibit them from making any copies of
191 +
your copyrighted material outside their relationship with you.
192 +
193 +
  Conveying under any other circumstances is permitted solely under
194 +
the conditions stated below.  Sublicensing is not allowed; section 10
195 +
makes it unnecessary.
196 +
197 +
  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
198 +
199 +
  No covered work shall be deemed part of an effective technological
200 +
measure under any applicable law fulfilling obligations under article
201 +
11 of the WIPO copyright treaty adopted on 20 December 1996, or
202 +
similar laws prohibiting or restricting circumvention of such
203 +
measures.
204 +
205 +
  When you convey a covered work, you waive any legal power to forbid
206 +
circumvention of technological measures to the extent such circumvention
207 +
is effected by exercising rights under this License with respect to
208 +
the covered work, and you disclaim any intention to limit operation or
209 +
modification of the work as a means of enforcing, against the work's
210 +
users, your or third parties' legal rights to forbid circumvention of
211 +
technological measures.
212 +
213 +
  4. Conveying Verbatim Copies.
214 +
215 +
  You may convey verbatim copies of the Program's source code as you
216 +
receive it, in any medium, provided that you conspicuously and
217 +
appropriately publish on each copy an appropriate copyright notice;
218 +
keep intact all notices stating that this License and any
219 +
non-permissive terms added in accord with section 7 apply to the code;
220 +
keep intact all notices of the absence of any warranty; and give all
221 +
recipients a copy of this License along with the Program.
222 +
223 +
  You may charge any price or no price for each copy that you convey,
224 +
and you may offer support or warranty protection for a fee.
225 +
226 +
  5. Conveying Modified Source Versions.
227 +
228 +
  You may convey a work based on the Program, or the modifications to
229 +
produce it from the Program, in the form of source code under the
230 +
terms of section 4, provided that you also meet all of these conditions:
231 +
232 +
    a) The work must carry prominent notices stating that you modified
233 +
    it, and giving a relevant date.
234 +
235 +
    b) The work must carry prominent notices stating that it is
236 +
    released under this License and any conditions added under section
237 +
    7.  This requirement modifies the requirement in section 4 to
238 +
    "keep intact all notices".
239 +
240 +
    c) You must license the entire work, as a whole, under this
241 +
    License to anyone who comes into possession of a copy.  This
242 +
    License will therefore apply, along with any applicable section 7
243 +
    additional terms, to the whole of the work, and all its parts,
244 +
    regardless of how they are packaged.  This License gives no
245 +
    permission to license the work in any other way, but it does not
246 +
    invalidate such permission if you have separately received it.
247 +
248 +
    d) If the work has interactive user interfaces, each must display
249 +
    Appropriate Legal Notices; however, if the Program has interactive
250 +
    interfaces that do not display Appropriate Legal Notices, your
251 +
    work need not make them do so.
252 +
253 +
  A compilation of a covered work with other separate and independent
254 +
works, which are not by their nature extensions of the covered work,
255 +
and which are not combined with it such as to form a larger program,
256 +
in or on a volume of a storage or distribution medium, is called an
257 +
"aggregate" if the compilation and its resulting copyright are not
258 +
used to limit the access or legal rights of the compilation's users
259 +
beyond what the individual works permit.  Inclusion of a covered work
260 +
in an aggregate does not cause this License to apply to the other
261 +
parts of the aggregate.
262 +
263 +
  6. Conveying Non-Source Forms.
264 +
265 +
  You may convey a covered work in object code form under the terms
266 +
of sections 4 and 5, provided that you also convey the
267 +
machine-readable Corresponding Source under the terms of this License,
268 +
in one of these ways:
269 +
270 +
    a) Convey the object code in, or embodied in, a physical product
271 +
    (including a physical distribution medium), accompanied by the
272 +
    Corresponding Source fixed on a durable physical medium
273 +
    customarily used for software interchange.
274 +
275 +
    b) Convey the object code in, or embodied in, a physical product
276 +
    (including a physical distribution medium), accompanied by a
277 +
    written offer, valid for at least three years and valid for as
278 +
    long as you offer spare parts or customer support for that product
279 +
    model, to give anyone who possesses the object code either (1) a
280 +
    copy of the Corresponding Source for all the software in the
281 +
    product that is covered by this License, on a durable physical
282 +
    medium customarily used for software interchange, for a price no
283 +
    more than your reasonable cost of physically performing this
284 +
    conveying of source, or (2) access to copy the
285 +
    Corresponding Source from a network server at no charge.
286 +
287 +
    c) Convey individual copies of the object code with a copy of the
288 +
    written offer to provide the Corresponding Source.  This
289 +
    alternative is allowed only occasionally and noncommercially, and
290 +
    only if you received the object code with such an offer, in accord
291 +
    with subsection 6b.
292 +
293 +
    d) Convey the object code by offering access from a designated
294 +
    place (gratis or for a charge), and offer equivalent access to the
295 +
    Corresponding Source in the same way through the same place at no
296 +
    further charge.  You need not require recipients to copy the
297 +
    Corresponding Source along with the object code.  If the place to
298 +
    copy the object code is a network server, the Corresponding Source
299 +
    may be on a different server (operated by you or a third party)
300 +
    that supports equivalent copying facilities, provided you maintain
301 +
    clear directions next to the object code saying where to find the
302 +
    Corresponding Source.  Regardless of what server hosts the
303 +
    Corresponding Source, you remain obligated to ensure that it is
304 +
    available for as long as needed to satisfy these requirements.
305 +
306 +
    e) Convey the object code using peer-to-peer transmission, provided
307 +
    you inform other peers where the object code and Corresponding
308 +
    Source of the work are being offered to the general public at no
309 +
    charge under subsection 6d.
310 +
311 +
  A separable portion of the object code, whose source code is excluded
312 +
from the Corresponding Source as a System Library, need not be
313 +
included in conveying the object code work.
314 +
315 +
  A "User Product" is either (1) a "consumer product", which means any
316 +
tangible personal property which is normally used for personal, family,
317 +
or household purposes, or (2) anything designed or sold for incorporation
318 +
into a dwelling.  In determining whether a product is a consumer product,
319 +
doubtful cases shall be resolved in favor of coverage.  For a particular
320 +
product received by a particular user, "normally used" refers to a
321 +
typical or common use of that class of product, regardless of the status
322 +
of the particular user or of the way in which the particular user
323 +
actually uses, or expects or is expected to use, the product.  A product
324 +
is a consumer product regardless of whether the product has substantial
325 +
commercial, industrial or non-consumer uses, unless such uses represent
326 +
the only significant mode of use of the product.
327 +
328 +
  "Installation Information" for a User Product means any methods,
329 +
procedures, authorization keys, or other information required to install
330 +
and execute modified versions of a covered work in that User Product from
331 +
a modified version of its Corresponding Source.  The information must
332 +
suffice to ensure that the continued functioning of the modified object
333 +
code is in no case prevented or interfered with solely because
334 +
modification has been made.
335 +
336 +
  If you convey an object code work under this section in, or with, or
337 +
specifically for use in, a User Product, and the conveying occurs as
338 +
part of a transaction in which the right of possession and use of the
339 +
User Product is transferred to the recipient in perpetuity or for a
340 +
fixed term (regardless of how the transaction is characterized), the
341 +
Corresponding Source conveyed under this section must be accompanied
342 +
by the Installation Information.  But this requirement does not apply
343 +
if neither you nor any third party retains the ability to install
344 +
modified object code on the User Product (for example, the work has
345 +
been installed in ROM).
346 +
347 +
  The requirement to provide Installation Information does not include a
348 +
requirement to continue to provide support service, warranty, or updates
349 +
for a work that has been modified or installed by the recipient, or for
350 +
the User Product in which it has been modified or installed.  Access to a
351 +
network may be denied when the modification itself materially and
352 +
adversely affects the operation of the network or violates the rules and
353 +
protocols for communication across the network.
354 +
355 +
  Corresponding Source conveyed, and Installation Information provided,
356 +
in accord with this section must be in a format that is publicly
357 +
documented (and with an implementation available to the public in
358 +
source code form), and must require no special password or key for
359 +
unpacking, reading or copying.
360 +
361 +
  7. Additional Terms.
362 +
363 +
  "Additional permissions" are terms that supplement the terms of this
364 +
License by making exceptions from one or more of its conditions.
365 +
Additional permissions that are applicable to the entire Program shall
366 +
be treated as though they were included in this License, to the extent
367 +
that they are valid under applicable law.  If additional permissions
368 +
apply only to part of the Program, that part may be used separately
369 +
under those permissions, but the entire Program remains governed by
370 +
this License without regard to the additional permissions.
371 +
372 +
  When you convey a copy of a covered work, you may at your option
373 +
remove any additional permissions from that copy, or from any part of
374 +
it.  (Additional permissions may be written to require their own
375 +
removal in certain cases when you modify the work.)  You may place
376 +
additional permissions on material, added by you to a covered work,
377 +
for which you have or can give appropriate copyright permission.
378 +
379 +
  Notwithstanding any other provision of this License, for material you
380 +
add to a covered work, you may (if authorized by the copyright holders of
381 +
that material) supplement the terms of this License with terms:
382 +
383 +
    a) Disclaiming warranty or limiting liability differently from the
384 +
    terms of sections 15 and 16 of this License; or
385 +
386 +
    b) Requiring preservation of specified reasonable legal notices or
387 +
    author attributions in that material or in the Appropriate Legal
388 +
    Notices displayed by works containing it; or
389 +
390 +
    c) Prohibiting misrepresentation of the origin of that material, or
391 +
    requiring that modified versions of such material be marked in
392 +
    reasonable ways as different from the original version; or
393 +
394 +
    d) Limiting the use for publicity purposes of names of licensors or
395 +
    authors of the material; or
396 +
397 +
    e) Declining to grant rights under trademark law for use of some
398 +
    trade names, trademarks, or service marks; or
399 +
400 +
    f) Requiring indemnification of licensors and authors of that
401 +
    material by anyone who conveys the material (or modified versions of
402 +
    it) with contractual assumptions of liability to the recipient, for
403 +
    any liability that these contractual assumptions directly impose on
404 +
    those licensors and authors.
405 +
406 +
  All other non-permissive additional terms are considered "further
407 +
restrictions" within the meaning of section 10.  If the Program as you
408 +
received it, or any part of it, contains a notice stating that it is
409 +
governed by this License along with a term that is a further
410 +
restriction, you may remove that term.  If a license document contains
411 +
a further restriction but permits relicensing or conveying under this
412 +
License, you may add to a covered work material governed by the terms
413 +
of that license document, provided that the further restriction does
414 +
not survive such relicensing or conveying.
415 +
416 +
  If you add terms to a covered work in accord with this section, you
417 +
must place, in the relevant source files, a statement of the
418 +
additional terms that apply to those files, or a notice indicating
419 +
where to find the applicable terms.
420 +
421 +
  Additional terms, permissive or non-permissive, may be stated in the
422 +
form of a separately written license, or stated as exceptions;
423 +
the above requirements apply either way.
424 +
425 +
  8. Termination.
426 +
427 +
  You may not propagate or modify a covered work except as expressly
428 +
provided under this License.  Any attempt otherwise to propagate or
429 +
modify it is void, and will automatically terminate your rights under
430 +
this License (including any patent licenses granted under the third
431 +
paragraph of section 11).
432 +
433 +
  However, if you cease all violation of this License, then your
434 +
license from a particular copyright holder is reinstated (a)
435 +
provisionally, unless and until the copyright holder explicitly and
436 +
finally terminates your license, and (b) permanently, if the copyright
437 +
holder fails to notify you of the violation by some reasonable means
438 +
prior to 60 days after the cessation.
439 +
440 +
  Moreover, your license from a particular copyright holder is
441 +
reinstated permanently if the copyright holder notifies you of the
442 +
violation by some reasonable means, this is the first time you have
443 +
received notice of violation of this License (for any work) from that
444 +
copyright holder, and you cure the violation prior to 30 days after
445 +
your receipt of the notice.
446 +
447 +
  Termination of your rights under this section does not terminate the
448 +
licenses of parties who have received copies or rights from you under
449 +
this License.  If your rights have been terminated and not permanently
450 +
reinstated, you do not qualify to receive new licenses for the same
451 +
material under section 10.
452 +
453 +
  9. Acceptance Not Required for Having Copies.
454 +
455 +
  You are not required to accept this License in order to receive or
456 +
run a copy of the Program.  Ancillary propagation of a covered work
457 +
occurring solely as a consequence of using peer-to-peer transmission
458 +
to receive a copy likewise does not require acceptance.  However,
459 +
nothing other than this License grants you permission to propagate or
460 +
modify any covered work.  These actions infringe copyright if you do
461 +
not accept this License.  Therefore, by modifying or propagating a
462 +
covered work, you indicate your acceptance of this License to do so.
463 +
464 +
  10. Automatic Licensing of Downstream Recipients.
465 +
466 +
  Each time you convey a covered work, the recipient automatically
467 +
receives a license from the original licensors, to run, modify and
468 +
propagate that work, subject to this License.  You are not responsible
469 +
for enforcing compliance by third parties with this License.
470 +
471 +
  An "entity transaction" is a transaction transferring control of an
472 +
organization, or substantially all assets of one, or subdividing an
473 +
organization, or merging organizations.  If propagation of a covered
474 +
work results from an entity transaction, each party to that
475 +
transaction who receives a copy of the work also receives whatever
476 +
licenses to the work the party's predecessor in interest had or could
477 +
give under the previous paragraph, plus a right to possession of the
478 +
Corresponding Source of the work from the predecessor in interest, if
479 +
the predecessor has it or can get it with reasonable efforts.
480 +
481 +
  You may not impose any further restrictions on the exercise of the
482 +
rights granted or affirmed under this License.  For example, you may
483 +
not impose a license fee, royalty, or other charge for exercise of
484 +
rights granted under this License, and you may not initiate litigation
485 +
(including a cross-claim or counterclaim in a lawsuit) alleging that
486 +
any patent claim is infringed by making, using, selling, offering for
487 +
sale, or importing the Program or any portion of it.
488 +
489 +
  11. Patents.
490 +
491 +
  A "contributor" is a copyright holder who authorizes use under this
492 +
License of the Program or a work on which the Program is based.  The
493 +
work thus licensed is called the contributor's "contributor version".
494 +
495 +
  A contributor's "essential patent claims" are all patent claims
496 +
owned or controlled by the contributor, whether already acquired or
497 +
hereafter acquired, that would be infringed by some manner, permitted
498 +
by this License, of making, using, or selling its contributor version,
499 +
but do not include claims that would be infringed only as a
500 +
consequence of further modification of the contributor version.  For
501 +
purposes of this definition, "control" includes the right to grant
502 +
patent sublicenses in a manner consistent with the requirements of
503 +
this License.
504 +
505 +
  Each contributor grants you a non-exclusive, worldwide, royalty-free
506 +
patent license under the contributor's essential patent claims, to
507 +
make, use, sell, offer for sale, import and otherwise run, modify and
508 +
propagate the contents of its contributor version.
509 +
510 +
  In the following three paragraphs, a "patent license" is any express
511 +
agreement or commitment, however denominated, not to enforce a patent
512 +
(such as an express permission to practice a patent or covenant not to
513 +
sue for patent infringement).  To "grant" such a patent license to a
514 +
party means to make such an agreement or commitment not to enforce a
515 +
patent against the party.
516 +
517 +
  If you convey a covered work, knowingly relying on a patent license,
518 +
and the Corresponding Source of the work is not available for anyone
519 +
to copy, free of charge and under the terms of this License, through a
520 +
publicly available network server or other readily accessible means,
521 +
then you must either (1) cause the Corresponding Source to be so
522 +
available, or (2) arrange to deprive yourself of the benefit of the
523 +
patent license for this particular work, or (3) arrange, in a manner
524 +
consistent with the requirements of this License, to extend the patent
525 +
license to downstream recipients.  "Knowingly relying" means you have
526 +
actual knowledge that, but for the patent license, your conveying the
527 +
covered work in a country, or your recipient's use of the covered work
528 +
in a country, would infringe one or more identifiable patents in that
529 +
country that you have reason to believe are valid.
530 +
531 +
  If, pursuant to or in connection with a single transaction or
532 +
arrangement, you convey, or propagate by procuring conveyance of, a
533 +
covered work, and grant a patent license to some of the parties
534 +
receiving the covered work authorizing them to use, propagate, modify
535 +
or convey a specific copy of the covered work, then the patent license
536 +
you grant is automatically extended to all recipients of the covered
537 +
work and works based on it.
538 +
539 +
  A patent license is "discriminatory" if it does not include within
540 +
the scope of its coverage, prohibits the exercise of, or is
541 +
conditioned on the non-exercise of one or more of the rights that are
542 +
specifically granted under this License.  You may not convey a covered
543 +
work if you are a party to an arrangement with a third party that is
544 +
in the business of distributing software, under which you make payment
545 +
to the third party based on the extent of your activity of conveying
546 +
the work, and under which the third party grants, to any of the
547 +
parties who would receive the covered work from you, a discriminatory
548 +
patent license (a) in connection with copies of the covered work
549 +
conveyed by you (or copies made from those copies), or (b) primarily
550 +
for and in connection with specific products or compilations that
551 +
contain the covered work, unless you entered into that arrangement,
552 +
or that patent license was granted, prior to 28 March 2007.
553 +
554 +
  Nothing in this License shall be construed as excluding or limiting
555 +
any implied license or other defenses to infringement that may
556 +
otherwise be available to you under applicable patent law.
557 +
558 +
  12. No Surrender of Others' Freedom.
559 +
560 +
  If conditions are imposed on you (whether by court order, agreement or
561 +
otherwise) that contradict the conditions of this License, they do not
562 +
excuse you from the conditions of this License.  If you cannot convey a
563 +
covered work so as to satisfy simultaneously your obligations under this
564 +
License and any other pertinent obligations, then as a consequence you may
565 +
not convey it at all.  For example, if you agree to terms that obligate you
566 +
to collect a royalty for further conveying from those to whom you convey
567 +
the Program, the only way you could satisfy both those terms and this
568 +
License would be to refrain entirely from conveying the Program.
569 +
570 +
  13. Use with the GNU Affero General Public License.
571 +
572 +
  Notwithstanding any other provision of this License, you have
573 +
permission to link or combine any covered work with a work licensed
574 +
under version 3 of the GNU Affero General Public License into a single
575 +
combined work, and to convey the resulting work.  The terms of this
576 +
License will continue to apply to the part which is the covered work,
577 +
but the special requirements of the GNU Affero General Public License,
578 +
section 13, concerning interaction through a network will apply to the
579 +
combination as such.
580 +
581 +
  14. Revised Versions of this License.
582 +
583 +
  The Free Software Foundation may publish revised and/or new versions of
584 +
the GNU General Public License from time to time.  Such new versions will
585 +
be similar in spirit to the present version, but may differ in detail to
586 +
address new problems or concerns.
587 +
588 +
  Each version is given a distinguishing version number.  If the
589 +
Program specifies that a certain numbered version of the GNU General
590 +
Public License "or any later version" applies to it, you have the
591 +
option of following the terms and conditions either of that numbered
592 +
version or of any later version published by the Free Software
593 +
Foundation.  If the Program does not specify a version number of the
594 +
GNU General Public License, you may choose any version ever published
595 +
by the Free Software Foundation.
596 +
597 +
  If the Program specifies that a proxy can decide which future
598 +
versions of the GNU General Public License can be used, that proxy's
599 +
public statement of acceptance of a version permanently authorizes you
600 +
to choose that version for the Program.
601 +
602 +
  Later license versions may give you additional or different
603 +
permissions.  However, no additional obligations are imposed on any
604 +
author or copyright holder as a result of your choosing to follow a
605 +
later version.
606 +
607 +
  15. Disclaimer of Warranty.
608 +
609 +
  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
610 +
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
611 +
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
612 +
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
613 +
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
614 +
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
615 +
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
616 +
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
617 +
618 +
  16. Limitation of Liability.
619 +
620 +
  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
621 +
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
622 +
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
623 +
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
624 +
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
625 +
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
626 +
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
627 +
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
628 +
SUCH DAMAGES.
629 +
630 +
  17. Interpretation of Sections 15 and 16.
631 +
632 +
  If the disclaimer of warranty and limitation of liability provided
633 +
above cannot be given local legal effect according to their terms,
634 +
reviewing courts shall apply local law that most closely approximates
635 +
an absolute waiver of all civil liability in connection with the
636 +
Program, unless a warranty or assumption of liability accompanies a
637 +
copy of the Program in return for a fee.
638 +
639 +
                     END OF TERMS AND CONDITIONS
640 +
641 +
            How to Apply These Terms to Your New Programs
642 +
643 +
  If you develop a new program, and you want it to be of the greatest
644 +
possible use to the public, the best way to achieve this is to make it
645 +
free software which everyone can redistribute and change under these terms.
646 +
647 +
  To do so, attach the following notices to the program.  It is safest
648 +
to attach them to the start of each source file to most effectively
649 +
state the exclusion of warranty; and each file should have at least
650 +
the "copyright" line and a pointer to where the full notice is found.
651 +
652 +
    <one line to give the program's name and a brief idea of what it does.>
653 +
    Copyright (C) <year>  <name of author>
654 +
655 +
    This program is free software: you can redistribute it and/or modify
656 +
    it under the terms of the GNU General Public License as published by
657 +
    the Free Software Foundation, either version 3 of the License, or
658 +
    (at your option) any later version.
659 +
660 +
    This program is distributed in the hope that it will be useful,
661 +
    but WITHOUT ANY WARRANTY; without even the implied warranty of
662 +
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
663 +
    GNU General Public License for more details.
664 +
665 +
    You should have received a copy of the GNU General Public License
666 +
    along with this program.  If not, see <https://www.gnu.org/licenses/>.
667 +
668 +
Also add information on how to contact you by electronic and paper mail.
669 +
670 +
  If the program does terminal interaction, make it output a short
671 +
notice like this when it starts in an interactive mode:
672 +
673 +
    <program>  Copyright (C) <year>  <name of author>
674 +
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
675 +
    This is free software, and you are welcome to redistribute it
676 +
    under certain conditions; type `show c' for details.
677 +
678 +
The hypothetical commands `show w' and `show c' should show the appropriate
679 +
parts of the General Public License.  Of course, your program's commands
680 +
might be different; for a GUI interface, you would use an "about box".
681 +
682 +
  You should also get your employer (if you work as a programmer) or school,
683 +
if any, to sign a "copyright disclaimer" for the program, if necessary.
684 +
For more information on this, and how to apply and follow the GNU GPL, see
685 +
<https://www.gnu.org/licenses/>.
686 +
687 +
  The GNU General Public License does not permit incorporating your program
688 +
into proprietary programs.  If your program is a subroutine library, you
689 +
may consider it more useful to permit linking proprietary applications with
690 +
the library.  If this is what you want to do, use the GNU Lesser General
691 +
Public License instead of this License.  But first, please read
692 +
<https://www.gnu.org/licenses/why-not-lgpl.html>.
LICENSE.dwm added +39 -0
1 +
Portions of dwl based on dwm code are used under the following license:
2 +
3 +
MIT/X Consortium License
4 +
5 +
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
6 +
© 2006-2009 Jukka Salmi <jukka at salmi dot ch>
7 +
© 2006-2007 Sander van Dijk <a dot h dot vandijk at gmail dot com>
8 +
© 2007-2011 Peter Hartlich <sgkkr at hartlich dot com>
9 +
© 2007-2009 Szabolcs Nagy <nszabolcs at gmail dot com>
10 +
© 2007-2009 Christof Musik <christof at sendfax dot de>
11 +
© 2007-2009 Premysl Hruby <dfenze at gmail dot com>
12 +
© 2007-2008 Enno Gottox Boland <gottox at s01 dot de>
13 +
© 2008 Martin Hurton <martin dot hurton at gmail dot com>
14 +
© 2008 Neale Pickett <neale dot woozle dot org>
15 +
© 2009 Mate Nagy <mnagy at port70 dot net>
16 +
© 2010-2016 Hiltjo Posthuma <hiltjo@codemadness.org>
17 +
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
18 +
© 2011 Christoph Lohmann <20h@r-36.net>
19 +
© 2015-2016 Quentin Rameau <quinq@fifth.space>
20 +
© 2015-2016 Eric Pruitt <eric.pruitt@gmail.com>
21 +
© 2016-2017 Markus Teich <markus.teich@stusta.mhn.de>
22 +
23 +
Permission is hereby granted, free of charge, to any person obtaining a
24 +
copy of this software and associated documentation files (the "Software"),
25 +
to deal in the Software without restriction, including without limitation
26 +
the rights to use, copy, modify, merge, publish, distribute, sublicense,
27 +
and/or sell copies of the Software, and to permit persons to whom the
28 +
Software is furnished to do so, subject to the following conditions:
29 +
30 +
The above copyright notice and this permission notice shall be included in
31 +
all copies or substantial portions of the Software.
32 +
33 +
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
34 +
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
35 +
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
36 +
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
37 +
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
38 +
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
39 +
DEALINGS IN THE SOFTWARE.
LICENSE.spectrwm added +23 -0
1 +
Copyright (c) 2009-2016 Marco Peereboom <marco@peereboom.us>
2 +
Copyright (c) 2009-2011 Ryan McBride <mcbride@countersiege.com>
3 +
Copyright (c) 2009 Darrin Chandler <dwchandler@stilyagin.com>
4 +
Copyright (c) 2009 Pierre-Yves Ritschard <pyr@spootnik.org>
5 +
Copyright (c) 2010 Tuukka Kataja <stuge@xor.fi>
6 +
Copyright (c) 2011 Jason L. Wright <jason@thought.net>
7 +
Copyright (c) 2011-2016 Reginald Kennedy <rk@rejii.com>
8 +
Copyright (c) 2011-2012 Lawrence Teo <lteo@lteo.net>
9 +
Copyright (c) 2011-2012 Tiago Cunha <tcunha@gmx.com>
10 +
Copyright (c) 2012-2015 David Hill <dhill@mindcry.org>
11 +
Copyright (c) 2014-2016 Yuri D'Elia <yuri.delia@eurac.edu>
12 +
13 +
Permission to use, copy, modify, and distribute this software for any
14 +
purpose with or without fee is hereby granted, provided that the above
15 +
copyright notice and this permission notice appear in all copies.
16 +
17 +
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
18 +
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
19 +
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
20 +
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
21 +
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
22 +
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
23 +
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
LICENSE.sway added +19 -0
1 +
Copyright (c) 2016-2017 Drew DeVault
2 +
3 +
Permission is hereby granted, free of charge, to any person obtaining a copy of
4 +
this software and associated documentation files (the "Software"), to deal in
5 +
the Software without restriction, including without limitation the rights to
6 +
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 +
of the Software, and to permit persons to whom the Software is furnished to do
8 +
so, subject to the following conditions:
9 +
10 +
The above copyright notice and this permission notice shall be included in all
11 +
copies or substantial portions of the Software.
12 +
13 +
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 +
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 +
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 +
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 +
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 +
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 +
SOFTWARE.
LICENSE.tinywl added +127 -0
1 +
dwl is originally based on TinyWL, which is used under the following license:
2 +
3 +
This work is licensed under CC0, which effectively puts it in the public domain.
4 +
5 +
---
6 +
7 +
Creative Commons Legal Code
8 +
9 +
CC0 1.0 Universal
10 +
11 +
    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
12 +
    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
13 +
    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
14 +
    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
15 +
    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
16 +
    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
17 +
    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
18 +
    HEREUNDER.
19 +
20 +
Statement of Purpose
21 +
22 +
The laws of most jurisdictions throughout the world automatically confer
23 +
exclusive Copyright and Related Rights (defined below) upon the creator
24 +
and subsequent owner(s) (each and all, an "owner") of an original work of
25 +
authorship and/or a database (each, a "Work").
26 +
27 +
Certain owners wish to permanently relinquish those rights to a Work for
28 +
the purpose of contributing to a commons of creative, cultural and
29 +
scientific works ("Commons") that the public can reliably and without fear
30 +
of later claims of infringement build upon, modify, incorporate in other
31 +
works, reuse and redistribute as freely as possible in any form whatsoever
32 +
and for any purposes, including without limitation commercial purposes.
33 +
These owners may contribute to the Commons to promote the ideal of a free
34 +
culture and the further production of creative, cultural and scientific
35 +
works, or to gain reputation or greater distribution for their Work in
36 +
part through the use and efforts of others.
37 +
38 +
For these and/or other purposes and motivations, and without any
39 +
expectation of additional consideration or compensation, the person
40 +
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
41 +
is an owner of Copyright and Related Rights in the Work, voluntarily
42 +
elects to apply CC0 to the Work and publicly distribute the Work under its
43 +
terms, with knowledge of his or her Copyright and Related Rights in the
44 +
Work and the meaning and intended legal effect of CC0 on those rights.
45 +
46 +
1. Copyright and Related Rights. A Work made available under CC0 may be
47 +
protected by copyright and related or neighboring rights ("Copyright and
48 +
Related Rights"). Copyright and Related Rights include, but are not
49 +
limited to, the following:
50 +
51 +
  i. the right to reproduce, adapt, distribute, perform, display,
52 +
     communicate, and translate a Work;
53 +
 ii. moral rights retained by the original author(s) and/or performer(s);
54 +
iii. publicity and privacy rights pertaining to a person's image or
55 +
     likeness depicted in a Work;
56 +
 iv. rights protecting against unfair competition in regards to a Work,
57 +
     subject to the limitations in paragraph 4(a), below;
58 +
  v. rights protecting the extraction, dissemination, use and reuse of data
59 +
     in a Work;
60 +
 vi. database rights (such as those arising under Directive 96/9/EC of the
61 +
     European Parliament and of the Council of 11 March 1996 on the legal
62 +
     protection of databases, and under any national implementation
63 +
     thereof, including any amended or successor version of such
64 +
     directive); and
65 +
vii. other similar, equivalent or corresponding rights throughout the
66 +
     world based on applicable law or treaty, and any national
67 +
     implementations thereof.
68 +
69 +
2. Waiver. To the greatest extent permitted by, but not in contravention
70 +
of, applicable law, Affirmer hereby overtly, fully, permanently,
71 +
irrevocably and unconditionally waives, abandons, and surrenders all of
72 +
Affirmer's Copyright and Related Rights and associated claims and causes
73 +
of action, whether now known or unknown (including existing as well as
74 +
future claims and causes of action), in the Work (i) in all territories
75 +
worldwide, (ii) for the maximum duration provided by applicable law or
76 +
treaty (including future time extensions), (iii) in any current or future
77 +
medium and for any number of copies, and (iv) for any purpose whatsoever,
78 +
including without limitation commercial, advertising or promotional
79 +
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
80 +
member of the public at large and to the detriment of Affirmer's heirs and
81 +
successors, fully intending that such Waiver shall not be subject to
82 +
revocation, rescission, cancellation, termination, or any other legal or
83 +
equitable action to disrupt the quiet enjoyment of the Work by the public
84 +
as contemplated by Affirmer's express Statement of Purpose.
85 +
86 +
3. Public License Fallback. Should any part of the Waiver for any reason
87 +
be judged legally invalid or ineffective under applicable law, then the
88 +
Waiver shall be preserved to the maximum extent permitted taking into
89 +
account Affirmer's express Statement of Purpose. In addition, to the
90 +
extent the Waiver is so judged Affirmer hereby grants to each affected
91 +
person a royalty-free, non transferable, non sublicensable, non exclusive,
92 +
irrevocable and unconditional license to exercise Affirmer's Copyright and
93 +
Related Rights in the Work (i) in all territories worldwide, (ii) for the
94 +
maximum duration provided by applicable law or treaty (including future
95 +
time extensions), (iii) in any current or future medium and for any number
96 +
of copies, and (iv) for any purpose whatsoever, including without
97 +
limitation commercial, advertising or promotional purposes (the
98 +
"License"). The License shall be deemed effective as of the date CC0 was
99 +
applied by Affirmer to the Work. Should any part of the License for any
100 +
reason be judged legally invalid or ineffective under applicable law, such
101 +
partial invalidity or ineffectiveness shall not invalidate the remainder
102 +
of the License, and in such case Affirmer hereby affirms that he or she
103 +
will not (i) exercise any of his or her remaining Copyright and Related
104 +
Rights in the Work or (ii) assert any associated claims and causes of
105 +
action with respect to the Work, in either case contrary to Affirmer's
106 +
express Statement of Purpose.
107 +
108 +
4. Limitations and Disclaimers.
109 +
110 +
 a. No trademark or patent rights held by Affirmer are waived, abandoned,
111 +
    surrendered, licensed or otherwise affected by this document.
112 +
 b. Affirmer offers the Work as-is and makes no representations or
113 +
    warranties of any kind concerning the Work, express, implied,
114 +
    statutory or otherwise, including without limitation warranties of
115 +
    title, merchantability, fitness for a particular purpose, non
116 +
    infringement, or the absence of latent or other defects, accuracy, or
117 +
    the present or absence of errors, whether or not discoverable, all to
118 +
    the greatest extent permissible under applicable law.
119 +
 c. Affirmer disclaims responsibility for clearing rights of other persons
120 +
    that may apply to the Work or any use thereof, including without
121 +
    limitation any person's Copyright and Related Rights in the Work.
122 +
    Further, Affirmer disclaims responsibility for obtaining any necessary
123 +
    consents, permissions or other rights required for any use of the
124 +
    Work.
125 +
 d. Affirmer understands and acknowledges that Creative Commons is not a
126 +
    party to this document and has no duty or obligation with respect to
127 +
    this CC0 or use of the Work.
Makefile added +109 -0
1 +
include config.mk
2 +
3 +
PKGS = wayland-server xkbcommon libinput xcb xcb-icccm
4 +
5 +
CPPFLAGS += -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L \
6 +
	        -DVERSION=\"$(VERSION)\" \
7 +
	        `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS)
8 +
CFLAGS   := -fvisibility=hidden -std=c23 -Os \
9 +
	        -Wall -Wextra -Wpedantic \
10 +
	        -Wformat=2 -Wformat-security \
11 +
	        -Wnull-dereference -Wno-format-nonliteral \
12 +
	        -Wunused -Wuninitialized -Wmissing-field-initializers \
13 +
	        -Wdeclaration-after-statement -Wno-unused-parameter \
14 +
	        -Wshadow -Wunused-macros \
15 +
	        -Werror=strict-prototypes -Werror=implicit -Werror=return-type \
16 +
	        -Werror=incompatible-pointer-types -Wfloat-conversion \
17 +
	        -fno-common -fstack-protector-all -mcmodel=medium
18 +
LDFLAGS  := -fuse-ld=lld
19 +
LDLIBS   := `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS)
20 +
21 +
SRC   := swm.c util.c ext-workspace-v1-protocol.c
22 +
OBJ   := $(SRC:.c=.o)
23 +
PROTO := \
24 +
	cursor-shape-v1-protocol.h \
25 +
	ext-workspace-v1-protocol.h \
26 +
	pointer-constraints-unstable-v1-protocol.h \
27 +
	wlr-layer-shell-unstable-v1-protocol.h \
28 +
	wlr-output-power-management-unstable-v1-protocol.h \
29 +
	xdg-shell-protocol.h
30 +
HDRS  := config.h swm.h util.h $(PROTO)
31 +
32 +
WL_SCANNER := $(shell $(PKG_CONFIG) --variable=wayland_scanner wayland-scanner)
33 +
WL_PROTO   := $(shell $(PKG_CONFIG) --variable=pkgdatadir wayland-protocols)
34 +
35 +
all: man swm
36 +
37 +
man: swm.1
38 +
39 +
swm.1: swm.1.adoc
40 +
	@echo "man  $< => $@"
41 +
	@asciidoctor -b manpage -o $@ $<
42 +
43 +
swm: $(OBJ)
44 +
	@echo "ld   $^ => $@"
45 +
	@$(CC) $(LDFLAGS) $^ $(LDLIBS) -o $@
46 +
	@echo "ok   $@"
47 +
48 +
$(OBJ): Makefile config.mk
49 +
50 +
%.o: %.c $(HDRS)
51 +
	@echo "cc   $< => $@"
52 +
	@$(CC) $(CPPFLAGS) $(CFLAGS) -c $< -o $@
53 +
54 +
cursor-shape-v1-protocol.h: \
55 +
	$(WL_PROTO)/staging/cursor-shape/cursor-shape-v1.xml
56 +
	@echo "hdr  $@"
57 +
	@$(WL_SCANNER) enum-header $< $@
58 +
ext-workspace-v1-protocol.h: \
59 +
	$(WL_PROTO)/staging/ext-workspace/ext-workspace-v1.xml
60 +
	@echo "hdr  $@"
61 +
	@$(WL_SCANNER) server-header $< $@
62 +
ext-workspace-v1-protocol.c: \
63 +
	$(WL_PROTO)/staging/ext-workspace/ext-workspace-v1.xml
64 +
	@echo "hdr  $@"
65 +
	@$(WL_SCANNER) private-code $< $@
66 +
pointer-constraints-unstable-v1-protocol.h: \
67 +
	$(WL_PROTO)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml
68 +
	@echo "hdr  $@"
69 +
	@$(WL_SCANNER) enum-header $< $@
70 +
wlr-layer-shell-unstable-v1-protocol.h: \
71 +
	protocols/wlr-layer-shell-unstable-v1.xml
72 +
	@echo "hdr  $@"
73 +
	@$(WL_SCANNER) enum-header $< $@
74 +
wlr-output-power-management-unstable-v1-protocol.h: \
75 +
	protocols/wlr-output-power-management-unstable-v1.xml
76 +
	@echo "hdr  $@"
77 +
	@$(WL_SCANNER) server-header $< $@
78 +
xdg-shell-protocol.h: \
79 +
	$(WL_PROTO)/stable/xdg-shell/xdg-shell.xml
80 +
	@echo "hdr  $@"
81 +
	@$(WL_SCANNER) server-header $< $@
82 +
83 +
config.h:
84 +
	@test -e $@ || cp config.def.h $@
85 +
86 +
fmt:
87 +
	git ls-files "*.c" "*.h" | xargs clang-format -i
88 +
89 +
install: swm swm.1
90 +
	install -Dm755 swm $(DESTDIR)$(PREFIX)/bin/swm
91 +
	install -Dm644 swm.1 $(DESTDIR)$(MANDIR)/man1/swm.1
92 +
	install -Dm644 swm.desktop \
93 +
		$(DESTDIR)$(DATADIR)/wayland-sessions/swm.desktop
94 +
95 +
uninstall:
96 +
	rm -f $(DESTDIR)$(PREFIX)/bin/swm
97 +
	rm -f $(DESTDIR)$(MANDIR)/man1/swm.1
98 +
	rm -f $(DESTDIR)$(DATADIR)/wayland-sessions/swm.desktop
99 +
100 +
archive:
101 +
	git archive --format=tar.gz --prefix=swm/ -o swm.tar.gz HEAD
102 +
103 +
package: PKGBUILD
104 +
	makepkg --force
105 +
106 +
clean:
107 +
	rm -f swm swm.1 *.o *-protocol.h *-protocol.c
108 +
109 +
.PHONY: all man fmt install uninstall archive package clean
PKGBUILD added +47 -0
1 +
# Maintainer: Alexis Sellier <self@cloudhead.io>
2 +
3 +
pkgname=swm
4 +
pkgver=1.0.0
5 +
pkgrel=1
6 +
pkgdesc='Simple window manager for Wayland'
7 +
arch=('x86_64')
8 +
url='https://github.com/cloudhead/swm'
9 +
license=('GPL-3.0-only')
10 +
options=('!debug')
11 +
depends=(
12 +
  'glibc'
13 +
  'libinput'
14 +
  'libxcb'
15 +
  'libxkbcommon'
16 +
  'wayland'
17 +
  'wlroots0.20'
18 +
  'xcb-util-wm'
19 +
)
20 +
optdepends=('xorg-xwayland: X11 application support')
21 +
makedepends=(
22 +
  'asciidoctor'
23 +
  'clang'
24 +
  'lld'
25 +
  'wayland-protocols'
26 +
)
27 +
source=()
28 +
b2sums=()
29 +
30 +
prepare() {
31 +
  cp "${startdir}"/{Makefile,config.mk,swm.c,swm.h,util.c,util.h} \
32 +
    "${srcdir}/"
33 +
  cp "${startdir}"/{config.def.h,swm.1.adoc,swm.desktop} \
34 +
    "${srcdir}/"
35 +
  install -d "${srcdir}/protocols"
36 +
  cp "${startdir}"/protocols/*.xml "${srcdir}/protocols/"
37 +
}
38 +
39 +
build() {
40 +
  cd "${srcdir}"
41 +
  make
42 +
}
43 +
44 +
package() {
45 +
  cd "${srcdir}"
46 +
  make DESTDIR="${pkgdir}" PREFIX=/usr install
47 +
}
README added +143 -0
1 +
2 +
3 +
4 +
     ███████╗██╗    ██╗███╗   ███╗
5 +
     ██╔════╝██║    ██║████╗ ████║
6 +
     ███████╗██║ █╗ ██║██╔████╔██║
7 +
     ╚════██║██║███╗██║██║╚██╔╝██║
8 +
     ███████║╚███╔███╔╝██║ ╚═╝ ██║
9 +
     ╚══════╝ ╚══╝╚══╝ ╚═╝     ╚═╝
10 +
          simple window manager
11 +
12 +
13 +
swm is a dynamic tiling Wayland compositor based on wlroots. It began as a
14 +
fork of dwl[0], replacing dwl's dwm-style window-management policy with one
15 +
inspired by spectrwm[1].
16 +
17 +
swm is configured in C and aims to remain small enough to understand, modify,
18 +
and extend. It supports native Wayland clients, X11 clients through Xwayland,
19 +
multiple outputs, global workspaces, server-side decorations, layer-shell
20 +
panels and launchers, session locking, idle inhibition, input methods, and
21 +
the standard wlroots output-management, screencopy, data-control, and
22 +
foreign-toplevel protocols.
23 +
24 +
25 +
# REQUIREMENTS
26 +
27 +
Building swm requires:
28 +
29 +
* C compiler that supports C23, and lld
30 +
* GNU or BSD make
31 +
* pkg-config
32 +
* asciidoctor (man page)
33 +
* wlroots 0.20
34 +
* wayland and wayland-protocols
35 +
* libinput and xkbcommon
36 +
* libxcb and xcb-icccm
37 +
38 +
Xwayland is optional at runtime. Without it, swm continues to run but cannot
39 +
manage X11 applications. The default configuration also expects `foot` and
40 +
`wmenu-run`; change these commands in `config.h` if they are not installed.
41 +
42 +
43 +
# BUILD AND CONFIGURATION
44 +
45 +
    make
46 +
47 +
On the first build, `config.def.h` is copied to `config.h`. Subsequent builds,
48 +
including forced builds, preserve `config.h`. Edit that file to configure
49 +
appearance, input, monitor rules, application rules, layouts, commands,
50 +
autostart programs, and bindings, then rebuild swm.
51 +
52 +
There is no runtime configuration file. To return to the distributed defaults,
53 +
remove `config.h` and rebuild; save any local configuration first.
54 +
55 +
The compiler, installation prefix, version, and wlroots include/library flags
56 +
can be overridden in `config.mk` or on the make command line. In particular,
57 +
`WLR_INCS` and `WLR_LIBS` can point at a separately built wlroots tree.
58 +
59 +
60 +
# INSTALL
61 +
62 +
The default prefix is `/usr/local`:
63 +
64 +
    sudo make install
65 +
66 +
This installs the executable, `swm(1)` manual page, and Wayland session entry.
67 +
Use `PREFIX` and `DESTDIR` to change or stage the installation, for example:
68 +
69 +
    make PREFIX=/usr DESTDIR="$pkgdir" install
70 +
71 +
To remove the same installed files:
72 +
73 +
    sudo make uninstall
74 +
75 +
76 +
# RUN
77 +
78 +
Start `swm` from a TTY, select it in a display manager, or run it nested inside
79 +
another Wayland session. `XDG_RUNTIME_DIR` must be set.
80 +
81 +
    swm [-s command]
82 +
83 +
The `-s` flag starts a command through `/bin/sh`; when swm exits it terminates
84 +
that command's process group. Programs listed in the `autostart` array in
85 +
`config.h` are started independently. Set `SWM_NO_AUTOSTART=1` to suppress that
86 +
array, which is useful for testing or a minimal session.
87 +
88 +
swm sets `WAYLAND_DISPLAY`, `DISPLAY` when Xwayland is available, and
89 +
`XDG_CURRENT_DESKTOP=swm` for programs it starts.
90 +
91 +
92 +
# USAGE
93 +
94 +
The defaults use Alt as Mod and provide ten global workspaces. A workspace can
95 +
appear on only one output; selecting one already visible elsewhere swaps the
96 +
two outputs' workspaces. Each workspace keeps its own layout state. Available
97 +
layouts place the master area on the left, top, right, or bottom, plus a max
98 +
layout. Floating and fullscreen are independent of the selected layout.
99 +
100 +
See `man swm` for the full set of key bindings.
101 +
102 +
103 +
# BARS AND STATUS
104 +
105 +
swm has no built-in bar. A layer-shell bar such as Waybar[2] can use the
106 +
`ext/workspaces` module, backed by swm's ext-workspace-v1 implementation.
107 +
Empty inactive workspaces are advertised as hidden; occupied and selected
108 +
workspaces remain visible.
109 +
110 +
On each state change swm also writes line-oriented status records to standard
111 +
output. When `-s` is used, that output is connected to the startup command's
112 +
standard input. The active window title is published atomically to
113 +
`$XDG_RUNTIME_DIR/swm-title`, which can be consumed by a custom bar module.
114 +
115 +
116 +
# TESTING AND DEVELOPMENT
117 +
118 +
Run the complete test suite with:
119 +
120 +
    make test
121 +
122 +
It builds unit tests and protocol clients, runs a headless nested-compositor
123 +
integration suite (including a two-output run), collects LLVM source coverage,
124 +
and requires at least 80% aggregate production-code line coverage. The test
125 +
target therefore also requires LLVM's coverage tools, a POSIX shell, and the
126 +
runtime libraries needed by the normal build.
127 +
128 +
Other development targets are:
129 +
130 +
    make man       # build swm.1 from swm.1.adoc
131 +
    make fmt       # format tracked C sources with clang-format
132 +
    make analyze   # rebuild under scan-build
133 +
    make clean
134 +
135 +
136 +
# LICENSE
137 +
138 +
swm inherits code and design from dwl, dwm, spectrwm, sway, and TinyWL. See
139 +
`LICENSE` and the `LICENSE.*` files for the applicable terms and attribution.
140 +
141 +
[0]: https://codeberg.org/dwl/dwl
142 +
[1]: https://github.com/conformal/spectrwm
143 +
[2]: https://github.com/Alexays/Waybar
config.def.h added +247 -0
1 +
// clang-format off
2 +
3 +
/* Short names used to compose modifiers in the key and button tables. */
4 +
#define MOD   WLR_MODIFIER_LOGO
5 +
#define SHIFT WLR_MODIFIER_SHIFT
6 +
#define CTRL  WLR_MODIFIER_CTRL
7 +
#define ALT   WLR_MODIFIER_ALT
8 +
9 +
/* Create this many numbered workspaces. Valid values are 1 through 32. */
10 +
#define WSCOUNT (10)
11 +
12 +
/* Convert an 0xRRGGBBAA color to the floating-point format used by wlroots. */
13 +
#define RGBA(hex)                                                              \
14 +
    { ((hex >> 24) & 0xFF) / 255.0f,                                           \
15 +
      ((hex >> 16) & 0xFF) / 255.0f,                                           \
16 +
      ((hex >> 8)  & 0xFF) / 255.0f,                                           \
17 +
       (hex        & 0xFF) / 255.0f }
18 +
19 +
/* Focus follows the pointer when enabled. */
20 +
static const bool         sloppyfocus   = true;
21 +
/* Borders are measured in physical layout pixels. */
22 +
static const unsigned int borderwidth   = 1;
23 +
/* Fill uncovered parts of the desktop with this color. */
24 +
static const float        rootcolor[]   = RGBA(0x222222ff);
25 +
/* Draw this color around an unfocused window. */
26 +
static const float        bordercolor[] = RGBA(0x101010ff);
27 +
/* Draw this color around the focused window. */
28 +
static const float        focuscolor[]  = RGBA(0x404040ff);
29 +
/* Draw this color around a window requesting attention. */
30 +
static const float        urgentcolor[] = RGBA(0xff0000ff);
31 +
/* Place this translucent color behind matching layer surfaces. */
32 +
static const float        dimcolor[]    = RGBA(0x00000080);
33 +
34 +
/* This background hides other windows behind transparent fullscreen content,
35 +
 * as required by the XDG shell protocol. Set its alpha to zero to disable it. */
36 +
static const float fullscreen_bg[] = {
37 +
    0.0f, 0.0f, 0.0f, 1.0f
38 +
};
39 +
40 +
/* Report errors only. Use WLR_DEBUG for detailed wlroots diagnostics. */
41 +
static int log_level = WLR_ERROR;
42 +
43 +
/* Window rules are applied in order; later matches override earlier ones.
44 +
 * Use "*" as the application ID to match every window. Workspace and monitor
45 +
 * numbers start at zero, and -1 keeps the window's default placement. */
46 +
static const rule_t rules[] = {
47 +
    /* application ID         title    workspace  floating  monitor  borderless */
48 +
    { "org.telegram.desktop", nullptr,  -1, true,        -1,        true },
49 +
    {},
50 +
};
51 +
52 +
/* Layer rules match panels, launchers, and other desktop surfaces by namespace. */
53 +
static const layer_rule_t layerrules[] = {
54 +
    /* namespace  background dim color */
55 +
    { "launcher", dimcolor },
56 +
};
57 +
58 +
/* Layouts marked true participate in MOD+Space cycling. The max-stack layout
59 +
 * is selected separately with MOD+F. */
60 +
static const layout_t layouts[] = {
61 +
    /* arrangement function  cycle with MOD+Space */
62 +
    { master_left,        true },
63 +
    { master_top,         true },
64 +
    { master_right,       true },
65 +
    { master_bottom,      true },
66 +
    { max_stack,          false },
67 +
};
68 +
69 +
/* Display rules match by output name. A nullptr name is the required fallback
70 +
 * rule. The position (-1, -1) asks wlroots to place the display automatically;
71 +
 * other negative positions can break XWayland applications. */
72 +
static const monitor_rule_t monrules[] = {
73 +
    /* name        scale  rotation/reflection          x    y
74 +
     * Example for a high-density laptop display:
75 +
     { "eDP-1",    2,    WL_OUTPUT_TRANSFORM_NORMAL,   -1,  -1 }, */
76 +
    { nullptr, 1.5, WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
77 +
    /* Keep at least one fallback rule. */
78 +
};
79 +
80 +
/* XKB keyboard layout shared by physical keyboards. */
81 +
static const struct xkb_rule_names xkb_rules = {
82 +
    /* XKB layout name. */
83 +
    .layout  = "us",
84 +
    /* Layout variant; altgr-intl provides international characters via AltGr. */
85 +
    .variant = "altgr-intl",
86 +
    /* Comma-separated XKB options, or nullptr for none. */
87 +
    .options = nullptr,
88 +
};
89 +
90 +
/* Repeat a held key this many times per second. */
91 +
static const int repeat_rate  = 40;
92 +
/* Wait this many milliseconds before a held key starts repeating. */
93 +
static const int repeat_delay = 180;
94 +
95 +
/* Pointer and touchpad behavior. Unsupported options are ignored per device. */
96 +
/* Tap the touchpad to produce a click. */
97 +
static const bool tap_to_click            = true;
98 +
/* Keep a tapped item grabbed while the finger moves. */
99 +
static const bool tap_and_drag            = true;
100 +
/* Keep dragging after the finger is briefly lifted and placed down again. */
101 +
static const bool drag_lock               = true;
102 +
/* Reverse the usual scrolling direction when enabled. */
103 +
static const bool natural_scrolling       = false;
104 +
/* Ignore touchpad input while typing. */
105 +
static const bool disable_while_typing    = true;
106 +
/* Swap the primary and secondary pointer buttons when enabled. */
107 +
static const bool left_handed             = false;
108 +
/* Press the left and right buttons together to produce a middle click. */
109 +
static const bool middle_button_emulation = false;
110 +
111 +
/* Scroll with two fingers. Other choices are NO_SCROLL, EDGE, and
112 +
 * ON_BUTTON_DOWN. */
113 +
static const enum libinput_config_scroll_method scroll_method =
114 +
    LIBINPUT_CONFIG_SCROLL_2FG;
115 +
116 +
/* Divide the bottom of a clickpad into physical button areas. Other choices
117 +
 * are NONE and CLICKFINGER. */
118 +
static const enum libinput_config_click_method click_method =
119 +
    LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS;
120 +
121 +
/* Keep pointer events enabled. They can instead be disabled always or only
122 +
 * while an external mouse is connected. */
123 +
static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED;
124 +
125 +
/* Use adaptive pointer acceleration. FLAT disables the acceleration curve. */
126 +
static const enum libinput_config_accel_profile accel_profile =
127 +
    LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE;
128 +
/* Pointer speed ranges from -1.0 (slowest) to 1.0 (fastest); 0.0 is neutral. */
129 +
static const double accel_speed = 0.0;
130 +
131 +
/* Map one-, two-, and three-finger taps to left, right, and middle clicks. */
132 +
static const enum libinput_config_tap_button_map button_map =
133 +
    LIBINPUT_CONFIG_TAP_MAP_LRM;
134 +
135 +
/* Commands are argument vectors and must end with nullptr. */
136 +
/* Terminal opened by MOD+Shift+Return. */
137 +
static const char *termcmd[] = { "foot", nullptr };
138 +
/* Application launcher opened by MOD+P. */
139 +
static const char *menucmd[] = { "fuzzel", nullptr };
140 +
/* Session locker started by MOD+X. */
141 +
static const char *lockcmd[] = { "swaylock", "-f", "-c", "000000", nullptr };
142 +
143 +
/* Run these commands once at startup. A nullptr separates commands, and a
144 +
 * second nullptr ends the list. Environment variables are expanded without
145 +
 * shell word splitting. */
146 +
static const char *const autostart[] = {
147 +
    "dbus-update-activation-environment", "--systemd",     "WAYLAND_DISPLAY", "XDG_CURRENT_DESKTOP", nullptr,
148 +
    "foot",                               "--login-shell",                                           nullptr,
149 +
    "waybar",                                                                                        nullptr,
150 +
    "swaybg",                             "-m", "tile", "-i", "$HOME/images/wallpaper.png",          nullptr,
151 +
    "flameshot",                                                                                     nullptr,
152 +
    "sh",                                 "-c", "wl-paste --type text --watch cliphist store",       nullptr,
153 +
    "sh",                                 "-c", "wl-paste --type image --watch cliphist store",      nullptr,
154 +
    nullptr /* End the command list. */
155 +
};
156 +
157 +
static const key_t keys[] = {
158 +
    /* Each row maps a modifier and key to an action and its optional argument. */
159 +
    /* modifier        key                  function           argument */
160 +
    { MOD,             XKB_KEY_p,           spawn,             { .v = menucmd } },
161 +
    { MOD | SHIFT,     XKB_KEY_Return,      spawn,             { .v = termcmd } },
162 +
    { MOD,             XKB_KEY_j,           focus_stack,       { .i = +1 } },
163 +
    { MOD,             XKB_KEY_Tab,         view,              { .i = -1 } },
164 +
    { MOD,             XKB_KEY_k,           focus_stack,       { .i = -1 } },
165 +
    { MOD,             XKB_KEY_h,           stack_config,      { .i = MASTER_SHRINK } },
166 +
    { MOD,             XKB_KEY_l,           stack_config,      { .i = MASTER_GROW } },
167 +
    { MOD,             XKB_KEY_comma,       stack_config,      { .i = MASTER_ADD } },
168 +
    { MOD,             XKB_KEY_period,      stack_config,      { .i = MASTER_DELETE } },
169 +
    { MOD | SHIFT,     XKB_KEY_less,        stack_config,      { .i = STACK_INCREMENT } },
170 +
    { MOD | SHIFT,     XKB_KEY_greater,     stack_config,      { .i = STACK_DECREMENT } },
171 +
    { MOD | SHIFT,     XKB_KEY_space,       stack_config,      { .i = STACK_RESET } },
172 +
    { MOD | SHIFT,     XKB_KEY_bar,         stack_config,      { .i = LAYOUT_FLIP } },
173 +
    { MOD | SHIFT,     XKB_KEY_j,           swap_client,       { .i = +1 } },
174 +
    { MOD | SHIFT,     XKB_KEY_k,           swap_client,       { .i = -1 } },
175 +
    { MOD,             XKB_KEY_Return,      zoom,              {} },
176 +
    { MOD,             XKB_KEY_m,           focus_main,        {} },
177 +
    { MOD,             XKB_KEY_u,           focus_urgent,      {} },
178 +
    { MOD,             XKB_KEY_r,           raise_client,      {} },
179 +
    { MOD,             XKB_KEY_a,           view,              { .i = -1 } },
180 +
    { MOD,             XKB_KEY_space,       cycle_layout,      {} },
181 +
    { MOD,             XKB_KEY_t,           toggle_floating,   {} },
182 +
    { MOD,             XKB_KEY_f,           toggle_max_stack,  {} },
183 +
    { MOD | SHIFT,     XKB_KEY_f,           toggle_fullscreen, {} },
184 +
    { MOD,             XKB_KEY_x,           spawn,             { .v = lockcmd } },
185 +
    { MOD | SHIFT,     XKB_KEY_C,           kill_client,       {} },
186 +
    /* Left and Right skip empty workspaces; Up and Down include them. Holding
187 +
     * Shift moves the focused window while cycling. */
188 +
    { MOD,             XKB_KEY_Right,       cycle_workspace,   { .i = WORKSPACE_NEXT } },
189 +
    { MOD,             XKB_KEY_Left,        cycle_workspace,   { .i = WORKSPACE_PREVIOUS } },
190 +
    { MOD,             XKB_KEY_Up,          cycle_workspace,   { .i = WORKSPACE_NEXT_ALL } },
191 +
    { MOD,             XKB_KEY_Down,        cycle_workspace,   { .i = WORKSPACE_PREVIOUS_ALL } },
192 +
    { MOD | SHIFT,     XKB_KEY_Up,          cycle_workspace,   { .i = WORKSPACE_NEXT_MOVE } },
193 +
    { MOD | SHIFT,     XKB_KEY_Down,        cycle_workspace,   { .i = WORKSPACE_PREVIOUS_MOVE } },
194 +
    /* Shift selects a neighboring display; Control moves the focused window. */
195 +
    { MOD | SHIFT,     XKB_KEY_Left,        focus_monitor,     { .i = WLR_DIRECTION_LEFT } },
196 +
    { MOD | SHIFT,     XKB_KEY_Right,       focus_monitor,     { .i = WLR_DIRECTION_RIGHT } },
197 +
    { MOD | CTRL,      XKB_KEY_Left,        tag_monitor,       { .i = WLR_DIRECTION_LEFT } },
198 +
    { MOD | CTRL,      XKB_KEY_Right,       tag_monitor,       { .i = WLR_DIRECTION_RIGHT } },
199 +
    { MOD,             XKB_KEY_1,           view,              { .i = 0 } },
200 +
    { MOD | SHIFT,     XKB_KEY_exclam,      tag,               { .i = 0 } },
201 +
    { MOD,             XKB_KEY_2,           view,              { .i = 1 } },
202 +
    { MOD | SHIFT,     XKB_KEY_at,          tag,               { .i = 1 } },
203 +
    { MOD,             XKB_KEY_3,           view,              { .i = 2 } },
204 +
    { MOD | SHIFT,     XKB_KEY_numbersign,  tag,               { .i = 2 } },
205 +
    { MOD,             XKB_KEY_4,           view,              { .i = 3 } },
206 +
    { MOD | SHIFT,     XKB_KEY_dollar,      tag,               { .i = 3 } },
207 +
    { MOD,             XKB_KEY_5,           view,              { .i = 4 } },
208 +
    { MOD | SHIFT,     XKB_KEY_percent,     tag,               { .i = 4 } },
209 +
    { MOD,             XKB_KEY_6,           view,              { .i = 5 } },
210 +
    { MOD | SHIFT,     XKB_KEY_asciicircum, tag,               { .i = 5 } },
211 +
    { MOD,             XKB_KEY_7,           view,              { .i = 6 } },
212 +
    { MOD | SHIFT,     XKB_KEY_ampersand,   tag,               { .i = 6 } },
213 +
    { MOD,             XKB_KEY_8,           view,              { .i = 7 } },
214 +
    { MOD | SHIFT,     XKB_KEY_asterisk,    tag,               { .i = 7 } },
215 +
    { MOD,             XKB_KEY_9,           view,              { .i = 8 } },
216 +
    { MOD | SHIFT,     XKB_KEY_parenleft,   tag,               { .i = 8 } },
217 +
    { MOD,             XKB_KEY_0,           view,              { .i = 9 } },
218 +
    { MOD | SHIFT,     XKB_KEY_parenright,  tag,               { .i = 9 } },
219 +
    { MOD | SHIFT,     XKB_KEY_q,           quit,              {} },
220 +
221 +
    /* Preserve the traditional Ctrl+Alt+Backspace shortcut for quitting. */
222 +
    { CTRL | ALT,      XKB_KEY_Terminate_Server, quit,      {} },
223 +
    /* Ctrl+Alt+Fn switches to another virtual terminal. */
224 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_1,  change_vt, { .u = 1 } },
225 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_2,  change_vt, { .u = 2 } },
226 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_3,  change_vt, { .u = 3 } },
227 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_4,  change_vt, { .u = 4 } },
228 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_5,  change_vt, { .u = 5 } },
229 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_6,  change_vt, { .u = 6 } },
230 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_7,  change_vt, { .u = 7 } },
231 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_8,  change_vt, { .u = 8 } },
232 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_9,  change_vt, { .u = 9 } },
233 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_10, change_vt, { .u = 10 } },
234 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_11, change_vt, { .u = 11 } },
235 +
    { CTRL | ALT,      XKB_KEY_XF86Switch_VT_12, change_vt, { .u = 12 } },
236 +
};
237 +
238 +
static const button_t buttons[] = {
239 +
    /* Hold MOD while clicking a window to move, toggle, or resize it. */
240 +
    /* Drag with the primary button to move the window. */
241 +
    { MOD, BTN_LEFT, move_resize, { .u = CURSOR_MOVE } },
242 +
    /* Click the middle button to toggle floating. */
243 +
    { MOD, BTN_MIDDLE, toggle_floating, {} },
244 +
    /* Drag with the secondary button to resize from the nearest corner. */
245 +
    { MOD, BTN_RIGHT, move_resize, { .u = CURSOR_RESIZE } },
246 +
};
247 +
// clang-format on
config.mk added +8 -0
1 +
VERSION    = 1.0.0
2 +
PKG_CONFIG = pkg-config
3 +
CC         = clang
4 +
PREFIX     = /usr/local
5 +
MANDIR     = $(PREFIX)/share/man
6 +
DATADIR    = $(PREFIX)/share
7 +
WLR_INCS   = `$(PKG_CONFIG) --cflags wlroots-0.20`
8 +
WLR_LIBS   = `$(PKG_CONFIG) --libs wlroots-0.20`
protocols/wlr-layer-shell-unstable-v1.xml added +390 -0
1 +
<?xml version="1.0" encoding="UTF-8"?>
2 +
<protocol name="wlr_layer_shell_unstable_v1">
3 +
  <copyright>
4 +
    Copyright © 2017 Drew DeVault
5 +
6 +
    Permission to use, copy, modify, distribute, and sell this
7 +
    software and its documentation for any purpose is hereby granted
8 +
    without fee, provided that the above copyright notice appear in
9 +
    all copies and that both that copyright notice and this permission
10 +
    notice appear in supporting documentation, and that the name of
11 +
    the copyright holders not be used in advertising or publicity
12 +
    pertaining to distribution of the software without specific,
13 +
    written prior permission.  The copyright holders make no
14 +
    representations about the suitability of this software for any
15 +
    purpose.  It is provided "as is" without express or implied
16 +
    warranty.
17 +
18 +
    THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
19 +
    SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 +
    FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 +
    SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
22 +
    WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
23 +
    AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
24 +
    ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
25 +
    THIS SOFTWARE.
26 +
  </copyright>
27 +
28 +
  <interface name="zwlr_layer_shell_v1" version="4">
29 +
    <description summary="create surfaces that are layers of the desktop">
30 +
      Clients can use this interface to assign the surface_layer role to
31 +
      wl_surfaces. Such surfaces are assigned to a "layer" of the output and
32 +
      rendered with a defined z-depth respective to each other. They may also be
33 +
      anchored to the edges and corners of a screen and specify input handling
34 +
      semantics. This interface should be suitable for the implementation of
35 +
      many desktop shell components, and a broad number of other applications
36 +
      that interact with the desktop.
37 +
    </description>
38 +
39 +
    <request name="get_layer_surface">
40 +
      <description summary="create a layer_surface from a surface">
41 +
        Create a layer surface for an existing surface. This assigns the role of
42 +
        layer_surface, or raises a protocol error if another role is already
43 +
        assigned.
44 +
45 +
        Creating a layer surface from a wl_surface which has a buffer attached
46 +
        or committed is a client error, and any attempts by a client to attach
47 +
        or manipulate a buffer prior to the first layer_surface.configure call
48 +
        must also be treated as errors.
49 +
50 +
        After creating a layer_surface object and setting it up, the client
51 +
        must perform an initial commit without any buffer attached.
52 +
        The compositor will reply with a layer_surface.configure event.
53 +
        The client must acknowledge it and is then allowed to attach a buffer
54 +
        to map the surface.
55 +
56 +
        You may pass NULL for output to allow the compositor to decide which
57 +
        output to use. Generally this will be the one that the user most
58 +
        recently interacted with.
59 +
60 +
        Clients can specify a namespace that defines the purpose of the layer
61 +
        surface.
62 +
      </description>
63 +
      <arg name="id" type="new_id" interface="zwlr_layer_surface_v1"/>
64 +
      <arg name="surface" type="object" interface="wl_surface"/>
65 +
      <arg name="output" type="object" interface="wl_output" allow-null="true"/>
66 +
      <arg name="layer" type="uint" enum="layer" summary="layer to add this surface to"/>
67 +
      <arg name="namespace" type="string" summary="namespace for the layer surface"/>
68 +
    </request>
69 +
70 +
    <enum name="error">
71 +
      <entry name="role" value="0" summary="wl_surface has another role"/>
72 +
      <entry name="invalid_layer" value="1" summary="layer value is invalid"/>
73 +
      <entry name="already_constructed" value="2" summary="wl_surface has a buffer attached or committed"/>
74 +
    </enum>
75 +
76 +
    <enum name="layer">
77 +
      <description summary="available layers for surfaces">
78 +
        These values indicate which layers a surface can be rendered in. They
79 +
        are ordered by z depth, bottom-most first. Traditional shell surfaces
80 +
        will typically be rendered between the bottom and top layers.
81 +
        Fullscreen shell surfaces are typically rendered at the top layer.
82 +
        Multiple surfaces can share a single layer, and ordering within a
83 +
        single layer is undefined.
84 +
      </description>
85 +
86 +
      <entry name="background" value="0"/>
87 +
      <entry name="bottom" value="1"/>
88 +
      <entry name="top" value="2"/>
89 +
      <entry name="overlay" value="3"/>
90 +
    </enum>
91 +
92 +
    <!-- Version 3 additions -->
93 +
94 +
    <request name="destroy" type="destructor" since="3">
95 +
      <description summary="destroy the layer_shell object">
96 +
        This request indicates that the client will not use the layer_shell
97 +
        object any more. Objects that have been created through this instance
98 +
        are not affected.
99 +
      </description>
100 +
    </request>
101 +
  </interface>
102 +
103 +
  <interface name="zwlr_layer_surface_v1" version="4">
104 +
    <description summary="layer metadata interface">
105 +
      An interface that may be implemented by a wl_surface, for surfaces that
106 +
      are designed to be rendered as a layer of a stacked desktop-like
107 +
      environment.
108 +
109 +
      Layer surface state (layer, size, anchor, exclusive zone,
110 +
      margin, interactivity) is double-buffered, and will be applied at the
111 +
      time wl_surface.commit of the corresponding wl_surface is called.
112 +
113 +
      Attaching a null buffer to a layer surface unmaps it.
114 +
115 +
      Unmapping a layer_surface means that the surface cannot be shown by the
116 +
      compositor until it is explicitly mapped again. The layer_surface
117 +
      returns to the state it had right after layer_shell.get_layer_surface.
118 +
      The client can re-map the surface by performing a commit without any
119 +
      buffer attached, waiting for a configure event and handling it as usual.
120 +
    </description>
121 +
122 +
    <request name="set_size">
123 +
      <description summary="sets the size of the surface">
124 +
        Sets the size of the surface in surface-local coordinates. The
125 +
        compositor will display the surface centered with respect to its
126 +
        anchors.
127 +
128 +
        If you pass 0 for either value, the compositor will assign it and
129 +
        inform you of the assignment in the configure event. You must set your
130 +
        anchor to opposite edges in the dimensions you omit; not doing so is a
131 +
        protocol error. Both values are 0 by default.
132 +
133 +
        Size is double-buffered, see wl_surface.commit.
134 +
      </description>
135 +
      <arg name="width" type="uint"/>
136 +
      <arg name="height" type="uint"/>
137 +
    </request>
138 +
139 +
    <request name="set_anchor">
140 +
      <description summary="configures the anchor point of the surface">
141 +
        Requests that the compositor anchor the surface to the specified edges
142 +
        and corners. If two orthogonal edges are specified (e.g. 'top' and
143 +
        'left'), then the anchor point will be the intersection of the edges
144 +
        (e.g. the top left corner of the output); otherwise the anchor point
145 +
        will be centered on that edge, or in the center if none is specified.
146 +
147 +
        Anchor is double-buffered, see wl_surface.commit.
148 +
      </description>
149 +
      <arg name="anchor" type="uint" enum="anchor"/>
150 +
    </request>
151 +
152 +
    <request name="set_exclusive_zone">
153 +
      <description summary="configures the exclusive geometry of this surface">
154 +
        Requests that the compositor avoids occluding an area with other
155 +
        surfaces. The compositor's use of this information is
156 +
        implementation-dependent - do not assume that this region will not
157 +
        actually be occluded.
158 +
159 +
        A positive value is only meaningful if the surface is anchored to one
160 +
        edge or an edge and both perpendicular edges. If the surface is not
161 +
        anchored, anchored to only two perpendicular edges (a corner), anchored
162 +
        to only two parallel edges or anchored to all edges, a positive value
163 +
        will be treated the same as zero.
164 +
165 +
        A positive zone is the distance from the edge in surface-local
166 +
        coordinates to consider exclusive.
167 +
168 +
        Surfaces that do not wish to have an exclusive zone may instead specify
169 +
        how they should interact with surfaces that do. If set to zero, the
170 +
        surface indicates that it would like to be moved to avoid occluding
171 +
        surfaces with a positive exclusive zone. If set to -1, the surface
172 +
        indicates that it would not like to be moved to accommodate for other
173 +
        surfaces, and the compositor should extend it all the way to the edges
174 +
        it is anchored to.
175 +
176 +
        For example, a panel might set its exclusive zone to 10, so that
177 +
        maximized shell surfaces are not shown on top of it. A notification
178 +
        might set its exclusive zone to 0, so that it is moved to avoid
179 +
        occluding the panel, but shell surfaces are shown underneath it. A
180 +
        wallpaper or lock screen might set their exclusive zone to -1, so that
181 +
        they stretch below or over the panel.
182 +
183 +
        The default value is 0.
184 +
185 +
        Exclusive zone is double-buffered, see wl_surface.commit.
186 +
      </description>
187 +
      <arg name="zone" type="int"/>
188 +
    </request>
189 +
190 +
    <request name="set_margin">
191 +
      <description summary="sets a margin from the anchor point">
192 +
        Requests that the surface be placed some distance away from the anchor
193 +
        point on the output, in surface-local coordinates. Setting this value
194 +
        for edges you are not anchored to has no effect.
195 +
196 +
        The exclusive zone includes the margin.
197 +
198 +
        Margin is double-buffered, see wl_surface.commit.
199 +
      </description>
200 +
      <arg name="top" type="int"/>
201 +
      <arg name="right" type="int"/>
202 +
      <arg name="bottom" type="int"/>
203 +
      <arg name="left" type="int"/>
204 +
    </request>
205 +
206 +
    <enum name="keyboard_interactivity">
207 +
      <description summary="types of keyboard interaction possible for a layer shell surface">
208 +
        Types of keyboard interaction possible for layer shell surfaces. The
209 +
        rationale for this is twofold: (1) some applications are not interested
210 +
        in keyboard events and not allowing them to be focused can improve the
211 +
        desktop experience; (2) some applications will want to take exclusive
212 +
        keyboard focus.
213 +
      </description>
214 +
215 +
      <entry name="none" value="0">
216 +
        <description summary="no keyboard focus is possible">
217 +
          This value indicates that this surface is not interested in keyboard
218 +
          events and the compositor should never assign it the keyboard focus.
219 +
220 +
          This is the default value, set for newly created layer shell surfaces.
221 +
222 +
          This is useful for e.g. desktop widgets that display information or
223 +
          only have interaction with non-keyboard input devices.
224 +
        </description>
225 +
      </entry>
226 +
      <entry name="exclusive" value="1">
227 +
        <description summary="request exclusive keyboard focus">
228 +
          Request exclusive keyboard focus if this surface is above the shell surface layer.
229 +
230 +
          For the top and overlay layers, the seat will always give
231 +
          exclusive keyboard focus to the top-most layer which has keyboard
232 +
          interactivity set to exclusive. If this layer contains multiple
233 +
          surfaces with keyboard interactivity set to exclusive, the compositor
234 +
          determines the one receiving keyboard events in an implementation-
235 +
          defined manner. In this case, no guarantee is made when this surface
236 +
          will receive keyboard focus (if ever).
237 +
238 +
          For the bottom and background layers, the compositor is allowed to use
239 +
          normal focus semantics.
240 +
241 +
          This setting is mainly intended for applications that need to ensure
242 +
          they receive all keyboard events, such as a lock screen or a password
243 +
          prompt.
244 +
        </description>
245 +
      </entry>
246 +
      <entry name="on_demand" value="2" since="4">
247 +
        <description summary="request regular keyboard focus semantics">
248 +
          This requests the compositor to allow this surface to be focused and
249 +
          unfocused by the user in an implementation-defined manner. The user
250 +
          should be able to unfocus this surface even regardless of the layer
251 +
          it is on.
252 +
253 +
          Typically, the compositor will want to use its normal mechanism to
254 +
          manage keyboard focus between layer shell surfaces with this setting
255 +
          and regular toplevels on the desktop layer (e.g. click to focus).
256 +
          Nevertheless, it is possible for a compositor to require a special
257 +
          interaction to focus or unfocus layer shell surfaces (e.g. requiring
258 +
          a click even if focus follows the mouse normally, or providing a
259 +
          keybinding to switch focus between layers).
260 +
261 +
          This setting is mainly intended for desktop shell components (e.g.
262 +
          panels) that allow keyboard interaction. Using this option can allow
263 +
          implementing a desktop shell that can be fully usable without the
264 +
          mouse.
265 +
        </description>
266 +
      </entry>
267 +
    </enum>
268 +
269 +
    <request name="set_keyboard_interactivity">
270 +
      <description summary="requests keyboard events">
271 +
        Set how keyboard events are delivered to this surface. By default,
272 +
        layer shell surfaces do not receive keyboard events; this request can
273 +
        be used to change this.
274 +
275 +
        This setting is inherited by child surfaces set by the get_popup
276 +
        request.
277 +
278 +
        Layer surfaces receive pointer, touch, and tablet events normally. If
279 +
        you do not want to receive them, set the input region on your surface
280 +
        to an empty region.
281 +
282 +
        Keyboard interactivity is double-buffered, see wl_surface.commit.
283 +
      </description>
284 +
      <arg name="keyboard_interactivity" type="uint" enum="keyboard_interactivity"/>
285 +
    </request>
286 +
287 +
    <request name="get_popup">
288 +
      <description summary="assign this layer_surface as an xdg_popup parent">
289 +
        This assigns an xdg_popup's parent to this layer_surface.  This popup
290 +
        should have been created via xdg_surface::get_popup with the parent set
291 +
        to NULL, and this request must be invoked before committing the popup's
292 +
        initial state.
293 +
294 +
        See the documentation of xdg_popup for more details about what an
295 +
        xdg_popup is and how it is used.
296 +
      </description>
297 +
      <arg name="popup" type="object" interface="xdg_popup"/>
298 +
    </request>
299 +
300 +
    <request name="ack_configure">
301 +
      <description summary="ack a configure event">
302 +
        When a configure event is received, if a client commits the
303 +
        surface in response to the configure event, then the client
304 +
        must make an ack_configure request sometime before the commit
305 +
        request, passing along the serial of the configure event.
306 +
307 +
        If the client receives multiple configure events before it
308 +
        can respond to one, it only has to ack the last configure event.
309 +
310 +
        A client is not required to commit immediately after sending
311 +
        an ack_configure request - it may even ack_configure several times
312 +
        before its next surface commit.
313 +
314 +
        A client may send multiple ack_configure requests before committing, but
315 +
        only the last request sent before a commit indicates which configure
316 +
        event the client really is responding to.
317 +
      </description>
318 +
      <arg name="serial" type="uint" summary="the serial from the configure event"/>
319 +
    </request>
320 +
321 +
    <request name="destroy" type="destructor">
322 +
      <description summary="destroy the layer_surface">
323 +
        This request destroys the layer surface.
324 +
      </description>
325 +
    </request>
326 +
327 +
    <event name="configure">
328 +
      <description summary="suggest a surface change">
329 +
        The configure event asks the client to resize its surface.
330 +
331 +
        Clients should arrange their surface for the new states, and then send
332 +
        an ack_configure request with the serial sent in this configure event at
333 +
        some point before committing the new surface.
334 +
335 +
        The client is free to dismiss all but the last configure event it
336 +
        received.
337 +
338 +
        The width and height arguments specify the size of the window in
339 +
        surface-local coordinates.
340 +
341 +
        The size is a hint, in the sense that the client is free to ignore it if
342 +
        it doesn't resize, pick a smaller size (to satisfy aspect ratio or
343 +
        resize in steps of NxM pixels). If the client picks a smaller size and
344 +
        is anchored to two opposite anchors (e.g. 'top' and 'bottom'), the
345 +
        surface will be centered on this axis.
346 +
347 +
        If the width or height arguments are zero, it means the client should
348 +
        decide its own window dimension.
349 +
      </description>
350 +
      <arg name="serial" type="uint"/>
351 +
      <arg name="width" type="uint"/>
352 +
      <arg name="height" type="uint"/>
353 +
    </event>
354 +
355 +
    <event name="closed">
356 +
      <description summary="surface should be closed">
357 +
        The closed event is sent by the compositor when the surface will no
358 +
        longer be shown. The output may have been destroyed or the user may
359 +
        have asked for it to be removed. Further changes to the surface will be
360 +
        ignored. The client should destroy the resource after receiving this
361 +
        event, and create a new surface if they so choose.
362 +
      </description>
363 +
    </event>
364 +
365 +
    <enum name="error">
366 +
      <entry name="invalid_surface_state" value="0" summary="provided surface state is invalid"/>
367 +
      <entry name="invalid_size" value="1" summary="size is invalid"/>
368 +
      <entry name="invalid_anchor" value="2" summary="anchor bitfield is invalid"/>
369 +
      <entry name="invalid_keyboard_interactivity" value="3" summary="keyboard interactivity is invalid"/>
370 +
    </enum>
371 +
372 +
    <enum name="anchor" bitfield="true">
373 +
      <entry name="top" value="1" summary="the top edge of the anchor rectangle"/>
374 +
      <entry name="bottom" value="2" summary="the bottom edge of the anchor rectangle"/>
375 +
      <entry name="left" value="4" summary="the left edge of the anchor rectangle"/>
376 +
      <entry name="right" value="8" summary="the right edge of the anchor rectangle"/>
377 +
    </enum>
378 +
379 +
    <!-- Version 2 additions -->
380 +
381 +
    <request name="set_layer" since="2">
382 +
      <description summary="change the layer of the surface">
383 +
        Change the layer that the surface is rendered on.
384 +
385 +
        Layer is double-buffered, see wl_surface.commit.
386 +
      </description>
387 +
      <arg name="layer" type="uint" enum="zwlr_layer_shell_v1.layer" summary="layer to move this surface to"/>
388 +
    </request>
389 +
  </interface>
390 +
</protocol>
protocols/wlr-output-power-management-unstable-v1.xml added +128 -0
1 +
<?xml version="1.0" encoding="UTF-8"?>
2 +
<protocol name="wlr_output_power_management_unstable_v1">
3 +
  <copyright>
4 +
    Copyright © 2019 Purism SPC
5 +
6 +
    Permission is hereby granted, free of charge, to any person obtaining a
7 +
    copy of this software and associated documentation files (the "Software"),
8 +
    to deal in the Software without restriction, including without limitation
9 +
    the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 +
    and/or sell copies of the Software, and to permit persons to whom the
11 +
    Software is furnished to do so, subject to the following conditions:
12 +
13 +
    The above copyright notice and this permission notice (including the next
14 +
    paragraph) shall be included in all copies or substantial portions of the
15 +
    Software.
16 +
17 +
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 +
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 +
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
20 +
    THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 +
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 +
    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 +
    DEALINGS IN THE SOFTWARE.
24 +
  </copyright>
25 +
26 +
  <description summary="Control power management modes of outputs">
27 +
    This protocol allows clients to control power management modes
28 +
    of outputs that are currently part of the compositor space. The
29 +
    intent is to allow special clients like desktop shells to power
30 +
    down outputs when the system is idle.
31 +
32 +
    To modify outputs not currently part of the compositor space see
33 +
    wlr-output-management.
34 +
35 +
    Warning! The protocol described in this file is experimental and
36 +
    backward incompatible changes may be made. Backward compatible changes
37 +
    may be added together with the corresponding interface version bump.
38 +
    Backward incompatible changes are done by bumping the version number in
39 +
    the protocol and interface names and resetting the interface version.
40 +
    Once the protocol is to be declared stable, the 'z' prefix and the
41 +
    version number in the protocol and interface names are removed and the
42 +
    interface version number is reset.
43 +
  </description>
44 +
45 +
  <interface name="zwlr_output_power_manager_v1" version="1">
46 +
    <description summary="manager to create per-output power management">
47 +
      This interface is a manager that allows creating per-output power
48 +
      management mode controls.
49 +
    </description>
50 +
51 +
    <request name="get_output_power">
52 +
      <description summary="get a power management for an output">
53 +
        Create a output power management mode control that can be used to
54 +
        adjust the power management mode for a given output.
55 +
      </description>
56 +
      <arg name="id" type="new_id" interface="zwlr_output_power_v1"/>
57 +
      <arg name="output" type="object" interface="wl_output"/>
58 +
    </request>
59 +
60 +
    <request name="destroy" type="destructor">
61 +
      <description summary="destroy the manager">
62 +
        All objects created by the manager will still remain valid, until their
63 +
        appropriate destroy request has been called.
64 +
      </description>
65 +
    </request>
66 +
  </interface>
67 +
68 +
  <interface name="zwlr_output_power_v1" version="1">
69 +
    <description summary="adjust power management mode for an output">
70 +
      This object offers requests to set the power management mode of
71 +
      an output.
72 +
    </description>
73 +
74 +
    <enum name="mode">
75 +
      <entry name="off" value="0"
76 +
             summary="Output is turned off."/>
77 +
      <entry name="on" value="1"
78 +
             summary="Output is turned on, no power saving"/>
79 +
    </enum>
80 +
81 +
    <enum name="error">
82 +
      <entry name="invalid_mode" value="1" summary="inexistent power save mode"/>
83 +
    </enum>
84 +
85 +
    <request name="set_mode">
86 +
      <description summary="Set an outputs power save mode">
87 +
        Set an output's power save mode to the given mode. The mode change
88 +
        is effective immediately. If the output does not support the given
89 +
        mode a failed event is sent.
90 +
      </description>
91 +
      <arg name="mode" type="uint" enum="mode" summary="the power save mode to set"/>
92 +
    </request>
93 +
94 +
    <event name="mode">
95 +
      <description summary="Report a power management mode change">
96 +
        Report the power management mode change of an output.
97 +
98 +
        The mode event is sent after an output changed its power
99 +
        management mode. The reason can be a client using set_mode or the
100 +
        compositor deciding to change an output's mode.
101 +
        This event is also sent immediately when the object is created
102 +
        so the client is informed about the current power management mode.
103 +
      </description>
104 +
      <arg name="mode" type="uint" enum="mode"
105 +
           summary="the output's new power management mode"/>
106 +
    </event>
107 +
108 +
    <event name="failed">
109 +
      <description summary="object no longer valid">
110 +
        This event indicates that the output power management mode control
111 +
        is no longer valid. This can happen for a number of reasons,
112 +
        including:
113 +
        - The output doesn't support power management
114 +
        - Another client already has exclusive power management mode control
115 +
          for this output
116 +
        - The output disappeared
117 +
118 +
        Upon receiving this event, the client should destroy this object.
119 +
      </description>
120 +
    </event>
121 +
122 +
    <request name="destroy" type="destructor">
123 +
      <description summary="destroy this power management">
124 +
        Destroys the output power management mode control object.
125 +
      </description>
126 +
    </request>
127 +
  </interface>
128 +
</protocol>
swm.1.adoc added +114 -0
1 +
= swm(1)
2 +
:doctype: manpage
3 +
:manmanual: SWM Manual
4 +
:mansource: swm 1.0.0
5 +
6 +
== NAME
7 +
8 +
swm - simple window manager for Wayland
9 +
10 +
== SYNOPSIS
11 +
12 +
*swm* [*-v*] [*-d*] [*-s* _command_]
13 +
14 +
== DESCRIPTION
15 +
16 +
*swm* is a simple Wayland window manager based on wlroots and forked from
17 +
*dwl*.
18 +
19 +
*swm* manages 10 global workspaces. A workspace is shown on at most one output;
20 +
switching to a workspace that is visible on another output swaps the workspaces
21 +
of the two outputs. Five layouts are available per workspace: master-left,
22 +
master-top, master-right, master-bottom, and max.
23 +
24 +
*swm* has no built-in bar and no runtime configuration file. Bars speaking
25 +
ext-workspace-v1 and wlr-layer-shell, such as *waybar*(5), work out of the box.
26 +
Users are encouraged to customize *swm* by editing the sources, in particular
27 +
_config.h_.
28 +
29 +
== OPTIONS
30 +
31 +
*-v*::
32 +
Write the name and version to standard error and exit unsuccessfully.
33 +
34 +
*-d*::
35 +
Enable full wlroots logging, including debug information.
36 +
37 +
*-s* _command_::
38 +
Start a shell process running _command_. On exit, send `SIGTERM` to the child
39 +
process group and wait for it to stop.
40 +
41 +
== KEY BINDINGS
42 +
43 +
The default key bindings (Mod = Alt) are as follows:
44 +
45 +
=== Workspaces
46 +
47 +
[cols="1,3", options="header"]
48 +
|===
49 +
|Binding |Action
50 +
51 +
|*Mod-[1-9,0]* |Switch to workspace 1-10.
52 +
|*Mod-Shift-[1-9,0]* |Send window to workspace 1-10.
53 +
|*Mod-a, Mod-Tab* |Switch to the previously shown workspace.
54 +
|*Mod-Right/Left* |Switch to the next/previous non-empty workspace.
55 +
|*Mod-Up/Down* |Switch to the next/previous workspace, including empty ones.
56 +
|*Mod-Shift-Up/Down* |As above, carrying the focused window along.
57 +
|===
58 +
59 +
=== Windows
60 +
61 +
[cols="1,3", options="header"]
62 +
|===
63 +
|Binding |Action
64 +
65 +
|*Mod-j/k* |Focus next/previous window.
66 +
|*Mod-Shift-j/k* |Swap window with the next/previous window.
67 +
|*Mod-Return* |Swap the focused window with the main window.
68 +
|*Mod-m* |Focus the main window; again to return.
69 +
|*Mod-u* |Focus the most recently urgent window.
70 +
|*Mod-r* |Raise the focused window.
71 +
|*Mod-t* |Toggle floating.
72 +
|*Mod-Shift-f* |Toggle fullscreen.
73 +
|*Mod-Shift-c* |Close the focused window.
74 +
|*Mod-Shift-Left/Right* |Focus the previous/next output.
75 +
|*Mod-Ctrl-Left/Right* |Send the focused window to the previous/next output.
76 +
|===
77 +
78 +
=== Layout
79 +
80 +
[cols="1,3", options="header"]
81 +
|===
82 +
|Binding |Action
83 +
84 +
|*Mod-Space* |Cycle layouts.
85 +
|*Mod-Shift-Space* |Reset the current layout.
86 +
|*Mod-h/l* |Shrink/grow the master area.
87 +
|*Mod-comma/period* |Add/remove windows to/from the master area.
88 +
|*Mod-Shift-less/greater* |Add/remove stack columns.
89 +
|*Mod-Shift-bar* |Flip master and stack areas.
90 +
|*Mod-f* |Toggle the max layout.
91 +
|===
92 +
93 +
=== Programs and system
94 +
95 +
[cols="1,3", options="header"]
96 +
|===
97 +
|Binding |Action
98 +
99 +
|*Mod-x* |Lock the screen with *swaylock*(1).
100 +
|*Mod-p* |Spawn *wmenu-run*(1).
101 +
|*Mod-Shift-Return* |Spawn *foot*(1).
102 +
|*Mod-Shift-q* |Quit *swm*.
103 +
|===
104 +
105 +
Window focus follows the mouse. Windows can be moved with Mod-left-click and
106 +
resized with Mod-right-click. Mod-middle-click toggles floating.
107 +
108 +
== SEE ALSO
109 +
110 +
*foot*(1), *wmenu*(1)
111 +
112 +
== AUTHORS
113 +
114 +
*swm* is a fork of *dwl*; see the LICENSE files for its authors.
swm.c added +6068 -0
1 +
/*
2 +
 * swm.c
3 +
 * Simple Wayland window manager.
4 +
 */
5 +
#include <errno.h>
6 +
#include <getopt.h>
7 +
#include <libinput.h>
8 +
#include <linux/input-event-codes.h>
9 +
#include <math.h>
10 +
#include <signal.h>
11 +
#include <stdbool.h>
12 +
#include <stdio.h>
13 +
#include <stdlib.h>
14 +
#include <string.h>
15 +
#include <sys/stat.h>
16 +
#include <sys/wait.h>
17 +
#include <time.h>
18 +
#include <unistd.h>
19 +
#include <wayland-server-core.h>
20 +
#include <wlr/backend.h>
21 +
#include <wlr/backend/libinput.h>
22 +
#include <wlr/render/allocator.h>
23 +
#include <wlr/render/wlr_renderer.h>
24 +
#include <wlr/types/wlr_alpha_modifier_v1.h>
25 +
#include <wlr/types/wlr_compositor.h>
26 +
#include <wlr/types/wlr_cursor.h>
27 +
#include <wlr/types/wlr_cursor_shape_v1.h>
28 +
#include <wlr/types/wlr_data_control_v1.h>
29 +
#include <wlr/types/wlr_data_device.h>
30 +
#include <wlr/types/wlr_drm.h>
31 +
#include <wlr/types/wlr_export_dmabuf_v1.h>
32 +
#include <wlr/types/wlr_ext_data_control_v1.h>
33 +
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
34 +
#include <wlr/types/wlr_foreign_toplevel_management_v1.h>
35 +
#include <wlr/types/wlr_fractional_scale_v1.h>
36 +
#include <wlr/types/wlr_gamma_control_v1.h>
37 +
#include <wlr/types/wlr_idle_inhibit_v1.h>
38 +
#include <wlr/types/wlr_idle_notify_v1.h>
39 +
#include <wlr/types/wlr_input_device.h>
40 +
#include <wlr/types/wlr_input_method_v2.h>
41 +
#include <wlr/types/wlr_keyboard.h>
42 +
#include <wlr/types/wlr_keyboard_group.h>
43 +
#include <wlr/types/wlr_keyboard_shortcuts_inhibit_v1.h>
44 +
#include <wlr/types/wlr_layer_shell_v1.h>
45 +
#include <wlr/types/wlr_linux_dmabuf_v1.h>
46 +
#include <wlr/types/wlr_linux_drm_syncobj_v1.h>
47 +
#include <wlr/types/wlr_output.h>
48 +
#include <wlr/types/wlr_output_layout.h>
49 +
#include <wlr/types/wlr_output_management_v1.h>
50 +
#include <wlr/types/wlr_output_power_management_v1.h>
51 +
#include <wlr/types/wlr_pointer.h>
52 +
#include <wlr/types/wlr_pointer_constraints_v1.h>
53 +
#include <wlr/types/wlr_presentation_time.h>
54 +
#include <wlr/types/wlr_primary_selection.h>
55 +
#include <wlr/types/wlr_primary_selection_v1.h>
56 +
#include <wlr/types/wlr_relative_pointer_v1.h>
57 +
#include <wlr/types/wlr_scene.h>
58 +
#include <wlr/types/wlr_screencopy_v1.h>
59 +
#include <wlr/types/wlr_seat.h>
60 +
#include <wlr/types/wlr_server_decoration.h>
61 +
#include <wlr/types/wlr_session_lock_v1.h>
62 +
#include <wlr/types/wlr_single_pixel_buffer_v1.h>
63 +
#include <wlr/types/wlr_subcompositor.h>
64 +
#include <wlr/types/wlr_text_input_v3.h>
65 +
#include <wlr/types/wlr_viewporter.h>
66 +
#include <wlr/types/wlr_virtual_keyboard_v1.h>
67 +
#include <wlr/types/wlr_virtual_pointer_v1.h>
68 +
#include <wlr/types/wlr_xcursor_manager.h>
69 +
#include <wlr/types/wlr_xdg_activation_v1.h>
70 +
#include <wlr/types/wlr_xdg_decoration_v1.h>
71 +
#include <wlr/types/wlr_xdg_output_v1.h>
72 +
#include <wlr/types/wlr_xdg_shell.h>
73 +
#include <wlr/util/log.h>
74 +
#include <wlr/util/region.h>
75 +
#include <wlr/xwayland.h>
76 +
#include <xcb/xcb.h>
77 +
#include <xcb/xcb_icccm.h>
78 +
#include <xkbcommon/xkbcommon.h>
79 +
80 +
#include "ext-workspace-v1-protocol.h"
81 +
#include "swm.h"
82 +
#include "util.h"
83 +
#include "xdg-shell-protocol.h"
84 +
85 +
/* Constants and helper macros. */
86 +
#define MIN(a, b)               ((a) < (b) ? (a) : (b))
87 +
#define MAX(A, B)               ((A) > (B) ? (A) : (B))
88 +
#define CLEANMASK(mask)         (mask & ~WLR_MODIFIER_CAPS)
89 +
#define VISIBLEON(C, M)         ((M) && (C)->ws && (C)->ws->mon == (M))
90 +
#define SLICE                   (32) /* Number of steps across the master area. */
91 +
#define LENGTH(X)               (sizeof X / sizeof X[0])
92 +
#define END(A)                  ((A) + LENGTH(A))
93 +
#define LISTEN(E, L, H)         wl_signal_add((E), ((L)->notify = (H), (L)))
94 +
#define MAX_CLIENTS             256
95 +
#define MAX_MONITORS            16
96 +
#define MAX_LAYER_SURFACES      256
97 +
#define MAX_KEYBOARD_GROUPS     32
98 +
#define MAX_POINTER_CONSTRAINTS 256
99 +
#define MAX_TEXT_INPUTS         128
100 +
#define MAX_INPUT_POPUPS        64
101 +
#define MAX_SESSION_LOCKS       4
102 +
#define MAX_PENDING_SPAWNS      256
103 +
#define MAX_WINDOW_STATES       512
104 +
#define MAX_WINDOW_STATE_FIELD  256
105 +
#define MAX_WINDOW_STATE_LINE   1024
106 +
#define MAX_STATE_PATH          4096
107 +
#define MAX_WS_ID               16
108 +
#define MAX_STATIC_LISTENERS    512
109 +
#define MAX_WS_MANAGERS         64
110 +
#define MAX_WS_HANDLES          (MAX_WS_MANAGERS * WSCOUNT)
111 +
#define MAX_AUTOSTART           64
112 +
#define MAX_COMMAND_SIZE        1024
113 +
#define MAX_COMMAND_ARGS        64
114 +
#define LISTEN_STATIC(E, H)     listen_static((E), (H))
115 +
116 +
/* Cursor interaction state. */
117 +
enum { CURSOR_NORMAL, CURSOR_PRESSED, CURSOR_MOVE, CURSOR_RESIZE };
118 +
119 +
/* Master-stack configuration commands. */
120 +
enum {
121 +
    MASTER_SHRINK,
122 +
    MASTER_GROW,
123 +
    MASTER_ADD,
124 +
    MASTER_DELETE,
125 +
    STACK_INCREMENT,
126 +
    STACK_DECREMENT,
127 +
    STACK_RESET,
128 +
    LAYOUT_FLIP
129 +
};
130 +
131 +
/* Workspace cycling operations. */
132 +
enum {
133 +
    WORKSPACE_NEXT,
134 +
    WORKSPACE_PREVIOUS,
135 +
    WORKSPACE_NEXT_ALL,
136 +
    WORKSPACE_PREVIOUS_ALL,
137 +
    WORKSPACE_NEXT_MOVE,
138 +
    WORKSPACE_PREVIOUS_MOVE
139 +
};
140 +
141 +
/* Client surface implementations. */
142 +
enum { XDG_SHELL, LAYER_SHELL, X11 };
143 +
144 +
/* Window layers, ordered from back to front. */
145 +
enum {
146 +
    LAYER_BACKGROUND,
147 +
    LAYER_BOTTOM,
148 +
    LAYER_TILE,
149 +
    LAYER_FLOAT,
150 +
    LAYER_TOP,
151 +
    LAYER_FULLSCREEN,
152 +
    LAYER_OVERLAY,
153 +
    LAYER_BLOCK,
154 +
    NUM_LAYERS
155 +
};
156 +
157 +
/* Command argument value. */
158 +
typedef union {
159 +
    int         i;
160 +
    uint32_t    u;
161 +
    float       f;
162 +
    const void *v;
163 +
} arg_t;
164 +
165 +
/* Pointer button binding. */
166 +
typedef struct {
167 +
    unsigned int mod;
168 +
    unsigned int button;
169 +
    void (*func)(const arg_t *);
170 +
    const arg_t arg;
171 +
} button_t;
172 +
173 +
/* Display and its window-manager state. */
174 +
typedef struct monitor_t monitor_t;
175 +
/* Global workspace state. */
176 +
typedef struct workspace_t workspace_t;
177 +
178 +
/* Managed XDG or Xwayland client. */
179 +
typedef struct {
180 +
    /* This must remain first so generic surface code can read it. */
181 +
    unsigned int type; /* XDG_SHELL or X11. */
182 +
183 +
    monitor_t             *mon;
184 +
    workspace_t           *ws;
185 +
    struct wlr_scene_tree *scene;
186 +
    struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
187 +
    struct wlr_scene_tree *scene_surface;
188 +
    struct wl_list         link;
189 +
    struct wl_list         flink;
190 +
    struct wlr_box         geom;          /* Position within the layout, including borders. */
191 +
    struct wlr_box         prev;          /* Previous position and size, including borders. */
192 +
    struct wlr_box         maxstack_prev; /* Floating position and size before max layout. */
193 +
    struct wlr_box         pending_geom;  /* Size and position requested by the latest resize. */
194 +
    struct wlr_box         bounds;        /* Only the width and height are used. */
195 +
    union {
196 +
        struct wlr_xdg_surface      *xdg;
197 +
        struct wlr_xwayland_surface *xwayland;
198 +
    } surface;
199 +
    struct wlr_xdg_toplevel_decoration_v1     *decoration;
200 +
    struct wl_listener                         commit;
201 +
    struct wl_listener                         map;
202 +
    struct wl_listener                         maximize;
203 +
    struct wl_listener                         unmap;
204 +
    struct wl_listener                         destroy;
205 +
    struct wl_listener                         set_title;
206 +
    struct wl_listener                         set_appid;
207 +
    struct wl_listener                         fullscreen;
208 +
    struct wl_listener                         set_decoration_mode;
209 +
    struct wl_listener                         destroy_decoration;
210 +
    struct wl_listener                         activate;
211 +
    struct wl_listener                         associate;
212 +
    struct wl_listener                         dissociate;
213 +
    struct wl_listener                         configure;
214 +
    struct wl_listener                         set_hints;
215 +
    struct wlr_foreign_toplevel_handle_v1     *ftl;
216 +
    struct wlr_ext_foreign_toplevel_handle_v1 *extftl;
217 +
    monitor_t                                 *ftl_monitor;
218 +
    struct wl_listener                         ftl_activate;
219 +
    struct wl_listener                         ftl_close;
220 +
    struct wl_listener                         ftl_fullscreen;
221 +
    unsigned int                               bw;
222 +
    bool                                       is_floating, is_urgent, is_fullscreen, is_borderless;
223 +
    bool                                       is_max_stacked;
224 +
    int                                        persist_float;
225 +
    int                                        pending_map;
226 +
    uint32_t                                   resize_edges;
227 +
    uint32_t                                   resize; /* ID of a resize awaiting confirmation. */
228 +
} client_t;
229 +
230 +
/* Keyboard binding. */
231 +
typedef struct {
232 +
    uint32_t     mod;
233 +
    xkb_keysym_t keysym;
234 +
    void (*func)(const arg_t *);
235 +
    const arg_t arg;
236 +
} key_t;
237 +
238 +
/* Physical or virtual keyboard group. */
239 +
typedef struct {
240 +
    struct wlr_keyboard_group *wlr_group;
241 +
    bool                       is_virtual;
242 +
    int                        nsyms;
243 +
    const xkb_keysym_t        *keysyms; /* Valid only when nsyms is nonzero. */
244 +
    uint32_t                   mods;    /* Valid only when nsyms is nonzero. */
245 +
    struct wl_event_source    *key_repeat_source;
246 +
    struct wl_listener         modifiers;
247 +
    struct wl_listener         key;
248 +
    struct wl_listener         destroy;
249 +
} keyboard_group_t;
250 +
251 +
/* Layer-shell surface. */
252 +
typedef struct {
253 +
    /* This must remain first so generic surface code can read it. */
254 +
    unsigned int type; /* Always LAYER_SHELL. */
255 +
256 +
    monitor_t                         *mon;
257 +
    struct wlr_scene_tree             *scene;
258 +
    struct wlr_scene_rect             *dim;
259 +
    struct wlr_scene_tree             *popups;
260 +
    struct wlr_scene_layer_surface_v1 *scene_layer;
261 +
    struct wl_list                     link;
262 +
    int                                mapped;
263 +
    struct wlr_layer_surface_v1       *layer_surface;
264 +
    struct wl_listener                 destroy;
265 +
    struct wl_listener                 unmap;
266 +
    struct wl_listener                 surface_commit;
267 +
} layer_surface_t;
268 +
269 +
/* Layer-shell matching rule. */
270 +
typedef struct {
271 +
    const char  *namespace;
272 +
    const float *dim;
273 +
} layer_rule_t;
274 +
275 +
/* Workspace layout implementation. */
276 +
typedef struct {
277 +
    void (*arrange)(monitor_t *);
278 +
    bool cycle;
279 +
} layout_t;
280 +
281 +
struct monitor_t {
282 +
    struct wl_list           link;
283 +
    struct wlr_output       *wlr_output;
284 +
    struct wlr_scene_output *scene_output;
285 +
    struct wlr_scene_rect   *fullscreen_bg; /* Hides other layers behind fullscreen windows. */
286 +
    struct wl_listener       frame;
287 +
    struct wl_listener       destroy;
288 +
    struct wl_listener       request_state;
289 +
    struct wl_listener       output_bind;
290 +
    struct wl_listener       destroy_lock_surface;
291 +
    struct wlr_session_lock_surface_v1 *lock_surface;
292 +
    struct wlr_box                      m;         /* Full display area within the layout. */
293 +
    struct wlr_box                      w;         /* Area available for regular windows. */
294 +
    struct wl_list                      layers[4]; /* Layer surfaces on this display. */
295 +
    workspace_t                        *ws;        /* Workspace shown on this display. */
296 +
    workspace_t *previous_workspace;               /* Workspace shown before the current one. */
297 +
    int          gamma_lut_changed;
298 +
    int          asleep;
299 +
};
300 +
301 +
/* Each workspace keeps separate settings for vertical and horizontal layouts. */
302 +
/* Master-stack layout parameters. */
303 +
typedef struct {
304 +
    int msize;  /* Master area width or height, in 1/SLICE steps. */
305 +
    int mwin;   /* Maximum number of windows in the master area. */
306 +
    int stacks; /* Stack columns, or rows in a horizontal layout. */
307 +
} stack_state_t;
308 +
309 +
/* A workspace owns its windows and layout settings, and can appear on only one display. */
310 +
struct workspace_t {
311 +
    char            name[4];
312 +
    int             idx;
313 +
    monitor_t      *mon; /* Display showing this workspace, or nullptr if hidden. */
314 +
    const layout_t *lt;
315 +
    const layout_t *prevlt;
316 +
    stack_state_t   v, h;
317 +
    struct wl_list  handles; /* Protocol handles that publish this workspace. */
318 +
};
319 +
320 +
/* Each connected workspace client gets one manager and one group for all displays. */
321 +
/* Bound ext-workspace manager. */
322 +
typedef struct {
323 +
    struct wl_resource *resource; /* Client's workspace manager. */
324 +
    struct wl_resource *group;    /* Client's group, or nullptr after it disconnects. */
325 +
    workspace_t        *pending;  /* Workspace to activate when the client commits. */
326 +
    struct wl_list      link;     /* Entry in ws_managers. */
327 +
} workspace_manager_t;
328 +
329 +
/* Exported ext-workspace handle. */
330 +
typedef struct {
331 +
    struct wl_resource  *resource;
332 +
    workspace_t         *ws;
333 +
    workspace_manager_t *mgr;  /* nullptr after the manager is destroyed. */
334 +
    struct wl_list       link; /* Entry in workspace_t.handles. */
335 +
} workspace_handle_t;
336 +
337 +
/* Spawn awaiting workspace assignment. */
338 +
typedef struct {
339 +
    pid_t          pid;
340 +
    workspace_t   *ws;
341 +
    struct wl_list link;
342 +
} pending_spawn_t;
343 +
344 +
/* Persisted client geometry. */
345 +
typedef struct {
346 +
    char           appid[MAX_WINDOW_STATE_FIELD];
347 +
    char           title[MAX_WINDOW_STATE_FIELD];
348 +
    struct wlr_box geom;
349 +
    struct wl_list link;
350 +
} window_state_t;
351 +
352 +
/* Connects applications to an input method such as fcitx5. */
353 +
/* Text-input relay state. */
354 +
typedef struct {
355 +
    struct wlr_text_input_v3 *ti;
356 +
    struct wl_listener        enable;
357 +
    struct wl_listener        commit;
358 +
    struct wl_listener        disable;
359 +
    struct wl_listener        destroy;
360 +
} text_input_t;
361 +
362 +
/* Input-method popup state. */
363 +
typedef struct {
364 +
    struct wlr_input_popup_surface_v2 *popup;
365 +
    struct wlr_scene_tree             *scene;
366 +
    struct wl_list                     link;
367 +
    struct wl_listener                 map;
368 +
    struct wl_listener                 unmap;
369 +
    struct wl_listener                 commit;
370 +
    struct wl_listener                 destroy;
371 +
} input_popup_t;
372 +
373 +
/* Output configuration rule. */
374 +
typedef struct {
375 +
    const char              *name;
376 +
    float                    scale;
377 +
    enum wl_output_transform rr;
378 +
    int                      x, y;
379 +
} monitor_rule_t;
380 +
381 +
/* Active pointer constraint. */
382 +
typedef struct {
383 +
    struct wlr_pointer_constraint_v1 *constraint;
384 +
    struct wl_listener                destroy;
385 +
} pointer_constraint_t;
386 +
387 +
/* Client matching rule. */
388 +
typedef struct {
389 +
    const char *id;
390 +
    const char *title;
391 +
    int         ws; /* Workspace number starting at zero; -1 keeps the current one. */
392 +
    bool        is_floating;
393 +
    int         monitor;
394 +
    bool        borderless;
395 +
} rule_t;
396 +
397 +
/* Session lock state. */
398 +
typedef struct {
399 +
    struct wlr_scene_tree      *scene;
400 +
    struct wlr_session_lock_v1 *lock;
401 +
    struct wl_listener          new_surface;
402 +
    struct wl_listener          unlock;
403 +
    struct wl_listener          destroy;
404 +
} session_lock_t;
405 +
406 +
/* Listener allocated from the static pool. */
407 +
typedef struct {
408 +
    struct wl_listener listener;
409 +
} static_listener_t;
410 +
411 +
/* Function declarations. */
412 +
static void         apply_bounds(client_t *c, struct wlr_box *bbox);
413 +
static void         apply_rules(client_t *c, workspace_t *defaultws);
414 +
static unsigned int client_border_width(client_t *c);
415 +
static void         arrange(monitor_t *m);
416 +
static void         arrange_layer(
417 +
    monitor_t *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive
418 +
);
419 +
static void              arrange_layers(monitor_t *m);
420 +
static void              autostart_exec(void);
421 +
static void              axis_notify(struct wl_listener *listener, void *data);
422 +
static void              button_press(struct wl_listener *listener, void *data);
423 +
static void              change_vt(const arg_t *arg);
424 +
static void              check_idle_inhibitor(struct wlr_surface *exclude);
425 +
static void              cleanup(void);
426 +
static void              cleanup_monitor(struct wl_listener *listener, void *data);
427 +
static void              cleanup_listeners(void);
428 +
static void              close_monitor(monitor_t *m);
429 +
static void              layer_surface_commit_notify(struct wl_listener *listener, void *data);
430 +
static void              commit_notify(struct wl_listener *listener, void *data);
431 +
static void              popup_commit(struct wl_listener *listener, void *data);
432 +
static void              create_decoration(struct wl_listener *listener, void *data);
433 +
static void              create_idle_inhibitor(struct wl_listener *listener, void *data);
434 +
static void              create_keyboard(struct wlr_keyboard *keyboard);
435 +
static keyboard_group_t *create_keyboard_group(bool is_virtual);
436 +
static void              create_layer_surface(struct wl_listener *listener, void *data);
437 +
static void              create_lock_surface(struct wl_listener *listener, void *data);
438 +
static void              create_monitor(struct wl_listener *listener, void *data);
439 +
static void              create_notify(struct wl_listener *listener, void *data);
440 +
static void              create_pointer(struct wlr_pointer *pointer);
441 +
static void              create_pointer_constraint(struct wl_listener *listener, void *data);
442 +
static void              create_popup(struct wl_listener *listener, void *data);
443 +
static void              cursor_constrain(struct wlr_pointer_constraint_v1 *constraint);
444 +
static void              cursor_frame(struct wl_listener *listener, void *data);
445 +
static void              cursor_warp_to_hint(void);
446 +
static void              destroy_decoration(struct wl_listener *listener, void *data);
447 +
static void              destroy_drag_icon(struct wl_listener *listener, void *data);
448 +
static void              destroy_idle_inhibitor(struct wl_listener *listener, void *data);
449 +
static void              layer_surface_destroy_notify(struct wl_listener *listener, void *data);
450 +
static void              destroy_lock(session_lock_t *lock, int unlocked);
451 +
static void              destroy_lock_surface(struct wl_listener *listener, void *data);
452 +
static void              destroy_notify(struct wl_listener *listener, void *data);
453 +
static void              destroy_pointer_constraint(struct wl_listener *listener, void *data);
454 +
static void              destroy_session_lock(struct wl_listener *listener, void *data);
455 +
static void              destroy_keyboard_group(struct wl_listener *listener, void *data);
456 +
static monitor_t        *direction_to_monitor(enum wlr_direction dir);
457 +
static void              cycle_layout(const arg_t *arg);
458 +
static void              cycle_workspace(const arg_t *arg);
459 +
static void              focus_client(client_t *c, int lift);
460 +
static client_t         *focus_close(client_t *c);
461 +
static void              focus_main(const arg_t *arg);
462 +
static void              focus_urgent(const arg_t *arg);
463 +
static void              ftl_activate_notify(struct wl_listener *listener, void *data);
464 +
static void              ftl_close_notify(struct wl_listener *listener, void *data);
465 +
static void              ftl_fullscreen_notify(struct wl_listener *listener, void *data);
466 +
static void              ftl_sync(client_t *c);
467 +
static void              input_method_commit_notify(struct wl_listener *listener, void *data);
468 +
static void              input_method_create_notify(struct wl_listener *listener, void *data);
469 +
static void              input_method_destroy_notify(struct wl_listener *listener, void *data);
470 +
static struct wlr_text_input_v3 *input_method_focused_text_input(void);
471 +
static void      input_method_grab_keyboard(struct wl_listener *listener, void *data);
472 +
static void      input_method_new_popup(struct wl_listener *listener, void *data);
473 +
static void      input_method_send_state(struct wlr_text_input_v3 *ti);
474 +
static void      input_method_set_focus(struct wlr_surface *surface);
475 +
static void      position_input_popups(void);
476 +
static void      input_popup_commit(struct wl_listener *listener, void *data);
477 +
static void      input_popup_destroy(struct wl_listener *listener, void *data);
478 +
static void      input_popup_map(struct wl_listener *listener, void *data);
479 +
static bool      input_popup_position(input_popup_t *p);
480 +
static void      input_popup_unmap(struct wl_listener *listener, void *data);
481 +
static void      text_input_create_notify(struct wl_listener *listener, void *data);
482 +
static void      text_input_commit_notify(struct wl_listener *listener, void *data);
483 +
static void      text_input_destroy_notify(struct wl_listener *listener, void *data);
484 +
static void      text_input_disable_notify(struct wl_listener *listener, void *data);
485 +
static void      text_input_enable_notify(struct wl_listener *listener, void *data);
486 +
static void      focus_monitor(const arg_t *arg);
487 +
static void      focus_stack(const arg_t *arg);
488 +
static client_t *focus_top(monitor_t *m);
489 +
static void      fullscreen_notify(struct wl_listener *listener, void *data);
490 +
static void      gpu_reset(struct wl_listener *listener, void *data);
491 +
static int       handle_signal(int signo, void *data);
492 +
static void      master_bottom(monitor_t *m);
493 +
static void      master_left(monitor_t *m);
494 +
static void      master_right(monitor_t *m);
495 +
static void      master_top(monitor_t *m);
496 +
static void      input_device(struct wl_listener *listener, void *data);
497 +
static int       key_binding(uint32_t mods, xkb_keysym_t sym, bool repeat);
498 +
static void      new_shortcuts_inhibitor(struct wl_listener *listener, void *data);
499 +
static bool      shortcuts_inhibited(void);
500 +
static void      key_press(struct wl_listener *listener, void *data);
501 +
static void      key_press_modifiers(struct wl_listener *listener, void *data);
502 +
static void      load_window_states(void);
503 +
static int       key_repeat(void *data);
504 +
static void      kill_client(const arg_t *arg);
505 +
static void      lock_session(struct wl_listener *listener, void *data);
506 +
static void      listen_static(struct wl_signal *signal, wl_notify_func_t notify);
507 +
static void      listener_release(struct wl_listener *listener);
508 +
static void      map_notify(struct wl_listener *listener, void *data);
509 +
static void      maximize_notify(struct wl_listener *listener, void *data);
510 +
static void      max_stack(monitor_t *m);
511 +
static void      motion_absolute(struct wl_listener *listener, void *data);
512 +
static void      motion_notify(
513 +
    uint32_t                 time,
514 +
    struct wlr_input_device *device,
515 +
    double                   sx,
516 +
    double                   sy,
517 +
    double                   sx_unaccel,
518 +
    double                   sy_unaccel,
519 +
    bool                     refocus
520 +
);
521 +
static void motion_relative(struct wl_listener *listener, void *data);
522 +
static void move_resize(const arg_t *arg);
523 +
static void output_manager_apply(struct wl_listener *listener, void *data);
524 +
static void output_manager_apply_or_test(struct wlr_output_configuration_v1 *config, bool test);
525 +
static void output_manager_test(struct wl_listener *listener, void *data);
526 +
static void pointer_focus(
527 +
    client_t *c, struct wlr_surface *surface, double sx, double sy, uint32_t time, bool refocus
528 +
);
529 +
static void         print_status(void);
530 +
static void         prepare_child(void);
531 +
static void         power_manager_set_mode(struct wl_listener *listener, void *data);
532 +
static void         quit(const arg_t *arg);
533 +
static void         raise_client(const arg_t *arg);
534 +
static void         render_monitor(struct wl_listener *listener, void *data);
535 +
static void         request_decoration_mode(struct wl_listener *listener, void *data);
536 +
static void         request_start_drag(struct wl_listener *listener, void *data);
537 +
static void         remember_client(client_t *c);
538 +
static void         forget_client(client_t *c);
539 +
static void         save_window_states(void);
540 +
static void         request_monitor_state(struct wl_listener *listener, void *data);
541 +
static void         resize(client_t *c, struct wlr_box geo, bool interact);
542 +
static void         resize_apply(client_t *c);
543 +
static void         restore_client(client_t *c);
544 +
static void         run(char *startup_cmd);
545 +
static void         set_cursor(struct wl_listener *listener, void *data);
546 +
static void         set_cursor_shape(struct wl_listener *listener, void *data);
547 +
static void         set_floating(client_t *c, bool floating);
548 +
static void         set_fullscreen(client_t *c, bool fullscreen);
549 +
static void         set_monitor(client_t *c, monitor_t *m, workspace_t *ws);
550 +
static void         set_workspace(client_t *c, workspace_t *ws);
551 +
static void         update_shortcuts_inhibitors(struct wlr_surface *surface);
552 +
static client_t    *client_main(client_t *c);
553 +
static bool         clients_related(client_t *a, client_t *b);
554 +
static void         assign_workspace(workspace_t *ws, monitor_t *m);
555 +
static workspace_t *free_workspace(void);
556 +
static void         view_workspace(workspace_t *ws, monitor_t *m);
557 +
static void         workspace_broadcast(void);
558 +
static void         workspace_handle_create(workspace_manager_t *mgr, workspace_t *ws);
559 +
static void         workspace_manager_bind(
560 +
    struct wl_client *client, void *data, uint32_t version, uint32_t id
561 +
);
562 +
static void       workspace_output_bind(struct wl_listener *listener, void *data);
563 +
static uint32_t   workspace_state(workspace_t *ws);
564 +
static void       set_primary_selection(struct wl_listener *listener, void *data);
565 +
static void       set_selection(struct wl_listener *listener, void *data);
566 +
static void       setup(void);
567 +
static void       spawn(const arg_t *arg);
568 +
static void       expand_argv(const char *const *argv, char **expanded, char *storage);
569 +
static void       swap_client(const arg_t *arg);
570 +
static void       stack_config(const arg_t *arg);
571 +
static void       stack_master(monitor_t *m, int rot, int flip);
572 +
static void       start_drag(struct wl_listener *listener, void *data);
573 +
static void       tag(const arg_t *arg);
574 +
static void       tag_monitor(const arg_t *arg);
575 +
static void       toggle_floating(const arg_t *arg);
576 +
static void       toggle_fullscreen(const arg_t *arg);
577 +
static void       toggle_max_stack(const arg_t *arg);
578 +
static void       unlock_session(struct wl_listener *listener, void *data);
579 +
static void       layer_surface_unmap_notify(struct wl_listener *listener, void *data);
580 +
static void       unmap_notify(struct wl_listener *listener, void *data);
581 +
static void       update_monitors(struct wl_listener *listener, void *data);
582 +
static void       update_app_id(struct wl_listener *listener, void *data);
583 +
static void       update_title(struct wl_listener *listener, void *data);
584 +
static void       urgent(struct wl_listener *listener, void *data);
585 +
static void       view(const arg_t *arg);
586 +
static void       virtual_keyboard(struct wl_listener *listener, void *data);
587 +
static void       virtual_pointer(struct wl_listener *listener, void *data);
588 +
static monitor_t *point_to_monitor(double x, double y);
589 +
static void       point_to_node(
590 +
    double               x,
591 +
    double               y,
592 +
    struct wlr_surface **psurface,
593 +
    client_t           **pc,
594 +
    layer_surface_t    **pl,
595 +
    double              *nx,
596 +
    double              *ny
597 +
);
598 +
static void zoom(const arg_t *arg);
599 +
600 +
/* Global state. */
601 +
static pid_t                   child_pid = -1;
602 +
static size_t                  autostart_len;
603 +
static bool                    locked;
604 +
static void                   *exclusive_focus;
605 +
static struct wl_display      *dpy;
606 +
static struct wl_event_loop   *event_loop;
607 +
static struct wl_event_source *signal_sources[3];
608 +
static sigset_t                original_signal_mask;
609 +
static struct wlr_backend     *backend;
610 +
static struct wlr_scene       *scene;
611 +
static struct wlr_scene_tree  *layers[NUM_LAYERS];
612 +
static struct wlr_scene_tree  *drag_icon;
613 +
/* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */
614 +
static const int layermap[] = { LAYER_BACKGROUND, LAYER_BOTTOM, LAYER_TOP, LAYER_OVERLAY };
615 +
static struct wlr_renderer   *drw;
616 +
static struct wlr_allocator  *alloc;
617 +
static struct wlr_compositor *compositor;
618 +
static struct wlr_session    *session;
619 +
620 +
static struct wlr_xdg_shell                             *xdg_shell;
621 +
static struct wlr_xdg_activation_v1                     *activation;
622 +
static struct wlr_xdg_decoration_manager_v1             *xdg_decoration_mgr;
623 +
static struct wl_list                                    clients; /* tiling order */
624 +
static struct wl_list                                    fstack;  /* focus order */
625 +
static struct wlr_idle_notifier_v1                      *idle_notifier;
626 +
static struct wlr_idle_inhibit_manager_v1               *idle_inhibit_mgr;
627 +
static struct wlr_layer_shell_v1                        *layer_shell;
628 +
static struct wlr_output_manager_v1                     *output_mgr;
629 +
static struct wlr_virtual_keyboard_manager_v1           *virtual_keyboard_mgr;
630 +
static struct wlr_virtual_pointer_manager_v1            *virtual_pointer_mgr;
631 +
static struct wlr_cursor_shape_manager_v1               *cursor_shape_mgr;
632 +
static struct wlr_output_power_manager_v1               *power_mgr;
633 +
static struct wlr_foreign_toplevel_manager_v1           *ftl_mgr;
634 +
static struct wlr_keyboard_shortcuts_inhibit_manager_v1 *kb_inhibit_mgr;
635 +
static struct wlr_input_method_manager_v2               *im_mgr;
636 +
static struct wlr_text_input_manager_v3                 *ti_mgr;
637 +
static struct wlr_input_method_v2                       *input_method; /* at most one */
638 +
static struct wl_listener im_commit    = { .notify = input_method_commit_notify };
639 +
static struct wl_listener im_destroy   = { .notify = input_method_destroy_notify };
640 +
static struct wl_listener im_grab_kb   = { .notify = input_method_grab_keyboard };
641 +
static struct wl_listener im_new_popup = { .notify = input_method_new_popup };
642 +
static struct wlr_ext_foreign_toplevel_list_v1 *ext_ftl_list;
643 +
644 +
static struct wlr_pointer_constraints_v1      *pointer_constraints;
645 +
static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
646 +
static struct wlr_pointer_constraint_v1       *active_constraint;
647 +
648 +
static struct wlr_cursor          *cursor;
649 +
static struct wlr_xcursor_manager *cursor_mgr;
650 +
651 +
static struct wlr_scene_rect              *root_bg;
652 +
static struct wlr_session_lock_manager_v1 *session_lock_mgr;
653 +
static struct wlr_scene_rect              *locked_bg;
654 +
static struct wlr_session_lock_v1         *cur_lock;
655 +
656 +
static struct wlr_seat  *seat;
657 +
static keyboard_group_t *kb_group;
658 +
static unsigned int      cursor_mode;
659 +
static client_t         *grabc;
660 +
static int               grabcx, grabcy; /* Pointer position within the grabbed window. */
661 +
static int               grabx, graby;   /* Pointer position within the display layout. */
662 +
static uint32_t          grabedges;
663 +
static struct wlr_box    grabgeom;
664 +
665 +
static struct wlr_output_layout *output_layout;
666 +
static struct wlr_box            sgeom;
667 +
static struct wl_list            mons;
668 +
static struct wl_list            ws_managers;    /* workspace_manager_t.link */
669 +
static struct wl_list            pending_spawns; /* pending_spawn_t.link */
670 +
static struct wl_list            window_states;  /* window_state_t.link */
671 +
static struct wl_list            input_popups;   /* input_popup_t.link */
672 +
static int                       virtual_keyboards;
673 +
static monitor_t                *selmon;
674 +
static bool                      arranging_pointer_focus;
675 +
676 +
/* Event handlers shared for the lifetime of the compositor. */
677 +
static struct wl_listener cursor_axis            = { .notify = axis_notify };
678 +
static struct wl_listener cursor_button          = { .notify = button_press };
679 +
static struct wl_listener cursor_frame_listener  = { .notify = cursor_frame };
680 +
static struct wl_listener cursor_motion          = { .notify = motion_relative };
681 +
static struct wl_listener cursor_motion_absolute = { .notify = motion_absolute };
682 +
static struct wl_listener gpu_reset_listener     = { .notify = gpu_reset };
683 +
static struct wl_listener layout_change          = { .notify = update_monitors };
684 +
static struct wl_listener new_idle_inhibitor     = { .notify = create_idle_inhibitor };
685 +
static struct wl_listener new_input_device       = { .notify = input_device };
686 +
static struct wl_listener new_input_method       = { .notify = input_method_create_notify };
687 +
static struct wl_listener new_virtual_keyboard   = { .notify = virtual_keyboard };
688 +
static struct wl_listener new_virtual_pointer    = { .notify = virtual_pointer };
689 +
static struct wl_listener new_pointer_constraint = { .notify = create_pointer_constraint };
690 +
static struct wl_listener new_shortcuts_inhibitor_listener = { .notify = new_shortcuts_inhibitor };
691 +
static struct wl_listener new_output                       = { .notify = create_monitor };
692 +
static struct wl_listener new_xdg_toplevel                 = { .notify = create_notify };
693 +
static struct wl_listener new_text_input                   = { .notify = text_input_create_notify };
694 +
static struct wl_listener new_xdg_popup                    = { .notify = create_popup };
695 +
static struct wl_listener new_xdg_decoration               = { .notify = create_decoration };
696 +
static struct wl_listener new_layer_surface                = { .notify = create_layer_surface };
697 +
static struct wl_listener output_mgr_apply                 = { .notify = output_manager_apply };
698 +
static struct wl_listener output_mgr_test                  = { .notify = output_manager_test };
699 +
static struct wl_listener output_power_mgr_set_mode        = { .notify = power_manager_set_mode };
700 +
static struct wl_listener request_activate                 = { .notify = urgent };
701 +
static struct wl_listener request_cursor                   = { .notify = set_cursor };
702 +
static struct wl_listener request_set_psel                 = { .notify = set_primary_selection };
703 +
static struct wl_listener request_set_sel                  = { .notify = set_selection };
704 +
static struct wl_listener request_set_cursor_shape         = { .notify = set_cursor_shape };
705 +
static struct wl_listener request_start_drag_listener      = { .notify = request_start_drag };
706 +
static struct wl_listener start_drag_listener              = { .notify = start_drag };
707 +
static struct wl_listener new_session_lock                 = { .notify = lock_session };
708 +
709 +
static void                 activate_x11(struct wl_listener *listener, void *data);
710 +
static void                 associate_x11(struct wl_listener *listener, void *data);
711 +
static void                 configure_x11(struct wl_listener *listener, void *data);
712 +
static void                 create_notify_x11(struct wl_listener *listener, void *data);
713 +
static void                 dissociate_x11(struct wl_listener *listener, void *data);
714 +
static void                 set_hints(struct wl_listener *listener, void *data);
715 +
static void                 xwayland_ready(struct wl_listener *listener, void *data);
716 +
static struct wl_listener   new_xwayland_surface    = { .notify = create_notify_x11 };
717 +
static struct wl_listener   xwayland_ready_listener = { .notify = xwayland_ready };
718 +
static struct wlr_xwayland *xwayland;
719 +
static bool                 xwayland_listeners_registered;
720 +
721 +
/* Load configuration after declaring the types and state it may use. */
722 +
#include "config.h"
723 +
724 +
static_assert(WSCOUNT > 0 && WSCOUNT <= 32, "WSCOUNT must be between 1 and 32");
725 +
726 +
static workspace_t workspaces[WSCOUNT];
727 +
728 +
static pid_t                           autostart_pids[MAX_AUTOSTART];
729 +
static struct wlr_backend_output_state output_state_pool[MAX_MONITORS];
730 +
731 +
/* Fixed-capacity object pools. */
732 +
typedef struct {
733 +
    void       *items;
734 +
    bool       *used;
735 +
    size_t      capacity;
736 +
    size_t      item_size;
737 +
    const char *name;
738 +
} pool_t;
739 +
740 +
static void *pool_take(pool_t *pool) {
741 +
    size_t i;
742 +
743 +
    for (i = 0; i < pool->capacity; i++) {
744 +
        if (pool->used[i])
745 +
            continue;
746 +
        pool->used[i] = true;
747 +
        return (char *)pool->items + i * pool->item_size;
748 +
    }
749 +
    fprintf(stderr, "swm: %s pool exhausted (limit %zu)\n", pool->name, pool->capacity);
750 +
    return nullptr;
751 +
}
752 +
753 +
static void pool_release(pool_t *pool, void *item) {
754 +
    size_t index = ((char *)item - (char *)pool->items) / pool->item_size;
755 +
756 +
    pool->used[index] = false;
757 +
    memset(item, 0, pool->item_size);
758 +
}
759 +
760 +
static client_t             client_items[MAX_CLIENTS];
761 +
static monitor_t            monitor_items[MAX_MONITORS];
762 +
static layer_surface_t      layer_surface_items[MAX_LAYER_SURFACES];
763 +
static keyboard_group_t     keyboard_group_items[MAX_KEYBOARD_GROUPS];
764 +
static pointer_constraint_t pointer_constraint_items[MAX_POINTER_CONSTRAINTS];
765 +
static text_input_t         text_input_items[MAX_TEXT_INPUTS];
766 +
static input_popup_t        input_popup_items[MAX_INPUT_POPUPS];
767 +
static session_lock_t       session_lock_items[MAX_SESSION_LOCKS];
768 +
static pending_spawn_t      pending_spawn_items[MAX_PENDING_SPAWNS];
769 +
static window_state_t       window_state_items[MAX_WINDOW_STATES];
770 +
static static_listener_t    static_listener_items[MAX_STATIC_LISTENERS];
771 +
static workspace_manager_t  workspace_manager_items[MAX_WS_MANAGERS];
772 +
static workspace_handle_t   workspace_handle_items[MAX_WS_HANDLES];
773 +
774 +
static bool client_used[MAX_CLIENTS], monitor_used[MAX_MONITORS];
775 +
static bool layer_surface_used[MAX_LAYER_SURFACES], keyboard_group_used[MAX_KEYBOARD_GROUPS];
776 +
static bool pointer_constraint_used[MAX_POINTER_CONSTRAINTS], text_input_used[MAX_TEXT_INPUTS];
777 +
static bool input_popup_used[MAX_INPUT_POPUPS], session_lock_used[MAX_SESSION_LOCKS];
778 +
static bool pending_spawn_used[MAX_PENDING_SPAWNS], window_state_used[MAX_WINDOW_STATES];
779 +
static bool static_listener_used[MAX_STATIC_LISTENERS], workspace_manager_used[MAX_WS_MANAGERS];
780 +
static bool workspace_handle_used[MAX_WS_HANDLES];
781 +
782 +
static pool_t client_pool = {
783 +
    client_items, client_used, LENGTH(client_items), sizeof *client_items, "client"
784 +
};
785 +
static pool_t monitor_pool = {
786 +
    monitor_items, monitor_used, LENGTH(monitor_items), sizeof *monitor_items, "monitor"
787 +
};
788 +
static pool_t layer_surface_pool      = { layer_surface_items,
789 +
                                          layer_surface_used,
790 +
                                          LENGTH(layer_surface_items),
791 +
                                          sizeof *layer_surface_items,
792 +
                                          "layer_surface" };
793 +
static pool_t keyboard_group_pool     = { keyboard_group_items,
794 +
                                          keyboard_group_used,
795 +
                                          LENGTH(keyboard_group_items),
796 +
                                          sizeof *keyboard_group_items,
797 +
                                          "keyboard_group" };
798 +
static pool_t pointer_constraint_pool = { pointer_constraint_items,
799 +
                                          pointer_constraint_used,
800 +
                                          LENGTH(pointer_constraint_items),
801 +
                                          sizeof *pointer_constraint_items,
802 +
                                          "pointer_constraint" };
803 +
static pool_t text_input_pool         = { text_input_items,
804 +
                                          text_input_used,
805 +
                                          LENGTH(text_input_items),
806 +
                                          sizeof *text_input_items,
807 +
                                          "text_input" };
808 +
static pool_t input_popup_pool        = { input_popup_items,
809 +
                                          input_popup_used,
810 +
                                          LENGTH(input_popup_items),
811 +
                                          sizeof *input_popup_items,
812 +
                                          "input_popup" };
813 +
static pool_t session_lock_pool       = { session_lock_items,
814 +
                                          session_lock_used,
815 +
                                          LENGTH(session_lock_items),
816 +
                                          sizeof *session_lock_items,
817 +
                                          "session_lock" };
818 +
static pool_t pending_spawn_pool      = { pending_spawn_items,
819 +
                                          pending_spawn_used,
820 +
                                          LENGTH(pending_spawn_items),
821 +
                                          sizeof *pending_spawn_items,
822 +
                                          "pending_spawn" };
823 +
static pool_t window_state_pool       = { window_state_items,
824 +
                                          window_state_used,
825 +
                                          LENGTH(window_state_items),
826 +
                                          sizeof *window_state_items,
827 +
                                          "window_state" };
828 +
static pool_t static_listener_pool    = { static_listener_items,
829 +
                                          static_listener_used,
830 +
                                          LENGTH(static_listener_items),
831 +
                                          sizeof *static_listener_items,
832 +
                                          "static_listener" };
833 +
static pool_t workspace_manager_pool  = { workspace_manager_items,
834 +
                                          workspace_manager_used,
835 +
                                          LENGTH(workspace_manager_items),
836 +
                                          sizeof *workspace_manager_items,
837 +
                                          "workspace_manager" };
838 +
static pool_t workspace_handle_pool   = { workspace_handle_items,
839 +
                                          workspace_handle_used,
840 +
                                          LENGTH(workspace_handle_items),
841 +
                                          sizeof *workspace_handle_items,
842 +
                                          "workspace_handle" };
843 +
844 +
/* Backend-specific client operations. */
845 +
/* Client helpers shared by the XDG shell and Xwayland implementations. These
846 +
 * are inline so unused helpers are omitted for builds that disable a backend. */
847 +
848 +
/* Return whether a client uses Xwayland. */
849 +
static inline bool client_is_x11(client_t *c) {
850 +
    return c->type == X11;
851 +
}
852 +
853 +
/* Return the underlying wlroots surface. */
854 +
static inline struct wlr_surface *client_surface(client_t *c) {
855 +
    if (client_is_x11(c))
856 +
        return c->surface.xwayland->surface;
857 +
    return c->surface.xdg->surface;
858 +
}
859 +
860 +
/* Resolve a surface to its owning toplevel. */
861 +
static inline int toplevel_from_wlr_surface(
862 +
    struct wlr_surface *s, client_t **pc, layer_surface_t **pl
863 +
) {
864 +
    struct wlr_xdg_surface      *xdg_surface, *tmp_xdg_surface;
865 +
    struct wlr_surface          *root_surface;
866 +
    struct wlr_layer_surface_v1 *layer_surface;
867 +
    client_t                    *c    = nullptr;
868 +
    layer_surface_t             *l    = nullptr;
869 +
    int                          type = -1;
870 +
    struct wlr_xwayland_surface *xsurface;
871 +
872 +
    if (!s)
873 +
        return -1;
874 +
    root_surface = wlr_surface_get_root_surface(s);
875 +
876 +
    if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) {
877 +
        c    = xsurface->data;
878 +
        type = c->type;
879 +
        goto end;
880 +
    }
881 +
    if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) {
882 +
        l    = layer_surface->data;
883 +
        type = LAYER_SHELL;
884 +
        goto end;
885 +
    }
886 +
    xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
887 +
888 +
    while (xdg_surface) {
889 +
        tmp_xdg_surface = nullptr;
890 +
891 +
        switch (xdg_surface->role) {
892 +
        case WLR_XDG_SURFACE_ROLE_POPUP:
893 +
894 +
            if (!xdg_surface->popup || !xdg_surface->popup->parent)
895 +
                return -1;
896 +
897 +
            tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent);
898 +
899 +
            if (!tmp_xdg_surface)
900 +
                return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl);
901 +
902 +
            xdg_surface = tmp_xdg_surface;
903 +
            break;
904 +
        case WLR_XDG_SURFACE_ROLE_TOPLEVEL:
905 +
            c    = xdg_surface->data;
906 +
            type = c->type;
907 +
            goto end;
908 +
        case WLR_XDG_SURFACE_ROLE_NONE:
909 +
            return -1;
910 +
        }
911 +
    }
912 +
end:
913 +
914 +
    if (pl)
915 +
        *pl = l;
916 +
    if (pc)
917 +
        *pc = c;
918 +
    return type;
919 +
}
920 +
921 +
/* Client operations. */
922 +
static inline void client_activate_surface(struct wlr_surface *s, bool activated) {
923 +
    struct wlr_xdg_toplevel     *toplevel;
924 +
    struct wlr_xwayland_surface *xsurface;
925 +
926 +
    if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
927 +
        wlr_xwayland_surface_activate(xsurface, activated);
928 +
        return;
929 +
    }
930 +
    if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s)))
931 +
        wlr_xdg_toplevel_set_activated(toplevel, activated);
932 +
}
933 +
934 +
/* Set the XDG configure bounds for a client. */
935 +
static inline uint32_t client_set_bounds(client_t *c, int32_t width, int32_t height) {
936 +
    if (client_is_x11(c))
937 +
        return 0;
938 +
939 +
    if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
940 +
            XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION &&
941 +
        width >= 0 && height >= 0 && (c->bounds.width != width || c->bounds.height != height)) {
942 +
        c->bounds.width  = width;
943 +
        c->bounds.height = height;
944 +
        return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height);
945 +
    }
946 +
    return 0;
947 +
}
948 +
949 +
/* Return the client application identifier. */
950 +
static inline const char *client_get_appid(client_t *c) {
951 +
    if (client_is_x11(c))
952 +
        return c->surface.xwayland->class ? c->surface.xwayland->class : "broken";
953 +
    return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken";
954 +
}
955 +
956 +
/* Return the client surface clipping rectangle. */
957 +
static inline void client_get_clip(client_t *c, struct wlr_box *clip) {
958 +
    *clip = (struct wlr_box){
959 +
        .x      = 0,
960 +
        .y      = 0,
961 +
        .width  = c->geom.width - c->bw,
962 +
        .height = c->geom.height - c->bw,
963 +
    };
964 +
965 +
    if (client_is_x11(c))
966 +
        return;
967 +
968 +
    clip->x = c->surface.xdg->geometry.x;
969 +
    clip->y = c->surface.xdg->geometry.y;
970 +
}
971 +
972 +
/* Return the client surface geometry. */
973 +
static inline void client_get_geometry(client_t *c, struct wlr_box *geom) {
974 +
    if (client_is_x11(c)) {
975 +
        geom->x      = c->surface.xwayland->x;
976 +
        geom->y      = c->surface.xwayland->y;
977 +
        geom->width  = c->surface.xwayland->width;
978 +
        geom->height = c->surface.xwayland->height;
979 +
        return;
980 +
    }
981 +
    *geom = c->surface.xdg->geometry;
982 +
}
983 +
984 +
/* Return the parent client, if any. */
985 +
static inline client_t *client_get_parent(client_t *c) {
986 +
    client_t *p = nullptr;
987 +
988 +
    if (client_is_x11(c)) {
989 +
        if (c->surface.xwayland->parent)
990 +
            toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, nullptr);
991 +
        return p;
992 +
    }
993 +
    if (c->surface.xdg->toplevel->parent)
994 +
        toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, nullptr);
995 +
    return p;
996 +
}
997 +
998 +
/* Return the client title. */
999 +
static inline const char *client_get_title(client_t *c) {
1000 +
    if (client_is_x11(c))
1001 +
        return c->surface.xwayland->title ? c->surface.xwayland->title : "broken";
1002 +
    return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken";
1003 +
}
1004 +
1005 +
/* Return whether the client should float by type. */
1006 +
static inline bool client_is_float_type(client_t *c) {
1007 +
    struct wlr_xdg_toplevel      *toplevel;
1008 +
    struct wlr_xdg_toplevel_state state;
1009 +
1010 +
    if (client_is_x11(c)) {
1011 +
        struct wlr_xwayland_surface *surface    = c->surface.xwayland;
1012 +
        xcb_size_hints_t            *size_hints = surface->size_hints;
1013 +
1014 +
        if (surface->modal)
1015 +
            return true;
1016 +
1017 +
        if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) ||
1018 +
            wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) ||
1019 +
            wlr_xwayland_surface_has_window_type(
1020 +
                surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR
1021 +
            ) ||
1022 +
            wlr_xwayland_surface_has_window_type(
1023 +
                surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY
1024 +
            )) {
1025 +
            return true;
1026 +
        }
1027 +
        return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 &&
1028 +
               (size_hints->max_width == size_hints->min_width ||
1029 +
                size_hints->max_height == size_hints->min_height);
1030 +
    }
1031 +
    toplevel = c->surface.xdg->toplevel;
1032 +
    state    = toplevel->current;
1033 +
    return toplevel->parent ||
1034 +
           (state.min_width != 0 && state.min_height != 0 &&
1035 +
            (state.min_width == state.max_width || state.min_height == state.max_height));
1036 +
}
1037 +
1038 +
/* Return whether the client is rendered on an output. */
1039 +
static inline bool client_is_rendered_on_mon(client_t *c, monitor_t *m) {
1040 +
    /* This is needed for when you don't want to check formal assignment,
1041 +
     * but rather actual displaying of the pixels.
1042 +
     * Usually VISIBLEON suffices and is also faster. */
1043 +
    struct wlr_surface_output *s;
1044 +
    int                        unused_lx, unused_ly;
1045 +
1046 +
    if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly))
1047 +
        return false;
1048 +
    wl_list_for_each(
1049 +
        s, &client_surface(c)->current_outputs, link
1050 +
    ) if (s->output == m->wlr_output) return true;
1051 +
    return false;
1052 +
}
1053 +
1054 +
/* Return whether the client process is stopped. */
1055 +
static inline bool client_is_stopped(client_t *c) {
1056 +
    int       pid;
1057 +
    siginfo_t in = {};
1058 +
1059 +
    if (client_is_x11(c))
1060 +
        return false;
1061 +
1062 +
    wl_client_get_credentials(c->surface.xdg->client->client, &pid, nullptr, nullptr);
1063 +
1064 +
    if (waitid(P_PID, pid, &in, WNOHANG | WCONTINUED | WSTOPPED | WNOWAIT) < 0) {
1065 +
        /* This process is not our child process, while is very unlikely that
1066 +
         * it is stopped, in order to do not skip frames, assume that it is. */
1067 +
        if (errno == ECHILD)
1068 +
            return true;
1069 +
    } else if (in.si_pid) {
1070 +
        if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED)
1071 +
            return true;
1072 +
1073 +
        if (in.si_code == CLD_CONTINUED)
1074 +
            return false;
1075 +
    }
1076 +
    return false;
1077 +
}
1078 +
1079 +
/* Return whether the client bypasses window management. */
1080 +
static inline bool client_is_unmanaged(client_t *c) {
1081 +
    if (client_is_x11(c))
1082 +
        return c->surface.xwayland->override_redirect;
1083 +
    return false;
1084 +
}
1085 +
1086 +
/* Send keyboard focus to a client surface. */
1087 +
static inline void client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) {
1088 +
    if (kb)
1089 +
        wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes, kb->num_keycodes, &kb->modifiers);
1090 +
    else
1091 +
        wlr_seat_keyboard_notify_enter(seat, s, nullptr, 0, nullptr);
1092 +
1093 +
    /* Send text-input enter only after wl_keyboard.enter has been queued. */
1094 +
    input_method_set_focus(s);
1095 +
}
1096 +
1097 +
/* Request that a client close. */
1098 +
static inline void client_send_close(client_t *c) {
1099 +
    if (client_is_x11(c)) {
1100 +
        wlr_xwayland_surface_close(c->surface.xwayland);
1101 +
        return;
1102 +
    }
1103 +
    wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel);
1104 +
}
1105 +
1106 +
/* Set the color of every client border. */
1107 +
static inline void client_set_border_color(client_t *c, const float color[static 4]) {
1108 +
    int i;
1109 +
1110 +
    for (i = 0; i < 4; i++)
1111 +
        wlr_scene_rect_set_color(c->border[i], color);
1112 +
}
1113 +
1114 +
/* Notify a client of its fullscreen state. */
1115 +
static inline void client_set_fullscreen(client_t *c, bool fullscreen) {
1116 +
    if (client_is_x11(c)) {
1117 +
        wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen);
1118 +
        return;
1119 +
    }
1120 +
    wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen);
1121 +
}
1122 +
1123 +
/* Set the preferred scale for a client surface. */
1124 +
static inline void client_set_scale(struct wlr_surface *s, float scale) {
1125 +
    wlr_fractional_scale_v1_notify_scale(s, scale);
1126 +
    wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale));
1127 +
}
1128 +
1129 +
/* Configure the client surface size. */
1130 +
static inline uint32_t client_set_size(client_t *c, uint32_t width, uint32_t height) {
1131 +
    if (client_is_x11(c)) {
1132 +
        wlr_xwayland_surface_configure(
1133 +
            c->surface.xwayland, c->geom.x + c->bw, c->geom.y + c->bw, width, height
1134 +
        );
1135 +
        return 0;
1136 +
    }
1137 +
    if ((int32_t)width == c->surface.xdg->toplevel->current.width &&
1138 +
        (int32_t)height == c->surface.xdg->toplevel->current.height)
1139 +
        return 0;
1140 +
    return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height);
1141 +
}
1142 +
1143 +
/* Notify a client of an interactive resize. */
1144 +
static inline void client_set_resizing(client_t *c, bool resizing) {
1145 +
    if (client_is_x11(c))
1146 +
        return;
1147 +
    wlr_xdg_toplevel_set_resizing(c->surface.xdg->toplevel, resizing);
1148 +
}
1149 +
1150 +
/* Notify a client of its tiled edges. */
1151 +
static inline void client_set_tiled(client_t *c, uint32_t edges) {
1152 +
    if (client_is_x11(c)) {
1153 +
        wlr_xwayland_surface_set_maximized(
1154 +
            c->surface.xwayland, edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE
1155 +
        );
1156 +
        return;
1157 +
    }
1158 +
    if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
1159 +
        XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) {
1160 +
        wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges);
1161 +
    } else {
1162 +
        wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE);
1163 +
    }
1164 +
}
1165 +
1166 +
/* Notify a client of its suspended state. */
1167 +
static inline void client_set_suspended(client_t *c, bool suspended) {
1168 +
    if (client_is_x11(c))
1169 +
        return;
1170 +
1171 +
    wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended);
1172 +
}
1173 +
1174 +
/* Return whether an unmanaged client requests focus. */
1175 +
static inline bool client_wants_focus(client_t *c) {
1176 +
    return client_is_unmanaged(c) &&
1177 +
           wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland) &&
1178 +
           wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) !=
1179 +
               WLR_ICCCM_INPUT_MODEL_NONE;
1180 +
}
1181 +
1182 +
/* Return whether a client requests fullscreen. */
1183 +
static inline bool client_wants_fullscreen(client_t *c) {
1184 +
    if (client_is_x11(c))
1185 +
        return c->surface.xwayland->fullscreen;
1186 +
    return c->surface.xdg->toplevel->requested.fullscreen;
1187 +
}
1188 +
1189 +
/* Keep these values independent of wlroots so this module has no I/O or
1190 +
 * compositor dependencies. They are the stable values from wayland-util.h. */
1191 +
#define EDGE_TOP  1
1192 +
#define EDGE_LEFT 4
1193 +
1194 +
/* Clamp a box to the minimum size and output bounds. */
1195 +
void swm_box_apply_bounds(struct swm_box *box, const struct swm_box *bounds, unsigned int border) {
1196 +
    int minimum = 1 + 2 * (int)border;
1197 +
1198 +
    if (box->width < minimum)
1199 +
        box->width = minimum;
1200 +
1201 +
    if (box->height < minimum)
1202 +
        box->height = minimum;
1203 +
1204 +
    if (box->x >= bounds->x + bounds->width)
1205 +
        box->x = bounds->x + bounds->width - box->width;
1206 +
1207 +
    if (box->y >= bounds->y + bounds->height)
1208 +
        box->y = bounds->y + bounds->height - box->height;
1209 +
1210 +
    if (box->x + box->width <= bounds->x)
1211 +
        box->x = bounds->x;
1212 +
1213 +
    if (box->y + box->height <= bounds->y)
1214 +
        box->y = bounds->y;
1215 +
}
1216 +
1217 +
/* Resize a box from the selected edges. */
1218 +
struct swm_box swm_box_resize(
1219 +
    const struct swm_box *box, int dx, int dy, unsigned int edges, unsigned int border
1220 +
) {
1221 +
    int            minimum = 1 + 2 * (int)border;
1222 +
    struct swm_box result  = *box;
1223 +
    int            width   = box->width + ((edges & EDGE_LEFT) ? -dx : dx);
1224 +
    int            height  = box->height + ((edges & EDGE_TOP) ? -dy : dy);
1225 +
1226 +
    result.width  = width < minimum ? minimum : width;
1227 +
    result.height = height < minimum ? minimum : height;
1228 +
1229 +
    if (edges & EDGE_LEFT)
1230 +
        result.x = box->x + box->width - result.width;
1231 +
1232 +
    if (edges & EDGE_TOP)
1233 +
        result.y = box->y + box->height - result.height;
1234 +
1235 +
    return result;
1236 +
}
1237 +
1238 +
/* Reconcile pending geometry with a committed size. */
1239 +
void swm_box_reconcile_commit(
1240 +
    struct swm_box *box, int width, int height, unsigned int border, unsigned int edges
1241 +
) {
1242 +
    int outer_width, outer_height;
1243 +
1244 +
    if (width <= 0 || height <= 0)
1245 +
        return;
1246 +
1247 +
    outer_width  = width + 2 * (int)border;
1248 +
    outer_height = height + 2 * (int)border;
1249 +
1250 +
    if (edges & EDGE_LEFT)
1251 +
        box->x += box->width - outer_width;
1252 +
1253 +
    if (edges & EDGE_TOP)
1254 +
        box->y += box->height - outer_height;
1255 +
1256 +
    box->width  = outer_width;
1257 +
    box->height = outer_height;
1258 +
}
1259 +
1260 +
/* Position an input popup inside an output. */
1261 +
struct swm_box swm_popup_position(
1262 +
    const struct swm_box *client,
1263 +
    unsigned int          border,
1264 +
    const struct swm_box *cursor_rect,
1265 +
    int                   width,
1266 +
    int                   height,
1267 +
    const struct swm_box *output
1268 +
) {
1269 +
    struct swm_box popup = {
1270 +
        .x      = client->x + (int)border + cursor_rect->x,
1271 +
        .y      = client->y + (int)border + cursor_rect->y + cursor_rect->height,
1272 +
        .width  = width,
1273 +
        .height = height,
1274 +
    };
1275 +
    if (!output)
1276 +
        return popup;
1277 +
1278 +
    if (popup.x + width > output->x + output->width)
1279 +
        popup.x = output->x + output->width - width;
1280 +
1281 +
    if (popup.x < output->x)
1282 +
        popup.x = output->x;
1283 +
1284 +
    if (popup.y + height > output->y + output->height)
1285 +
        popup.y = client->y + (int)border + cursor_rect->y - height;
1286 +
1287 +
    if (popup.y < output->y)
1288 +
        popup.y = output->y;
1289 +
1290 +
    return popup;
1291 +
}
1292 +
1293 +
/* Compute exported workspace state flags. */
1294 +
unsigned int swm_workspace_state(bool active, bool occupied, bool urgent) {
1295 +
    unsigned int state = 0;
1296 +
1297 +
    if (active)
1298 +
        state |= SWM_WORKSPACE_ACTIVE;
1299 +
    if (urgent)
1300 +
        state |= SWM_WORKSPACE_URGENT;
1301 +
    if (!active && !occupied)
1302 +
        state |= SWM_WORKSPACE_HIDDEN;
1303 +
1304 +
    return state;
1305 +
}
1306 +
1307 +
/* Find the next eligible workspace. */
1308 +
int swm_workspace_next(
1309 +
    int         current,
1310 +
    int         count,
1311 +
    int         direction,
1312 +
    int         allow_empty,
1313 +
    const bool *visible_elsewhere,
1314 +
    const bool *occupied
1315 +
) {
1316 +
    int step, index;
1317 +
1318 +
    if (count <= 1 || (direction != -1 && direction != 1) || current < 0 || current >= count)
1319 +
        return -1;
1320 +
1321 +
    for (step = 1; step < count; step++) {
1322 +
        index = (current + direction * step + count) % count;
1323 +
1324 +
        if (visible_elsewhere[index])
1325 +
            continue;
1326 +
1327 +
        if (!allow_empty && !occupied[index])
1328 +
            continue;
1329 +
        return index;
1330 +
    }
1331 +
    return -1;
1332 +
}
1333 +
1334 +
/* Apply a master-stack configuration command. */
1335 +
bool swm_stack_configure(struct swm_stack_state *state, int command, int side, int *new_side) {
1336 +
    if (!state || !new_side || side < SWM_MASTER_LEFT || side > SWM_MASTER_BOTTOM)
1337 +
        return false;
1338 +
1339 +
    *new_side = side;
1340 +
1341 +
    switch (command) {
1342 +
    case SWM_MASTER_SHRINK:
1343 +
        if (state->msize > 1)
1344 +
            state->msize--;
1345 +
        break;
1346 +
    case SWM_MASTER_GROW:
1347 +
        if (state->msize < SWM_SLICE - 1)
1348 +
            state->msize++;
1349 +
        break;
1350 +
    case SWM_MASTER_ADD:
1351 +
        state->mwin++;
1352 +
        break;
1353 +
    case SWM_MASTER_DEL:
1354 +
        if (state->mwin > 0)
1355 +
            state->mwin--;
1356 +
        break;
1357 +
    case SWM_STACK_INC:
1358 +
        state->stacks++;
1359 +
        break;
1360 +
    case SWM_STACK_DEC:
1361 +
        if (state->stacks > 1)
1362 +
            state->stacks--;
1363 +
        break;
1364 +
    case SWM_STACK_RESET:
1365 +
        state->msize  = SWM_SLICE / 2;
1366 +
        state->mwin   = 1;
1367 +
        state->stacks = 1;
1368 +
1369 +
        if (side == SWM_MASTER_RIGHT)
1370 +
            *new_side = SWM_MASTER_LEFT;
1371 +
        else if (side == SWM_MASTER_BOTTOM)
1372 +
            *new_side = SWM_MASTER_TOP;
1373 +
        break;
1374 +
    case SWM_FLIP_LAYOUT:
1375 +
        *new_side = side == SWM_MASTER_LEFT    ? SWM_MASTER_RIGHT
1376 +
                    : side == SWM_MASTER_RIGHT ? SWM_MASTER_LEFT
1377 +
                    : side == SWM_MASTER_TOP   ? SWM_MASTER_BOTTOM
1378 +
                                               : SWM_MASTER_TOP;
1379 +
        break;
1380 +
    default:
1381 +
        return false;
1382 +
    }
1383 +
    return true;
1384 +
}
1385 +
1386 +
/* Return whether a rule matches a client identity. */
1387 +
bool swm_rule_matches(
1388 +
    const char *rule_id, const char *rule_title, const char *appid, const char *title
1389 +
) {
1390 +
    appid = appid ? appid : "";
1391 +
    title = title ? title : "";
1392 +
    if (!rule_id && !rule_title)
1393 +
        return false;
1394 +
1395 +
    return (!rule_id || !strcmp(rule_id, "*") || strstr(appid, rule_id)) &&
1396 +
           (!rule_title || strstr(title, rule_title));
1397 +
}
1398 +
1399 +
/* Replace state-file delimiters in a field. */
1400 +
void swm_sanitize_field(char *dst, size_t size, const char *src, const char *fallback) {
1401 +
    size_t i;
1402 +
1403 +
    if (!size)
1404 +
        return;
1405 +
1406 +
    if (!src || !*src)
1407 +
        src = fallback ? fallback : "";
1408 +
1409 +
    for (i = 0; src[i] && i + 1 < size; i++)
1410 +
        dst[i] = src[i] == '\t' || src[i] == '\n' || src[i] == '\r' ? ' ' : src[i];
1411 +
1412 +
    dst[i] = '\0';
1413 +
}
1414 +
1415 +
/* Parse one persisted window-state record. */
1416 +
bool swm_parse_window_state(
1417 +
    const char     *line,
1418 +
    char           *appid,
1419 +
    size_t          appid_size,
1420 +
    char           *title,
1421 +
    size_t          title_size,
1422 +
    struct swm_box *geometry
1423 +
) {
1424 +
    char parsed_appid[256], parsed_title[256];
1425 +
    int  fields;
1426 +
1427 +
    if (!line || !appid || !appid_size || !title || !title_size || !geometry)
1428 +
        return false;
1429 +
1430 +
    fields = sscanf(
1431 +
        line,
1432 +
        "%255[^\t]\t%255[^\t]\t%d\t%d\t%d\t%d",
1433 +
        parsed_appid,
1434 +
        parsed_title,
1435 +
        &geometry->x,
1436 +
        &geometry->y,
1437 +
        &geometry->width,
1438 +
        &geometry->height
1439 +
    );
1440 +
1441 +
    if (fields != 6) {
1442 +
        fields = sscanf(
1443 +
            line,
1444 +
            "%255[^\t]\t%d\t%d\t%d\t%d",
1445 +
            parsed_appid,
1446 +
            &geometry->x,
1447 +
            &geometry->y,
1448 +
            &geometry->width,
1449 +
            &geometry->height
1450 +
        );
1451 +
        if (fields != 5)
1452 +
            return false;
1453 +
1454 +
        parsed_title[0] = '\0';
1455 +
    }
1456 +
    swm_sanitize_field(appid, appid_size, parsed_appid, "broken");
1457 +
    swm_sanitize_field(title, title_size, parsed_title, nullptr);
1458 +
1459 +
    return true;
1460 +
}
1461 +
1462 +
/* Compute the effective client border width. */
1463 +
unsigned int swm_border_width(
1464 +
    unsigned int configured,
1465 +
    bool         fullscreen,
1466 +
    bool         borderless,
1467 +
    bool         x11,
1468 +
    bool         unmanaged,
1469 +
    bool         client_side
1470 +
) {
1471 +
    if (fullscreen || borderless || (x11 && unmanaged) || client_side)
1472 +
        return 0;
1473 +
    return configured;
1474 +
}
1475 +
1476 +
/* Find the next enabled layout. */
1477 +
int swm_next_layout(int current, int count, const bool *cycle) {
1478 +
    int step, index;
1479 +
1480 +
    if (!cycle || count <= 0 || current < 0 || current >= count)
1481 +
        return -1;
1482 +
1483 +
    for (step = 1; step <= count; step++) {
1484 +
        index = (current + step) % count;
1485 +
1486 +
        if (cycle[index])
1487 +
            return index;
1488 +
    }
1489 +
    return -1;
1490 +
}
1491 +
1492 +
/* Arrange clients into master and stack areas. */
1493 +
size_t swm_stack_layout(
1494 +
    const struct swm_box         *area,
1495 +
    const struct swm_stack_state *state,
1496 +
    int                           rotate,
1497 +
    int                           flip,
1498 +
    size_t                        count,
1499 +
    struct swm_box               *boxes
1500 +
) {
1501 +
    struct swm_box root = *area, master, stack, column, cell;
1502 +
    int            n    = (int)count, mwin, sn, stacks, slice;
1503 +
    int            j, ci, cw, extra, rows, base, remainder, offset;
1504 +
    size_t         i;
1505 +
1506 +
    if (!count)
1507 +
        return 0;
1508 +
1509 +
    if (rotate) {
1510 +
        int t       = root.x;
1511 +
        root.x      = root.y;
1512 +
        root.y      = t;
1513 +
        t           = root.width;
1514 +
        root.width  = root.height;
1515 +
        root.height = t;
1516 +
    }
1517 +
    slice = root.width / SWM_SLICE;
1518 +
    mwin  = MIN(state->mwin, n);
1519 +
1520 +
    if (mwin < 0)
1521 +
        mwin = 0;
1522 +
    sn     = n - mwin;
1523 +
    stacks = MIN(state->stacks, sn);
1524 +
1525 +
    if (stacks < 0)
1526 +
        stacks = 0;
1527 +
1528 +
    master = stack = root;
1529 +
1530 +
    if (stacks && mwin) {
1531 +
        master.width = slice * state->msize;
1532 +
1533 +
        if (master.width < 0)
1534 +
            master.width = 0;
1535 +
1536 +
        if (master.width > root.width)
1537 +
            master.width = root.width;
1538 +
        stack.width -= master.width;
1539 +
1540 +
        if (flip)
1541 +
            master.x += stack.width;
1542 +
        else
1543 +
            stack.x += master.width;
1544 +
    }
1545 +
    j      = 0;
1546 +
    ci     = -1;
1547 +
    rows   = mwin;
1548 +
    column = master;
1549 +
    cw     = stacks ? stack.width / stacks : 0;
1550 +
    extra  = stacks ? stack.width % stacks : 0;
1551 +
1552 +
    for (i = 0; i < count; i++) {
1553 +
        while (j >= rows) {
1554 +
            if (stacks <= 0)
1555 +
                return i;
1556 +
            j = 0;
1557 +
            ci++;
1558 +
            rows         = sn / stacks + (stacks - ci <= sn % stacks ? 1 : 0);
1559 +
            column       = stack;
1560 +
            column.width = cw + (ci == stacks - 1 ? extra : 0);
1561 +
1562 +
            if (flip)
1563 +
                column.x = stack.x + stack.width - (ci + 1) * cw - (ci == stacks - 1 ? extra : 0);
1564 +
            else
1565 +
                column.x = stack.x + ci * cw;
1566 +
        }
1567 +
        base        = column.height / rows;
1568 +
        remainder   = column.height % rows;
1569 +
        offset      = j * base + MIN(j, remainder);
1570 +
        cell.x      = column.x;
1571 +
        cell.y      = column.y + offset;
1572 +
        cell.width  = column.width;
1573 +
        cell.height = base + (j < remainder ? 1 : 0);
1574 +
1575 +
        if (rotate) {
1576 +
            int t       = cell.x;
1577 +
            cell.x      = cell.y;
1578 +
            cell.y      = t;
1579 +
            t           = cell.width;
1580 +
            cell.width  = cell.height;
1581 +
            cell.height = t;
1582 +
        }
1583 +
        boxes[i] = cell;
1584 +
        j++;
1585 +
    }
1586 +
    return count;
1587 +
}
1588 +
1589 +
/* Function implementations. */
1590 +
/* Clamp client geometry to the supplied bounds. */
1591 +
void apply_bounds(client_t *c, struct wlr_box *bbox) {
1592 +
    swm_box_apply_bounds((struct swm_box *)&c->geom, (const struct swm_box *)bbox, c->bw);
1593 +
}
1594 +
1595 +
/* Return the border width appropriate for a window's type and state. */
1596 +
unsigned int client_border_width(client_t *c) {
1597 +
    bool x11 = false, unmanaged = false;
1598 +
    bool client_side = c->decoration && c->decoration->requested_mode ==
1599 +
                                            WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE;
1600 +
    x11              = client_is_x11(c);
1601 +
    unmanaged        = x11 && client_is_unmanaged(c);
1602 +
    return swm_border_width(
1603 +
        borderwidth, c->is_fullscreen, c->is_borderless, x11, unmanaged, client_side
1604 +
    );
1605 +
}
1606 +
1607 +
/* Apply matching rules to choose a window's workspace, display, and style. */
1608 +
void apply_rules(client_t *c, workspace_t *defaultws) {
1609 +
    /* Later matching rules override earlier ones. */
1610 +
    const char   *appid, *title;
1611 +
    workspace_t  *ws = defaultws;
1612 +
    int           i;
1613 +
    const rule_t *r;
1614 +
    monitor_t    *mon = defaultws && defaultws->mon ? defaultws->mon : selmon, *m;
1615 +
1616 +
    appid = client_get_appid(c);
1617 +
    title = client_get_title(c);
1618 +
1619 +
    for (r = rules; r < END(rules); r++) {
1620 +
        if (swm_rule_matches(r->id, r->title, appid, title)) {
1621 +
            c->is_floating   = r->is_floating;
1622 +
            c->is_borderless = r->borderless;
1623 +
1624 +
            if (r->ws >= 0 && r->ws < WSCOUNT)
1625 +
                ws = &workspaces[r->ws];
1626 +
            i = 0;
1627 +
            wl_list_for_each(m, &mons, link) {
1628 +
                if (r->monitor == i++)
1629 +
                    mon = m;
1630 +
            }
1631 +
        }
1632 +
    }
1633 +
    c->is_floating |= client_is_float_type(c);
1634 +
    set_monitor(c, mon, ws);
1635 +
}
1636 +
1637 +
/* Update the visibility, layer, size, and focus of windows on a display. */
1638 +
void arrange(monitor_t *m) {
1639 +
    client_t *c;
1640 +
    client_t *focus;
1641 +
    int       max;
1642 +
1643 +
    if (!m->wlr_output->enabled)
1644 +
        return;
1645 +
1646 +
    focus = focus_top(m);
1647 +
    max   = m->ws && m->ws->lt->arrange == max_stack;
1648 +
    wl_list_for_each(c, &clients, link) {
1649 +
        if (c->mon == m) {
1650 +
            int visible;
1651 +
1652 +
            if (c->is_max_stacked && (!c->ws || c->ws->lt->arrange != max_stack)) {
1653 +
                c->is_max_stacked = false;
1654 +
                resize(c, c->maxstack_prev, 0);
1655 +
            }
1656 +
            visible = !c->pending_map && VISIBLEON(c, m) &&
1657 +
                      (!max || c->is_fullscreen || clients_related(c, focus));
1658 +
            wlr_scene_node_set_enabled(&c->scene->node, visible);
1659 +
            client_set_suspended(c, !visible);
1660 +
        }
1661 +
    }
1662 +
    wlr_scene_node_set_enabled(&m->fullscreen_bg->node, focus && focus->is_fullscreen);
1663 +
1664 +
    /* In a floating layout, keep tiled and floating windows in one layer so
1665 +
     * their normal stacking order is preserved. */
1666 +
    wl_list_for_each(c, &clients, link) {
1667 +
        if (c->mon != m || !m->ws || c->scene->node.parent == layers[LAYER_FULLSCREEN])
1668 +
            continue;
1669 +
1670 +
        wlr_scene_node_reparent(
1671 +
            &c->scene->node,
1672 +
            (!m->ws->lt->arrange && c->is_floating)  ? layers[LAYER_TILE]
1673 +
            : (m->ws->lt->arrange && c->is_floating) ? layers[LAYER_FLOAT]
1674 +
                                                     : c->scene->node.parent
1675 +
        );
1676 +
    }
1677 +
    if (m->ws && m->ws->lt->arrange)
1678 +
        m->ws->lt->arrange(m);
1679 +
    /* Recheck pointer focus after windows move. Avoid repeating this if the
1680 +
     * focus change itself rearranges the max layout. */
1681 +
    if (!arranging_pointer_focus) {
1682 +
        arranging_pointer_focus = true;
1683 +
        motion_notify(0, nullptr, 0, 0, 0, 0, 1);
1684 +
        arranging_pointer_focus = false;
1685 +
    }
1686 +
    check_idle_inhibitor(nullptr);
1687 +
}
1688 +
1689 +
/* Position the surfaces in one desktop layer and reserve any requested space. */
1690 +
void arrange_layer(monitor_t *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) {
1691 +
    layer_surface_t *l;
1692 +
    struct wlr_box   full_area = m->m;
1693 +
1694 +
    wl_list_for_each(l, list, link) {
1695 +
        struct wlr_layer_surface_v1 *layer_surface = l->layer_surface;
1696 +
1697 +
        if (!layer_surface->initialized)
1698 +
            continue;
1699 +
1700 +
        if (exclusive != (layer_surface->current.exclusive_zone > 0))
1701 +
            continue;
1702 +
1703 +
        wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area);
1704 +
        wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y);
1705 +
1706 +
        if (l->dim) {
1707 +
            wlr_scene_node_set_position(&l->dim->node, full_area.x, full_area.y);
1708 +
            wlr_scene_rect_set_size(l->dim, full_area.width, full_area.height);
1709 +
        }
1710 +
    }
1711 +
}
1712 +
1713 +
/* Arrange desktop layers and give keyboard focus to the highest eligible surface. */
1714 +
void arrange_layers(monitor_t *m) {
1715 +
    int              i;
1716 +
    struct wlr_box   usable_area = m->m;
1717 +
    layer_surface_t *l;
1718 +
    uint32_t         layers_above_shell[] = {
1719 +
        ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
1720 +
        ZWLR_LAYER_SHELL_V1_LAYER_TOP,
1721 +
    };
1722 +
1723 +
    if (!m->wlr_output->enabled)
1724 +
        return;
1725 +
1726 +
    /* Reserve space for panels from the top layer down. */
1727 +
    for (i = 3; i >= 0; i--)
1728 +
        arrange_layer(m, &m->layers[i], &usable_area, 1);
1729 +
1730 +
    if (!wlr_box_equal(&usable_area, &m->w)) {
1731 +
        m->w = usable_area;
1732 +
        arrange(m);
1733 +
    }
1734 +
    /* Place overlays that do not reserve space, from the top layer down. */
1735 +
    for (i = 3; i >= 0; i--)
1736 +
        arrange_layer(m, &m->layers[i], &usable_area, 0);
1737 +
1738 +
    /* Give keyboard focus to the highest layer that requests it. */
1739 +
    for (i = 0; i < (int)LENGTH(layers_above_shell); i++) {
1740 +
        wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) {
1741 +
            if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped)
1742 +
                continue;
1743 +
            /* Deactivate the focused client. */
1744 +
            focus_client(nullptr, 0);
1745 +
            exclusive_focus = l;
1746 +
            client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat));
1747 +
            return;
1748 +
        }
1749 +
    }
1750 +
}
1751 +
1752 +
/* Expand $NAME variables into caller-owned stack storage. */
1753 +
void expand_argv(const char *const *argv, char **expanded, char *storage) {
1754 +
    size_t i, remaining = MAX_COMMAND_SIZE, size;
1755 +
1756 +
    for (i = 0; argv[i]; i++) {
1757 +
        if (i + 1 >= MAX_COMMAND_ARGS)
1758 +
            die("command has too many arguments");
1759 +
        expanded[i] = storage;
1760 +
        if (!(size = env_expand(storage, remaining, argv[i])))
1761 +
            die("expanded command exceeds %d bytes", MAX_COMMAND_SIZE);
1762 +
        storage   += size;
1763 +
        remaining -= size;
1764 +
    }
1765 +
    expanded[i] = nullptr;
1766 +
}
1767 +
1768 +
/* Start every command in the configured autostart list. */
1769 +
void autostart_exec(void) {
1770 +
    /* Run each configured startup command. A nullptr separates commands, and
1771 +
     * a second nullptr ends the list. */
1772 +
    const char *const *p;
1773 +
    size_t             i = 0;
1774 +
1775 +
    for (p = autostart; *p && autostart_len < MAX_AUTOSTART; autostart_len++, p++)
1776 +
1777 +
        while (*++p)
1778 +
            ;
1779 +
1780 +
    if (*p)
1781 +
        fprintf(stderr, "swm: autostart limit reached (%d)\n", MAX_AUTOSTART);
1782 +
1783 +
    for (p = autostart; *p && i < autostart_len; i++, p++) {
1784 +
        if ((autostart_pids[i] = fork()) == 0) {
1785 +
            char *argv[MAX_COMMAND_ARGS], storage[MAX_COMMAND_SIZE];
1786 +
1787 +
            expand_argv(p, argv, storage);
1788 +
1789 +
            prepare_child();
1790 +
            setsid();
1791 +
            execvp(argv[0], argv);
1792 +
            die("autostart: execvp %s:", argv[0]);
1793 +
        }
1794 +
        while (*++p)
1795 +
            ;
1796 +
    }
1797 +
}
1798 +
1799 +
/* Forward a scroll event to the application under the pointer. */
1800 +
void axis_notify(struct wl_listener *listener, void *data) {
1801 +
    /* A mouse wheel or touchpad produced a scrolling event. */
1802 +
    struct wlr_pointer_axis_event *event = data;
1803 +
1804 +
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
1805 +
    /* TODO: Allow scroll events to trigger compositor bindings. */
1806 +
    /* Forward the scroll event to the application under the pointer. */
1807 +
    wlr_seat_pointer_notify_axis(
1808 +
        seat,
1809 +
        event->time_msec,
1810 +
        event->orientation,
1811 +
        event->delta,
1812 +
        event->delta_discrete,
1813 +
        event->source,
1814 +
        event->relative_direction
1815 +
    );
1816 +
}
1817 +
1818 +
/* Focus, move, or resize a window in response to a pointer button. */
1819 +
void button_press(struct wl_listener *listener, void *data) {
1820 +
    struct wlr_pointer_button_event *event = data;
1821 +
    struct wlr_keyboard             *keyboard;
1822 +
    uint32_t                         mods;
1823 +
    client_t                        *c;
1824 +
    const button_t                  *b;
1825 +
1826 +
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
1827 +
1828 +
    switch (event->state) {
1829 +
    case WL_POINTER_BUTTON_STATE_PRESSED:
1830 +
        cursor_mode = CURSOR_PRESSED;
1831 +
        selmon      = point_to_monitor(cursor->x, cursor->y);
1832 +
1833 +
        if (locked)
1834 +
            break;
1835 +
1836 +
        /* Focus a window when a button is pressed over it. */
1837 +
        point_to_node(cursor->x, cursor->y, nullptr, &c, nullptr, nullptr, nullptr);
1838 +
1839 +
        if (c && (!client_is_unmanaged(c) || client_wants_focus(c)))
1840 +
            focus_client(c, 1);
1841 +
1842 +
        keyboard = wlr_seat_get_keyboard(seat);
1843 +
        mods     = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0;
1844 +
1845 +
        for (b = buttons; b < END(buttons); b++) {
1846 +
            if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && b->func) {
1847 +
                b->func(&b->arg);
1848 +
                return;
1849 +
            }
1850 +
        }
1851 +
        break;
1852 +
    case WL_POINTER_BUTTON_STATE_RELEASED:
1853 +
        /* Releasing any button ends an interactive move or resize. */
1854 +
        /* TODO: Restore the cursor requested by the window under the pointer. */
1855 +
        if (!locked && cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED) {
1856 +
            if (cursor_mode == CURSOR_RESIZE)
1857 +
                client_set_resizing(grabc, 0);
1858 +
            wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
1859 +
            cursor_mode = CURSOR_NORMAL;
1860 +
            /* Move the window to the workspace on its new display. */
1861 +
            selmon = point_to_monitor(cursor->x, cursor->y);
1862 +
            set_monitor(grabc, selmon, 0);
1863 +
            remember_client(grabc);
1864 +
            grabc = nullptr;
1865 +
            return;
1866 +
        }
1867 +
        cursor_mode = CURSOR_NORMAL;
1868 +
        break;
1869 +
    }
1870 +
    /* Forward buttons that were not handled here to the application under the pointer. */
1871 +
    wlr_seat_pointer_notify_button(seat, event->time_msec, event->button, event->state);
1872 +
}
1873 +
1874 +
/* Switch to the requested virtual terminal. */
1875 +
void change_vt(const arg_t *arg) {
1876 +
    wlr_session_change_vt(session, arg->u);
1877 +
}
1878 +
1879 +
/* Prevent idling while a visible application requests it. */
1880 +
void check_idle_inhibitor(struct wlr_surface *exclude) {
1881 +
    int                           inhibited = 0, unused_lx, unused_ly;
1882 +
    struct wlr_idle_inhibitor_v1 *inhibitor;
1883 +
1884 +
    wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) {
1885 +
        struct wlr_surface    *surface = wlr_surface_get_root_surface(inhibitor->surface);
1886 +
        struct wlr_scene_tree *tree    = surface->data;
1887 +
1888 +
        if (exclude != surface && tree &&
1889 +
            wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)) {
1890 +
            inhibited = 1;
1891 +
            break;
1892 +
        }
1893 +
    }
1894 +
    wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
1895 +
}
1896 +
1897 +
/* Release compositor resources in dependency order before exiting. */
1898 +
void cleanup(void) {
1899 +
    size_t i;
1900 +
1901 +
    cleanup_listeners();
1902 +
    /* Destroy windows before the protocol objects that listen to their
1903 +
     * surfaces, so popup cleanup cannot call an expired listener. */
1904 +
    wl_display_destroy_clients(dpy);
1905 +
1906 +
    if (xwayland) {
1907 +
        wlr_xwayland_destroy(xwayland);
1908 +
        xwayland = nullptr;
1909 +
    }
1910 +
    if (child_pid > 0) {
1911 +
        kill(-child_pid, SIGTERM);
1912 +
        waitpid(child_pid, nullptr, WNOHANG);
1913 +
    }
1914 +
    for (i = 0; i < autostart_len; i++) {
1915 +
        if (autostart_pids[i] > 0) {
1916 +
            kill(-autostart_pids[i], SIGTERM);
1917 +
            waitpid(autostart_pids[i], nullptr, WNOHANG);
1918 +
        }
1919 +
    }
1920 +
    for (i = 0; i < LENGTH(signal_sources); i++)
1921 +
        wl_event_source_remove(signal_sources[i]);
1922 +
    wlr_xcursor_manager_destroy(cursor_mgr);
1923 +
1924 +
    destroy_keyboard_group(&kb_group->destroy, nullptr);
1925 +
1926 +
    /* Destroy this explicitly because wlroots may otherwise retain a pointer
1927 +
     * to the seat after the seat is gone. */
1928 +
    wlr_backend_destroy(backend);
1929 +
1930 +
    wl_display_destroy(dpy);
1931 +
    /* Destroy the scene after the display, once every display is gone. */
1932 +
    wlr_scene_node_destroy(&scene->tree.node);
1933 +
}
1934 +
1935 +
/* Remove a disconnected display and release everything attached to it. */
1936 +
void cleanup_monitor(struct wl_listener *listener, void *data) {
1937 +
    monitor_t       *m = wl_container_of(listener, m, destroy);
1938 +
    layer_surface_t *l, *tmp;
1939 +
    size_t           i;
1940 +
1941 +
    /* The layer lists belong to the display and disappear with it. */
1942 +
    for (i = 0; i < LENGTH(m->layers); i++) {
1943 +
        wl_list_for_each_safe(l, tmp, &m->layers[i], link)
1944 +
            wlr_layer_surface_v1_destroy(l->layer_surface);
1945 +
    }
1946 +
    wl_list_remove(&m->destroy.link);
1947 +
    wl_list_remove(&m->frame.link);
1948 +
    wl_list_remove(&m->link);
1949 +
    wl_list_remove(&m->request_state.link);
1950 +
    wl_list_remove(&m->output_bind.link);
1951 +
1952 +
    if (m->lock_surface)
1953 +
        destroy_lock_surface(&m->destroy_lock_surface, nullptr);
1954 +
    m->wlr_output->data = nullptr;
1955 +
    wlr_output_layout_remove(output_layout, m->wlr_output);
1956 +
    wlr_scene_output_destroy(m->scene_output);
1957 +
1958 +
    close_monitor(m);
1959 +
    wlr_scene_node_destroy(&m->fullscreen_bg->node);
1960 +
    pool_release(&monitor_pool, m);
1961 +
}
1962 +
1963 +
/* Detach global event listeners before their event sources are destroyed. */
1964 +
void cleanup_listeners(void) {
1965 +
    wl_list_remove(&cursor_axis.link);
1966 +
    wl_list_remove(&cursor_button.link);
1967 +
    wl_list_remove(&cursor_frame_listener.link);
1968 +
    wl_list_remove(&cursor_motion.link);
1969 +
    wl_list_remove(&cursor_motion_absolute.link);
1970 +
    wl_list_remove(&gpu_reset_listener.link);
1971 +
    wl_list_remove(&new_idle_inhibitor.link);
1972 +
    wl_list_remove(&layout_change.link);
1973 +
    wl_list_remove(&new_input_device.link);
1974 +
    wl_list_remove(&new_input_method.link);
1975 +
    wl_list_remove(&new_virtual_keyboard.link);
1976 +
    wl_list_remove(&new_virtual_pointer.link);
1977 +
    wl_list_remove(&new_pointer_constraint.link);
1978 +
    wl_list_remove(&new_shortcuts_inhibitor_listener.link);
1979 +
    wl_list_remove(&new_output.link);
1980 +
    wl_list_remove(&new_xdg_toplevel.link);
1981 +
    wl_list_remove(&new_text_input.link);
1982 +
    wl_list_remove(&new_xdg_decoration.link);
1983 +
    wl_list_remove(&new_xdg_popup.link);
1984 +
    wl_list_remove(&new_layer_surface.link);
1985 +
    wl_list_remove(&output_mgr_apply.link);
1986 +
    wl_list_remove(&output_mgr_test.link);
1987 +
    wl_list_remove(&output_power_mgr_set_mode.link);
1988 +
    wl_list_remove(&request_activate.link);
1989 +
    wl_list_remove(&request_cursor.link);
1990 +
    wl_list_remove(&request_set_psel.link);
1991 +
    wl_list_remove(&request_set_sel.link);
1992 +
    wl_list_remove(&request_set_cursor_shape.link);
1993 +
    wl_list_remove(&request_start_drag_listener.link);
1994 +
    wl_list_remove(&start_drag_listener.link);
1995 +
    wl_list_remove(&new_session_lock.link);
1996 +
1997 +
    if (xwayland_listeners_registered) {
1998 +
        wl_list_remove(&new_xwayland_surface.link);
1999 +
        wl_list_remove(&xwayland_ready_listener.link);
2000 +
        xwayland_listeners_registered = false;
2001 +
    }
2002 +
}
2003 +
2004 +
/* Hide a display's workspace and move its windows to a valid display. */
2005 +
void close_monitor(monitor_t *m) {
2006 +
    /* If the selected display closes, select another one. Its workspace
2007 +
     * becomes hidden, but its windows remain there. */
2008 +
    client_t      *c;
2009 +
    monitor_t     *candidate;
2010 +
    workspace_t   *ws;
2011 +
    struct wlr_box geom;
2012 +
2013 +
    if (m == selmon || (selmon && !selmon->wlr_output->enabled)) {
2014 +
        selmon = nullptr;
2015 +
        wl_list_for_each(candidate, &mons, link) {
2016 +
            if (candidate != m && candidate->wlr_output->enabled) {
2017 +
                selmon = candidate;
2018 +
                break;
2019 +
            }
2020 +
        }
2021 +
    }
2022 +
    if (m->ws)
2023 +
        m->ws->mon = nullptr;
2024 +
    m->ws = m->previous_workspace = nullptr;
2025 +
2026 +
    for (ws = workspaces; ws < END(workspaces); ws++) {
2027 +
        if (ws->mon == m)
2028 +
            ws->mon = nullptr;
2029 +
    }
2030 +
    /* Keep each window tied to a valid display; its workspace decides visibility. */
2031 +
    wl_list_for_each(c, &clients, link) {
2032 +
        if (c->mon == m) {
2033 +
            geom = c->geom;
2034 +
2035 +
            if (c->is_floating && selmon) {
2036 +
                geom.x = selmon->m.x + c->geom.x - m->m.x;
2037 +
                geom.y = selmon->m.y + c->geom.y - m->m.y;
2038 +
            }
2039 +
            c->mon = selmon;
2040 +
2041 +
            if (selmon)
2042 +
                resize(c, c->is_fullscreen ? selmon->m : geom, 0);
2043 +
        }
2044 +
        if (c->ftl_monitor == m) {
2045 +
            if (c->ftl && !m->wlr_output->enabled)
2046 +
                wlr_foreign_toplevel_handle_v1_output_leave(c->ftl, m->wlr_output);
2047 +
            c->ftl_monitor = nullptr;
2048 +
        }
2049 +
    }
2050 +
    focus_client(focus_top(selmon), 1);
2051 +
    print_status();
2052 +
}
2053 +
2054 +
/* Apply a panel or background surface's newly committed state. */
2055 +
void layer_surface_commit_notify(struct wl_listener *listener, void *data) {
2056 +
    layer_surface_t                  *l             = wl_container_of(listener, l, surface_commit);
2057 +
    struct wlr_layer_surface_v1      *layer_surface = l->layer_surface;
2058 +
    struct wlr_scene_tree            *scene_layer = layers[layermap[layer_surface->current.layer]];
2059 +
    struct wlr_layer_surface_v1_state old_state;
2060 +
2061 +
    if (l->layer_surface->initial_commit) {
2062 +
        client_set_scale(layer_surface->surface, l->mon->wlr_output->scale);
2063 +
2064 +
        /* Arrange the surface using the state it is about to commit. */
2065 +
        old_state                 = l->layer_surface->current;
2066 +
        l->layer_surface->current = l->layer_surface->pending;
2067 +
        arrange_layers(l->mon);
2068 +
        l->layer_surface->current = old_state;
2069 +
        return;
2070 +
    }
2071 +
    if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped)
2072 +
        return;
2073 +
    l->mapped = layer_surface->surface->mapped;
2074 +
2075 +
    if (l->dim)
2076 +
        wlr_scene_node_set_enabled(&l->dim->node, l->mapped);
2077 +
2078 +
    if (scene_layer != l->scene->node.parent) {
2079 +
        wlr_scene_node_reparent(&l->scene->node, scene_layer);
2080 +
2081 +
        if (l->dim) {
2082 +
            wlr_scene_node_reparent(&l->dim->node, scene_layer);
2083 +
            wlr_scene_node_place_below(&l->dim->node, &l->scene->node);
2084 +
        }
2085 +
        wl_list_remove(&l->link);
2086 +
        wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link);
2087 +
        wlr_scene_node_reparent(
2088 +
            &l->popups->node,
2089 +
            (layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LAYER_TOP]
2090 +
                                                                          : scene_layer)
2091 +
        );
2092 +
    }
2093 +
    arrange_layers(l->mon);
2094 +
}
2095 +
2096 +
/* Apply a Wayland window's initial state or confirmed resize. */
2097 +
void commit_notify(struct wl_listener *listener, void *data) {
2098 +
    client_t      *c = wl_container_of(listener, c, commit);
2099 +
    struct wlr_box geom;
2100 +
2101 +
    if (c->surface.xdg->initial_commit) {
2102 +
        /* Pick a display early so the application receives the right scale.
2103 +
         * A title-based rule may move it once its title is available. */
2104 +
        apply_rules(c, nullptr);
2105 +
2106 +
        if (c->mon) {
2107 +
            client_set_scale(client_surface(c), c->mon->wlr_output->scale);
2108 +
        }
2109 +
        set_monitor(c, nullptr, 0); /* Reapply matching rules when the window maps. */
2110 +
2111 +
        wlr_xdg_toplevel_set_wm_capabilities(
2112 +
            c->surface.xdg->toplevel, WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN
2113 +
        );
2114 +
2115 +
        if (c->decoration)
2116 +
            request_decoration_mode(&c->set_decoration_mode, c->decoration);
2117 +
        wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0);
2118 +
        return;
2119 +
    }
2120 +
    /* Some applications, especially terminals, round requested sizes. Once
2121 +
     * they confirm the resize, use the size they actually chose. */
2122 +
    if (!c->is_fullscreen && c->resize) {
2123 +
        client_get_geometry(c, &geom);
2124 +
        swm_box_reconcile_commit(
2125 +
            (struct swm_box *)&c->geom, geom.width, geom.height, c->bw, c->resize_edges
2126 +
        );
2127 +
    }
2128 +
    if (c->resize) {
2129 +
        uint32_t resize_serial = c->resize;
2130 +
        int      latest        = resize_serial <= c->surface.xdg->current.configure_serial;
2131 +
2132 +
        apply_bounds(c, &c->mon->w);
2133 +
        resize_apply(c);
2134 +
2135 +
        if (latest)
2136 +
            c->resize = 0;
2137 +
        else
2138 +
            c->geom = c->pending_geom;
2139 +
    } else {
2140 +
        resize(c, c->geom, c->is_floating && !c->is_fullscreen);
2141 +
    }
2142 +
    if (!c->resize)
2143 +
        c->resize_edges = WLR_EDGE_NONE;
2144 +
}
2145 +
2146 +
/* Create and constrain a popup after its initial state is committed. */
2147 +
void popup_commit(struct wl_listener *listener, void *data) {
2148 +
    struct wlr_surface   *surface = data;
2149 +
    struct wlr_xdg_popup *popup   = wlr_xdg_popup_try_from_wlr_surface(surface);
2150 +
    layer_surface_t      *l       = nullptr;
2151 +
    client_t             *c       = nullptr;
2152 +
    struct wlr_box        box;
2153 +
    int                   type = -1;
2154 +
2155 +
    if (!popup) {
2156 +
        wl_list_remove(&listener->link);
2157 +
        listener_release(listener);
2158 +
        return;
2159 +
    }
2160 +
    if (!popup->base->initial_commit)
2161 +
        return;
2162 +
2163 +
    type = toplevel_from_wlr_surface(popup->base->surface, &c, &l);
2164 +
2165 +
    if ((type == LAYER_SHELL && (!l || !l->mon)) || (type != LAYER_SHELL && (!c || !c->mon)) ||
2166 +
        !popup->parent || !popup->parent->data)
2167 +
        goto destroy_popup;
2168 +
    popup->base->surface->data = wlr_scene_xdg_surface_create(popup->parent->data, popup->base);
2169 +
2170 +
    if (!popup->base->surface->data)
2171 +
        goto destroy_popup;
2172 +
    box    = type == LAYER_SHELL ? l->mon->m : c->mon->w;
2173 +
    box.x -= (type == LAYER_SHELL ? l->scene->node.x : c->geom.x);
2174 +
    box.y -= (type == LAYER_SHELL ? l->scene->node.y : c->geom.y);
2175 +
    wlr_xdg_popup_unconstrain_from_box(popup, &box);
2176 +
    wl_list_remove(&listener->link);
2177 +
    listener_release(listener);
2178 +
    return;
2179 +
2180 +
destroy_popup:
2181 +
    wl_list_remove(&listener->link);
2182 +
    listener_release(listener);
2183 +
    wlr_xdg_popup_destroy(popup);
2184 +
}
2185 +
2186 +
/* Attach an event listener from the fixed-capacity listener pool. */
2187 +
void listen_static(struct wl_signal *signal, wl_notify_func_t notify) {
2188 +
    static_listener_t *slot = pool_take(&static_listener_pool);
2189 +
2190 +
    if (!slot)
2191 +
        return;
2192 +
    slot->listener.notify = notify;
2193 +
    wl_signal_add(signal, &slot->listener);
2194 +
}
2195 +
2196 +
/* Release a listener allocated from the static pool. */
2197 +
void listener_release(struct wl_listener *listener) {
2198 +
    static_listener_t *slot = wl_container_of(listener, slot, listener);
2199 +
    pool_release(&static_listener_pool, slot);
2200 +
}
2201 +
2202 +
/* Track a window's requested border-decoration mode. */
2203 +
void create_decoration(struct wl_listener *listener, void *data) {
2204 +
    struct wlr_xdg_toplevel_decoration_v1 *deco = data;
2205 +
    client_t                              *c    = deco->toplevel->base->data;
2206 +
2207 +
    if (!c)
2208 +
        return;
2209 +
    c->decoration = deco;
2210 +
2211 +
    LISTEN(&deco->events.request_mode, &c->set_decoration_mode, request_decoration_mode);
2212 +
    LISTEN(&deco->events.destroy, &c->destroy_decoration, destroy_decoration);
2213 +
2214 +
    request_decoration_mode(&c->set_decoration_mode, deco);
2215 +
}
2216 +
2217 +
/* Track a new request to keep the session awake. */
2218 +
void create_idle_inhibitor(struct wl_listener *listener, void *data) {
2219 +
    struct wlr_idle_inhibitor_v1 *idle_inhibitor = data;
2220 +
2221 +
    LISTEN_STATIC(&idle_inhibitor->events.destroy, destroy_idle_inhibitor);
2222 +
2223 +
    check_idle_inhibitor(nullptr);
2224 +
}
2225 +
2226 +
/* Add a physical keyboard to the shared keyboard group. */
2227 +
void create_keyboard(struct wlr_keyboard *keyboard) {
2228 +
    /* Use the keyboard group's key mapping. */
2229 +
    wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap);
2230 +
2231 +
    /* Add the keyboard to the shared group. */
2232 +
    wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard);
2233 +
}
2234 +
2235 +
/* Create a keyboard group with the configured key map and repeat settings. */
2236 +
keyboard_group_t *create_keyboard_group(bool is_virtual) {
2237 +
    keyboard_group_t   *group = pool_take(&keyboard_group_pool);
2238 +
    struct xkb_context *context;
2239 +
    struct xkb_keymap  *keymap;
2240 +
2241 +
    if (!group)
2242 +
        return nullptr;
2243 +
    group->wlr_group = wlr_keyboard_group_create();
2244 +
2245 +
    if (!group->wlr_group) {
2246 +
        pool_release(&keyboard_group_pool, group);
2247 +
        return nullptr;
2248 +
    }
2249 +
    group->wlr_group->data = group;
2250 +
    group->is_virtual      = is_virtual;
2251 +
2252 +
    /* Prepare an XKB keymap and assign it to the keyboard group. */
2253 +
    context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
2254 +
2255 +
    if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS)))
2256 +
        die("failed to compile keymap");
2257 +
2258 +
    wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap);
2259 +
    xkb_keymap_unref(keymap);
2260 +
    xkb_context_unref(context);
2261 +
2262 +
    wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, repeat_rate, repeat_delay);
2263 +
2264 +
    /* Listen for key and modifier changes. */
2265 +
    LISTEN(&group->wlr_group->keyboard.events.key, &group->key, key_press);
2266 +
    LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, key_press_modifiers);
2267 +
2268 +
    group->key_repeat_source = wl_event_loop_add_timer(event_loop, key_repeat, group);
2269 +
2270 +
    /* Wayland exposes one active keyboard per seat, so combine physical
2271 +
     * keyboards into one group. */
2272 +
    if (!is_virtual)
2273 +
        wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
2274 +
    return group;
2275 +
}
2276 +
2277 +
/* Add a new panel, background, or overlay surface to its display. */
2278 +
void create_layer_surface(struct wl_listener *listener, void *data) {
2279 +
    struct wlr_layer_surface_v1 *layer_surface = data;
2280 +
    layer_surface_t             *l;
2281 +
    const layer_rule_t          *r;
2282 +
    monitor_t                   *m;
2283 +
    struct wlr_surface          *surface     = layer_surface->surface;
2284 +
    struct wlr_scene_tree       *scene_layer = layers[layermap[layer_surface->pending.layer]];
2285 +
2286 +
    if (!layer_surface->output &&
2287 +
        !(layer_surface->output = selmon ? selmon->wlr_output : nullptr)) {
2288 +
        wlr_layer_surface_v1_destroy(layer_surface);
2289 +
        return;
2290 +
    }
2291 +
    if (!(m = layer_surface->output->data)) {
2292 +
        wlr_layer_surface_v1_destroy(layer_surface);
2293 +
        return;
2294 +
    }
2295 +
    if (!(l = pool_take(&layer_surface_pool))) {
2296 +
        wlr_layer_surface_v1_destroy(layer_surface);
2297 +
        return;
2298 +
    }
2299 +
    l->type          = LAYER_SHELL;
2300 +
    l->layer_surface = layer_surface;
2301 +
    l->mon           = m;
2302 +
    l->scene_layer   = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface);
2303 +
2304 +
    if (!l->scene_layer) {
2305 +
        pool_release(&layer_surface_pool, l);
2306 +
        wlr_layer_surface_v1_destroy(layer_surface);
2307 +
        return;
2308 +
    }
2309 +
    l->scene = l->scene_layer->tree;
2310 +
2311 +
    for (r = layerrules; r < END(layerrules); r++) {
2312 +
        if (strcmp(layer_surface->namespace, r->namespace))
2313 +
            continue;
2314 +
        l->dim = wlr_scene_rect_create(scene_layer, 0, 0, r->dim);
2315 +
2316 +
        if (!l->dim) {
2317 +
            wlr_scene_node_destroy(&l->scene->node);
2318 +
            pool_release(&layer_surface_pool, l);
2319 +
            wlr_layer_surface_v1_destroy(layer_surface);
2320 +
            return;
2321 +
        }
2322 +
        wlr_scene_node_place_below(&l->dim->node, &l->scene->node);
2323 +
        wlr_scene_node_set_enabled(&l->dim->node, 0);
2324 +
        break;
2325 +
    }
2326 +
    l->popups = wlr_scene_tree_create(
2327 +
        layer_surface->current.layer < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LAYER_TOP]
2328 +
                                                                     : scene_layer
2329 +
    );
2330 +
2331 +
    if (!l->popups) {
2332 +
        if (l->dim)
2333 +
            wlr_scene_node_destroy(&l->dim->node);
2334 +
        wlr_scene_node_destroy(&l->scene->node);
2335 +
        pool_release(&layer_surface_pool, l);
2336 +
        wlr_layer_surface_v1_destroy(layer_surface);
2337 +
        return;
2338 +
    }
2339 +
    layer_surface->data = l;
2340 +
    surface->data       = l->popups;
2341 +
    l->scene->node.data = l->popups->node.data = l;
2342 +
    LISTEN(&surface->events.commit, &l->surface_commit, layer_surface_commit_notify);
2343 +
    LISTEN(&surface->events.unmap, &l->unmap, layer_surface_unmap_notify);
2344 +
    LISTEN(&layer_surface->events.destroy, &l->destroy, layer_surface_destroy_notify);
2345 +
2346 +
    wl_list_insert(&l->mon->layers[layer_surface->pending.layer], &l->link);
2347 +
    wlr_surface_send_enter(surface, layer_surface->output);
2348 +
}
2349 +
2350 +
/* Cover a display with a new session-lock surface. */
2351 +
void create_lock_surface(struct wl_listener *listener, void *data) {
2352 +
    session_lock_t                     *lock         = wl_container_of(listener, lock, new_surface);
2353 +
    struct wlr_session_lock_surface_v1 *lock_surface = data;
2354 +
    monitor_t             *m = lock_surface->output ? lock_surface->output->data : nullptr;
2355 +
    struct wlr_scene_tree *scene_tree;
2356 +
2357 +
    if (!m) {
2358 +
        wlr_session_lock_v1_destroy(lock->lock);
2359 +
        return;
2360 +
    }
2361 +
    scene_tree = lock_surface->surface->data =
2362 +
        wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface);
2363 +
2364 +
    if (!scene_tree) {
2365 +
        wlr_session_lock_v1_destroy(lock->lock);
2366 +
        return;
2367 +
    }
2368 +
    m->lock_surface = lock_surface;
2369 +
2370 +
    wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
2371 +
    wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height);
2372 +
2373 +
    LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroy_lock_surface);
2374 +
2375 +
    if (m == selmon)
2376 +
        client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat));
2377 +
}
2378 +
2379 +
/* Configure and publish a newly connected display. */
2380 +
void create_monitor(struct wl_listener *listener, void *data) {
2381 +
    /* The backend found a new display. */
2382 +
    struct wlr_output      *wlr_output = data;
2383 +
    const monitor_rule_t   *r;
2384 +
    size_t                  i;
2385 +
    struct wlr_output_state state;
2386 +
    monitor_t              *m;
2387 +
2388 +
    if (!wlr_output_init_render(wlr_output, alloc, drw))
2389 +
        return;
2390 +
2391 +
    if (!(m = pool_take(&monitor_pool))) {
2392 +
        fprintf(stderr, "swm: ignoring output %s: monitor limit reached\n", wlr_output->name);
2393 +
        return;
2394 +
    }
2395 +
    wlr_output->data = m;
2396 +
    m->wlr_output    = wlr_output;
2397 +
2398 +
    for (i = 0; i < LENGTH(m->layers); i++)
2399 +
        wl_list_init(&m->layers[i]);
2400 +
2401 +
    wlr_output_state_init(&state);
2402 +
    /* Initialize the display from its matching configuration rule. */
2403 +
    for (r = monrules; r < END(monrules); r++) {
2404 +
        if (!r->name || strstr(wlr_output->name, r->name)) {
2405 +
            m->m.x = r->x;
2406 +
            m->m.y = r->y;
2407 +
            wlr_output_state_set_scale(&state, r->scale);
2408 +
            wlr_output_state_set_transform(&state, r->rr);
2409 +
            break;
2410 +
        }
2411 +
    }
2412 +
    /* Show the lowest-numbered hidden workspace on this output; client
2413 +
     * monitor pointers are synced in update_monitors() once the output has its
2414 +
     * final geometry. */
2415 +
    m->ws = m->previous_workspace = free_workspace();
2416 +
2417 +
    if (m->ws)
2418 +
        m->ws->mon = m;
2419 +
2420 +
    /* The mode is a tuple of (width, height, refresh rate), and each
2421 +
     * monitor supports only a specific set of modes. We just pick the
2422 +
     * monitor's preferred mode; a more sophisticated compositor would let
2423 +
     * the user configure it. */
2424 +
    wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output));
2425 +
2426 +
    /* Listen for display events. */
2427 +
    LISTEN(&wlr_output->events.frame, &m->frame, render_monitor);
2428 +
    LISTEN(&wlr_output->events.destroy, &m->destroy, cleanup_monitor);
2429 +
    LISTEN(&wlr_output->events.request_state, &m->request_state, request_monitor_state);
2430 +
    LISTEN(&wlr_output->events.bind, &m->output_bind, workspace_output_bind);
2431 +
2432 +
    wlr_output_state_set_enabled(&state, 1);
2433 +
    wlr_output_commit_state(wlr_output, &state);
2434 +
    wlr_output_state_finish(&state);
2435 +
2436 +
    wl_list_insert(&mons, &m->link);
2437 +
    print_status();
2438 +
2439 +
    /* Hide unrelated content behind a transparent fullscreen window, as the
2440 +
     * XDG shell protocol requires. */
2441 +
    /* update_monitors() will set the final size and position. */
2442 +
    m->fullscreen_bg = wlr_scene_rect_create(layers[LAYER_FULLSCREEN], 0, 0, fullscreen_bg);
2443 +
2444 +
    if (!m->fullscreen_bg)
2445 +
        die("failed to create monitor fullscreen background");
2446 +
    wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0);
2447 +
2448 +
    /* Add the display at its configured position and publish its properties
2449 +
     * to applications. */
2450 +
    m->scene_output = wlr_scene_output_create(scene, wlr_output);
2451 +
2452 +
    if (!m->scene_output)
2453 +
        die("failed to create scene output");
2454 +
2455 +
    if (m->m.x == -1 && m->m.y == -1)
2456 +
        wlr_output_layout_add_auto(output_layout, wlr_output);
2457 +
    else
2458 +
        wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y);
2459 +
}
2460 +
2461 +
/* Allocate state and listeners for a new Wayland window. */
2462 +
void create_notify(struct wl_listener *listener, void *data) {
2463 +
    /* This event is raised when a client creates a new toplevel (application
2464 +
     * window). */
2465 +
    struct wlr_xdg_toplevel *toplevel = data;
2466 +
    client_t                *c        = nullptr;
2467 +
2468 +
    /* Allocate window state for this surface. */
2469 +
    if (!(c = pool_take(&client_pool))) {
2470 +
        wlr_xdg_toplevel_send_close(toplevel);
2471 +
        return;
2472 +
    }
2473 +
    toplevel->base->data = c;
2474 +
    c->surface.xdg       = toplevel->base;
2475 +
    c->bw                = borderwidth;
2476 +
2477 +
    LISTEN(&toplevel->base->surface->events.commit, &c->commit, commit_notify);
2478 +
    LISTEN(&toplevel->base->surface->events.map, &c->map, map_notify);
2479 +
    LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmap_notify);
2480 +
    LISTEN(&toplevel->events.destroy, &c->destroy, destroy_notify);
2481 +
    LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreen_notify);
2482 +
    LISTEN(&toplevel->events.request_maximize, &c->maximize, maximize_notify);
2483 +
    LISTEN(&toplevel->events.set_title, &c->set_title, update_title);
2484 +
    LISTEN(&toplevel->events.set_app_id, &c->set_appid, update_app_id);
2485 +
}
2486 +
2487 +
/* Attach a pointer device and apply its configured input settings. */
2488 +
void create_pointer(struct wlr_pointer *pointer) {
2489 +
    struct libinput_device *device;
2490 +
2491 +
    if (wlr_input_device_is_libinput(&pointer->base) &&
2492 +
        (device = wlr_libinput_get_device_handle(&pointer->base))) {
2493 +
2494 +
        if (libinput_device_config_tap_get_finger_count(device)) {
2495 +
            libinput_device_config_tap_set_enabled(device, tap_to_click);
2496 +
            libinput_device_config_tap_set_drag_enabled(device, tap_and_drag);
2497 +
            libinput_device_config_tap_set_drag_lock_enabled(device, drag_lock);
2498 +
            libinput_device_config_tap_set_button_map(device, button_map);
2499 +
        }
2500 +
        if (libinput_device_config_scroll_has_natural_scroll(device))
2501 +
            libinput_device_config_scroll_set_natural_scroll_enabled(device, natural_scrolling);
2502 +
2503 +
        if (libinput_device_config_dwt_is_available(device))
2504 +
            libinput_device_config_dwt_set_enabled(device, disable_while_typing);
2505 +
2506 +
        if (libinput_device_config_left_handed_is_available(device))
2507 +
            libinput_device_config_left_handed_set(device, left_handed);
2508 +
2509 +
        if (libinput_device_config_middle_emulation_is_available(device))
2510 +
            libinput_device_config_middle_emulation_set_enabled(device, middle_button_emulation);
2511 +
2512 +
        if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL)
2513 +
            libinput_device_config_scroll_set_method(device, scroll_method);
2514 +
2515 +
        if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE)
2516 +
            libinput_device_config_click_set_method(device, click_method);
2517 +
2518 +
        if (libinput_device_config_send_events_get_modes(device))
2519 +
            libinput_device_config_send_events_set_mode(device, send_events_mode);
2520 +
2521 +
        if (libinput_device_config_accel_is_available(device)) {
2522 +
            libinput_device_config_accel_set_profile(device, accel_profile);
2523 +
            libinput_device_config_accel_set_speed(device, accel_speed);
2524 +
        }
2525 +
    }
2526 +
    wlr_cursor_attach_input_device(cursor, &pointer->base);
2527 +
}
2528 +
2529 +
/* Track an application's request to confine or lock the pointer. */
2530 +
void create_pointer_constraint(struct wl_listener *listener, void *data) {
2531 +
    pointer_constraint_t *pointer_constraint = pool_take(&pointer_constraint_pool);
2532 +
2533 +
    if (!pointer_constraint)
2534 +
        return;
2535 +
    pointer_constraint->constraint = data;
2536 +
    LISTEN(
2537 +
        &pointer_constraint->constraint->events.destroy,
2538 +
        &pointer_constraint->destroy,
2539 +
        destroy_pointer_constraint
2540 +
    );
2541 +
2542 +
    if (pointer_constraint->constraint->surface == seat->pointer_state.focused_surface)
2543 +
        cursor_constrain(pointer_constraint->constraint);
2544 +
}
2545 +
2546 +
/* Wait for a new popup's initial commit before adding it to the scene. */
2547 +
void create_popup(struct wl_listener *listener, void *data) {
2548 +
    /* This event is raised when a client (either xdg-shell or layer-shell)
2549 +
     * creates a new popup. */
2550 +
    struct wlr_xdg_popup *popup = data;
2551 +
2552 +
    LISTEN_STATIC(&popup->base->surface->events.commit, popup_commit);
2553 +
}
2554 +
2555 +
/* Activate the pointer constraint belonging to the focused surface. */
2556 +
void cursor_constrain(struct wlr_pointer_constraint_v1 *constraint) {
2557 +
    if (active_constraint == constraint)
2558 +
        return;
2559 +
2560 +
    if (active_constraint)
2561 +
        wlr_pointer_constraint_v1_send_deactivated(active_constraint);
2562 +
2563 +
    active_constraint = constraint;
2564 +
2565 +
    if (constraint)
2566 +
        wlr_pointer_constraint_v1_send_activated(constraint);
2567 +
}
2568 +
2569 +
/* Finish a group of pointer events for the focused application. */
2570 +
void cursor_frame(struct wl_listener *listener, void *data) {
2571 +
    /* This event is forwarded by the cursor when a pointer emits a frame
2572 +
     * event. Frame events are sent after regular pointer events to group
2573 +
     * multiple events together. For instance, two axis events may happen at the
2574 +
     * same time, in which case a frame event won't be sent in between. */
2575 +
    /* Notify the client with pointer focus of the frame event. */
2576 +
    wlr_seat_pointer_notify_frame(seat);
2577 +
}
2578 +
2579 +
/* Move the pointer to an application's requested position when a constraint ends. */
2580 +
void cursor_warp_to_hint(void) {
2581 +
    client_t *c  = nullptr;
2582 +
    double    sx = active_constraint->current.cursor_hint.x;
2583 +
    double    sy = active_constraint->current.cursor_hint.y;
2584 +
2585 +
    toplevel_from_wlr_surface(active_constraint->surface, &c, nullptr);
2586 +
2587 +
    if (c && active_constraint->current.cursor_hint.enabled) {
2588 +
        wlr_cursor_warp(cursor, nullptr, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw);
2589 +
        wlr_seat_pointer_warp(active_constraint->seat, sx, sy);
2590 +
    }
2591 +
}
2592 +
2593 +
/* Stop tracking a window's decoration request after it is destroyed. */
2594 +
void destroy_decoration(struct wl_listener *listener, void *data) {
2595 +
    client_t    *c     = wl_container_of(listener, c, destroy_decoration);
2596 +
    unsigned int oldbw = c->bw;
2597 +
2598 +
    wl_list_remove(&c->destroy_decoration.link);
2599 +
    wl_list_remove(&c->set_decoration_mode.link);
2600 +
    c->decoration = nullptr;
2601 +
    c->bw         = client_border_width(c);
2602 +
2603 +
    if (oldbw != c->bw && client_surface(c)->mapped && c->scene)
2604 +
        resize(c, c->geom, 0);
2605 +
}
2606 +
2607 +
/* Remove a drag icon and restore pointer focus beneath it. */
2608 +
void destroy_drag_icon(struct wl_listener *listener, void *data) {
2609 +
    /* Focus enter isn't sent during drag, so refocus the focused node. */
2610 +
    focus_client(focus_top(selmon), 1);
2611 +
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
2612 +
    wl_list_remove(&listener->link);
2613 +
    listener_release(listener);
2614 +
}
2615 +
2616 +
/* Remove an idle request and recalculate whether the session may sleep. */
2617 +
void destroy_idle_inhibitor(struct wl_listener *listener, void *data) {
2618 +
    /* `data` is the wlr_surface of the idle inhibitor being destroyed,
2619 +
     * and is still in the manager's list at this point. */
2620 +
    check_idle_inhibitor(wlr_surface_get_root_surface(data));
2621 +
    wl_list_remove(&listener->link);
2622 +
    listener_release(listener);
2623 +
}
2624 +
2625 +
/* Remove a destroyed panel, background, or overlay from its display. */
2626 +
void layer_surface_destroy_notify(struct wl_listener *listener, void *data) {
2627 +
    layer_surface_t *l = wl_container_of(listener, l, destroy);
2628 +
2629 +
    wl_list_remove(&l->link);
2630 +
    wl_list_remove(&l->destroy.link);
2631 +
    wl_list_remove(&l->unmap.link);
2632 +
    wl_list_remove(&l->surface_commit.link);
2633 +
    wlr_scene_node_destroy(&l->popups->node);
2634 +
2635 +
    if (l->dim)
2636 +
        wlr_scene_node_destroy(&l->dim->node);
2637 +
    pool_release(&layer_surface_pool, l);
2638 +
}
2639 +
2640 +
/* Handle a session locker that exits without unlocking cleanly. */
2641 +
void destroy_lock(session_lock_t *lock, int unlock) {
2642 +
    wlr_seat_keyboard_notify_clear_focus(seat);
2643 +
    input_method_set_focus(nullptr);
2644 +
    locked = !unlock;
2645 +
2646 +
    if (unlock) {
2647 +
        wlr_scene_node_set_enabled(&locked_bg->node, 0);
2648 +
        focus_client(focus_top(selmon), 0);
2649 +
        motion_notify(0, nullptr, 0, 0, 0, 0, 1);
2650 +
    }
2651 +
    wl_list_remove(&lock->new_surface.link);
2652 +
    wl_list_remove(&lock->unlock.link);
2653 +
    wl_list_remove(&lock->destroy.link);
2654 +
2655 +
    wlr_scene_node_destroy(&lock->scene->node);
2656 +
    cur_lock = nullptr;
2657 +
    pool_release(&session_lock_pool, lock);
2658 +
}
2659 +
2660 +
/* Remove a display's session-lock surface and restore its background. */
2661 +
void destroy_lock_surface(struct wl_listener *listener, void *data) {
2662 +
    monitor_t                          *m = wl_container_of(listener, m, destroy_lock_surface);
2663 +
    struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface;
2664 +
2665 +
    m->lock_surface = nullptr;
2666 +
    wl_list_remove(&m->destroy_lock_surface.link);
2667 +
2668 +
    if (lock_surface->surface != seat->keyboard_state.focused_surface)
2669 +
        return;
2670 +
2671 +
    if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) {
2672 +
        surface = wl_container_of(cur_lock->surfaces.next, surface, link);
2673 +
        client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat));
2674 +
    } else if (!locked) {
2675 +
        focus_client(focus_top(selmon), 1);
2676 +
    } else {
2677 +
        wlr_seat_keyboard_clear_focus(seat);
2678 +
        input_method_set_focus(nullptr);
2679 +
    }
2680 +
}
2681 +
2682 +
/* Release all state associated with a destroyed Wayland window. */
2683 +
void destroy_notify(struct wl_listener *listener, void *data) {
2684 +
    /* Called when the xdg_toplevel is destroyed. */
2685 +
    client_t *c = wl_container_of(listener, c, destroy);
2686 +
    wl_list_remove(&c->destroy.link);
2687 +
    wl_list_remove(&c->set_title.link);
2688 +
    wl_list_remove(&c->set_appid.link);
2689 +
    wl_list_remove(&c->fullscreen.link);
2690 +
2691 +
    if (c->type != XDG_SHELL) {
2692 +
        wl_list_remove(&c->activate.link);
2693 +
        wl_list_remove(&c->associate.link);
2694 +
        wl_list_remove(&c->configure.link);
2695 +
        wl_list_remove(&c->dissociate.link);
2696 +
        wl_list_remove(&c->set_hints.link);
2697 +
    } else {
2698 +
        wl_list_remove(&c->commit.link);
2699 +
        wl_list_remove(&c->map.link);
2700 +
        wl_list_remove(&c->unmap.link);
2701 +
        wl_list_remove(&c->maximize.link);
2702 +
2703 +
        if (c->decoration) {
2704 +
            wl_list_remove(&c->destroy_decoration.link);
2705 +
            wl_list_remove(&c->set_decoration_mode.link);
2706 +
            c->decoration = nullptr;
2707 +
        }
2708 +
    }
2709 +
    pool_release(&client_pool, c);
2710 +
}
2711 +
2712 +
/* Remove a pointer constraint and restore unrestricted movement. */
2713 +
void destroy_pointer_constraint(struct wl_listener *listener, void *data) {
2714 +
    pointer_constraint_t *pointer_constraint =
2715 +
        wl_container_of(listener, pointer_constraint, destroy);
2716 +
2717 +
    if (active_constraint == pointer_constraint->constraint) {
2718 +
        cursor_warp_to_hint();
2719 +
        active_constraint = nullptr;
2720 +
    }
2721 +
    wl_list_remove(&pointer_constraint->destroy.link);
2722 +
    pool_release(&pointer_constraint_pool, pointer_constraint);
2723 +
}
2724 +
2725 +
/* Release a completed session lock and its remaining surfaces. */
2726 +
void destroy_session_lock(struct wl_listener *listener, void *data) {
2727 +
    session_lock_t *lock = wl_container_of(listener, lock, destroy);
2728 +
    destroy_lock(lock, 0);
2729 +
}
2730 +
2731 +
/* Remove a keyboard group, its timer, and its event listeners. */
2732 +
void destroy_keyboard_group(struct wl_listener *listener, void *data) {
2733 +
    keyboard_group_t *group       = wl_container_of(listener, group, destroy);
2734 +
    int               was_current = wlr_seat_get_keyboard(seat) == &group->wlr_group->keyboard;
2735 +
    bool              is_main     = group == kb_group;
2736 +
2737 +
    if (group->is_virtual)
2738 +
        virtual_keyboards--;
2739 +
    wl_event_source_remove(group->key_repeat_source);
2740 +
    wl_list_remove(&group->key.link);
2741 +
    wl_list_remove(&group->modifiers.link);
2742 +
    wl_list_remove(&group->destroy.link);
2743 +
    wlr_keyboard_group_destroy(group->wlr_group);
2744 +
    pool_release(&keyboard_group_pool, group);
2745 +
2746 +
    if (is_main) {
2747 +
        kb_group = nullptr;
2748 +
        return;
2749 +
    }
2750 +
    if (was_current)
2751 +
        wlr_seat_set_keyboard(seat, &kb_group->wlr_group->keyboard);
2752 +
    wlr_seat_set_capabilities(
2753 +
        seat,
2754 +
        WL_SEAT_CAPABILITY_POINTER |
2755 +
            (!wl_list_empty(&kb_group->wlr_group->devices) || virtual_keyboards
2756 +
                 ? WL_SEAT_CAPABILITY_KEYBOARD
2757 +
                 : 0)
2758 +
    );
2759 +
}
2760 +
2761 +
/* Return the nearest enabled display in the requested direction. */
2762 +
monitor_t *direction_to_monitor(enum wlr_direction dir) {
2763 +
    struct wlr_output *next;
2764 +
2765 +
    if (!wlr_output_layout_get(output_layout, selmon->wlr_output))
2766 +
        return selmon;
2767 +
2768 +
    if ((next = wlr_output_layout_adjacent_output(
2769 +
             output_layout, dir, selmon->wlr_output, selmon->m.x, selmon->m.y
2770 +
         )))
2771 +
        return next->data;
2772 +
2773 +
    if ((next = wlr_output_layout_farthest_output(
2774 +
             output_layout,
2775 +
             dir ^ (WLR_DIRECTION_LEFT | WLR_DIRECTION_RIGHT),
2776 +
             selmon->wlr_output,
2777 +
             selmon->m.x,
2778 +
             selmon->m.y
2779 +
         )))
2780 +
        return next->data;
2781 +
2782 +
    return selmon;
2783 +
}
2784 +
2785 +
/* Transfer keyboard focus to a window and update stacking and border state. */
2786 +
void focus_client(client_t *c, int lift) {
2787 +
    struct wlr_surface *old = seat->keyboard_state.focused_surface;
2788 +
    int                 unused_lx, unused_ly, old_client_type;
2789 +
    client_t           *old_c = nullptr;
2790 +
    layer_surface_t    *old_l = nullptr;
2791 +
2792 +
    if (locked)
2793 +
        return;
2794 +
2795 +
    /* Raise the window when requested. */
2796 +
    if (c && lift)
2797 +
        wlr_scene_node_raise_to_top(&c->scene->node);
2798 +
2799 +
    if (c && client_surface(c) == old)
2800 +
        return;
2801 +
2802 +
    if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDG_SHELL) {
2803 +
        struct wlr_xdg_popup *popup, *tmp;
2804 +
        wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link)
2805 +
            wlr_xdg_popup_destroy(popup);
2806 +
    }
2807 +
    /* Put the new window first in the focus history and select its display. */
2808 +
    if (c && !client_is_unmanaged(c)) {
2809 +
        wl_list_remove(&c->flink);
2810 +
        wl_list_insert(&fstack, &c->flink);
2811 +
        selmon       = c->mon;
2812 +
        c->is_urgent = false;
2813 +
2814 +
        if (c->mon && c->mon->ws && c->mon->ws->lt->arrange == max_stack)
2815 +
            arrange(c->mon);
2816 +
2817 +
        /* Don't change border color if there is an exclusive focus or we are
2818 +
         * handling a drag operation. */
2819 +
        if (!exclusive_focus && !seat->drag)
2820 +
            client_set_border_color(c, focuscolor);
2821 +
    }
2822 +
    /* Deactivate the old window when focus changes. */
2823 +
    if (old && (!c || client_surface(c) != old)) {
2824 +
        /* If an overlay is focused, don't focus or activate the client,
2825 +
         * but only update its position in fstack to render its border with
2826 +
         * focuscolor and focus it after the overlay is closed. */
2827 +
        if (old_client_type == LAYER_SHELL &&
2828 +
            wlr_scene_node_coords(&old_l->scene->node, &unused_lx, &unused_ly) &&
2829 +
            old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) {
2830 +
            return;
2831 +
        } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) {
2832 +
            return;
2833 +
            /* Don't deactivate old client if the new one wants focus, as this
2834 +
             * avoids breaking applications such as winecfg. */
2835 +
        } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) {
2836 +
            client_set_border_color(old_c, bordercolor);
2837 +
            client_activate_surface(old, 0);
2838 +
        }
2839 +
    }
2840 +
    print_status();
2841 +
2842 +
    if (!c) {
2843 +
        /* Clear keyboard focus when no window replaces the old one. */
2844 +
        wlr_seat_keyboard_notify_clear_focus(seat);
2845 +
        input_method_set_focus(nullptr);
2846 +
        return;
2847 +
    }
2848 +
    /* Update the cursor image for the focused surface. */
2849 +
    motion_notify(0, nullptr, 0, 0, 0, 0, 0);
2850 +
    /* Give keyboard focus to the window's main surface. */
2851 +
    client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat));
2852 +
    /* Mark the new window as active. */
2853 +
    client_activate_surface(client_surface(c), 1);
2854 +
}
2855 +
2856 +
/* Return the main client in a related client group. */
2857 +
client_t *client_main(client_t *c) {
2858 +
    client_t *p;
2859 +
2860 +
    while (c && (p = client_get_parent(c)) && p != c)
2861 +
        c = p;
2862 +
    return c;
2863 +
}
2864 +
2865 +
/* Return whether two clients belong to one group. */
2866 +
bool clients_related(client_t *a, client_t *b) {
2867 +
    return a && b && client_main(a) == client_main(b);
2868 +
}
2869 +
2870 +
/* Focus the nearest related window when the current one closes. */
2871 +
client_t *focus_close(client_t *c) {
2872 +
    client_t       *p;
2873 +
    struct wl_list *node;
2874 +
2875 +
    if (!c || !c->mon)
2876 +
        return nullptr;
2877 +
2878 +
    if ((p = client_get_parent(c)) && VISIBLEON(p, c->mon))
2879 +
        return p;
2880 +
2881 +
    for (node = c->link.prev; node != &c->link; node = node->prev) {
2882 +
        if (node == &clients)
2883 +
            continue;
2884 +
        p = wl_container_of(node, p, link);
2885 +
2886 +
        if (VISIBLEON(p, c->mon) && !clients_related(p, c))
2887 +
            return p;
2888 +
    }
2889 +
    return nullptr;
2890 +
}
2891 +
2892 +
/* Select the next enabled display and focus its top window. */
2893 +
void focus_monitor(const arg_t *arg) {
2894 +
    int i = 0, nmons = wl_list_length(&mons);
2895 +
2896 +
    if (nmons) {
2897 +
        do /* Skip disabled displays. */
2898 +
            selmon = direction_to_monitor(arg->i);
2899 +
        while (!selmon->wlr_output->enabled && i++ < nmons);
2900 +
    }
2901 +
    focus_client(focus_top(selmon), 1);
2902 +
}
2903 +
2904 +
/* Move focus forward or backward through tiled windows. */
2905 +
void focus_stack(const arg_t *arg) {
2906 +
    /* Focus the next or previous tiled window on the selected display. */
2907 +
    client_t       *c = nullptr, *sel = focus_top(selmon);
2908 +
    struct wl_list *node;
2909 +
2910 +
    if (!sel)
2911 +
        return;
2912 +
2913 +
    for (node = arg->i > 0 ? sel->link.next : sel->link.prev; node != &sel->link;
2914 +
         node = arg->i > 0 ? node->next : node->prev) {
2915 +
        if (node == &clients)
2916 +
            continue;
2917 +
        c = wl_container_of(node, c, link);
2918 +
2919 +
        if (VISIBLEON(c, selmon) && !client_get_parent(c)) {
2920 +
            bool fullscreen = sel->is_fullscreen;
2921 +
2922 +
            if (fullscreen)
2923 +
                set_fullscreen(sel, 0);
2924 +
            focus_client(c, 1);
2925 +
2926 +
            if (fullscreen)
2927 +
                set_fullscreen(c, 1);
2928 +
            return;
2929 +
        }
2930 +
    }
2931 +
}
2932 +
2933 +
/* Toggle focus between the master window and the previous window. */
2934 +
void focus_main(const arg_t *arg) {
2935 +
    /* focus the main (first tiled) window; if it
2936 +
     * already has focus, return focus to the previously focused window. */
2937 +
    client_t *c, *sel = focus_top(selmon);
2938 +
2939 +
    wl_list_for_each(c, &clients, link) {
2940 +
        if (VISIBLEON(c, selmon) && !c->is_floating) {
2941 +
            if (c != sel) {
2942 +
                focus_client(c, 1);
2943 +
            } else {
2944 +
                client_t *p;
2945 +
                wl_list_for_each(p, &fstack, flink) {
2946 +
                    if (p != sel && VISIBLEON(p, selmon)) {
2947 +
                        focus_client(p, 1);
2948 +
                        break;
2949 +
                    }
2950 +
                }
2951 +
            }
2952 +
            return;
2953 +
        }
2954 +
    }
2955 +
}
2956 +
2957 +
/* Show and focus the most recently urgent window. */
2958 +
void focus_urgent(const arg_t *arg) {
2959 +
    /* focus the most recently urgent window, switching to its
2960 +
     * workspace if necessary. */
2961 +
    client_t *c;
2962 +
2963 +
    wl_list_for_each(c, &fstack, flink) {
2964 +
        if (c->is_urgent && c->ws) {
2965 +
            if (c->ws->mon != selmon)
2966 +
                view_workspace(c->ws, selmon);
2967 +
            focus_client(c, 1);
2968 +
            return;
2969 +
        }
2970 +
    }
2971 +
}
2972 +
2973 +
/* Return the most recently focused visible window. */
2974 +
client_t *focus_top(monitor_t *m) {
2975 +
    client_t *c;
2976 +
    wl_list_for_each(c, &fstack, flink) {
2977 +
        if (VISIBLEON(c, m))
2978 +
            return c;
2979 +
    }
2980 +
    return nullptr;
2981 +
}
2982 +
2983 +
/* Show and focus a window requested by a taskbar or switcher. */
2984 +
void ftl_activate_notify(struct wl_listener *listener, void *data) {
2985 +
    /* A taskbar requested this window; show its workspace and focus it. */
2986 +
    client_t *c = wl_container_of(listener, c, ftl_activate);
2987 +
2988 +
    if (c->ws && c->ws->mon != selmon)
2989 +
        view_workspace(c->ws, selmon);
2990 +
    focus_client(c, 1);
2991 +
}
2992 +
2993 +
/* Forward a taskbar's close request to the selected window. */
2994 +
void ftl_close_notify(struct wl_listener *listener, void *data) {
2995 +
    client_t *c = wl_container_of(listener, c, ftl_close);
2996 +
2997 +
    client_send_close(c);
2998 +
}
2999 +
3000 +
/* Apply a taskbar's fullscreen request to a window. */
3001 +
void ftl_fullscreen_notify(struct wl_listener *listener, void *data) {
3002 +
    struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data;
3003 +
    client_t *c = wl_container_of(listener, c, ftl_fullscreen);
3004 +
3005 +
    set_fullscreen(c, event->fullscreen);
3006 +
}
3007 +
3008 +
/* Publish a window's title, application ID, display, and state. */
3009 +
void ftl_sync(client_t *c) {
3010 +
    /* Publish this window's current state to taskbars and switchers. */
3011 +
    const char *title, *appid;
3012 +
3013 +
    if (!c->ftl)
3014 +
        return;
3015 +
    title = client_get_title(c);
3016 +
    appid = client_get_appid(c);
3017 +
    wlr_foreign_toplevel_handle_v1_set_title(c->ftl, title ? title : "");
3018 +
    wlr_foreign_toplevel_handle_v1_set_app_id(c->ftl, appid ? appid : "");
3019 +
3020 +
    if (c->ftl_monitor != c->mon) {
3021 +
        if (c->ftl_monitor)
3022 +
            wlr_foreign_toplevel_handle_v1_output_leave(c->ftl, c->ftl_monitor->wlr_output);
3023 +
3024 +
        if (c->mon)
3025 +
            wlr_foreign_toplevel_handle_v1_output_enter(c->ftl, c->mon->wlr_output);
3026 +
        c->ftl_monitor = c->mon;
3027 +
    }
3028 +
    wlr_foreign_toplevel_handle_v1_set_activated(c->ftl, c == focus_top(selmon));
3029 +
    wlr_foreign_toplevel_handle_v1_set_fullscreen(c->ftl, c->is_fullscreen);
3030 +
3031 +
    if (c->extftl) {
3032 +
        struct wlr_ext_foreign_toplevel_handle_v1_state state = {
3033 +
            .app_id = appid,
3034 +
            .title  = title,
3035 +
        };
3036 +
        wlr_ext_foreign_toplevel_handle_v1_update_state(c->extftl, &state);
3037 +
    }
3038 +
}
3039 +
3040 +
/* Apply an application's requested fullscreen state. */
3041 +
void fullscreen_notify(struct wl_listener *listener, void *data) {
3042 +
    client_t *c = wl_container_of(listener, c, fullscreen);
3043 +
    set_fullscreen(c, client_wants_fullscreen(c));
3044 +
}
3045 +
3046 +
/* Return the enabled text input belonging to the focused application. */
3047 +
struct wlr_text_input_v3 *input_method_focused_text_input(void) {
3048 +
    /* Find the enabled text input for the focused application. */
3049 +
    struct wlr_text_input_v3 *ti;
3050 +
    struct wlr_surface       *surface = seat->keyboard_state.focused_surface;
3051 +
3052 +
    if (!surface)
3053 +
        return nullptr;
3054 +
    wl_list_for_each(ti, &ti_mgr->text_inputs, link) {
3055 +
        if (ti->focused_surface == surface && ti->current_enabled)
3056 +
            return ti;
3057 +
    }
3058 +
    return nullptr;
3059 +
}
3060 +
3061 +
/* Send the focused application's text-input state to the input method. */
3062 +
void input_method_send_state(struct wlr_text_input_v3 *ti) {
3063 +
    /* Send the application's text-input state to the input method. */
3064 +
    if (!input_method)
3065 +
        return;
3066 +
3067 +
    if (ti->active_features & WLR_TEXT_INPUT_V3_FEATURE_SURROUNDING_TEXT) {
3068 +
        wlr_input_method_v2_send_surrounding_text(
3069 +
            input_method,
3070 +
            ti->current.surrounding.text,
3071 +
            ti->current.surrounding.cursor,
3072 +
            ti->current.surrounding.anchor
3073 +
        );
3074 +
        wlr_input_method_v2_send_text_change_cause(input_method, ti->current.text_change_cause);
3075 +
    }
3076 +
    if (ti->active_features & WLR_TEXT_INPUT_V3_FEATURE_CONTENT_TYPE)
3077 +
        wlr_input_method_v2_send_content_type(
3078 +
            input_method, ti->current.content_type.hint, ti->current.content_type.purpose
3079 +
        );
3080 +
    wlr_input_method_v2_send_done(input_method);
3081 +
}
3082 +
3083 +
/* Update text-input and input-method state after keyboard focus changes. */
3084 +
void input_method_set_focus(struct wlr_surface *surface) {
3085 +
    /* Tell text-input clients when keyboard focus moves, and deactivate the
3086 +
     * input method if its application lost focus. */
3087 +
    struct wlr_text_input_v3 *ti;
3088 +
3089 +
    wl_list_for_each(ti, &ti_mgr->text_inputs, link) {
3090 +
        if (ti->focused_surface) {
3091 +
            if (ti->focused_surface == surface)
3092 +
                continue;
3093 +
3094 +
            if (input_method && ti->current_enabled) {
3095 +
                wlr_input_method_v2_send_deactivate(input_method);
3096 +
                wlr_input_method_v2_send_done(input_method);
3097 +
            }
3098 +
            wlr_text_input_v3_send_leave(ti);
3099 +
        }
3100 +
        if (surface &&
3101 +
            wl_resource_get_client(ti->resource) == wl_resource_get_client(surface->resource))
3102 +
            wlr_text_input_v3_send_enter(ti, surface);
3103 +
    }
3104 +
    update_shortcuts_inhibitors(surface);
3105 +
    position_input_popups();
3106 +
}
3107 +
3108 +
/* Forward text and edits committed by the input method to the application. */
3109 +
void input_method_commit_notify(struct wl_listener *listener, void *data) {
3110 +
    /* Send completed input-method text and edits to the application. */
3111 +
    struct wlr_input_method_v2 *im = input_method;
3112 +
    struct wlr_text_input_v3   *ti = input_method_focused_text_input();
3113 +
3114 +
    if (!im || !ti)
3115 +
        return;
3116 +
3117 +
    if (im->current.preedit.text)
3118 +
        wlr_text_input_v3_send_preedit_string(
3119 +
            ti,
3120 +
            im->current.preedit.text,
3121 +
            im->current.preedit.cursor_begin,
3122 +
            im->current.preedit.cursor_end
3123 +
        );
3124 +
3125 +
    if (im->current.commit_text)
3126 +
        wlr_text_input_v3_send_commit_string(ti, im->current.commit_text);
3127 +
3128 +
    if (im->current.delete.before_length || im->current.delete.after_length)
3129 +
        wlr_text_input_v3_send_delete_surrounding_text(
3130 +
            ti, im->current.delete.before_length, im->current.delete.after_length
3131 +
        );
3132 +
    wlr_text_input_v3_send_done(ti);
3133 +
}
3134 +
3135 +
/* Connect a newly available input method to the focused text input. */
3136 +
void input_method_create_notify(struct wl_listener *listener, void *data) {
3137 +
    struct wlr_input_method_v2 *im = data;
3138 +
    struct wlr_text_input_v3   *ti;
3139 +
3140 +
    if (input_method) {
3141 +
        /* Only one input method can serve a seat. */
3142 +
        wlr_input_method_v2_send_unavailable(im);
3143 +
        return;
3144 +
    }
3145 +
    input_method = im;
3146 +
    wl_signal_add(&im->events.commit, &im_commit);
3147 +
    wl_signal_add(&im->events.destroy, &im_destroy);
3148 +
    wl_signal_add(&im->events.grab_keyboard, &im_grab_kb);
3149 +
    wl_signal_add(&im->events.new_popup_surface, &im_new_popup);
3150 +
3151 +
    if ((ti = input_method_focused_text_input())) {
3152 +
        wlr_input_method_v2_send_activate(input_method);
3153 +
        input_method_send_state(ti);
3154 +
    }
3155 +
}
3156 +
3157 +
/* Disconnect the active input method and remove its listeners. */
3158 +
void input_method_destroy_notify(struct wl_listener *listener, void *data) {
3159 +
    wl_list_remove(&im_commit.link);
3160 +
    wl_list_remove(&im_destroy.link);
3161 +
    wl_list_remove(&im_grab_kb.link);
3162 +
    wl_list_remove(&im_new_popup.link);
3163 +
    input_method = nullptr;
3164 +
}
3165 +
3166 +
/* Give the input method access to the active keyboard. */
3167 +
void input_method_grab_keyboard(struct wl_listener *listener, void *data) {
3168 +
    struct wlr_input_method_keyboard_grab_v2 *grab = data;
3169 +
3170 +
    wlr_input_method_keyboard_grab_v2_set_keyboard(grab, &kb_group->wlr_group->keyboard);
3171 +
}
3172 +
3173 +
/* Add a new input-method popup above regular windows. */
3174 +
void input_method_new_popup(struct wl_listener *listener, void *data) {
3175 +
    struct wlr_input_popup_surface_v2 *popup = data;
3176 +
    input_popup_t                     *p     = pool_take(&input_popup_pool);
3177 +
3178 +
    if (!p)
3179 +
        return;
3180 +
3181 +
    p->popup = popup;
3182 +
    p->scene = wlr_scene_subsurface_tree_create(layers[LAYER_OVERLAY], popup->surface);
3183 +
3184 +
    if (!p->scene) {
3185 +
        pool_release(&input_popup_pool, p);
3186 +
        return;
3187 +
    }
3188 +
    wlr_scene_node_set_enabled(&p->scene->node, 0);
3189 +
    wl_list_insert(&input_popups, &p->link);
3190 +
3191 +
    LISTEN(&popup->surface->events.map, &p->map, input_popup_map);
3192 +
    LISTEN(&popup->surface->events.unmap, &p->unmap, input_popup_unmap);
3193 +
    LISTEN(&popup->surface->events.commit, &p->commit, input_popup_commit);
3194 +
    LISTEN(&popup->events.destroy, &p->destroy, input_popup_destroy);
3195 +
}
3196 +
3197 +
/* Position an input-method popup beside the focused text cursor. */
3198 +
bool input_popup_position(input_popup_t *p) {
3199 +
    /* Place the input-method popup below the application's text cursor. */
3200 +
    struct wlr_text_input_v3 *ti = input_method_focused_text_input();
3201 +
    struct wlr_box            rect, output;
3202 +
    client_t                 *c = nullptr;
3203 +
    struct swm_box            position;
3204 +
    int                       width, height;
3205 +
3206 +
    if (!ti)
3207 +
        return false;
3208 +
    toplevel_from_wlr_surface(ti->focused_surface, &c, nullptr);
3209 +
3210 +
    if (!c)
3211 +
        return false;
3212 +
    rect = ti->current.cursor_rectangle;
3213 +
3214 +
    if (!(ti->current.features & WLR_TEXT_INPUT_V3_FEATURE_CURSOR_RECTANGLE))
3215 +
        rect = (struct wlr_box){};
3216 +
    width  = p->popup->surface->current.width;
3217 +
    height = p->popup->surface->current.height;
3218 +
3219 +
    if (c->mon)
3220 +
        output = c->mon->m;
3221 +
    position = swm_popup_position(
3222 +
        (const struct swm_box *)&c->geom,
3223 +
        c->bw,
3224 +
        (const struct swm_box *)&rect,
3225 +
        width,
3226 +
        height,
3227 +
        c->mon ? (const struct swm_box *)&output : nullptr
3228 +
    );
3229 +
    wlr_scene_node_set_position(&p->scene->node, position.x, position.y);
3230 +
    wlr_input_popup_surface_v2_send_text_input_rectangle(
3231 +
        p->popup, &(struct wlr_box){ .y = -rect.height, .width = rect.width, .height = rect.height }
3232 +
    );
3233 +
    return true;
3234 +
}
3235 +
3236 +
/* Reposition and show all mapped input-method popups. */
3237 +
void position_input_popups(void) {
3238 +
    input_popup_t *p;
3239 +
3240 +
    wl_list_for_each(p, &input_popups, link) wlr_scene_node_set_enabled(
3241 +
        &p->scene->node, p->popup->surface->mapped && input_popup_position(p)
3242 +
    );
3243 +
}
3244 +
3245 +
/* Position and show a newly mapped input-method popup. */
3246 +
void input_popup_map(struct wl_listener *listener, void *data) {
3247 +
    input_popup_t *p = wl_container_of(listener, p, map);
3248 +
3249 +
    wlr_scene_node_set_enabled(&p->scene->node, input_popup_position(p));
3250 +
}
3251 +
3252 +
/* Hide an input-method popup when it is unmapped. */
3253 +
void input_popup_unmap(struct wl_listener *listener, void *data) {
3254 +
    input_popup_t *p = wl_container_of(listener, p, unmap);
3255 +
3256 +
    wlr_scene_node_set_enabled(&p->scene->node, 0);
3257 +
}
3258 +
3259 +
/* Reposition an input-method popup after its content changes. */
3260 +
void input_popup_commit(struct wl_listener *listener, void *data) {
3261 +
    input_popup_t *p = wl_container_of(listener, p, commit);
3262 +
3263 +
    wlr_scene_node_set_enabled(
3264 +
        &p->scene->node, p->popup->surface->mapped && input_popup_position(p)
3265 +
    );
3266 +
}
3267 +
3268 +
/* Remove a destroyed input-method popup and release its state. */
3269 +
void input_popup_destroy(struct wl_listener *listener, void *data) {
3270 +
    input_popup_t *p = wl_container_of(listener, p, destroy);
3271 +
3272 +
    wl_list_remove(&p->link);
3273 +
    wl_list_remove(&p->map.link);
3274 +
    wl_list_remove(&p->unmap.link);
3275 +
    wl_list_remove(&p->commit.link);
3276 +
    wl_list_remove(&p->destroy.link);
3277 +
    wlr_scene_node_destroy(&p->scene->node);
3278 +
    pool_release(&input_popup_pool, p);
3279 +
}
3280 +
3281 +
/* Track a new application's text-input connection. */
3282 +
void text_input_create_notify(struct wl_listener *listener, void *data) {
3283 +
    struct wlr_text_input_v3 *wlr_ti  = data;
3284 +
    struct wlr_surface       *surface = seat->keyboard_state.focused_surface;
3285 +
    text_input_t             *ti      = pool_take(&text_input_pool);
3286 +
3287 +
    if (!ti)
3288 +
        return;
3289 +
3290 +
    ti->ti = wlr_ti;
3291 +
    LISTEN(&wlr_ti->events.enable, &ti->enable, text_input_enable_notify);
3292 +
    LISTEN(&wlr_ti->events.commit, &ti->commit, text_input_commit_notify);
3293 +
    LISTEN(&wlr_ti->events.disable, &ti->disable, text_input_disable_notify);
3294 +
    LISTEN(&wlr_ti->events.destroy, &ti->destroy, text_input_destroy_notify);
3295 +
3296 +
    if (surface &&
3297 +
        wl_resource_get_client(wlr_ti->resource) == wl_resource_get_client(surface->resource))
3298 +
        wlr_text_input_v3_send_enter(wlr_ti, surface);
3299 +
}
3300 +
3301 +
/* Activate the input method for an enabled text field. */
3302 +
void text_input_enable_notify(struct wl_listener *listener, void *data) {
3303 +
    text_input_t *ti = wl_container_of(listener, ti, enable);
3304 +
3305 +
    if (!input_method || ti->ti != input_method_focused_text_input())
3306 +
        return;
3307 +
    wlr_input_method_v2_send_activate(input_method);
3308 +
    input_method_send_state(ti->ti);
3309 +
}
3310 +
3311 +
/* Forward an application's updated text-field state to the input method. */
3312 +
void text_input_commit_notify(struct wl_listener *listener, void *data) {
3313 +
    text_input_t *ti = wl_container_of(listener, ti, commit);
3314 +
3315 +
    if (!input_method || ti->ti != input_method_focused_text_input())
3316 +
        return;
3317 +
    input_method_send_state(ti->ti);
3318 +
    position_input_popups();
3319 +
}
3320 +
3321 +
/* Deactivate the input method when an application leaves its text field. */
3322 +
void text_input_disable_notify(struct wl_listener *listener, void *data) {
3323 +
    text_input_t *ti = wl_container_of(listener, ti, disable);
3324 +
3325 +
    if (!input_method || ti->ti->focused_surface != seat->keyboard_state.focused_surface)
3326 +
        return;
3327 +
    wlr_input_method_v2_send_deactivate(input_method);
3328 +
    wlr_input_method_v2_send_done(input_method);
3329 +
}
3330 +
3331 +
/* Disconnect and release a destroyed text-input connection. */
3332 +
void text_input_destroy_notify(struct wl_listener *listener, void *data) {
3333 +
    text_input_t *ti = wl_container_of(listener, ti, destroy);
3334 +
3335 +
    if (input_method && ti->ti->current_enabled &&
3336 +
        ti->ti->focused_surface == seat->keyboard_state.focused_surface) {
3337 +
        wlr_input_method_v2_send_deactivate(input_method);
3338 +
        wlr_input_method_v2_send_done(input_method);
3339 +
    }
3340 +
    wl_list_remove(&ti->enable.link);
3341 +
    wl_list_remove(&ti->commit.link);
3342 +
    wl_list_remove(&ti->disable.link);
3343 +
    wl_list_remove(&ti->destroy.link);
3344 +
    pool_release(&text_input_pool, ti);
3345 +
}
3346 +
3347 +
/* Recover from a GPU reset. */
3348 +
void gpu_reset(struct wl_listener *listener, void *data) {
3349 +
    struct wlr_renderer  *old_drw   = drw;
3350 +
    struct wlr_allocator *old_alloc = alloc;
3351 +
    struct monitor_t     *m;
3352 +
3353 +
    if (!(drw = wlr_renderer_autocreate(backend)))
3354 +
        die("couldn't recreate renderer");
3355 +
3356 +
    if (!(alloc = wlr_allocator_autocreate(backend, drw)))
3357 +
        die("couldn't recreate allocator");
3358 +
3359 +
    wl_list_remove(&gpu_reset_listener.link);
3360 +
    wl_signal_add(&drw->events.lost, &gpu_reset_listener);
3361 +
3362 +
    wlr_compositor_set_renderer(compositor, drw);
3363 +
3364 +
    wl_list_for_each(m, &mons, link) {
3365 +
        wlr_output_init_render(m->wlr_output, alloc, drw);
3366 +
    }
3367 +
    wlr_allocator_destroy(old_alloc);
3368 +
    wlr_renderer_destroy(old_drw);
3369 +
}
3370 +
3371 +
/* Reap child processes or begin shutdown when a queued signal arrives. */
3372 +
int handle_signal(int signo, void *data) {
3373 +
    if (signo == SIGCHLD) {
3374 +
        pid_t            pid;
3375 +
        pending_spawn_t *ps;
3376 +
        struct wl_list  *node, *next;
3377 +
3378 +
        while ((pid = waitpid(-1, nullptr, WNOHANG)) > 0) {
3379 +
            size_t i;
3380 +
3381 +
            if (pid == child_pid)
3382 +
                child_pid = -1;
3383 +
3384 +
            for (i = 0; i < autostart_len; i++)
3385 +
3386 +
                if (autostart_pids[i] == pid)
3387 +
                    autostart_pids[i] = -1;
3388 +
3389 +
            for (node = pending_spawns.next; node != &pending_spawns; node = next) {
3390 +
                next = node->next;
3391 +
                ps   = wl_container_of(node, ps, link);
3392 +
3393 +
                if (ps->pid != pid)
3394 +
                    continue;
3395 +
                wl_list_remove(&ps->link);
3396 +
                pool_release(&pending_spawn_pool, ps);
3397 +
            }
3398 +
        }
3399 +
    } else if (signo == SIGINT || signo == SIGTERM)
3400 +
        quit(nullptr);
3401 +
    return 0;
3402 +
}
3403 +
3404 +
/* Add a newly connected keyboard or pointer and update seat capabilities. */
3405 +
void input_device(struct wl_listener *listener, void *data) {
3406 +
    /* This event is raised by the backend when a new input device becomes
3407 +
     * available. */
3408 +
    struct wlr_input_device *device = data;
3409 +
    uint32_t                 caps;
3410 +
3411 +
    switch (device->type) {
3412 +
    case WLR_INPUT_DEVICE_KEYBOARD:
3413 +
        create_keyboard(wlr_keyboard_from_input_device(device));
3414 +
        break;
3415 +
    case WLR_INPUT_DEVICE_POINTER:
3416 +
        create_pointer(wlr_pointer_from_input_device(device));
3417 +
        break;
3418 +
    default:
3419 +
        /* TODO: Handle other input device types. */
3420 +
        break;
3421 +
    }
3422 +
    /* Advertise the input devices available to applications. swm always has
3423 +
     * a cursor, even when no physical pointing device is connected. */
3424 +
    /* TODO: Check whether swm truly needs to always advertise a pointer. */
3425 +
    caps = WL_SEAT_CAPABILITY_POINTER;
3426 +
3427 +
    if (!wl_list_empty(&kb_group->wlr_group->devices) || virtual_keyboards)
3428 +
        caps |= WL_SEAT_CAPABILITY_KEYBOARD;
3429 +
    wlr_seat_set_capabilities(seat, caps);
3430 +
}
3431 +
3432 +
/* Run the matching window-manager shortcut and report whether it may repeat. */
3433 +
int key_binding(uint32_t mods, xkb_keysym_t sym, bool repeat) {
3434 +
    /* Handle window-manager shortcuts before applications receive the key. */
3435 +
    const key_t *k;
3436 +
    int          inhibited = shortcuts_inhibited();
3437 +
3438 +
    for (k = keys; k < END(keys); k++) {
3439 +
        if (CLEANMASK(mods) == CLEANMASK(k->mod) &&
3440 +
            xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym) && k->func) {
3441 +
            int repeatable = k->func == focus_stack || k->func == swap_client ||
3442 +
                             k->func == stack_config || k->func == cycle_workspace;
3443 +
3444 +
            if (repeat && !repeatable)
3445 +
                continue;
3446 +
            /* Keep core window-management controls available even when a
3447 +
             * fullscreen client inhibits application-facing shortcuts. */
3448 +
            if (inhibited && k->func != change_vt && k->func != quit && k->func != focus_stack &&
3449 +
                k->func != toggle_max_stack && k->func != toggle_fullscreen)
3450 +
                continue;
3451 +
            k->func(&k->arg);
3452 +
            return repeatable ? 2 : 1;
3453 +
        }
3454 +
    }
3455 +
    return 0;
3456 +
}
3457 +
3458 +
/* Handle shortcuts and forward unhandled key events to the focused application. */
3459 +
void key_press(struct wl_listener *listener, void *data) {
3460 +
    int i;
3461 +
3462 +
    /* This event is raised when a key is pressed or released. */
3463 +
    keyboard_group_t              *group = wl_container_of(listener, group, key);
3464 +
    struct wlr_keyboard_key_event *event = data;
3465 +
3466 +
    /* Convert the input key code to the numbering used by XKB. */
3467 +
    uint32_t keycode = event->keycode + 8;
3468 +
3469 +
    /* Resolve the key to one or more symbols using the active key map. */
3470 +
    const xkb_keysym_t *syms;
3471 +
    int      nsyms   = xkb_state_key_get_syms(group->wlr_group->keyboard.xkb_state, keycode, &syms);
3472 +
    int      handled = 0;
3473 +
    uint32_t mods    = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard);
3474 +
3475 +
    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
3476 +
3477 +
    /* Handle shortcuts only on key press and while the session is unlocked. */
3478 +
    if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) {
3479 +
        for (i = 0; i < nsyms; i++)
3480 +
            handled |= key_binding(mods, syms[i], 0);
3481 +
    }
3482 +
    if ((handled & 2) && group->wlr_group->keyboard.repeat_info.delay > 0) {
3483 +
        group->mods    = mods;
3484 +
        group->keysyms = syms;
3485 +
        group->nsyms   = nsyms;
3486 +
        wl_event_source_timer_update(
3487 +
            group->key_repeat_source, group->wlr_group->keyboard.repeat_info.delay
3488 +
        );
3489 +
    } else {
3490 +
        group->nsyms = 0;
3491 +
        wl_event_source_timer_update(group->key_repeat_source, 0);
3492 +
    }
3493 +
    if (handled)
3494 +
        return;
3495 +
3496 +
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3497 +
    /* Forward unhandled keys to the focused application. */
3498 +
    wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state);
3499 +
}
3500 +
3501 +
/* Forward a keyboard's updated modifier state to the focused application. */
3502 +
void key_press_modifiers(struct wl_listener *listener, void *data) {
3503 +
    /* A modifier such as Shift or Alt changed. */
3504 +
    keyboard_group_t *group = wl_container_of(listener, group, modifiers);
3505 +
3506 +
    wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard);
3507 +
    /* Forward the new modifier state to the focused application. */
3508 +
    wlr_seat_keyboard_notify_modifiers(seat, &group->wlr_group->keyboard.modifiers);
3509 +
}
3510 +
3511 +
/* Repeat the active shortcut at the keyboard's configured rate. */
3512 +
int key_repeat(void *data) {
3513 +
    keyboard_group_t *group = data;
3514 +
    int               i;
3515 +
3516 +
    if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0)
3517 +
        return 0;
3518 +
3519 +
    wl_event_source_timer_update(
3520 +
        group->key_repeat_source, 1000 / group->wlr_group->keyboard.repeat_info.rate
3521 +
    );
3522 +
3523 +
    for (i = 0; i < group->nsyms; i++)
3524 +
        key_binding(group->mods, group->keysyms[i], 1);
3525 +
3526 +
    return 0;
3527 +
}
3528 +
3529 +
/* Ask the focused application to close its window. */
3530 +
void kill_client(const arg_t *arg) {
3531 +
    client_t *sel = focus_top(selmon);
3532 +
3533 +
    if (sel)
3534 +
        client_send_close(sel);
3535 +
}
3536 +
3537 +
/* Accept a session lock and prevent applications from receiving input. */
3538 +
void lock_session(struct wl_listener *listener, void *data) {
3539 +
    struct wlr_session_lock_v1 *session_lock = data;
3540 +
    session_lock_t             *lock;
3541 +
3542 +
    if (cur_lock) {
3543 +
        wlr_session_lock_v1_destroy(session_lock);
3544 +
        return;
3545 +
    }
3546 +
    if (!(lock = pool_take(&session_lock_pool))) {
3547 +
        wlr_session_lock_v1_destroy(session_lock);
3548 +
        return;
3549 +
    }
3550 +
    lock->scene = wlr_scene_tree_create(layers[LAYER_BLOCK]);
3551 +
3552 +
    if (!lock->scene) {
3553 +
        pool_release(&session_lock_pool, lock);
3554 +
        wlr_session_lock_v1_destroy(session_lock);
3555 +
        return;
3556 +
    }
3557 +
    session_lock->data = lock;
3558 +
    wlr_scene_node_set_enabled(&locked_bg->node, 1);
3559 +
    focus_client(nullptr, 0);
3560 +
3561 +
    cur_lock = lock->lock = session_lock;
3562 +
    locked                = true;
3563 +
3564 +
    LISTEN(&session_lock->events.new_surface, &lock->new_surface, create_lock_surface);
3565 +
    LISTEN(&session_lock->events.destroy, &lock->destroy, destroy_session_lock);
3566 +
    LISTEN(&session_lock->events.unlock, &lock->unlock, unlock_session);
3567 +
3568 +
    wlr_session_lock_v1_send_locked(session_lock);
3569 +
}
3570 +
3571 +
/* Build the path used to persist floating window geometry. */
3572 +
static const char *window_state_path(void) {
3573 +
    static char path[MAX_STATE_PATH];
3574 +
    const char *base = getenv("XDG_STATE_HOME");
3575 +
    const char *home = getenv("HOME");
3576 +
3577 +
    if (base && *base)
3578 +
        snprintf(path, sizeof(path), "%s/swm/windows", base);
3579 +
    else
3580 +
        snprintf(path, sizeof(path), "%s/.local/state/swm/windows", home ? home : "/tmp");
3581 +
    return path;
3582 +
}
3583 +
3584 +
/* Replace delimiters that would corrupt a persisted state record. */
3585 +
static void state_field(char dst[MAX_WINDOW_STATE_FIELD], const char *src) {
3586 +
    swm_sanitize_field(dst, MAX_WINDOW_STATE_FIELD, src, "broken");
3587 +
}
3588 +
3589 +
/* Find persisted state matching a client. */
3590 +
static window_state_t *find_window_state(client_t *c) {
3591 +
    window_state_t *state;
3592 +
    char            appid[MAX_WINDOW_STATE_FIELD], title[MAX_WINDOW_STATE_FIELD];
3593 +
3594 +
    state_field(appid, client_get_appid(c));
3595 +
    state_field(title, client_get_title(c));
3596 +
3597 +
    wl_list_for_each(state, &window_states, link) {
3598 +
        if (!strcmp(state->appid, appid) && (!state->title[0] || !strcmp(state->title, title))) {
3599 +
            return state;
3600 +
        }
3601 +
    }
3602 +
    return nullptr;
3603 +
}
3604 +
3605 +
/* Write all remembered floating-window geometries to disk. */
3606 +
void save_window_states(void) {
3607 +
    window_state_t *state;
3608 +
    FILE           *f;
3609 +
    const char     *path = window_state_path();
3610 +
    char            dir[MAX_STATE_PATH], tmppath[MAX_STATE_PATH], *slash, *p;
3611 +
    int             ok;
3612 +
3613 +
    snprintf(dir, sizeof(dir), "%s", path);
3614 +
3615 +
    if ((slash = strrchr(dir, '/'))) {
3616 +
        *slash = '\0';
3617 +
        for (p = dir + 1; *p; p++) {
3618 +
            if (*p != '/')
3619 +
                continue;
3620 +
            *p = '\0';
3621 +
            if (mkdir(dir, 0700) < 0 && errno != EEXIST)
3622 +
                return;
3623 +
            *p = '/';
3624 +
        }
3625 +
        if (mkdir(dir, 0700) < 0 && errno != EEXIST)
3626 +
            return;
3627 +
    }
3628 +
    if (snprintf(tmppath, sizeof(tmppath), "%s.tmp.%ld", path, (long)getpid()) >=
3629 +
        (int)sizeof(tmppath))
3630 +
        return;
3631 +
3632 +
    if (!(f = fopen(tmppath, "w"))) {
3633 +
        fprintf(stderr, "swm: cannot save floating window state: %s\n", strerror(errno));
3634 +
        return;
3635 +
    }
3636 +
    wl_list_for_each(state, &window_states, link) fprintf(
3637 +
        f,
3638 +
        "%s\t%s\t%d\t%d\t%d\t%d\n",
3639 +
        state->appid,
3640 +
        state->title,
3641 +
        state->geom.x,
3642 +
        state->geom.y,
3643 +
        state->geom.width,
3644 +
        state->geom.height
3645 +
    );
3646 +
    ok = !ferror(f) && fflush(f) == 0;
3647 +
3648 +
    if (fclose(f) < 0)
3649 +
        ok = 0;
3650 +
3651 +
    if (!ok || rename(tmppath, path) < 0) {
3652 +
        fprintf(stderr, "swm: cannot save floating window state: %s\n", strerror(errno));
3653 +
        unlink(tmppath);
3654 +
    }
3655 +
}
3656 +
3657 +
/* Load remembered floating-window geometries from disk. */
3658 +
void load_window_states(void) {
3659 +
    FILE           *f;
3660 +
    window_state_t *state;
3661 +
    char            appid[MAX_WINDOW_STATE_FIELD], title[MAX_WINDOW_STATE_FIELD];
3662 +
    char            line[MAX_WINDOW_STATE_LINE];
3663 +
    struct swm_box  geometry;
3664 +
3665 +
    if (!(f = fopen(window_state_path(), "r")))
3666 +
        return;
3667 +
3668 +
    while (fgets(line, sizeof(line), f)) {
3669 +
        if (!swm_parse_window_state(line, appid, sizeof(appid), title, sizeof(title), &geometry))
3670 +
            continue;
3671 +
3672 +
        if (!(state = pool_take(&window_state_pool))) {
3673 +
            fprintf(stderr, "swm: ignoring remaining saved window states\n");
3674 +
            break;
3675 +
        }
3676 +
        state_field(state->appid, appid);
3677 +
3678 +
        if (title[0])
3679 +
            state_field(state->title, title);
3680 +
        else
3681 +
            state->title[0] = '\0';
3682 +
        state->geom = (struct wlr_box){ geometry.x, geometry.y, geometry.width, geometry.height };
3683 +
        wl_list_insert(window_states.prev, &state->link);
3684 +
    }
3685 +
    fclose(f);
3686 +
}
3687 +
3688 +
/* Save a floating window's current geometry for a later session. */
3689 +
void remember_client(client_t *c) {
3690 +
    window_state_t *state;
3691 +
3692 +
    if (!c || !c->persist_float || !c->is_floating || client_get_parent(c))
3693 +
        return;
3694 +
3695 +
    if (!(state = find_window_state(c))) {
3696 +
        if (!(state = pool_take(&window_state_pool)))
3697 +
            return;
3698 +
        state_field(state->appid, client_get_appid(c));
3699 +
        state_field(state->title, client_get_title(c));
3700 +
        wl_list_insert(window_states.prev, &state->link);
3701 +
    } else if (!state->title[0]) {
3702 +
        state_field(state->title, client_get_title(c));
3703 +
    }
3704 +
    state->geom = c->geom;
3705 +
    save_window_states();
3706 +
}
3707 +
3708 +
/* Remove a window's saved geometry. */
3709 +
void forget_client(client_t *c) {
3710 +
    window_state_t *state;
3711 +
3712 +
    if (!c || !(state = find_window_state(c)))
3713 +
        return;
3714 +
    wl_list_remove(&state->link);
3715 +
    pool_release(&window_state_pool, state);
3716 +
    save_window_states();
3717 +
}
3718 +
3719 +
/* Restore a floating window to its saved geometry when available. */
3720 +
void restore_client(client_t *c) {
3721 +
    window_state_t *state;
3722 +
3723 +
    if (!c->persist_float || client_get_parent(c))
3724 +
        return;
3725 +
3726 +
    if ((state = find_window_state(c))) {
3727 +
        resize(c, state->geom, 0);
3728 +
        set_floating(c, 1);
3729 +
    }
3730 +
}
3731 +
3732 +
/* Make a new window visible, place it, publish it, and focus it. */
3733 +
void map_notify(struct wl_listener *listener, void *data) {
3734 +
    /* Called when the surface is mapped, or ready to display on-screen. */
3735 +
    client_t        *p = nullptr;
3736 +
    client_t        *w, *c = wl_container_of(listener, c, map);
3737 +
    monitor_t       *m;
3738 +
    pending_spawn_t *ps, *pstmp;
3739 +
    workspace_t     *spawnws = nullptr;
3740 +
    pid_t            pid     = -1;
3741 +
    unsigned int     oldbw;
3742 +
    int              i, inherit_fullscreen = 0;
3743 +
3744 +
    /* Create drawing nodes for the window and its border. */
3745 +
    c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LAYER_TILE]);
3746 +
3747 +
    if (!c->scene) {
3748 +
        client_send_close(c);
3749 +
        return;
3750 +
    }
3751 +
    /* arrange() makes the window visible later. */
3752 +
    wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c));
3753 +
    c->scene_surface = c->type == XDG_SHELL
3754 +
                           ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
3755 +
                           : wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
3756 +
3757 +
    if (!c->scene_surface) {
3758 +
        wlr_scene_node_destroy(&c->scene->node);
3759 +
        c->scene = client_surface(c)->data = nullptr;
3760 +
        client_send_close(c);
3761 +
        return;
3762 +
    }
3763 +
    c->scene->node.data = c->scene_surface->node.data = c;
3764 +
3765 +
    client_get_geometry(c, &c->geom);
3766 +
3767 +
    /* Handle unmanaged windows before creating borders. */
3768 +
    if (client_is_unmanaged(c)) {
3769 +
        /* Unmanaged X11 windows always float. */
3770 +
        wlr_scene_node_reparent(&c->scene->node, layers[LAYER_FLOAT]);
3771 +
        wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
3772 +
        client_set_size(c, c->geom.width, c->geom.height);
3773 +
3774 +
        if (client_wants_focus(c)) {
3775 +
            focus_client(c, 1);
3776 +
            exclusive_focus = c;
3777 +
        }
3778 +
        return;
3779 +
    }
3780 +
    for (i = 0; i < 4; i++) {
3781 +
        c->border[i] =
3782 +
            wlr_scene_rect_create(c->scene, 0, 0, c->is_urgent ? urgentcolor : bordercolor);
3783 +
3784 +
        if (!c->border[i]) {
3785 +
            wlr_scene_node_destroy(&c->scene->node);
3786 +
            memset(c->border, 0, sizeof(c->border));
3787 +
            c->scene = client_surface(c)->data = nullptr;
3788 +
            client_send_close(c);
3789 +
            return;
3790 +
        }
3791 +
        c->border[i]->node.data = c;
3792 +
    }
3793 +
    /* Include the border in the window's initial size. */
3794 +
    client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT);
3795 +
    c->geom.width  += 2 * c->bw;
3796 +
    c->geom.height += 2 * c->bw;
3797 +
3798 +
    /* Add new windows after existing tiled windows so they do not replace the
3799 +
     * master. Keep the newest window first in the focus history. */
3800 +
    c->pending_map = 1;
3801 +
    wl_list_insert(clients.prev, &c->link);
3802 +
    wl_list_insert(&fstack, &c->flink);
3803 +
3804 +
    if (!client_is_x11(c)) {
3805 +
        wl_client_get_credentials(c->surface.xdg->client->client, &pid, nullptr, nullptr);
3806 +
        wl_list_for_each_safe(ps, pstmp, &pending_spawns, link) {
3807 +
            if (ps->pid != pid)
3808 +
                continue;
3809 +
            spawnws = ps->ws;
3810 +
            wl_list_remove(&ps->link);
3811 +
            pool_release(&pending_spawn_pool, ps);
3812 +
            break;
3813 +
        }
3814 +
    }
3815 +
    /* Child windows float on their parent's workspace and display. Apply the
3816 +
     * configured matching rules to other windows. */
3817 +
    if ((p = client_get_parent(c))) {
3818 +
        c->is_floating = true;
3819 +
        set_monitor(c, p->mon, p->ws);
3820 +
    } else {
3821 +
        apply_rules(c, spawnws);
3822 +
        c->persist_float  = c->is_floating;
3823 +
        oldbw             = c->bw;
3824 +
        c->bw             = client_border_width(c);
3825 +
        c->geom.width    += 2 * ((int)c->bw - (int)oldbw);
3826 +
        c->geom.height   += 2 * ((int)c->bw - (int)oldbw);
3827 +
        restore_client(c);
3828 +
    }
3829 +
    /* Publish the window to taskbars and switchers. */
3830 +
    c->ftl = wlr_foreign_toplevel_handle_v1_create(ftl_mgr);
3831 +
3832 +
    if (c->ftl) {
3833 +
        LISTEN(&c->ftl->events.request_activate, &c->ftl_activate, ftl_activate_notify);
3834 +
        LISTEN(&c->ftl->events.request_close, &c->ftl_close, ftl_close_notify);
3835 +
        LISTEN(&c->ftl->events.request_fullscreen, &c->ftl_fullscreen, ftl_fullscreen_notify);
3836 +
    }
3837 +
    c->extftl = wlr_ext_foreign_toplevel_handle_v1_create(
3838 +
        ext_ftl_list,
3839 +
        &(struct wlr_ext_foreign_toplevel_handle_v1_state){
3840 +
            .app_id = client_get_appid(c),
3841 +
            .title  = client_get_title(c),
3842 +
        }
3843 +
    );
3844 +
    ftl_sync(c);
3845 +
    /* A managed window can receive keyboard focus only after it is visible. */
3846 +
    if (c->ws && c->ws->mon)
3847 +
        focus_client(c, 1);
3848 +
    c->pending_map = 0;
3849 +
3850 +
    if (c->mon)
3851 +
        arrange(c->mon);
3852 +
    print_status();
3853 +
3854 +
    m = c->mon ? c->mon : point_to_monitor(c->geom.x, c->geom.y);
3855 +
    wl_list_for_each(w, &clients, link) {
3856 +
        if (!p && w != c && w->is_fullscreen && m == w->mon && w->ws == c->ws) {
3857 +
            inherit_fullscreen = 1;
3858 +
            set_fullscreen(w, 0);
3859 +
        }
3860 +
    }
3861 +
    if (inherit_fullscreen)
3862 +
        set_fullscreen(c, 1);
3863 +
}
3864 +
3865 +
/* Acknowledge unsupported maximize requests from older applications. */
3866 +
void maximize_notify(struct wl_listener *listener, void *data) {
3867 +
    /* swm does not support maximization, but older applications may still ask
3868 +
     * for it. Send an unchanged configuration to acknowledge the request. */
3869 +
    client_t *c = wl_container_of(listener, c, maximize);
3870 +
3871 +
    if (c->surface.xdg->initialized && wl_resource_get_version(c->surface.xdg->toplevel->resource) <
3872 +
                                           XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
3873 +
        wlr_xdg_surface_schedule_configure(c->surface.xdg);
3874 +
}
3875 +
3876 +
/* Fill the usable display area with every tiled window in the workspace. */
3877 +
void max_stack(monitor_t *m) {
3878 +
    client_t *c;
3879 +
3880 +
    wl_list_for_each(c, &clients, link) {
3881 +
        if (!VISIBLEON(c, m) || c->is_fullscreen)
3882 +
            continue;
3883 +
3884 +
        if (c->is_floating && !c->is_max_stacked) {
3885 +
            c->maxstack_prev  = c->geom;
3886 +
            c->is_max_stacked = true;
3887 +
        }
3888 +
        resize(c, m->w, 0);
3889 +
    }
3890 +
    if ((c = focus_top(m)))
3891 +
        wlr_scene_node_raise_to_top(&c->scene->node);
3892 +
}
3893 +
3894 +
/* Convert absolute pointer input into movement across the display layout. */
3895 +
void motion_absolute(struct wl_listener *listener, void *data) {
3896 +
    /* Convert absolute pointer coordinates, such as those from a tablet or a
3897 +
     * nested compositor, into display-layout coordinates. */
3898 +
    struct wlr_pointer_motion_absolute_event *event = data;
3899 +
    double                                    lx, ly, dx, dy;
3900 +
3901 +
    if (!event->time_msec) /* Virtual pointers use a zero timestamp. */
3902 +
        wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y);
3903 +
3904 +
    wlr_cursor_absolute_to_layout_coords(
3905 +
        cursor, &event->pointer->base, event->x, event->y, &lx, &ly
3906 +
    );
3907 +
    dx = lx - cursor->x;
3908 +
    dy = ly - cursor->y;
3909 +
    motion_notify(event->time_msec, &event->pointer->base, dx, dy, dx, dy, 1);
3910 +
}
3911 +
3912 +
/* Move the pointer or grabbed window and update the surface under it. */
3913 +
void motion_notify(
3914 +
    uint32_t                 time,
3915 +
    struct wlr_input_device *device,
3916 +
    double                   dx,
3917 +
    double                   dy,
3918 +
    double                   dx_unaccel,
3919 +
    double                   dy_unaccel,
3920 +
    bool                     refocus
3921 +
) {
3922 +
    double                            sx = 0, sy = 0, sx_confined, sy_confined;
3923 +
    client_t                         *c = nullptr, *w = nullptr;
3924 +
    layer_surface_t                  *l          = nullptr;
3925 +
    struct wlr_surface               *surface    = nullptr;
3926 +
    struct wlr_pointer_constraint_v1 *constraint = active_constraint;
3927 +
3928 +
    /* A zero timestamp marks an internal pointer-focus update. */
3929 +
    if (time) {
3930 +
        wlr_relative_pointer_manager_v1_send_relative_motion(
3931 +
            relative_pointer_mgr, seat, (uint64_t)time * 1000, dx, dy, dx_unaccel, dy_unaccel
3932 +
        );
3933 +
3934 +
        if (constraint && constraint->surface != seat->pointer_state.focused_surface)
3935 +
            constraint = nullptr;
3936 +
3937 +
        if (constraint && cursor_mode != CURSOR_RESIZE && cursor_mode != CURSOR_MOVE) {
3938 +
            toplevel_from_wlr_surface(constraint->surface, &c, nullptr);
3939 +
3940 +
            if (c) {
3941 +
                sx = cursor->x - c->geom.x - c->bw;
3942 +
                sy = cursor->y - c->geom.y - c->bw;
3943 +
3944 +
                if (wlr_region_confine(
3945 +
                        &constraint->region, sx, sy, sx + dx, sy + dy, &sx_confined, &sy_confined
3946 +
                    )) {
3947 +
                    dx = sx_confined - sx;
3948 +
                    dy = sy_confined - sy;
3949 +
                }
3950 +
                if (constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) {
3951 +
                    wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
3952 +
                    return;
3953 +
                }
3954 +
            }
3955 +
        }
3956 +
        wlr_cursor_move(cursor, device, dx, dy);
3957 +
        wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
3958 +
3959 +
        /* Update the selected display even while dragging a window. */
3960 +
        if (sloppyfocus)
3961 +
            selmon = point_to_monitor(cursor->x, cursor->y);
3962 +
    }
3963 +
    /* Hit-test at the updated cursor position. */
3964 +
    point_to_node(cursor->x, cursor->y, &surface, &c, nullptr, &sx, &sy);
3965 +
3966 +
    if (cursor_mode == CURSOR_PRESSED && !seat->drag &&
3967 +
        surface != seat->pointer_state.focused_surface &&
3968 +
        toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) {
3969 +
        c       = w;
3970 +
        surface = seat->pointer_state.focused_surface;
3971 +
        sx      = cursor->x - (l ? l->scene->node.x : w->geom.x);
3972 +
        sy      = cursor->y - (l ? l->scene->node.y : w->geom.y);
3973 +
    }
3974 +
    /* Keep the drag icon under the pointer. */
3975 +
    wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y));
3976 +
3977 +
    /* Move or resize the grabbed window instead of changing pointer focus. */
3978 +
    if (cursor_mode == CURSOR_MOVE) {
3979 +
        /* Move the grabbed client to the new position. */
3980 +
        resize(
3981 +
            grabc,
3982 +
            (struct wlr_box){ .x      = (int)round(cursor->x) - grabcx,
3983 +
                              .y      = (int)round(cursor->y) - grabcy,
3984 +
                              .width  = grabc->geom.width,
3985 +
                              .height = grabc->geom.height },
3986 +
            1
3987 +
        );
3988 +
        return;
3989 +
    } else if (cursor_mode == CURSOR_RESIZE) {
3990 +
        int            movedx = (int)round(cursor->x) - grabx;
3991 +
        int            movedy = (int)round(cursor->y) - graby;
3992 +
        struct swm_box pure =
3993 +
            swm_box_resize((const struct swm_box *)&grabgeom, movedx, movedy, grabedges, grabc->bw);
3994 +
        struct wlr_box geo  = { pure.x, pure.y, pure.width, pure.height };
3995 +
        grabc->resize_edges = grabedges;
3996 +
        resize(grabc, geo, 1);
3997 +
        return;
3998 +
    }
3999 +
    /* Show the default cursor over the background and window borders. */
4000 +
    if (!surface && !seat->drag)
4001 +
        wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
4002 +
4003 +
    pointer_focus(c, surface, sx, sy, time, refocus);
4004 +
}
4005 +
4006 +
/* Apply pointer movement reported relative to the previous position. */
4007 +
void motion_relative(struct wl_listener *listener, void *data) {
4008 +
    /* A pointer reported movement relative to its previous position. */
4009 +
    struct wlr_pointer_motion_event *event = data;
4010 +
4011 +
    /* Move the cursor by the reported distance. wlroots applies display bounds
4012 +
     * and any settings specific to the input device. */
4013 +
    motion_notify(
4014 +
        event->time_msec,
4015 +
        &event->pointer->base,
4016 +
        event->delta_x,
4017 +
        event->delta_y,
4018 +
        event->unaccel_dx,
4019 +
        event->unaccel_dy,
4020 +
        1
4021 +
    );
4022 +
}
4023 +
4024 +
/* Begin moving or resizing the window under the pointer. */
4025 +
void move_resize(const arg_t *arg) {
4026 +
    if (cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED)
4027 +
        return;
4028 +
    point_to_node(cursor->x, cursor->y, nullptr, &grabc, nullptr, nullptr, nullptr);
4029 +
4030 +
    if (!grabc || client_is_unmanaged(grabc) || grabc->is_fullscreen)
4031 +
        return;
4032 +
4033 +
    /* Float the window and let pointer motion move or resize it. */
4034 +
    grabc->persist_float = 1;
4035 +
    set_floating(grabc, 1);
4036 +
4037 +
    switch (cursor_mode = arg->u) {
4038 +
    case CURSOR_MOVE:
4039 +
        grabcx = (int)round(cursor->x) - grabc->geom.x;
4040 +
        grabcy = (int)round(cursor->y) - grabc->geom.y;
4041 +
        wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll");
4042 +
        break;
4043 +
    case CURSOR_RESIZE:
4044 +
        grabx     = (int)round(cursor->x);
4045 +
        graby     = (int)round(cursor->y);
4046 +
        grabgeom  = grabc->geom;
4047 +
        grabedges = (grabx < grabgeom.x + grabgeom.width / 2 ? WLR_EDGE_LEFT : WLR_EDGE_RIGHT) |
4048 +
                    (graby < grabgeom.y + grabgeom.height / 2 ? WLR_EDGE_TOP : WLR_EDGE_BOTTOM);
4049 +
        client_set_resizing(grabc, 1);
4050 +
        wlr_cursor_set_xcursor(
4051 +
            cursor,
4052 +
            cursor_mgr,
4053 +
            grabedges == (WLR_EDGE_TOP | WLR_EDGE_LEFT)      ? "nw-resize"
4054 +
            : grabedges == (WLR_EDGE_TOP | WLR_EDGE_RIGHT)   ? "ne-resize"
4055 +
            : grabedges == (WLR_EDGE_BOTTOM | WLR_EDGE_LEFT) ? "sw-resize"
4056 +
                                                             : "se-resize"
4057 +
        );
4058 +
        break;
4059 +
    }
4060 +
}
4061 +
4062 +
/* Track a new request to capture window-manager shortcuts. */
4063 +
void new_shortcuts_inhibitor(struct wl_listener *listener, void *data) {
4064 +
    update_shortcuts_inhibitors(seat->keyboard_state.focused_surface);
4065 +
}
4066 +
4067 +
/* Activate only the shortcut inhibitor belonging to the focused surface. */
4068 +
void update_shortcuts_inhibitors(struct wlr_surface *surface) {
4069 +
    struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;
4070 +
4071 +
    wl_list_for_each(inhibitor, &kb_inhibit_mgr->inhibitors, link) {
4072 +
        if (inhibitor->surface == surface) {
4073 +
            if (!inhibitor->active)
4074 +
                wlr_keyboard_shortcuts_inhibitor_v1_activate(inhibitor);
4075 +
        } else if (inhibitor->active) {
4076 +
            wlr_keyboard_shortcuts_inhibitor_v1_deactivate(inhibitor);
4077 +
        }
4078 +
    }
4079 +
}
4080 +
4081 +
/* Return whether the focused application currently captures shortcuts. */
4082 +
bool shortcuts_inhibited(void) {
4083 +
    struct wlr_surface                         *surface = seat->keyboard_state.focused_surface;
4084 +
    struct wlr_keyboard_shortcuts_inhibitor_v1 *inhibitor;
4085 +
4086 +
    if (!surface)
4087 +
        return false;
4088 +
    wl_list_for_each(inhibitor, &kb_inhibit_mgr->inhibitors, link) {
4089 +
        if (inhibitor->surface == surface && inhibitor->active)
4090 +
            return true;
4091 +
    }
4092 +
    return false;
4093 +
}
4094 +
4095 +
/* Apply a display configuration requested by an external client. */
4096 +
void output_manager_apply(struct wl_listener *listener, void *data) {
4097 +
    struct wlr_output_configuration_v1 *config = data;
4098 +
4099 +
    output_manager_apply_or_test(config, 0);
4100 +
}
4101 +
4102 +
/* Validate a requested display configuration and optionally apply it. */
4103 +
void output_manager_apply_or_test(struct wlr_output_configuration_v1 *config, bool test) {
4104 +
    /* Test or apply a display change requested by a tool such as wlr-randr.
4105 +
     * update_monitors() updates local state after the layout reports the change. */
4106 +
    struct wlr_output_configuration_head_v1 *config_head;
4107 +
    size_t                                   states_len = wl_list_length(&config->heads), i = 0;
4108 +
    int                                      ok;
4109 +
4110 +
    if (!states_len || states_len > MAX_MONITORS) {
4111 +
        wlr_output_configuration_v1_send_failed(config);
4112 +
        wlr_output_configuration_v1_destroy(config);
4113 +
        return;
4114 +
    }
4115 +
    wl_list_for_each(config_head, &config->heads, link) {
4116 +
        output_state_pool[i].output = config_head->state.output;
4117 +
        wlr_output_state_init(&output_state_pool[i].base);
4118 +
        wlr_output_head_v1_state_apply(&config_head->state, &output_state_pool[i].base);
4119 +
        i++;
4120 +
    }
4121 +
    ok = wlr_backend_test(backend, output_state_pool, states_len);
4122 +
4123 +
    if (ok && !test)
4124 +
        ok = wlr_backend_commit(backend, output_state_pool, states_len);
4125 +
4126 +
    for (i = 0; i < states_len; i++)
4127 +
        wlr_output_state_finish(&output_state_pool[i].base);
4128 +
4129 +
    if (ok && !test) {
4130 +
        wl_list_for_each(config_head, &config->heads, link) {
4131 +
            struct wlr_output *wlr_output = config_head->state.output;
4132 +
            monitor_t         *m          = wlr_output->data;
4133 +
4134 +
            /* An applied output-management configuration supersedes DPMS. */
4135 +
            m->asleep = 0;
4136 +
            /* Don't move disabled outputs or positions which did not change. */
4137 +
            if (wlr_output->enabled &&
4138 +
                (m->m.x != config_head->state.x || m->m.y != config_head->state.y))
4139 +
                wlr_output_layout_add(
4140 +
                    output_layout, wlr_output, config_head->state.x, config_head->state.y
4141 +
                );
4142 +
        }
4143 +
    }
4144 +
    if (ok)
4145 +
        wlr_output_configuration_v1_send_succeeded(config);
4146 +
    else
4147 +
        wlr_output_configuration_v1_send_failed(config);
4148 +
4149 +
    wlr_output_configuration_v1_destroy(config);
4150 +
4151 +
    if (!test && ok)
4152 +
        update_monitors(nullptr, nullptr);
4153 +
}
4154 +
4155 +
/* Validate a requested display configuration without applying it. */
4156 +
void output_manager_test(struct wl_listener *listener, void *data) {
4157 +
    struct wlr_output_configuration_v1 *config = data;
4158 +
4159 +
    output_manager_apply_or_test(config, 1);
4160 +
}
4161 +
4162 +
/* Notify applications when the pointer enters, moves within, or leaves a surface. */
4163 +
void pointer_focus(
4164 +
    client_t *c, struct wlr_surface *surface, double sx, double sy, uint32_t time, bool refocus
4165 +
) {
4166 +
    struct wlr_pointer_constraint_v1 *constraint, *focused = nullptr;
4167 +
    struct timespec                   now;
4168 +
4169 +
    if (surface != seat->pointer_state.focused_surface && sloppyfocus && refocus && c &&
4170 +
        !client_is_unmanaged(c))
4171 +
        focus_client(c, 0);
4172 +
4173 +
    /* Clear pointer focus when there is no surface. */
4174 +
    if (!surface) {
4175 +
        cursor_constrain(nullptr);
4176 +
        wlr_seat_pointer_notify_clear_focus(seat);
4177 +
        return;
4178 +
    }
4179 +
    if (!time) {
4180 +
        clock_gettime(CLOCK_MONOTONIC, &now);
4181 +
        time = now.tv_sec * 1000 + now.tv_nsec / 1000000;
4182 +
    }
4183 +
    /* Let the client know that the mouse cursor has entered one
4184 +
     * of its surfaces, and make keyboard focus follow if desired.
4185 +
     * wlroots does nothing if the surface is already focused. */
4186 +
    wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
4187 +
    wlr_seat_pointer_notify_motion(seat, time, sx, sy);
4188 +
    wl_list_for_each(constraint, &pointer_constraints->constraints, link) {
4189 +
        if (constraint->surface == surface) {
4190 +
            focused = constraint;
4191 +
            break;
4192 +
        }
4193 +
    }
4194 +
    cursor_constrain(focused);
4195 +
}
4196 +
4197 +
/* Replace line-breaking characters before writing a status field. */
4198 +
static void status_field(char *dst, size_t size, const char *src) {
4199 +
    swm_sanitize_field(dst, size, src, nullptr);
4200 +
}
4201 +
4202 +
/* Publish current display, workspace, and focus state on standard output. */
4203 +
void print_status(void) {
4204 +
    monitor_t  *m = nullptr;
4205 +
    client_t   *c;
4206 +
    FILE       *titlefile;
4207 +
    char        titlepath[4096], tmppath[4096], title[1024], appid[1024];
4208 +
    const char *runtime, *src;
4209 +
    uint32_t    occ, urg;
4210 +
4211 +
    occ = urg = 0;
4212 +
    wl_list_for_each(c, &clients, link) {
4213 +
        ftl_sync(c);
4214 +
4215 +
        if (!c->ws)
4216 +
            continue;
4217 +
        occ |= 1u << c->ws->idx;
4218 +
4219 +
        if (c->is_urgent)
4220 +
            urg |= 1u << c->ws->idx;
4221 +
    }
4222 +
    wl_list_for_each(m, &mons, link) {
4223 +
        if ((c = focus_top(m))) {
4224 +
            status_field(title, sizeof(title), client_get_title(c));
4225 +
            status_field(appid, sizeof(appid), client_get_appid(c));
4226 +
            printf("%s title %s\n", m->wlr_output->name, title);
4227 +
            printf("%s appid %s\n", m->wlr_output->name, appid);
4228 +
            printf("%s fullscreen %d\n", m->wlr_output->name, c->is_fullscreen);
4229 +
            printf("%s floating %d\n", m->wlr_output->name, c->is_floating);
4230 +
        } else {
4231 +
            printf("%s title \n", m->wlr_output->name);
4232 +
            printf("%s appid \n", m->wlr_output->name);
4233 +
            printf("%s fullscreen \n", m->wlr_output->name);
4234 +
            printf("%s floating \n", m->wlr_output->name);
4235 +
        }
4236 +
        printf("%s selmon %u\n", m->wlr_output->name, m == selmon);
4237 +
        printf("%s workspace %s\n", m->wlr_output->name, m->ws ? m->ws->name : "");
4238 +
        printf(
4239 +
            "%s tags %" PRIu32 " %" PRIu32 " %" PRIu32 " %" PRIu32 "\n",
4240 +
            m->wlr_output->name,
4241 +
            occ,
4242 +
            m->ws ? 1u << m->ws->idx : 0,
4243 +
            m->ws ? 1u << m->ws->idx : 0,
4244 +
            urg
4245 +
        );
4246 +
    }
4247 +
    runtime = getenv("XDG_RUNTIME_DIR");
4248 +
4249 +
    if (runtime && selmon) {
4250 +
        src = (c = focus_top(selmon)) ? client_get_title(c) : "";
4251 +
        status_field(title, sizeof(title), src);
4252 +
        snprintf(titlepath, sizeof(titlepath), "%s/swm-title", runtime);
4253 +
4254 +
        if (snprintf(tmppath, sizeof(tmppath), "%s.tmp.%ld", titlepath, (long)getpid()) <
4255 +
                (int)sizeof(tmppath) &&
4256 +
            (titlefile = fopen(tmppath, "w"))) {
4257 +
            int ok = fprintf(titlefile, "%s\n", title) >= 0 && fflush(titlefile) == 0;
4258 +
4259 +
            if (fclose(titlefile) < 0)
4260 +
                ok = 0;
4261 +
4262 +
            if (!ok || rename(tmppath, titlepath) < 0)
4263 +
                unlink(tmppath);
4264 +
        }
4265 +
    }
4266 +
    fflush(stdout);
4267 +
    workspace_broadcast();
4268 +
}
4269 +
4270 +
/* Restore process state that child commands should not inherit from swm. */
4271 +
void prepare_child(void) {
4272 +
    if (sigprocmask(SIG_SETMASK, &original_signal_mask, nullptr) < 0)
4273 +
        die("sigprocmask:");
4274 +
    signal(SIGPIPE, SIG_DFL);
4275 +
}
4276 +
4277 +
/* Turn a display on or off at an external client's request. */
4278 +
void power_manager_set_mode(struct wl_listener *listener, void *data) {
4279 +
    struct wlr_output_power_v1_set_mode_event *event = data;
4280 +
    struct wlr_output_state                    state = {};
4281 +
    monitor_t                                 *m     = event->output->data;
4282 +
4283 +
    if (!m)
4284 +
        return;
4285 +
4286 +
    m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
4287 +
    wlr_output_state_set_enabled(&state, event->mode);
4288 +
    wlr_output_commit_state(m->wlr_output, &state);
4289 +
4290 +
    m->asleep = !event->mode;
4291 +
    update_monitors(nullptr, nullptr);
4292 +
}
4293 +
4294 +
/* Stop the Wayland event loop and begin shutdown. */
4295 +
void quit(const arg_t *arg) {
4296 +
    wl_display_terminate(dpy);
4297 +
}
4298 +
4299 +
/* Draw a display when its next frame is due. */
4300 +
void render_monitor(struct wl_listener *listener, void *data) {
4301 +
    /* This function is called every time an output is ready to display a frame,
4302 +
     * generally at the output's refresh rate (e.g. 60Hz). */
4303 +
    monitor_t              *m = wl_container_of(listener, m, frame);
4304 +
    client_t               *c;
4305 +
    struct wlr_output_state pending = {};
4306 +
    struct timespec         now;
4307 +
4308 +
    /* Render if no XDG clients have an outstanding resize and are visible on
4309 +
     * this monitor. */
4310 +
    wl_list_for_each(c, &clients, link) {
4311 +
        if (c->resize && !c->is_floating && client_is_rendered_on_mon(c, m) &&
4312 +
            !client_is_stopped(c))
4313 +
            goto skip;
4314 +
    }
4315 +
    wlr_scene_output_commit(m->scene_output, nullptr);
4316 +
4317 +
skip:
4318 +
    /* Tell applications that the frame has been drawn. */
4319 +
    clock_gettime(CLOCK_MONOTONIC, &now);
4320 +
    wlr_scene_output_send_frame_done(m->scene_output, &now);
4321 +
    wlr_output_state_finish(&pending);
4322 +
}
4323 +
4324 +
/* Choose and send the border-decoration mode for a Wayland window. */
4325 +
void request_decoration_mode(struct wl_listener *listener, void *data) {
4326 +
    client_t                                *c = wl_container_of(listener, c, set_decoration_mode);
4327 +
    unsigned int                             oldbw = c->bw;
4328 +
    enum wlr_xdg_toplevel_decoration_v1_mode mode =
4329 +
        c->decoration->requested_mode == WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
4330 +
            ? WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_CLIENT_SIDE
4331 +
            : WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE;
4332 +
4333 +
    c->bw = client_border_width(c);
4334 +
    /* Keep the requested border mode until the surface is initialized and
4335 +
     * wlroots can send it to the application. */
4336 +
    if (c->surface.xdg->initialized) {
4337 +
        wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, mode);
4338 +
4339 +
        if (oldbw != c->bw && client_surface(c)->mapped && c->scene)
4340 +
            resize(c, c->geom, 0);
4341 +
    }
4342 +
}
4343 +
4344 +
/* Accept a drag only when its button press has a valid serial number. */
4345 +
void request_start_drag(struct wl_listener *listener, void *data) {
4346 +
    struct wlr_seat_request_start_drag_event *event = data;
4347 +
4348 +
    if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, event->serial))
4349 +
        wlr_seat_start_pointer_drag(seat, event->drag, event->serial);
4350 +
    else
4351 +
        wlr_data_source_destroy(event->drag->source);
4352 +
}
4353 +
4354 +
/* Commit a display-state change requested by the backend. */
4355 +
void request_monitor_state(struct wl_listener *listener, void *data) {
4356 +
    struct wlr_output_event_request_state *event = data;
4357 +
4358 +
    wlr_output_commit_state(event->output, event->state);
4359 +
    update_monitors(nullptr, nullptr);
4360 +
}
4361 +
4362 +
/* Request a new window geometry and apply it when the application is ready. */
4363 +
void resize(client_t *c, struct wlr_box geo, bool interact) {
4364 +
    struct wlr_box *bbox;
4365 +
4366 +
    if (!c->mon || !client_surface(c)->mapped)
4367 +
        return;
4368 +
4369 +
    bbox = interact ? &sgeom : &c->mon->w;
4370 +
4371 +
    geo.width  = MAX(1 + 2 * (int)c->bw, geo.width);
4372 +
    geo.height = MAX(1 + 2 * (int)c->bw, geo.height);
4373 +
    client_set_bounds(c, geo.width, geo.height);
4374 +
    c->geom = geo;
4375 +
    apply_bounds(c, bbox);
4376 +
4377 +
    /* During an interactive Wayland resize, keep drawing the old size until
4378 +
     * the application confirms the new one. XWayland resizes immediately. */
4379 +
    c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
4380 +
4381 +
    if (c->resize)
4382 +
        c->pending_geom = c->geom;
4383 +
4384 +
    if (interact && c->resize) {
4385 +
        return;
4386 +
    }
4387 +
    resize_apply(c);
4388 +
4389 +
    if (c == focus_top(c->mon))
4390 +
        position_input_popups();
4391 +
}
4392 +
4393 +
/* Commit a window's geometry to its surface, drawing nodes, and borders. */
4394 +
void resize_apply(client_t *c) {
4395 +
    struct wlr_box clip;
4396 +
4397 +
    /* Update the window's drawing nodes, including its borders. */
4398 +
    wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
4399 +
    wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
4400 +
    wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
4401 +
    wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
4402 +
    wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
4403 +
    wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
4404 +
    wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
4405 +
    wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
4406 +
    wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw);
4407 +
4408 +
    client_get_clip(c, &clip);
4409 +
    wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
4410 +
}
4411 +
4412 +
/* Start the backend and child commands, then run the Wayland event loop. */
4413 +
void run(char *startup_cmd) {
4414 +
    /* Add a Unix socket to the Wayland display. */
4415 +
    const char *socket = wl_display_add_socket_auto(dpy);
4416 +
4417 +
    if (!socket)
4418 +
        die("startup: display_add_socket_auto");
4419 +
    setenv("WAYLAND_DISPLAY", socket, 1);
4420 +
    setenv("XDG_CURRENT_DESKTOP", "swm", 1);
4421 +
4422 +
    /* Start the backend. This will enumerate outputs and inputs, become the DRM
4423 +
     * master, and control the display devices. */
4424 +
    if (!wlr_backend_start(backend))
4425 +
        die("startup: backend_start");
4426 +
4427 +
    /* Now that the socket exists and the backend is started, run the
4428 +
     * autostart commands and the startup command. */
4429 +
    if (!getenv("SWM_NO_AUTOSTART"))
4430 +
        autostart_exec();
4431 +
4432 +
    if (startup_cmd) {
4433 +
        int piperw[2];
4434 +
4435 +
        if (pipe(piperw) < 0)
4436 +
            die("startup: pipe:");
4437 +
4438 +
        if ((child_pid = fork()) < 0)
4439 +
            die("startup: fork:");
4440 +
4441 +
        if (child_pid == 0) {
4442 +
            prepare_child();
4443 +
            setsid();
4444 +
            dup2(piperw[0], STDIN_FILENO);
4445 +
            close(piperw[0]);
4446 +
            close(piperw[1]);
4447 +
            execl("/bin/sh", "/bin/sh", "-c", startup_cmd, nullptr);
4448 +
            die("startup: execl:");
4449 +
        }
4450 +
        dup2(piperw[1], STDOUT_FILENO);
4451 +
        close(piperw[1]);
4452 +
        close(piperw[0]);
4453 +
    }
4454 +
    /* Mark stdout as non-blocking to avoid the startup script
4455 +
     * causing swm to freeze when a user neither closes stdin
4456 +
     * nor reads standard input in the startup script. */
4457 +
4458 +
    if (fd_set_nonblock(STDOUT_FILENO) < 0)
4459 +
        close(STDOUT_FILENO);
4460 +
4461 +
    print_status();
4462 +
4463 +
    /* At this point the outputs are initialized, choose initial selmon based on
4464 +
     * cursor position, and set the default cursor image. */
4465 +
    selmon = point_to_monitor(cursor->x, cursor->y);
4466 +
4467 +
    /* TODO: Avoid the cursor briefly appearing at (0, 0) during startup. */
4468 +
    wlr_cursor_warp_closest(cursor, nullptr, cursor->x, cursor->y);
4469 +
    wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
4470 +
4471 +
    /* Run the Wayland event loop. This does not return until you exit the
4472 +
     * compositor. Starting the backend rigged up all of the necessary event
4473 +
     * loop configuration to listen to libinput events, DRM events, generate
4474 +
     * frame events at the refresh rate, and so on. */
4475 +
    wl_display_run(dpy);
4476 +
}
4477 +
4478 +
/* Use the cursor image requested by the application under the pointer. */
4479 +
void set_cursor(struct wl_listener *listener, void *data) {
4480 +
    /* An application supplied a new cursor image. */
4481 +
    struct wlr_seat_pointer_request_set_cursor_event *event = data;
4482 +
4483 +
    /* Keep the move or resize cursor until the pointer grab ends. The
4484 +
     * application will request its cursor again when the pointer re-enters. */
4485 +
    if (cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED)
4486 +
        return;
4487 +
    /* This can be sent by any client, so we check to make sure this one
4488 +
     * actually has pointer focus first. If so, we can tell the cursor to
4489 +
     * use the provided surface as the cursor image. It will set the
4490 +
     * hardware cursor on the output that it's currently on and continue to
4491 +
     * do so as the cursor moves between outputs. */
4492 +
    if (event->seat_client == seat->pointer_state.focused_client)
4493 +
        wlr_cursor_set_surface(cursor, event->surface, event->hotspot_x, event->hotspot_y);
4494 +
}
4495 +
4496 +
/* Use the named cursor shape requested by the application under the pointer. */
4497 +
void set_cursor_shape(struct wl_listener *listener, void *data) {
4498 +
    struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data;
4499 +
4500 +
    if (cursor_mode != CURSOR_NORMAL && cursor_mode != CURSOR_PRESSED)
4501 +
        return;
4502 +
    /* This can be sent by any client, so we check to make sure this one
4503 +
     * actually has pointer focus first. If so, we can tell the cursor to
4504 +
     * use the provided cursor shape. */
4505 +
    if (event->seat_client == seat->pointer_state.focused_client)
4506 +
        wlr_cursor_set_xcursor(cursor, cursor_mgr, wlr_cursor_shape_v1_name(event->shape));
4507 +
}
4508 +
4509 +
/* Move a window between tiled and floating layout behavior. */
4510 +
void set_floating(client_t *c, bool floating) {
4511 +
    client_t *p    = client_get_parent(c);
4512 +
    c->is_floating = floating;
4513 +
    c->bw          = client_border_width(c);
4514 +
    client_set_tiled(
4515 +
        c, floating ? 0 : WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT
4516 +
    );
4517 +
    /* Preserve the window's layer when the whole layout is floating. */
4518 +
    if (!c->mon || !client_surface(c)->mapped || !c->mon->ws || !c->mon->ws->lt->arrange)
4519 +
        return;
4520 +
    wlr_scene_node_reparent(
4521 +
        &c->scene->node,
4522 +
        layers
4523 +
            [c->is_fullscreen || (p && p->is_fullscreen) ? LAYER_FULLSCREEN
4524 +
             : c->is_floating                            ? LAYER_FLOAT
4525 +
                                                         : LAYER_TILE]
4526 +
    );
4527 +
    resize(c, c->geom, 0);
4528 +
4529 +
    if (!c->pending_map) {
4530 +
        arrange(c->mon);
4531 +
        print_status();
4532 +
    }
4533 +
}
4534 +
4535 +
/* Enter or leave fullscreen while preserving the window's previous geometry. */
4536 +
void set_fullscreen(client_t *c, bool fullscreen) {
4537 +
    if (c->is_fullscreen == fullscreen) {
4538 +
        if (c->mon && client_surface(c)->mapped)
4539 +
            client_set_fullscreen(c, fullscreen);
4540 +
        return;
4541 +
    }
4542 +
    c->is_fullscreen = fullscreen;
4543 +
4544 +
    if (!c->mon || !client_surface(c)->mapped)
4545 +
        return;
4546 +
    c->bw = client_border_width(c);
4547 +
    client_set_fullscreen(c, fullscreen);
4548 +
    wlr_scene_node_reparent(
4549 +
        &c->scene->node,
4550 +
        layers
4551 +
            [c->is_fullscreen ? LAYER_FULLSCREEN
4552 +
             : c->is_floating ? LAYER_FLOAT
4553 +
                              : LAYER_TILE]
4554 +
    );
4555 +
4556 +
    if (fullscreen) {
4557 +
        c->prev = c->geom;
4558 +
        resize(c, c->mon->m, 0);
4559 +
    } else {
4560 +
        /* restore previous size instead of arrange for floating windows since
4561 +
         * their positions were chosen by the user. */
4562 +
        resize(c, c->prev, 0);
4563 +
    }
4564 +
    if (!c->pending_map) {
4565 +
        arrange(c->mon);
4566 +
        print_status();
4567 +
    }
4568 +
}
4569 +
4570 +
/* Select the next or previous layout for the focused workspace. */
4571 +
void cycle_layout(const arg_t *arg) {
4572 +
    workspace_t *ws;
4573 +
    bool         cycle[LENGTH(layouts)];
4574 +
    int          current = -1, next;
4575 +
    size_t       i;
4576 +
4577 +
    if (!selmon || !(ws = selmon->ws))
4578 +
        return;
4579 +
4580 +
    for (i = 0; i < LENGTH(layouts); i++)
4581 +
4582 +
        if (ws->lt == &layouts[i]) {
4583 +
            current = (int)i;
4584 +
            break;
4585 +
        }
4586 +
    if (current < 0)
4587 +
        return;
4588 +
4589 +
    for (i = 0; i < LENGTH(layouts); i++)
4590 +
        cycle[i] = layouts[i].cycle;
4591 +
    next = swm_next_layout(current, (int)LENGTH(layouts), cycle);
4592 +
4593 +
    if (next < 0)
4594 +
        return;
4595 +
    ws->prevlt = ws->lt;
4596 +
    ws->lt     = &layouts[next];
4597 +
    arrange(selmon);
4598 +
    print_status();
4599 +
}
4600 +
4601 +
/* Select a neighboring workspace, optionally moving the focused window with it. */
4602 +
void cycle_workspace(const arg_t *arg) {
4603 +
    /* Handle next/previous workspaces, optionally including empty ones or
4604 +
     * taking the focused window along. Workspaces visible on other outputs
4605 +
     * are skipped. */
4606 +
    int dir =
4607 +
        (arg->i == WORKSPACE_NEXT || arg->i == WORKSPACE_NEXT_ALL || arg->i == WORKSPACE_NEXT_MOVE)
4608 +
            ? +1
4609 +
            : -1;
4610 +
    int          allowempty = (arg->i != WORKSPACE_NEXT && arg->i != WORKSPACE_PREVIOUS);
4611 +
    int          mv         = (arg->i == WORKSPACE_NEXT_MOVE || arg->i == WORKSPACE_PREVIOUS_MOVE);
4612 +
    workspace_t *ws, *cur;
4613 +
    client_t    *c, *sel;
4614 +
    bool         visible_elsewhere[WSCOUNT] = {};
4615 +
    bool         occupied[WSCOUNT]          = {};
4616 +
    int          i, idx;
4617 +
4618 +
    if (!selmon || !(cur = selmon->ws))
4619 +
        return;
4620 +
    sel = focus_top(selmon);
4621 +
4622 +
    for (i = 0; i < WSCOUNT; i++)
4623 +
        visible_elsewhere[i] = workspaces[i].mon && workspaces[i].mon != selmon;
4624 +
    wl_list_for_each(c, &clients, link) if (c->ws) occupied[c->ws->idx] = 1;
4625 +
    idx = swm_workspace_next(cur->idx, WSCOUNT, dir, allowempty, visible_elsewhere, occupied);
4626 +
4627 +
    if (idx < 0)
4628 +
        return;
4629 +
    ws = &workspaces[idx];
4630 +
4631 +
    if (mv && sel)
4632 +
        set_workspace(sel, ws);
4633 +
    view_workspace(ws, selmon);
4634 +
4635 +
    if (mv && sel)
4636 +
        focus_client(sel, 1);
4637 +
}
4638 +
4639 +
/* Move a window to a display and keep its workspace and scale consistent. */
4640 +
void set_monitor(client_t *c, monitor_t *m, workspace_t *ws) {
4641 +
    monitor_t *oldmon = c->mon;
4642 +
4643 +
    if (!ws)
4644 +
        ws = m ? m->ws : c->ws;
4645 +
    c->ws = ws;
4646 +
    /* If moving to a monitor and the target workspace is already visible,
4647 +
     * use that monitor. A nullptr monitor explicitly detaches an unmapped client
4648 +
     * and must not be redirected back onto the workspace's monitor. */
4649 +
    if (m && ws && ws->mon)
4650 +
        m = ws->mon;
4651 +
4652 +
    if (oldmon == m) {
4653 +
        if (m)
4654 +
            arrange(m);
4655 +
        return;
4656 +
    }
4657 +
    c->mon = m;
4658 +
4659 +
    if (!c->is_fullscreen)
4660 +
        c->prev = c->geom;
4661 +
4662 +
    /* Moving this node tells applications which displays they overlap. */
4663 +
    if (oldmon)
4664 +
        arrange(oldmon);
4665 +
    /* An initial commit may assign a monitor before map_notify() creates the
4666 +
     * client's scene tree. Defer all scene operations until the surface maps. */
4667 +
    if (m && client_surface(c)->mapped) {
4668 +
        /* Keep the window overlapping its display. */
4669 +
        if (c->is_fullscreen) {
4670 +
            client_set_fullscreen(c, 1);
4671 +
            wlr_scene_node_reparent(&c->scene->node, layers[LAYER_FULLSCREEN]);
4672 +
            resize(c, m->m, 0);
4673 +
        } else {
4674 +
            resize(c, c->geom, 0);
4675 +
            set_floating(c, c->is_floating);
4676 +
        }
4677 +
    }
4678 +
    focus_client(focus_top(selmon), 1);
4679 +
}
4680 +
4681 +
/* Move a window and any related windows to another workspace. */
4682 +
void set_workspace(client_t *c, workspace_t *ws) {
4683 +
    /* Move a window to another workspace. */
4684 +
    client_t  *w;
4685 +
    monitor_t *oldmon;
4686 +
    client_t  *main = client_main(c);
4687 +
4688 +
    if (!main || !ws || main->ws == ws)
4689 +
        return;
4690 +
    oldmon = main->mon;
4691 +
    wl_list_for_each(w, &clients, link) {
4692 +
        if (!clients_related(w, main))
4693 +
            continue;
4694 +
        w->ws = ws;
4695 +
4696 +
        if (ws->mon && ws->mon != w->mon) {
4697 +
            w->mon = ws->mon;
4698 +
4699 +
            if (w->is_fullscreen) {
4700 +
                client_set_fullscreen(w, 1);
4701 +
                wlr_scene_node_reparent(&w->scene->node, layers[LAYER_FULLSCREEN]);
4702 +
                resize(w, w->mon->m, 0);
4703 +
            } else {
4704 +
                w->prev = w->geom;
4705 +
                resize(w, w->geom, 0);
4706 +
                set_floating(w, w->is_floating);
4707 +
            }
4708 +
        }
4709 +
    }
4710 +
    if (oldmon)
4711 +
        arrange(oldmon);
4712 +
4713 +
    if (ws->mon && ws->mon != oldmon)
4714 +
        arrange(ws->mon);
4715 +
    focus_client(focus_top(selmon), 1);
4716 +
    print_status();
4717 +
}
4718 +
4719 +
/* Show a workspace on a display and synchronize all of its windows. */
4720 +
void assign_workspace(workspace_t *ws, monitor_t *m) {
4721 +
    /* Attach a workspace to a display and update all of its windows. */
4722 +
    client_t *c;
4723 +
4724 +
    ws->mon = m;
4725 +
4726 +
    if (!m)
4727 +
        return;
4728 +
    wl_list_for_each(c, &clients, link) {
4729 +
        if (c->ws != ws)
4730 +
            continue;
4731 +
4732 +
        if (c->mon != m) {
4733 +
            c->mon = m;
4734 +
4735 +
            if (!c->is_fullscreen)
4736 +
                c->prev = c->geom;
4737 +
            resize(c, c->geom, 0);
4738 +
        }
4739 +
        if (c->is_fullscreen)
4740 +
            resize(c, m->m, 0);
4741 +
    }
4742 +
}
4743 +
4744 +
/* Return the first workspace not shown on an output. */
4745 +
workspace_t *free_workspace(void) {
4746 +
    workspace_t *ws;
4747 +
4748 +
    for (ws = workspaces; ws < END(workspaces); ws++) {
4749 +
        if (!ws->mon)
4750 +
            return ws;
4751 +
    }
4752 +
    return nullptr;
4753 +
}
4754 +
4755 +
/* Show a workspace, exchanging it with another display when already visible. */
4756 +
void view_workspace(workspace_t *ws, monitor_t *m) {
4757 +
    /* show workspace ws on output m; if ws is visible on another output,
4758 +
     * the two displays exchange workspaces. */
4759 +
    monitor_t   *other;
4760 +
    workspace_t *old;
4761 +
4762 +
    if (!m || !ws || ws == m->ws)
4763 +
        return;
4764 +
    old = m->ws;
4765 +
4766 +
    if ((other = ws->mon)) {
4767 +
        other->ws                 = old;
4768 +
        other->previous_workspace = ws;
4769 +
4770 +
        if (old)
4771 +
            assign_workspace(old, other);
4772 +
    } else if (old) {
4773 +
        assign_workspace(old, nullptr);
4774 +
    }
4775 +
    m->previous_workspace = old;
4776 +
    m->ws                 = ws;
4777 +
    assign_workspace(ws, m);
4778 +
    arrange(m);
4779 +
4780 +
    if (other)
4781 +
        arrange(other);
4782 +
    /* Arrange first, then transfer keyboard focus to the workspace's most
4783 +
     * recently focused client. */
4784 +
    focus_client(focus_top(m), 1);
4785 +
    print_status();
4786 +
}
4787 +
4788 +
/*
4789 +
 * ext-workspace-v1 implementation. wlroots 0.19 ships no helper for this
4790 +
 * protocol, so the resources are managed by hand. swm advertises a single
4791 +
 * workspace group spanning all outputs, containing every workspace; the
4792 +
 * only capability is activate, which behaves like a workspace switch.
4793 +
 */
4794 +
static void workspace_manager_commit(struct wl_client *client, struct wl_resource *resource) {
4795 +
    workspace_manager_t *mgr = wl_resource_get_user_data(resource);
4796 +
    workspace_t         *ws;
4797 +
4798 +
    if (!mgr || !(ws = mgr->pending))
4799 +
        return;
4800 +
    mgr->pending = nullptr;
4801 +
    view_workspace(ws, selmon);
4802 +
}
4803 +
4804 +
/* Stop publishing workspaces to a manager. */
4805 +
static void workspace_manager_stop(struct wl_client *client, struct wl_resource *resource) {
4806 +
    ext_workspace_manager_v1_send_finished(resource);
4807 +
    wl_resource_destroy(resource);
4808 +
}
4809 +
4810 +
static const struct ext_workspace_manager_v1_interface ws_manager_impl = {
4811 +
    .commit = workspace_manager_commit,
4812 +
    .stop   = workspace_manager_stop,
4813 +
};
4814 +
4815 +
/* Stop publishing workspaces after a manager client disconnects. */
4816 +
static void workspace_manager_destroy(struct wl_resource *resource) {
4817 +
    workspace_manager_t *mgr = wl_resource_get_user_data(resource);
4818 +
    workspace_handle_t  *h;
4819 +
    int                  i;
4820 +
4821 +
    if (!mgr)
4822 +
        return;
4823 +
    /* Handles and the group may outlive the manager resource. */
4824 +
    for (i = 0; i < WSCOUNT; i++) {
4825 +
        wl_list_for_each(h, &workspaces[i].handles, link) {
4826 +
            if (h->mgr == mgr)
4827 +
                h->mgr = nullptr;
4828 +
        }
4829 +
    }
4830 +
    if (mgr->group)
4831 +
        wl_resource_set_user_data(mgr->group, nullptr);
4832 +
    wl_list_remove(&mgr->link);
4833 +
    pool_release(&workspace_manager_pool, mgr);
4834 +
}
4835 +
4836 +
/* Reject a client-created workspace. */
4837 +
static void workspace_group_create_workspace(
4838 +
    struct wl_client *client, struct wl_resource *resource, const char *name
4839 +
) {
4840 +
    /* Ignore creation because swm has a fixed set of workspaces. */
4841 +
}
4842 +
4843 +
/* Reject destruction of the global workspace group. */
4844 +
static void workspace_group_destroy_request(
4845 +
    struct wl_client *client, struct wl_resource *resource
4846 +
) {
4847 +
    wl_resource_destroy(resource);
4848 +
}
4849 +
4850 +
static const struct ext_workspace_group_handle_v1_interface ws_group_impl = {
4851 +
    .create_workspace = workspace_group_create_workspace,
4852 +
    .destroy          = workspace_group_destroy_request,
4853 +
};
4854 +
4855 +
/* Remove a disconnected client's global workspace group. */
4856 +
static void workspace_group_destroy(struct wl_resource *resource) {
4857 +
    workspace_manager_t *mgr = wl_resource_get_user_data(resource);
4858 +
4859 +
    if (mgr)
4860 +
        mgr->group = nullptr;
4861 +
}
4862 +
4863 +
/* Queue workspace activation. */
4864 +
static void workspace_activate(struct wl_client *client, struct wl_resource *resource) {
4865 +
    workspace_handle_t *h = wl_resource_get_user_data(resource);
4866 +
4867 +
    if (h && h->mgr)
4868 +
        h->mgr->pending = h->ws;
4869 +
}
4870 +
4871 +
/* Ignore workspace deactivation. */
4872 +
static void workspace_deactivate(struct wl_client *client, struct wl_resource *resource) {
4873 +
    /* Ignore deactivation because swm always has an active workspace. */
4874 +
}
4875 +
4876 +
/* Ignore client workspace assignment. */
4877 +
static void workspace_assign(
4878 +
    struct wl_client *client, struct wl_resource *resource, struct wl_resource *group
4879 +
) {
4880 +
    /* Ignore assignment because swm has only one workspace group. */
4881 +
}
4882 +
4883 +
/* Ignore client workspace removal. */
4884 +
static void workspace_remove_request(struct wl_client *client, struct wl_resource *resource) {
4885 +
    /* Ignore removal because swm has a fixed set of workspaces. */
4886 +
}
4887 +
4888 +
/* Ignore client workspace destruction. */
4889 +
static void workspace_destroy_request(struct wl_client *client, struct wl_resource *resource) {
4890 +
    wl_resource_destroy(resource);
4891 +
}
4892 +
4893 +
static const struct ext_workspace_handle_v1_interface ws_handle_impl = {
4894 +
    .destroy    = workspace_destroy_request,
4895 +
    .activate   = workspace_activate,
4896 +
    .deactivate = workspace_deactivate,
4897 +
    .assign     = workspace_assign,
4898 +
    .remove     = workspace_remove_request,
4899 +
};
4900 +
4901 +
/* Remove one client's handle for a published workspace. */
4902 +
static void workspace_handle_destroy(struct wl_resource *resource) {
4903 +
    workspace_handle_t *h = wl_resource_get_user_data(resource);
4904 +
4905 +
    if (!h)
4906 +
        return;
4907 +
    wl_list_remove(&h->link);
4908 +
    pool_release(&workspace_handle_pool, h);
4909 +
}
4910 +
4911 +
/* Publish one workspace to a connected workspace manager. */
4912 +
void workspace_handle_create(workspace_manager_t *mgr, workspace_t *ws) {
4913 +
    workspace_handle_t *h = pool_take(&workspace_handle_pool);
4914 +
    uint32_t            coord;
4915 +
    struct wl_array     coords = { sizeof(coord), sizeof(coord), &coord };
4916 +
    char                id[MAX_WS_ID];
4917 +
4918 +
    if (!h) {
4919 +
        wl_client_post_no_memory(wl_resource_get_client(mgr->resource));
4920 +
        return;
4921 +
    }
4922 +
    h->ws       = ws;
4923 +
    h->mgr      = mgr;
4924 +
    h->resource = wl_resource_create(
4925 +
        wl_resource_get_client(mgr->resource), &ext_workspace_handle_v1_interface, 1, 0
4926 +
    );
4927 +
4928 +
    if (!h->resource) {
4929 +
        pool_release(&workspace_handle_pool, h);
4930 +
        wl_client_post_no_memory(wl_resource_get_client(mgr->resource));
4931 +
        return;
4932 +
    }
4933 +
    wl_resource_set_implementation(h->resource, &ws_handle_impl, h, workspace_handle_destroy);
4934 +
    wl_list_insert(&ws->handles, &h->link);
4935 +
4936 +
    ext_workspace_manager_v1_send_workspace(mgr->resource, h->resource);
4937 +
    snprintf(id, sizeof(id), "ws%d", ws->idx + 1);
4938 +
    ext_workspace_handle_v1_send_id(h->resource, id);
4939 +
    ext_workspace_handle_v1_send_name(h->resource, ws->name);
4940 +
    coord = (uint32_t)ws->idx;
4941 +
    ext_workspace_handle_v1_send_coordinates(h->resource, &coords);
4942 +
    ext_workspace_handle_v1_send_capabilities(
4943 +
        h->resource, EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE
4944 +
    );
4945 +
    ext_workspace_handle_v1_send_state(h->resource, workspace_state(ws));
4946 +
4947 +
    if (mgr->group)
4948 +
        ext_workspace_group_handle_v1_send_workspace_enter(mgr->group, h->resource);
4949 +
}
4950 +
4951 +
/* Publish swm's workspace group to a newly connected client. */
4952 +
void workspace_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) {
4953 +
    workspace_manager_t *mgr = pool_take(&workspace_manager_pool);
4954 +
    monitor_t           *m;
4955 +
    struct wl_resource  *out;
4956 +
    int                  i;
4957 +
4958 +
    if (!mgr) {
4959 +
        wl_client_post_no_memory(client);
4960 +
        return;
4961 +
    }
4962 +
    mgr->resource =
4963 +
        wl_resource_create(client, &ext_workspace_manager_v1_interface, (int)version, id);
4964 +
4965 +
    if (!mgr->resource) {
4966 +
        pool_release(&workspace_manager_pool, mgr);
4967 +
        wl_client_post_no_memory(client);
4968 +
        return;
4969 +
    }
4970 +
    wl_resource_set_implementation(mgr->resource, &ws_manager_impl, mgr, workspace_manager_destroy);
4971 +
    wl_list_insert(&ws_managers, &mgr->link);
4972 +
4973 +
    mgr->group = wl_resource_create(client, &ext_workspace_group_handle_v1_interface, 1, 0);
4974 +
4975 +
    if (!mgr->group) {
4976 +
        wl_client_post_no_memory(client);
4977 +
        return;
4978 +
    }
4979 +
    wl_resource_set_implementation(mgr->group, &ws_group_impl, mgr, workspace_group_destroy);
4980 +
    ext_workspace_manager_v1_send_workspace_group(mgr->resource, mgr->group);
4981 +
    ext_workspace_group_handle_v1_send_capabilities(mgr->group, 0);
4982 +
    wl_list_for_each(m, &mons, link) {
4983 +
        wl_resource_for_each(out, &m->wlr_output->resources) {
4984 +
            if (wl_resource_get_client(out) == client)
4985 +
                ext_workspace_group_handle_v1_send_output_enter(mgr->group, out);
4986 +
        }
4987 +
    }
4988 +
    for (i = 0; i < WSCOUNT; i++)
4989 +
        workspace_handle_create(mgr, &workspaces[i]);
4990 +
    ext_workspace_manager_v1_send_done(mgr->resource);
4991 +
}
4992 +
4993 +
/* Add a newly bound display to every published workspace group. */
4994 +
void workspace_output_bind(struct wl_listener *listener, void *data) {
4995 +
    /* Tell workspace clients about the newly bound display. */
4996 +
    struct wlr_output_event_bind *event  = data;
4997 +
    struct wl_client             *client = wl_resource_get_client(event->resource);
4998 +
    workspace_manager_t          *mgr;
4999 +
5000 +
    wl_list_for_each(mgr, &ws_managers, link) {
5001 +
        if (mgr->group && wl_resource_get_client(mgr->resource) == client) {
5002 +
            ext_workspace_group_handle_v1_send_output_enter(mgr->group, event->resource);
5003 +
            ext_workspace_manager_v1_send_done(mgr->resource);
5004 +
        }
5005 +
    }
5006 +
}
5007 +
5008 +
/* Compute and publish a workspace's active, hidden, urgent, and focus state. */
5009 +
uint32_t workspace_state(workspace_t *ws) {
5010 +
    _Static_assert(
5011 +
        (unsigned int)SWM_WORKSPACE_ACTIVE == (unsigned int)EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE,
5012 +
        "workspace active bit changed"
5013 +
    );
5014 +
    _Static_assert(
5015 +
        (unsigned int)SWM_WORKSPACE_URGENT == (unsigned int)EXT_WORKSPACE_HANDLE_V1_STATE_URGENT,
5016 +
        "workspace urgent bit changed"
5017 +
    );
5018 +
    _Static_assert(
5019 +
        (unsigned int)SWM_WORKSPACE_HIDDEN == (unsigned int)EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN,
5020 +
        "workspace hidden bit changed"
5021 +
    );
5022 +
    client_t *c;
5023 +
    uint32_t  state;
5024 +
    int       occupied = 0;
5025 +
    int       urgent   = 0;
5026 +
5027 +
    wl_list_for_each(c, &clients, link) {
5028 +
        if (c->ws != ws)
5029 +
            continue;
5030 +
        occupied  = 1;
5031 +
        urgent   |= c->is_urgent;
5032 +
    }
5033 +
    /* ext-workspace-v1 has no occupied state. Mark empty, inactive
5034 +
     * workspaces hidden so bars can omit them while retaining occupied and
5035 +
     * currently selected workspaces. */
5036 +
    state = swm_workspace_state(ws->mon != nullptr, occupied, urgent);
5037 +
    return state;
5038 +
}
5039 +
5040 +
/* Publish current workspace state to every connected workspace client. */
5041 +
void workspace_broadcast(void) {
5042 +
    workspace_manager_t *mgr;
5043 +
    workspace_handle_t  *h;
5044 +
    uint32_t             state;
5045 +
    int                  i;
5046 +
5047 +
    if (wl_list_empty(&ws_managers))
5048 +
        return;
5049 +
5050 +
    for (i = 0; i < WSCOUNT; i++) {
5051 +
        state = workspace_state(&workspaces[i]);
5052 +
        wl_list_for_each(h, &workspaces[i].handles, link)
5053 +
            ext_workspace_handle_v1_send_state(h->resource, state);
5054 +
    }
5055 +
    wl_list_for_each(mgr, &ws_managers, link) ext_workspace_manager_v1_send_done(mgr->resource);
5056 +
}
5057 +
5058 +
/* Accept an application's request to replace the primary selection. */
5059 +
void set_primary_selection(struct wl_listener *listener, void *data) {
5060 +
    /* Honor an application's request to set the primary selection. */
5061 +
    struct wlr_seat_request_set_primary_selection_event *event = data;
5062 +
5063 +
    wlr_seat_set_primary_selection(seat, event->source, event->serial);
5064 +
}
5065 +
5066 +
/* Accept an application's request to replace the clipboard contents. */
5067 +
void set_selection(struct wl_listener *listener, void *data) {
5068 +
    /* Honor an application's request to set the clipboard selection. */
5069 +
    struct wlr_seat_request_set_selection_event *event = data;
5070 +
5071 +
    wlr_seat_set_selection(seat, event->source, event->serial);
5072 +
}
5073 +
5074 +
/* Create the Wayland server, protocols, input state, and rendering resources. */
5075 +
void setup(void) {
5076 +
    static const int signals[] = { SIGCHLD, SIGINT, SIGTERM };
5077 +
    int              drm_fd, i;
5078 +
5079 +
    wlr_log_init(log_level, nullptr);
5080 +
5081 +
    /* Create the Wayland server and its event loop. */
5082 +
    dpy        = wl_display_create();
5083 +
    event_loop = wl_display_get_event_loop(dpy);
5084 +
5085 +
    if (sigprocmask(SIG_BLOCK, nullptr, &original_signal_mask) < 0)
5086 +
        die("failed to read signal mask:");
5087 +
5088 +
    for (i = 0; i < (int)LENGTH(signals); i++) {
5089 +
        signal_sources[i] =
5090 +
            wl_event_loop_add_signal(event_loop, signals[i], handle_signal, nullptr);
5091 +
5092 +
        if (!signal_sources[i])
5093 +
            die("failed to register signal handler");
5094 +
    }
5095 +
    signal(SIGPIPE, SIG_IGN);
5096 +
5097 +
    /* Choose a backend for the current environment and its input and display devices. */
5098 +
    if (!(backend = wlr_backend_autocreate(event_loop, &session)))
5099 +
        die("couldn't create backend");
5100 +
5101 +
    /* Create the tree that controls window position and drawing order. */
5102 +
    scene = wlr_scene_create();
5103 +
5104 +
    if (!scene)
5105 +
        die("failed to create scene");
5106 +
    root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, rootcolor);
5107 +
5108 +
    if (!root_bg)
5109 +
        die("failed to create root background");
5110 +
5111 +
    for (i = 0; i < NUM_LAYERS; i++) {
5112 +
        layers[i] = wlr_scene_tree_create(&scene->tree);
5113 +
5114 +
        if (!layers[i])
5115 +
            die("failed to create scene layer");
5116 +
    }
5117 +
    drag_icon = wlr_scene_tree_create(&scene->tree);
5118 +
5119 +
    if (!drag_icon)
5120 +
        die("failed to create drag icon layer");
5121 +
    wlr_scene_node_place_below(&drag_icon->node, &layers[LAYER_BLOCK]->node);
5122 +
5123 +
    /* Choose a renderer. WLR_RENDERER can override the automatic choice. */
5124 +
    if (!(drw = wlr_renderer_autocreate(backend)))
5125 +
        die("couldn't create renderer");
5126 +
    wl_signal_add(&drw->events.lost, &gpu_reset_listener);
5127 +
5128 +
    /* Advertise the buffer types supported by both the renderer and scene code. */
5129 +
    wlr_renderer_init_wl_shm(drw, dpy);
5130 +
5131 +
    if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) {
5132 +
        wlr_drm_create(dpy, drw);
5133 +
        wlr_scene_set_linux_dmabuf_v1(scene, wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw));
5134 +
    }
5135 +
    if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline &&
5136 +
        backend->features.timeline)
5137 +
        wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd);
5138 +
5139 +
    /* Create the buffers that connect the renderer to the display backend. */
5140 +
    if (!(alloc = wlr_allocator_autocreate(backend, drw)))
5141 +
        die("couldn't create allocator");
5142 +
5143 +
    /* Advertise the core protocols for windows, buffers, capture, and clipboard data. */
5144 +
    compositor = wlr_compositor_create(dpy, 6, drw);
5145 +
    wlr_subcompositor_create(dpy);
5146 +
    wlr_data_device_manager_create(dpy);
5147 +
    wlr_export_dmabuf_manager_v1_create(dpy);
5148 +
    wlr_screencopy_manager_v1_create(dpy);
5149 +
    wlr_data_control_manager_v1_create(dpy);
5150 +
    wlr_ext_data_control_manager_v1_create(dpy, 1);
5151 +
    wlr_primary_selection_v1_device_manager_create(dpy);
5152 +
    wlr_viewporter_create(dpy);
5153 +
    wlr_single_pixel_buffer_manager_v1_create(dpy);
5154 +
    wlr_fractional_scale_manager_v1_create(dpy, 1);
5155 +
    wlr_presentation_create(dpy, backend, 2);
5156 +
    wlr_alpha_modifier_v1_create(dpy);
5157 +
5158 +
    /* Accept application requests for attention. */
5159 +
    activation = wlr_xdg_activation_v1_create(dpy);
5160 +
    wl_signal_add(&activation->events.request_activate, &request_activate);
5161 +
5162 +
    wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy));
5163 +
5164 +
    power_mgr = wlr_output_power_manager_v1_create(dpy);
5165 +
    wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode);
5166 +
5167 +
    /* Track the position of each display in the combined desktop. */
5168 +
    output_layout = wlr_output_layout_create(dpy);
5169 +
    wl_signal_add(&output_layout->events.change, &layout_change);
5170 +
5171 +
    wlr_xdg_output_manager_v1_create(dpy, output_layout);
5172 +
5173 +
    /* Workspaces are global and independent of outputs. */
5174 +
    for (i = 0; i < WSCOUNT; i++) {
5175 +
        workspaces[i].idx = i;
5176 +
        snprintf(workspaces[i].name, sizeof(workspaces[i].name), "%d", i + 1);
5177 +
        workspaces[i].lt     = &layouts[0];
5178 +
        workspaces[i].prevlt = nullptr;
5179 +
        workspaces[i].v = workspaces[i].h = (stack_state_t){ SLICE / 2, 1, 1 };
5180 +
        wl_list_init(&workspaces[i].handles);
5181 +
    }
5182 +
    wl_list_init(&ws_managers);
5183 +
    wl_list_init(&pending_spawns);
5184 +
    wl_list_init(&window_states);
5185 +
    wl_list_init(&input_popups);
5186 +
    load_window_states();
5187 +
    wl_global_create(dpy, &ext_workspace_manager_v1_interface, 1, nullptr, workspace_manager_bind);
5188 +
5189 +
    /* Publish window lists for taskbars and switchers. */
5190 +
    ftl_mgr      = wlr_foreign_toplevel_manager_v1_create(dpy);
5191 +
    ext_ftl_list = wlr_ext_foreign_toplevel_list_v1_create(dpy, 1);
5192 +
5193 +
    /* Let virtual machines and remote desktops capture window-manager shortcuts. */
5194 +
    kb_inhibit_mgr = wlr_keyboard_shortcuts_inhibit_v1_create(dpy);
5195 +
    wl_signal_add(&kb_inhibit_mgr->events.new_inhibitor, &new_shortcuts_inhibitor_listener);
5196 +
5197 +
    /* Listen for newly connected displays. */
5198 +
    wl_list_init(&mons);
5199 +
    wl_signal_add(&backend->events.new_output, &new_output);
5200 +
5201 +
    /* Set up application windows, panels, backgrounds, and other desktop surfaces. */
5202 +
    wl_list_init(&clients);
5203 +
    wl_list_init(&fstack);
5204 +
5205 +
    xdg_shell = wlr_xdg_shell_create(dpy, 6);
5206 +
    wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel);
5207 +
    wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup);
5208 +
5209 +
    layer_shell = wlr_layer_shell_v1_create(dpy, 3);
5210 +
    wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface);
5211 +
5212 +
    idle_notifier = wlr_idle_notifier_v1_create(dpy);
5213 +
5214 +
    idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy);
5215 +
    wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor);
5216 +
5217 +
    session_lock_mgr = wlr_session_lock_manager_v1_create(dpy);
5218 +
    wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock);
5219 +
    locked_bg = wlr_scene_rect_create(
5220 +
        layers[LAYER_BLOCK], sgeom.width, sgeom.height, (float[4]){ 0.1f, 0.1f, 0.1f, 1.0f }
5221 +
    );
5222 +
5223 +
    if (!locked_bg)
5224 +
        die("failed to create lock background");
5225 +
    wlr_scene_node_set_enabled(&locked_bg->node, 0);
5226 +
5227 +
    /* Prefer window borders drawn by the compositor. */
5228 +
    wlr_server_decoration_manager_set_default_mode(
5229 +
        wlr_server_decoration_manager_create(dpy), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER
5230 +
    );
5231 +
    xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy);
5232 +
    wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration);
5233 +
5234 +
    pointer_constraints = wlr_pointer_constraints_v1_create(dpy);
5235 +
    wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint);
5236 +
5237 +
    relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy);
5238 +
5239 +
    /* Track the pointer across all displays. */
5240 +
    cursor = wlr_cursor_create();
5241 +
    wlr_cursor_attach_output_layout(cursor, output_layout);
5242 +
5243 +
    /* Load cursor images at the scales required by connected displays. */
5244 +
    cursor_mgr = wlr_xcursor_manager_create(nullptr, 24);
5245 +
    setenv("XCURSOR_SIZE", "24", 1);
5246 +
5247 +
    /* Combine pointer devices and handle their movement, buttons, scrolling, and frames. */
5248 +
    wl_signal_add(&cursor->events.motion, &cursor_motion);
5249 +
    wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute);
5250 +
    wl_signal_add(&cursor->events.button, &cursor_button);
5251 +
    wl_signal_add(&cursor->events.axis, &cursor_axis);
5252 +
    wl_signal_add(&cursor->events.frame, &cursor_frame_listener);
5253 +
5254 +
    cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1);
5255 +
    wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape);
5256 +
5257 +
    /* A seat groups the input devices controlled by one user. */
5258 +
    wl_signal_add(&backend->events.new_input, &new_input_device);
5259 +
    virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
5260 +
    wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, &new_virtual_keyboard);
5261 +
    virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy);
5262 +
    wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, &new_virtual_pointer);
5263 +
5264 +
    seat = wlr_seat_create(dpy, "seat0");
5265 +
    wl_signal_add(&seat->events.request_set_cursor, &request_cursor);
5266 +
    wl_signal_add(&seat->events.request_set_selection, &request_set_sel);
5267 +
    wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel);
5268 +
    wl_signal_add(&seat->events.request_start_drag, &request_start_drag_listener);
5269 +
    wl_signal_add(&seat->events.start_drag, &start_drag_listener);
5270 +
5271 +
    /* Connect applications to an input method such as fcitx5. */
5272 +
    im_mgr = wlr_input_method_manager_v2_create(dpy);
5273 +
    wl_signal_add(&im_mgr->events.new_input_method, &new_input_method);
5274 +
    ti_mgr = wlr_text_input_manager_v3_create(dpy);
5275 +
    wl_signal_add(&ti_mgr->events.new_text_input, &new_text_input);
5276 +
5277 +
    kb_group = create_keyboard_group(false);
5278 +
5279 +
    if (!kb_group)
5280 +
        die("failed to allocate primary keyboard group");
5281 +
    wl_list_init(&kb_group->destroy.link);
5282 +
5283 +
    output_mgr = wlr_output_manager_v1_create(dpy);
5284 +
    wl_signal_add(&output_mgr->events.apply, &output_mgr_apply);
5285 +
    wl_signal_add(&output_mgr->events.test, &output_mgr_test);
5286 +
5287 +
    /* Keep XWayland applications on this compositor rather than a parent X server. */
5288 +
    unsetenv("DISPLAY");
5289 +
    /* Create the XWayland server; it starts when the first X11 application opens. */
5290 +
    if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) {
5291 +
        wl_signal_add(&xwayland->events.ready, &xwayland_ready_listener);
5292 +
        wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface);
5293 +
        xwayland_listeners_registered = true;
5294 +
5295 +
        setenv("DISPLAY", xwayland->display_name, 1);
5296 +
    } else {
5297 +
        fprintf(stderr, "failed to setup XWayland X server, continuing without it\n");
5298 +
    }
5299 +
}
5300 +
5301 +
/* Start a command and assign its first window to the current workspace. */
5302 +
void spawn(const arg_t *arg) {
5303 +
    pid_t            pid;
5304 +
    pending_spawn_t *ps;
5305 +
5306 +
    if ((pid = fork()) == 0) {
5307 +
        char *argv[MAX_COMMAND_ARGS], storage[MAX_COMMAND_SIZE];
5308 +
5309 +
        expand_argv(arg->v, argv, storage);
5310 +
5311 +
        prepare_child();
5312 +
        dup2(STDERR_FILENO, STDOUT_FILENO);
5313 +
        setsid();
5314 +
        execvp(argv[0], argv);
5315 +
        die("execvp %s failed:", argv[0]);
5316 +
    }
5317 +
    if (pid > 0 && selmon && selmon->ws) {
5318 +
        ps = pool_take(&pending_spawn_pool);
5319 +
5320 +
        if (!ps)
5321 +
            return;
5322 +
        ps->pid = pid;
5323 +
        ps->ws  = selmon->ws;
5324 +
        wl_list_insert(&pending_spawns, &ps->link);
5325 +
    }
5326 +
}
5327 +
5328 +
/* Display and track an application's drag icon. */
5329 +
void start_drag(struct wl_listener *listener, void *data) {
5330 +
    struct wlr_drag       *drag = data;
5331 +
    struct wlr_scene_tree *icon;
5332 +
5333 +
    if (!drag->icon)
5334 +
        return;
5335 +
5336 +
    icon = wlr_scene_drag_icon_create(drag_icon, drag->icon);
5337 +
5338 +
    if (!icon)
5339 +
        return;
5340 +
    drag->icon->data = &icon->node;
5341 +
    LISTEN_STATIC(&drag->icon->events.destroy, destroy_drag_icon);
5342 +
}
5343 +
5344 +
/* Move the focused window to the requested workspace. */
5345 +
void tag(const arg_t *arg) {
5346 +
    client_t *sel = focus_top(selmon);
5347 +
5348 +
    if (!sel || arg->i < 0 || arg->i >= WSCOUNT)
5349 +
        return;
5350 +
5351 +
    set_workspace(sel, &workspaces[arg->i]);
5352 +
    arrange(selmon);
5353 +
    print_status();
5354 +
}
5355 +
5356 +
/* Move the focused window to the workspace on a neighboring display. */
5357 +
void tag_monitor(const arg_t *arg) {
5358 +
    client_t *sel = focus_top(selmon);
5359 +
5360 +
    if (sel)
5361 +
        set_monitor(sel, direction_to_monitor(arg->i), nullptr);
5362 +
}
5363 +
5364 +
/* Adjust the master size, master count, stack count, or layout direction. */
5365 +
void stack_config(const arg_t *arg) {
5366 +
    /* adjust the master/stack parameters of the focused workspace's
5367 +
     * current layout. */
5368 +
    workspace_t    *ws;
5369 +
    stack_state_t  *st;
5370 +
    const layout_t *next        = nullptr;
5371 +
    void (*target)(monitor_t *) = nullptr;
5372 +
    struct swm_stack_state state;
5373 +
    int                    side, newside;
5374 +
    size_t                 i;
5375 +
5376 +
    if (!arg || !selmon || !(ws = selmon->ws))
5377 +
        return;
5378 +
5379 +
    if (ws->lt->arrange == master_left || ws->lt->arrange == master_right)
5380 +
        st = &ws->v;
5381 +
    else if (ws->lt->arrange == master_top || ws->lt->arrange == master_bottom)
5382 +
        st = &ws->h;
5383 +
    else
5384 +
        return;
5385 +
5386 +
    if (ws->lt->arrange == master_left)
5387 +
        side = SWM_MASTER_LEFT;
5388 +
    else if (ws->lt->arrange == master_top)
5389 +
        side = SWM_MASTER_TOP;
5390 +
    else if (ws->lt->arrange == master_right)
5391 +
        side = SWM_MASTER_RIGHT;
5392 +
    else
5393 +
        side = SWM_MASTER_BOTTOM;
5394 +
5395 +
    state = (struct swm_stack_state){ st->msize, st->mwin, st->stacks };
5396 +
5397 +
    if (!swm_stack_configure(&state, arg->i, side, &newside))
5398 +
        return;
5399 +
    *st = (stack_state_t){ state.msize, state.mwin, state.stacks };
5400 +
    if (newside != side)
5401 +
        target = newside == SWM_MASTER_LEFT    ? master_left
5402 +
                 : newside == SWM_MASTER_TOP   ? master_top
5403 +
                 : newside == SWM_MASTER_RIGHT ? master_right
5404 +
                                               : master_bottom;
5405 +
5406 +
    if (target) {
5407 +
        for (i = 0; i < LENGTH(layouts); i++)
5408 +
5409 +
            if (layouts[i].arrange == target) {
5410 +
                next = &layouts[i];
5411 +
                break;
5412 +
            }
5413 +
        if (next) {
5414 +
            ws->prevlt = ws->lt;
5415 +
            ws->lt     = next;
5416 +
        }
5417 +
    }
5418 +
    arrange(selmon);
5419 +
    print_status();
5420 +
}
5421 +
5422 +
/* Tile windows into a master area followed by evenly divided stacks. */
5423 +
void stack_master(monitor_t *m, int rot, int flip) {
5424 +
    /* Master/stack tiler: a master area holding up to mwin
5425 +
     * windows and a stacking area split into st->stacks columns. All
5426 +
     * geometry is computed in "vertical" coordinates and transposed at
5427 +
     * the end for the horizontal layout. */
5428 +
    client_t              *c;
5429 +
    stack_state_t         *st = rot ? &m->ws->h : &m->ws->v;
5430 +
    struct swm_box         boxes[MAX_CLIENTS];
5431 +
    struct swm_stack_state state = { st->msize, st->mwin, st->stacks };
5432 +
    int                    n     = 0;
5433 +
    size_t                 i     = 0;
5434 +
5435 +
    wl_list_for_each(c, &clients, link) if (VISIBLEON(c, m) && !c->is_floating && !c->is_fullscreen)
5436 +
        n++;
5437 +
    swm_stack_layout((const struct swm_box *)&m->w, &state, rot, flip, (size_t)n, boxes);
5438 +
    wl_list_for_each(c, &clients, link) {
5439 +
        if (!VISIBLEON(c, m) || c->is_floating || c->is_fullscreen)
5440 +
            continue;
5441 +
        resize(c, (struct wlr_box){ boxes[i].x, boxes[i].y, boxes[i].width, boxes[i].height }, 0);
5442 +
        i++;
5443 +
    }
5444 +
}
5445 +
5446 +
/* Tile the master area along the bottom edge. */
5447 +
void master_bottom(monitor_t *m) {
5448 +
    stack_master(m, 1, 1);
5449 +
}
5450 +
5451 +
/* Tile the master area along the left edge. */
5452 +
void master_left(monitor_t *m) {
5453 +
    stack_master(m, 0, 0);
5454 +
}
5455 +
5456 +
/* Tile the master area along the right edge. */
5457 +
void master_right(monitor_t *m) {
5458 +
    stack_master(m, 0, 1);
5459 +
}
5460 +
5461 +
/* Tile the master area along the top edge. */
5462 +
void master_top(monitor_t *m) {
5463 +
    stack_master(m, 1, 0);
5464 +
}
5465 +
5466 +
/* Toggle the focused window between tiled and floating. */
5467 +
void toggle_floating(const arg_t *arg) {
5468 +
    client_t *sel = focus_top(selmon);
5469 +
    /* Fullscreen windows cannot become floating. */
5470 +
    if (!sel || sel->is_fullscreen)
5471 +
        return;
5472 +
5473 +
    if (sel->is_floating) {
5474 +
        sel->persist_float = 0;
5475 +
        set_floating(sel, 0);
5476 +
        forget_client(sel);
5477 +
    } else {
5478 +
        sel->persist_float = 1;
5479 +
        set_floating(sel, 1);
5480 +
    }
5481 +
}
5482 +
5483 +
/* Toggle fullscreen for the focused window. */
5484 +
void toggle_fullscreen(const arg_t *arg) {
5485 +
    client_t *sel = focus_top(selmon);
5486 +
5487 +
    if (sel)
5488 +
        set_fullscreen(sel, !sel->is_fullscreen);
5489 +
}
5490 +
5491 +
/* Toggle the workspace between its normal layout and overlapping maximized windows. */
5492 +
void toggle_max_stack(const arg_t *arg) {
5493 +
    workspace_t    *ws;
5494 +
    const layout_t *max = nullptr;
5495 +
    size_t          i;
5496 +
5497 +
    if (!selmon || !(ws = selmon->ws))
5498 +
        return;
5499 +
5500 +
    for (i = 0; i < LENGTH(layouts); i++)
5501 +
5502 +
        if (layouts[i].arrange == max_stack) {
5503 +
            max = &layouts[i];
5504 +
            break;
5505 +
        }
5506 +
    if (!max)
5507 +
        return;
5508 +
5509 +
    if (ws->lt == max)
5510 +
        ws->lt = ws->prevlt && ws->prevlt != max ? ws->prevlt : &layouts[0];
5511 +
    else {
5512 +
        ws->prevlt = ws->lt;
5513 +
        ws->lt     = max;
5514 +
    }
5515 +
    arrange(selmon);
5516 +
    print_status();
5517 +
}
5518 +
5519 +
/* Remove the active session lock after the locker authenticates the user. */
5520 +
void unlock_session(struct wl_listener *listener, void *data) {
5521 +
    session_lock_t *lock = wl_container_of(listener, lock, unlock);
5522 +
    destroy_lock(lock, 1);
5523 +
}
5524 +
5525 +
/* Hide an unmapped desktop-layer surface and reclaim its reserved space. */
5526 +
void layer_surface_unmap_notify(struct wl_listener *listener, void *data) {
5527 +
    layer_surface_t *l = wl_container_of(listener, l, unmap);
5528 +
5529 +
    l->mapped = 0;
5530 +
    wlr_scene_node_set_enabled(&l->scene->node, 0);
5531 +
5532 +
    if (l->dim)
5533 +
        wlr_scene_node_set_enabled(&l->dim->node, 0);
5534 +
5535 +
    if (l == exclusive_focus)
5536 +
        exclusive_focus = nullptr;
5537 +
5538 +
    if (l->layer_surface->output && (l->mon = l->layer_surface->output->data))
5539 +
        arrange_layers(l->mon);
5540 +
5541 +
    if (l->layer_surface->surface == seat->keyboard_state.focused_surface)
5542 +
        focus_client(focus_top(selmon), 1);
5543 +
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
5544 +
}
5545 +
5546 +
/* Hide an unmapped window, save its state, and choose new focus. */
5547 +
void unmap_notify(struct wl_listener *listener, void *data) {
5548 +
    /* Called when the surface is unmapped, and should no longer be shown. */
5549 +
    client_t  *c    = wl_container_of(listener, c, unmap);
5550 +
    client_t  *next = nullptr, *under = nullptr;
5551 +
    monitor_t *oldmon     = c->mon;
5552 +
    int        wasfocused = !client_is_unmanaged(c) && c == focus_top(c->mon);
5553 +
5554 +
    if (c == grabc) {
5555 +
        if (cursor_mode == CURSOR_RESIZE)
5556 +
            client_set_resizing(c, 0);
5557 +
        cursor_mode = CURSOR_NORMAL;
5558 +
        grabc       = nullptr;
5559 +
    }
5560 +
    if (!c->scene)
5561 +
        return;
5562 +
5563 +
    if (client_is_unmanaged(c)) {
5564 +
        if (c == exclusive_focus) {
5565 +
            exclusive_focus = nullptr;
5566 +
            focus_client(focus_top(selmon), 1);
5567 +
        }
5568 +
    } else {
5569 +
        remember_client(c);
5570 +
5571 +
        if (wasfocused)
5572 +
            next = focus_close(c);
5573 +
        wl_list_remove(&c->link);
5574 +
        wl_list_remove(&c->flink);
5575 +
        c->mon = nullptr;
5576 +
5577 +
        if (wasfocused && !sloppyfocus)
5578 +
            focus_client(next ? next : focus_top(selmon), 1);
5579 +
5580 +
        if (oldmon)
5581 +
            arrange(oldmon);
5582 +
    }
5583 +
    if (c->ftl) {
5584 +
        wl_list_remove(&c->ftl_activate.link);
5585 +
        wl_list_remove(&c->ftl_close.link);
5586 +
        wl_list_remove(&c->ftl_fullscreen.link);
5587 +
        wlr_foreign_toplevel_handle_v1_destroy(c->ftl);
5588 +
        c->ftl         = nullptr;
5589 +
        c->ftl_monitor = nullptr;
5590 +
    }
5591 +
    if (c->extftl) {
5592 +
        wlr_ext_foreign_toplevel_handle_v1_destroy(c->extftl);
5593 +
        c->extftl = nullptr;
5594 +
    }
5595 +
    c->resize       = 0;
5596 +
    c->resize_edges = WLR_EDGE_NONE;
5597 +
    /* Hide the old scene before hit-testing, but keep its borders alive until
5598 +
     * focus_client() has finished deactivating the old surface. */
5599 +
    wlr_scene_node_set_enabled(&c->scene->node, 0);
5600 +
5601 +
    if (wasfocused && sloppyfocus) {
5602 +
        /* Hit-test after hiding the old scene node so focus follows the
5603 +
         * surface newly exposed beneath the stationary cursor. */
5604 +
        point_to_node(cursor->x, cursor->y, nullptr, &under, nullptr, nullptr, nullptr);
5605 +
5606 +
        if (under && !client_is_unmanaged(under))
5607 +
            focus_client(under, 0);
5608 +
        else
5609 +
            focus_client(next ? next : focus_top(selmon), 1);
5610 +
    }
5611 +
    wlr_scene_node_destroy(&c->scene->node);
5612 +
    c->scene = client_surface(c)->data = nullptr;
5613 +
    c->scene_surface                   = nullptr;
5614 +
    memset(c->border, 0, sizeof(c->border));
5615 +
    print_status();
5616 +
    motion_notify(0, nullptr, 0, 0, 0, 0, 1);
5617 +
}
5618 +
5619 +
/* Reconcile display layout changes with workspaces, windows, focus, and color state. */
5620 +
void update_monitors(struct wl_listener *listener, void *data) {
5621 +
    /*
5622 +
     * Called whenever the output layout changes: adding or removing a
5623 +
     * monitor, changing an output's mode or position, etc. This is where
5624 +
     * the change officially happens and we update geometry, window
5625 +
     * positions, focus, and the stored configuration in wlroots'
5626 +
     * output-manager implementation.
5627 +
     */
5628 +
    struct wlr_output_configuration_v1      *config = wlr_output_configuration_v1_create();
5629 +
    client_t                                *c;
5630 +
    struct wlr_output_configuration_head_v1 *config_head;
5631 +
    monitor_t                               *m;
5632 +
5633 +
    /* Remove disabled displays from the layout first. */
5634 +
    wl_list_for_each(m, &mons, link) {
5635 +
        if (m->wlr_output->enabled || m->asleep)
5636 +
            continue;
5637 +
        config_head                = wlr_output_configuration_head_v1_create(config, m->wlr_output);
5638 +
        config_head->state.enabled = 0;
5639 +
        /* Remove the display so the pointer cannot enter it. */
5640 +
        wlr_output_layout_remove(output_layout, m->wlr_output);
5641 +
        close_monitor(m);
5642 +
        m->m = m->w = (struct wlr_box){};
5643 +
    }
5644 +
    /* Add newly enabled displays to the layout. */
5645 +
    wl_list_for_each(m, &mons, link) {
5646 +
        if (m->wlr_output->enabled && !wlr_output_layout_get(output_layout, m->wlr_output))
5647 +
            wlr_output_layout_add_auto(output_layout, m->wlr_output);
5648 +
    }
5649 +
    /* Recalculate the bounds of the combined desktop. */
5650 +
    wlr_output_layout_get_box(output_layout, nullptr, &sgeom);
5651 +
5652 +
    wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y);
5653 +
    wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height);
5654 +
5655 +
    /* Cover all windows while the session is locked. */
5656 +
    wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y);
5657 +
    wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height);
5658 +
5659 +
    wl_list_for_each(m, &mons, link) {
5660 +
        if (!m->wlr_output->enabled)
5661 +
            continue;
5662 +
        config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
5663 +
5664 +
        /* Read the display's position in the combined desktop. */
5665 +
        wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m);
5666 +
        m->w = m->m;
5667 +
        wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y);
5668 +
5669 +
        wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y);
5670 +
        wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height);
5671 +
5672 +
        if (m->lock_surface) {
5673 +
            struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data;
5674 +
            wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y);
5675 +
            wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height);
5676 +
        }
5677 +
        /* Reserve panel space before arranging regular windows. */
5678 +
        arrange_layers(m);
5679 +
        /* Sync clients of this output's workspace (it may have been
5680 +
         * hidden or shown elsewhere before this display appeared). */
5681 +
        if (m->ws)
5682 +
            assign_workspace(m->ws, m);
5683 +
        /* Keep windows on their workspace when displays are connected. */
5684 +
        arrange(m);
5685 +
        /* Resize fullscreen windows to fill their display. */
5686 +
        if ((c = focus_top(m)) && c->is_fullscreen)
5687 +
            resize(c, m->m, 0);
5688 +
5689 +
        /* Reapply color correction in case this display was just re-enabled. */
5690 +
        m->gamma_lut_changed = 1;
5691 +
5692 +
        config_head->state.x = m->m.x;
5693 +
        config_head->state.y = m->m.y;
5694 +
5695 +
        if (!selmon) {
5696 +
            selmon = m;
5697 +
        }
5698 +
    }
5699 +
    if (selmon && selmon->wlr_output->enabled) {
5700 +
        wl_list_for_each(c, &clients, link) {
5701 +
            if (!c->mon && client_surface(c)->mapped)
5702 +
                set_monitor(c, selmon, c->ws);
5703 +
        }
5704 +
        focus_client(focus_top(selmon), 1);
5705 +
5706 +
        if (selmon->lock_surface) {
5707 +
            client_notify_enter(selmon->lock_surface->surface, wlr_seat_get_keyboard(seat));
5708 +
            client_activate_surface(selmon->lock_surface->surface, 1);
5709 +
        }
5710 +
    }
5711 +
    /* Refresh the cursor image after displays return without reporting false
5712 +
     * pointer movement to applications. */
5713 +
    wlr_cursor_move(cursor, nullptr, 0, 0);
5714 +
5715 +
    wlr_output_manager_v1_set_configuration(output_mgr, config);
5716 +
}
5717 +
5718 +
/* Publish a window's changed application ID. */
5719 +
void update_app_id(struct wl_listener *listener, void *data) {
5720 +
    client_t *c = wl_container_of(listener, c, set_appid);
5721 +
5722 +
    ftl_sync(c);
5723 +
5724 +
    if (c == focus_top(c->mon))
5725 +
        print_status();
5726 +
}
5727 +
5728 +
/* Publish a window's changed title. */
5729 +
void update_title(struct wl_listener *listener, void *data) {
5730 +
    client_t *c = wl_container_of(listener, c, set_title);
5731 +
5732 +
    ftl_sync(c);
5733 +
5734 +
    if (c == focus_top(c->mon))
5735 +
        print_status();
5736 +
}
5737 +
5738 +
/* Mark an unfocused window as needing attention. */
5739 +
void urgent(struct wl_listener *listener, void *data) {
5740 +
    struct wlr_xdg_activation_v1_request_activate_event *event = data;
5741 +
    client_t                                            *c     = nullptr;
5742 +
    toplevel_from_wlr_surface(event->surface, &c, nullptr);
5743 +
5744 +
    if (!c || c == focus_top(selmon))
5745 +
        return;
5746 +
5747 +
    c->is_urgent = true;
5748 +
    print_status();
5749 +
5750 +
    if (client_surface(c)->mapped)
5751 +
        client_set_border_color(c, urgentcolor);
5752 +
}
5753 +
5754 +
/* Show a numbered workspace or return to the previously shown one. */
5755 +
void view(const arg_t *arg) {
5756 +
    /* A negative index selects the previously shown workspace. */
5757 +
    if (!selmon)
5758 +
        return;
5759 +
5760 +
    if (arg->i < 0)
5761 +
        view_workspace(selmon->previous_workspace, selmon);
5762 +
    else if (arg->i < WSCOUNT)
5763 +
        view_workspace(&workspaces[arg->i], selmon);
5764 +
}
5765 +
5766 +
/* Create an independent keyboard group for a virtual keyboard. */
5767 +
void virtual_keyboard(struct wl_listener *listener, void *data) {
5768 +
    struct wlr_virtual_keyboard_v1 *kb = data;
5769 +
5770 +
    /* Give each virtual keyboard its own group. */
5771 +
    keyboard_group_t *group = create_keyboard_group(true);
5772 +
5773 +
    if (!group) {
5774 +
        wl_resource_post_no_memory(kb->resource);
5775 +
        return;
5776 +
    }
5777 +
    /* Use the virtual keyboard's key mapping. */
5778 +
    wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap);
5779 +
    LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroy_keyboard_group);
5780 +
5781 +
    /* Add the virtual keyboard to its group. */
5782 +
    wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard);
5783 +
    virtual_keyboards++;
5784 +
    wlr_seat_set_capabilities(seat, WL_SEAT_CAPABILITY_POINTER | WL_SEAT_CAPABILITY_KEYBOARD);
5785 +
}
5786 +
5787 +
/* Attach a virtual pointer to the shared cursor. */
5788 +
void virtual_pointer(struct wl_listener *listener, void *data) {
5789 +
    struct wlr_virtual_pointer_v1_new_pointer_event *event  = data;
5790 +
    struct wlr_input_device                         *device = &event->new_pointer->pointer.base;
5791 +
5792 +
    wlr_cursor_attach_input_device(cursor, device);
5793 +
5794 +
    if (event->suggested_output)
5795 +
        wlr_cursor_map_input_to_output(cursor, device, event->suggested_output);
5796 +
}
5797 +
5798 +
/* Return the display containing a point, or the nearest display to it. */
5799 +
monitor_t *point_to_monitor(double x, double y) {
5800 +
    struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y);
5801 +
5802 +
    return o ? o->data : nullptr;
5803 +
}
5804 +
5805 +
/* Find the surface, window, and local coordinates at a layout position. */
5806 +
void point_to_node(
5807 +
    double               x,
5808 +
    double               y,
5809 +
    struct wlr_surface **psurface,
5810 +
    client_t           **pc,
5811 +
    layer_surface_t    **pl,
5812 +
    double              *nx,
5813 +
    double              *ny
5814 +
) {
5815 +
    struct wlr_scene_node    *node, *pnode;
5816 +
    struct wlr_scene_surface *scene_surface;
5817 +
    struct wlr_surface       *surface = nullptr;
5818 +
    client_t                 *c       = nullptr;
5819 +
    layer_surface_t          *l       = nullptr;
5820 +
    int                       layer;
5821 +
5822 +
    for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) {
5823 +
        if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny)))
5824 +
            continue;
5825 +
5826 +
        if (node->type == WLR_SCENE_NODE_BUFFER &&
5827 +
            (scene_surface = wlr_scene_surface_try_from_buffer(wlr_scene_buffer_from_node(node))))
5828 +
            surface = scene_surface->surface;
5829 +
        /* Walk upward until a node identifies its window. */
5830 +
        for (pnode = node; pnode && !c;) {
5831 +
            c = pnode->data;
5832 +
5833 +
            if (!c)
5834 +
                pnode = pnode->parent ? &pnode->parent->node : nullptr;
5835 +
        }
5836 +
        if (c && c->type == LAYER_SHELL) {
5837 +
            c = nullptr;
5838 +
            l = pnode->data;
5839 +
        }
5840 +
    }
5841 +
    if (psurface)
5842 +
        *psurface = surface;
5843 +
    if (pc)
5844 +
        *pc = c;
5845 +
    if (pl)
5846 +
        *pl = l;
5847 +
}
5848 +
5849 +
/* Swap the focused tiled window with its neighbor. */
5850 +
void swap_client(const arg_t *arg) {
5851 +
    /* swap the focused window with the next/previous window in the
5852 +
     * stacking order. */
5853 +
    client_t       *c = nullptr, *sel = focus_top(selmon);
5854 +
    struct wl_list *node;
5855 +
5856 +
    if (!sel || !selmon || !selmon->ws || !selmon->ws->lt->arrange || sel->is_floating ||
5857 +
        sel->is_fullscreen)
5858 +
        return;
5859 +
5860 +
    for (node = arg->i > 0 ? sel->link.next : sel->link.prev; node != &sel->link;
5861 +
         node = arg->i > 0 ? node->next : node->prev) {
5862 +
        if (node == &clients)
5863 +
            continue;
5864 +
        c = wl_container_of(node, c, link);
5865 +
5866 +
        if (VISIBLEON(c, selmon) && !c->is_floating && !c->is_fullscreen)
5867 +
            break;
5868 +
    }
5869 +
    if (node == &sel->link || !c)
5870 +
        return;
5871 +
5872 +
    /* Move the selected window next to its swap target. */
5873 +
    wl_list_remove(&sel->link);
5874 +
5875 +
    if (arg->i > 0)
5876 +
        wl_list_insert(&c->link, &sel->link);
5877 +
    else
5878 +
        wl_list_insert(c->link.prev, &sel->link);
5879 +
5880 +
    arrange(selmon);
5881 +
    print_status();
5882 +
}
5883 +
5884 +
/* Raise the focused floating window above other windows. */
5885 +
void raise_client(const arg_t *arg) {
5886 +
    /* Raise the focused window above the others. */
5887 +
    client_t *sel = focus_top(selmon);
5888 +
5889 +
    if (sel)
5890 +
        wlr_scene_node_raise_to_top(&sel->scene->node);
5891 +
}
5892 +
5893 +
/* Swap the focused tiled window with the master window. */
5894 +
void zoom(const arg_t *arg) {
5895 +
    client_t *c, *sel = focus_top(selmon);
5896 +
5897 +
    if (!sel || !selmon || !selmon->ws || !selmon->ws->lt->arrange || sel->is_floating)
5898 +
        return;
5899 +
5900 +
    /* Search for the first tiled window that is not sel, marking sel as
5901 +
     * nullptr if we pass it along the way. */
5902 +
    wl_list_for_each(c, &clients, link) {
5903 +
        if (VISIBLEON(c, selmon) && !c->is_floating) {
5904 +
            if (c != sel)
5905 +
                break;
5906 +
            sel = nullptr;
5907 +
        }
5908 +
    }
5909 +
    /* Stop if there is no other tiled window. */
5910 +
    if (&c->link == &clients)
5911 +
        return;
5912 +
5913 +
    /* If we passed sel, move c to the front; otherwise, move sel to the
5914 +
     * front. */
5915 +
    if (!sel)
5916 +
        sel = c;
5917 +
    wl_list_remove(&sel->link);
5918 +
    wl_list_insert(&clients, &sel->link);
5919 +
5920 +
    focus_client(sel, 1);
5921 +
    arrange(selmon);
5922 +
}
5923 +
5924 +
/* Honor an activation request for a managed X11 window. */
5925 +
void activate_x11(struct wl_listener *listener, void *data) {
5926 +
    client_t *c = wl_container_of(listener, c, activate);
5927 +
5928 +
    /* Only managed X11 windows can be activated. */
5929 +
    if (!client_is_unmanaged(c))
5930 +
        wlr_xwayland_surface_activate(c->surface.xwayland, 1);
5931 +
}
5932 +
5933 +
/* Attach a newly ready X11 surface to its window state. */
5934 +
void associate_x11(struct wl_listener *listener, void *data) {
5935 +
    client_t *c = wl_container_of(listener, c, associate);
5936 +
5937 +
    LISTEN(&client_surface(c)->events.map, &c->map, map_notify);
5938 +
    LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmap_notify);
5939 +
}
5940 +
5941 +
/* Apply an unmanaged X11 window's requested position and size. */
5942 +
void configure_x11(struct wl_listener *listener, void *data) {
5943 +
    client_t                                    *c     = wl_container_of(listener, c, configure);
5944 +
    struct wlr_xwayland_surface_configure_event *event = data;
5945 +
5946 +
    if (!client_surface(c) || !client_surface(c)->mapped) {
5947 +
        wlr_xwayland_surface_configure(
5948 +
            c->surface.xwayland, event->x, event->y, event->width, event->height
5949 +
        );
5950 +
        return;
5951 +
    }
5952 +
    if (client_is_unmanaged(c)) {
5953 +
        wlr_scene_node_set_position(&c->scene->node, event->x, event->y);
5954 +
        wlr_xwayland_surface_configure(
5955 +
            c->surface.xwayland, event->x, event->y, event->width, event->height
5956 +
        );
5957 +
        return;
5958 +
    }
5959 +
    if ((c->is_floating && c != grabc) || !c->mon->ws || !c->mon->ws->lt->arrange) {
5960 +
        resize(
5961 +
            c,
5962 +
            (struct wlr_box){ .x      = event->x - c->bw,
5963 +
                              .y      = event->y - c->bw,
5964 +
                              .width  = event->width + c->bw * 2,
5965 +
                              .height = event->height + c->bw * 2 },
5966 +
            0
5967 +
        );
5968 +
    } else {
5969 +
        arrange(c->mon);
5970 +
    }
5971 +
}
5972 +
5973 +
/* Allocate state and listeners for a new X11 window. */
5974 +
void create_notify_x11(struct wl_listener *listener, void *data) {
5975 +
    struct wlr_xwayland_surface *xsurface = data;
5976 +
    client_t                    *c;
5977 +
5978 +
    /* Allocate window state for this X11 surface. */
5979 +
    if (!(c = pool_take(&client_pool))) {
5980 +
        wlr_xwayland_surface_close(xsurface);
5981 +
        return;
5982 +
    }
5983 +
    xsurface->data      = c;
5984 +
    c->surface.xwayland = xsurface;
5985 +
    c->type             = X11;
5986 +
    c->bw               = client_is_unmanaged(c) ? 0 : borderwidth;
5987 +
5988 +
    /* Listen for changes to the X11 window. */
5989 +
    LISTEN(&xsurface->events.associate, &c->associate, associate_x11);
5990 +
    LISTEN(&xsurface->events.destroy, &c->destroy, destroy_notify);
5991 +
    LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociate_x11);
5992 +
    LISTEN(&xsurface->events.request_activate, &c->activate, activate_x11);
5993 +
    LISTEN(&xsurface->events.request_configure, &c->configure, configure_x11);
5994 +
    LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreen_notify);
5995 +
    LISTEN(&xsurface->events.set_hints, &c->set_hints, set_hints);
5996 +
    LISTEN(&xsurface->events.set_title, &c->set_title, update_title);
5997 +
    LISTEN(&xsurface->events.set_class, &c->set_appid, update_app_id);
5998 +
}
5999 +
6000 +
/* Detach an X11 surface while retaining its window state for reassociation. */
6001 +
void dissociate_x11(struct wl_listener *listener, void *data) {
6002 +
    client_t *c = wl_container_of(listener, c, dissociate);
6003 +
    wl_list_remove(&c->map.link);
6004 +
    wl_list_remove(&c->unmap.link);
6005 +
}
6006 +
6007 +
/* Update urgency and input behavior from an X11 window's hints. */
6008 +
void set_hints(struct wl_listener *listener, void *data) {
6009 +
    client_t           *c       = wl_container_of(listener, c, set_hints);
6010 +
    struct wlr_surface *surface = client_surface(c);
6011 +
6012 +
    if (c == focus_top(selmon) || !c->surface.xwayland->hints)
6013 +
        return;
6014 +
6015 +
    c->is_urgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints);
6016 +
    print_status();
6017 +
6018 +
    if (c->is_urgent && surface && surface->mapped)
6019 +
        client_set_border_color(c, urgentcolor);
6020 +
}
6021 +
6022 +
/* Connect XWayland to swm's seat and set its default cursor. */
6023 +
void xwayland_ready(struct wl_listener *listener, void *data) {
6024 +
    struct wlr_xcursor *xcursor;
6025 +
6026 +
    /* Assign swm's single seat to XWayland. */
6027 +
    wlr_xwayland_set_seat(xwayland, seat);
6028 +
6029 +
    /* Set the default XWayland cursor to match the rest of swm. */
6030 +
    if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1)))
6031 +
        wlr_xwayland_set_cursor(
6032 +
            xwayland,
6033 +
            wlr_xcursor_image_get_buffer(xcursor->images[0]),
6034 +
            xcursor->images[0]->hotspot_x,
6035 +
            xcursor->images[0]->hotspot_y
6036 +
        );
6037 +
}
6038 +
6039 +
/* Parse command-line options, validate the environment, and run swm. */
6040 +
int main(int argc, char *argv[]) {
6041 +
    char *startup_cmd = nullptr;
6042 +
    int   c;
6043 +
6044 +
    while ((c = getopt(argc, argv, "s:hdv")) != -1) {
6045 +
        if (c == 's')
6046 +
            startup_cmd = optarg;
6047 +
        else if (c == 'd')
6048 +
            log_level = WLR_DEBUG;
6049 +
        else if (c == 'v') {
6050 +
            printf("swm %s\n", VERSION);
6051 +
            return EXIT_SUCCESS;
6052 +
        } else
6053 +
            goto usage;
6054 +
    }
6055 +
    if (optind < argc)
6056 +
        goto usage;
6057 +
6058 +
    /* Wayland needs XDG_RUNTIME_DIR to create its communication socket. */
6059 +
    if (!getenv("XDG_RUNTIME_DIR"))
6060 +
        die("XDG_RUNTIME_DIR must be set");
6061 +
    setup();
6062 +
    run(startup_cmd);
6063 +
    cleanup();
6064 +
    return EXIT_SUCCESS;
6065 +
6066 +
usage:
6067 +
    die("usage: %s [-v] [-d] [-s startup command]", argv[0]);
6068 +
}
swm.desktop added +5 -0
1 +
[Desktop Entry]
2 +
Name=swm
3 +
Comment=Simple Window Manager
4 +
Exec=swm
5 +
Type=Application
swm.h added +116 -0
1 +
#ifndef SWM_H
2 +
#define SWM_H
3 +
4 +
#include <stdbool.h>
5 +
#include <stddef.h>
6 +
7 +
#define SWM_SLICE 32
8 +
9 +
/* Integer rectangle in compositor layout coordinates. */
10 +
struct swm_box {
11 +
    int x, y, width, height;
12 +
};
13 +
14 +
/* Parameters controlling a master-stack layout. */
15 +
struct swm_stack_state {
16 +
    int msize;
17 +
    int mwin;
18 +
    int stacks;
19 +
};
20 +
21 +
/* Workspace state flags exported through ext-workspace. */
22 +
enum swm_workspace_state {
23 +
    SWM_WORKSPACE_ACTIVE = 1u << 0,
24 +
    SWM_WORKSPACE_URGENT = 1u << 1,
25 +
    SWM_WORKSPACE_HIDDEN = 1u << 2,
26 +
};
27 +
28 +
/* Commands which update master-stack parameters. */
29 +
enum swm_stack_command {
30 +
    SWM_MASTER_SHRINK,
31 +
    SWM_MASTER_GROW,
32 +
    SWM_MASTER_ADD,
33 +
    SWM_MASTER_DEL,
34 +
    SWM_STACK_INC,
35 +
    SWM_STACK_DEC,
36 +
    SWM_STACK_RESET,
37 +
    SWM_FLIP_LAYOUT,
38 +
};
39 +
40 +
/* Side of the output occupied by the master area. */
41 +
enum swm_layout_side {
42 +
    SWM_MASTER_LEFT,
43 +
    SWM_MASTER_TOP,
44 +
    SWM_MASTER_RIGHT,
45 +
    SWM_MASTER_BOTTOM,
46 +
};
47 +
48 +
/* Clamp a box to the minimum client size and output bounds. */
49 +
void swm_box_apply_bounds(struct swm_box *box, const struct swm_box *bounds, unsigned int border);
50 +
/* Resize a box from the selected edges. */
51 +
struct swm_box swm_box_resize(
52 +
    const struct swm_box *box, int dx, int dy, unsigned int edges, unsigned int border
53 +
);
54 +
/* Arrange clients into master and stack areas. */
55 +
size_t swm_stack_layout(
56 +
    const struct swm_box         *area,
57 +
    const struct swm_stack_state *state,
58 +
    int                           rotate,
59 +
    int                           flip,
60 +
    size_t                        count,
61 +
    struct swm_box               *boxes
62 +
);
63 +
/* Reconcile pending geometry with a committed client size. */
64 +
void swm_box_reconcile_commit(
65 +
    struct swm_box *box, int width, int height, unsigned int border, unsigned int edges
66 +
);
67 +
/* Position an input popup inside an output. */
68 +
struct swm_box swm_popup_position(
69 +
    const struct swm_box *client,
70 +
    unsigned int          border,
71 +
    const struct swm_box *cursor_rect,
72 +
    int                   width,
73 +
    int                   height,
74 +
    const struct swm_box *output
75 +
);
76 +
/* Compute the exported state flags for a workspace. */
77 +
unsigned int swm_workspace_state(bool active, bool occupied, bool urgent);
78 +
/* Find the next eligible workspace in the requested direction. */
79 +
int swm_workspace_next(
80 +
    int         current,
81 +
    int         count,
82 +
    int         direction,
83 +
    int         allow_empty,
84 +
    const bool *visible_elsewhere,
85 +
    const bool *occupied
86 +
);
87 +
/* Apply a command to master-stack parameters. */
88 +
bool swm_stack_configure(struct swm_stack_state *state, int command, int side, int *new_side);
89 +
/* Return whether a rule matches the client identity. */
90 +
bool swm_rule_matches(
91 +
    const char *rule_id, const char *rule_title, const char *appid, const char *title
92 +
);
93 +
/* Copy a field while replacing state-file delimiters. */
94 +
void swm_sanitize_field(char *dst, size_t size, const char *src, const char *fallback);
95 +
/* Parse one persisted window-state record. */
96 +
bool swm_parse_window_state(
97 +
    const char     *line,
98 +
    char           *appid,
99 +
    size_t          appid_size,
100 +
    char           *title,
101 +
    size_t          title_size,
102 +
    struct swm_box *geometry
103 +
);
104 +
/* Compute the effective border width for a client. */
105 +
unsigned int swm_border_width(
106 +
    unsigned int configured,
107 +
    bool         fullscreen,
108 +
    bool         borderless,
109 +
    bool         x11,
110 +
    bool         unmanaged,
111 +
    bool         client_side
112 +
);
113 +
/* Find the next enabled layout in the cycle. */
114 +
int swm_next_layout(int current, int count, const bool *cycle);
115 +
116 +
#endif
util.c added +89 -0
1 +
#include <fcntl.h>
2 +
#include <stdarg.h>
3 +
#include <stdio.h>
4 +
#include <stdlib.h>
5 +
#include <string.h>
6 +
7 +
#include "util.h"
8 +
9 +
#define MAX_ENV_SIZE 1024
10 +
11 +
/* Print an error and terminate the process. */
12 +
[[noreturn]] void die(const char *fmt, ...) {
13 +
    va_list ap;
14 +
15 +
    fputs("swm: ", stderr);
16 +
    va_start(ap, fmt);
17 +
    vfprintf(stderr, fmt, ap);
18 +
    va_end(ap);
19 +
20 +
    if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
21 +
        fputc(' ', stderr);
22 +
        perror(nullptr);
23 +
    } else {
24 +
        fputc('\n', stderr);
25 +
    }
26 +
    exit(1);
27 +
}
28 +
29 +
/* Expand environment variables while preserving a single argv element. */
30 +
size_t env_expand(char *output, size_t capacity, const char *input) {
31 +
    const char *value;
32 +
    size_t      i = 0, len, out = 0, start;
33 +
    char        name[MAX_ENV_SIZE];
34 +
35 +
    while (input[i]) {
36 +
        if (input[i] != '$') {
37 +
            if (out + 1 >= capacity)
38 +
                return 0;
39 +
            output[out++] = input[i];
40 +
            i++;
41 +
            continue;
42 +
        }
43 +
        start = ++i;
44 +
        if (!((input[i] >= 'A' && input[i] <= 'Z') || (input[i] >= 'a' && input[i] <= 'z') ||
45 +
              input[i] == '_')) {
46 +
            if (out + 1 >= capacity)
47 +
                return 0;
48 +
            output[out++] = '$';
49 +
            continue;
50 +
        }
51 +
        for (; (input[i] >= 'A' && input[i] <= 'Z') || (input[i] >= 'a' && input[i] <= 'z') ||
52 +
               (input[i] >= '0' && input[i] <= '9') || input[i] == '_';
53 +
             i++)
54 +
            ;
55 +
56 +
        len = i - start;
57 +
        if (len >= sizeof(name))
58 +
            return 0;
59 +
        memcpy(name, input + start, len);
60 +
        name[len] = '\0';
61 +
        value     = getenv(name);
62 +
        if (!value)
63 +
            continue;
64 +
65 +
        len = strlen(value);
66 +
        if (out + len >= capacity)
67 +
            return 0;
68 +
        memcpy(output + out, value, len);
69 +
        out += len;
70 +
    }
71 +
    output[out] = '\0';
72 +
73 +
    return out + 1;
74 +
}
75 +
76 +
/* Put a file descriptor into nonblocking mode. */
77 +
int fd_set_nonblock(int fd) {
78 +
    int flags = fcntl(fd, F_GETFL);
79 +
80 +
    if (flags < 0) {
81 +
        perror("fcntl(F_GETFL)");
82 +
        return -1;
83 +
    }
84 +
    if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
85 +
        perror("fcntl(F_SETFL)");
86 +
        return -1;
87 +
    }
88 +
    return 0;
89 +
}
util.h added +13 -0
1 +
#ifndef SWM_UTIL_H
2 +
#define SWM_UTIL_H
3 +
4 +
#include <stddef.h>
5 +
6 +
/* Print an error and terminate the process. */
7 +
[[noreturn]] void die(const char *fmt, ...);
8 +
/* Expand $NAME references without shell evaluation, returning bytes written or zero. */
9 +
size_t env_expand(char *output, size_t capacity, const char *input);
10 +
/* Put a file descriptor into nonblocking mode. */
11 +
int fd_set_nonblock(int fd);
12 +
13 +
#endif