Show More
| @@ -0,0 +1,20 | |||||
|
|
1 | syntax: glob | |||
|
|
2 | swindle | |||
|
|
3 | smsg/smsg | |||
|
|
4 | *.o | |||
|
|
5 | *-protocol.c | |||
|
|
6 | *-protocol.h | |||
|
|
7 | smsg/*-protocol.c | |||
|
|
8 | smsg/*-protocol.h | |||
|
|
9 | result | |||
|
|
10 | result-* | |||
|
|
11 | .direnv/ | |||
|
|
12 | .vscode/ | |||
|
|
13 | .idea/ | |||
|
|
14 | .cache/ | |||
|
|
15 | compile_commands.json | |||
|
|
16 | tags | |||
|
|
17 | *.swp | |||
|
|
18 | *.swo | |||
|
|
19 | *~ | |||
|
|
20 | include/lua5.4/* | |||
| This diff has been collapsed as it changes many lines, (693 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,693 | |||||
|
|
1 | swindle - wayland compositor base wlroots | |||
|
|
2 | ||||
|
|
3 | Copyright Β© 2026 kantiankant | |||
|
|
4 | ||||
|
|
5 | See also the files licenses/LICENSE.dwl, licenses/LICENSE.tinywl, | |||
|
|
6 | licenses/LICENSE.dwm, licenses/LICENSE.sway, and licenses/LICENSE.mangowm | |||
|
|
7 | ||||
|
|
8 | This program is free software: you can redistribute it and/or modify | |||
|
|
9 | it under the terms of the GNU General Public License as published by | |||
|
|
10 | the Free Software Foundation, either version 3 of the License, or | |||
|
|
11 | (at your option) any later version. | |||
|
|
12 | ||||
|
|
13 | This program is distributed in the hope that it will be useful, | |||
|
|
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
|
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
|
16 | GNU General Public License for more details. | |||
|
|
17 | ||||
|
|
18 | ---- | |||
|
|
19 | ||||
|
|
20 | GNU GENERAL PUBLIC LICENSE | |||
|
|
21 | Version 3, 29 June 2007 | |||
|
|
22 | ||||
|
|
23 | Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/> | |||
|
|
24 | Everyone is permitted to copy and distribute verbatim copies | |||
|
|
25 | of this license document, but changing it is not allowed. | |||
|
|
26 | ||||
|
|
27 | Preamble | |||
|
|
28 | ||||
|
|
29 | The GNU General Public License is a free, copyleft license for | |||
|
|
30 | software and other kinds of works. | |||
|
|
31 | ||||
|
|
32 | The licenses for most software and other practical works are designed | |||
|
|
33 | to take away your freedom to share and change the works. By contrast, | |||
|
|
34 | the GNU General Public License is intended to guarantee your freedom to | |||
|
|
35 | share and change all versions of a program--to make sure it remains free | |||
|
|
36 | software for all its users. We, the Free Software Foundation, use the | |||
|
|
37 | GNU General Public License for most of our software; it applies also to | |||
|
|
38 | any other work released this way by its authors. You can apply it to | |||
|
|
39 | your programs, too. | |||
|
|
40 | ||||
|
|
41 | When we speak of free software, we are referring to freedom, not | |||
|
|
42 | price. Our General Public Licenses are designed to make sure that you | |||
|
|
43 | have the freedom to distribute copies of free software (and charge for | |||
|
|
44 | them if you wish), that you receive source code or can get it if you | |||
|
|
45 | want it, that you can change the software or use pieces of it in new | |||
|
|
46 | free programs, and that you know you can do these things. | |||
|
|
47 | ||||
|
|
48 | To protect your rights, we need to prevent others from denying you | |||
|
|
49 | these rights or asking you to surrender the rights. Therefore, you have | |||
|
|
50 | certain responsibilities if you distribute copies of the software, or if | |||
|
|
51 | you modify it: responsibilities to respect the freedom of others. | |||
|
|
52 | ||||
|
|
53 | For example, if you distribute copies of such a program, whether | |||
|
|
54 | gratis or for a fee, you must pass on to the recipients the same | |||
|
|
55 | freedoms that you received. You must make sure that they, too, receive | |||
|
|
56 | or can get the source code. And you must show them these terms so they | |||
|
|
57 | know their rights. | |||
|
|
58 | ||||
|
|
59 | Developers that use the GNU GPL protect your rights with two steps: | |||
|
|
60 | (1) assert copyright on the software, and (2) offer you this License | |||
|
|
61 | giving you legal permission to copy, distribute and/or modify it. | |||
|
|
62 | ||||
|
|
63 | For the developers' and authors' protection, the GPL clearly explains | |||
|
|
64 | that there is no warranty for this free software. For both users' and | |||
|
|
65 | authors' sake, the GPL requires that modified versions be marked as | |||
|
|
66 | changed, so that their problems will not be attributed erroneously to | |||
|
|
67 | authors of previous versions. | |||
|
|
68 | ||||
|
|
69 | Some devices are designed to deny users access to install or run | |||
|
|
70 | modified versions of the software inside them, although the manufacturer | |||
|
|
71 | can do so. This is fundamentally incompatible with the aim of | |||
|
|
72 | protecting users' freedom to change the software. The systematic | |||
|
|
73 | pattern of such abuse occurs in the area of products for individuals to | |||
|
|
74 | use, which is precisely where it is most unacceptable. Therefore, we | |||
|
|
75 | have designed this version of the GPL to prohibit the practice for those | |||
|
|
76 | products. If such problems arise substantially in other domains, we | |||
|
|
77 | stand ready to extend this provision to those domains in future versions | |||
|
|
78 | of the GPL, as needed to protect the freedom of users. | |||
|
|
79 | ||||
|
|
80 | Finally, every program is threatened constantly by software patents. | |||
|
|
81 | States should not allow patents to restrict development and use of | |||
|
|
82 | software on general-purpose computers, but in those that do, we wish to | |||
|
|
83 | avoid the special danger that patents applied to a free program could | |||
|
|
84 | make it effectively proprietary. To prevent this, the GPL assures that | |||
|
|
85 | patents cannot be used to render the program non-free. | |||
|
|
86 | ||||
|
|
87 | The precise terms and conditions for copying, distribution and | |||
|
|
88 | modification follow. | |||
|
|
89 | ||||
|
|
90 | TERMS AND CONDITIONS | |||
|
|
91 | ||||
|
|
92 | 1. Definitions. | |||
|
|
93 | ||||
|
|
94 | "This License" refers to version 3 of the GNU General Public License. | |||
|
|
95 | ||||
|
|
96 | "Copyright" also means copyright-like laws that apply to other kinds of | |||
|
|
97 | works, such as semiconductor masks. | |||
|
|
98 | ||||
|
|
99 | "The Program" refers to any copyrightable work licensed under this | |||
|
|
100 | License. Each licensee is addressed as "you". "Licensees" and | |||
|
|
101 | "recipients" may be individuals or organizations. | |||
|
|
102 | ||||
|
|
103 | To "modify" a work means to copy from or adapt all or part of the work | |||
|
|
104 | in a fashion requiring copyright permission, other than the making of an | |||
|
|
105 | exact copy. The resulting work is called a "modified version" of the | |||
|
|
106 | earlier work or a work "based on" the earlier work. | |||
|
|
107 | ||||
|
|
108 | A "covered work" means either the unmodified Program or a work based | |||
|
|
109 | on the Program. | |||
|
|
110 | ||||
|
|
111 | To "propagate" a work means to do anything with it that, without | |||
|
|
112 | permission, would make you directly or secondarily liable for | |||
|
|
113 | infringement under applicable copyright law, except executing it on a | |||
|
|
114 | computer or modifying a private copy. Propagation includes copying, | |||
|
|
115 | distribution (with or without modification), making available to the | |||
|
|
116 | public, and in some countries other activities as well. | |||
|
|
117 | ||||
|
|
118 | To "convey" a work means any kind of propagation that enables other | |||
|
|
119 | parties to make or receive copies. Mere interaction with a user through | |||
|
|
120 | a computer network, with no transfer of a copy, is not conveying. | |||
|
|
121 | ||||
|
|
122 | An interactive user interface displays "Appropriate Legal Notices" | |||
|
|
123 | to the extent that it includes a convenient and prominently visible | |||
|
|
124 | feature that (1) displays an appropriate copyright notice, and (2) | |||
|
|
125 | tells the user that there is no warranty for the work (except to the | |||
|
|
126 | extent that warranties are provided), that licensees may convey the | |||
|
|
127 | work under this License, and how to view a copy of this License. If | |||
|
|
128 | the interface presents a list of user commands or options, such as a | |||
|
|
129 | menu, a prominent item in the list meets this criterion. | |||
|
|
130 | ||||
|
|
131 | 1. Source Code. | |||
|
|
132 | ||||
|
|
133 | The "source code" for a work means the preferred form of the work | |||
|
|
134 | for making modifications to it. "Object code" means any non-source | |||
|
|
135 | form of a work. | |||
|
|
136 | ||||
|
|
137 | A "Standard Interface" means an interface that either is an official | |||
|
|
138 | standard defined by a recognized standards body, or, in the case of | |||
|
|
139 | interfaces specified for a particular programming language, one that | |||
|
|
140 | is widely used among developers working in that language. | |||
|
|
141 | ||||
|
|
142 | The "System Libraries" of an executable work include anything, other | |||
|
|
143 | than the work as a whole, that (a) is included in the normal form of | |||
|
|
144 | packaging a Major Component, but which is not part of that Major | |||
|
|
145 | Component, and (b) serves only to enable use of the work with that | |||
|
|
146 | Major Component, or to implement a Standard Interface for which an | |||
|
|
147 | implementation is available to the public in source code form. A | |||
|
|
148 | "Major Component", in this context, means a major essential component | |||
|
|
149 | (kernel, window system, and so on) of the specific operating system | |||
|
|
150 | (if any) on which the executable work runs, or a compiler used to | |||
|
|
151 | produce the work, or an object code interpreter used to run it. | |||
|
|
152 | ||||
|
|
153 | The "Corresponding Source" for a work in object code form means all | |||
|
|
154 | the source code needed to generate, install, and (for an executable | |||
|
|
155 | work) run the object code and to modify the work, including scripts to | |||
|
|
156 | control those activities. However, it does not include the work's | |||
|
|
157 | System Libraries, or general-purpose tools or generally available free | |||
|
|
158 | programs which are used unmodified in performing those activities but | |||
|
|
159 | which are not part of the work. For example, Corresponding Source | |||
|
|
160 | includes interface definition files associated with source files for | |||
|
|
161 | the work, and the source code for shared libraries and dynamically | |||
|
|
162 | linked subprograms that the work is specifically designed to require, | |||
|
|
163 | such as by intimate data communication or control flow between those | |||
|
|
164 | subprograms and other parts of the work. | |||
|
|
165 | ||||
|
|
166 | The Corresponding Source need not include anything that users | |||
|
|
167 | can regenerate automatically from other parts of the Corresponding | |||
|
|
168 | Source. | |||
|
|
169 | ||||
|
|
170 | The Corresponding Source for a work in source code form is that | |||
|
|
171 | same work. | |||
|
|
172 | ||||
|
|
173 | 2. Basic Permissions. | |||
|
|
174 | ||||
|
|
175 | All rights granted under this License are granted for the term of | |||
|
|
176 | copyright on the Program, and are irrevocable provided the stated | |||
|
|
177 | conditions are met. This License explicitly affirms your unlimited | |||
|
|
178 | permission to run the unmodified Program. The output from running a | |||
|
|
179 | covered work is covered by this License only if the output, given its | |||
|
|
180 | content, constitutes a covered work. This License acknowledges your | |||
|
|
181 | rights of fair use or other equivalent, as provided by copyright law. | |||
|
|
182 | ||||
|
|
183 | You may make, run and propagate covered works that you do not | |||
|
|
184 | convey, without conditions so long as your license otherwise remains | |||
|
|
185 | in force. You may convey covered works to others for the sole purpose | |||
|
|
186 | of having them make modifications exclusively for you, or provide you | |||
|
|
187 | with facilities for running those works, provided that you comply with | |||
|
|
188 | the terms of this License in conveying all material for which you do | |||
|
|
189 | not control copyright. Those thus making or running the covered works | |||
|
|
190 | for you must do so exclusively on your behalf, under your direction | |||
|
|
191 | and control, on terms that prohibit them from making any copies of | |||
|
|
192 | your copyrighted material outside their relationship with you. | |||
|
|
193 | ||||
|
|
194 | Conveying under any other circumstances is permitted solely under | |||
|
|
195 | the conditions stated below. Sublicensing is not allowed; section 10 | |||
|
|
196 | makes it unnecessary. | |||
|
|
197 | ||||
|
|
198 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. | |||
|
|
199 | ||||
|
|
200 | No covered work shall be deemed part of an effective technological | |||
|
|
201 | measure under any applicable law fulfilling obligations under article | |||
|
|
202 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or | |||
|
|
203 | similar laws prohibiting or restricting circumvention of such | |||
|
|
204 | measures. | |||
|
|
205 | ||||
|
|
206 | When you convey a covered work, you waive any legal power to forbid | |||
|
|
207 | circumvention of technological measures to the extent such circumvention | |||
|
|
208 | is effected by exercising rights under this License with respect to | |||
|
|
209 | the covered work, and you disclaim any intention to limit operation or | |||
|
|
210 | modification of the work as a means of enforcing, against the work's | |||
|
|
211 | users, your or third parties' legal rights to forbid circumvention of | |||
|
|
212 | technological measures. | |||
|
|
213 | ||||
|
|
214 | 4. Conveying Verbatim Copies. | |||
|
|
215 | ||||
|
|
216 | You may convey verbatim copies of the Program's source code as you | |||
|
|
217 | receive it, in any medium, provided that you conspicuously and | |||
|
|
218 | appropriately publish on each copy an appropriate copyright notice; | |||
|
|
219 | keep intact all notices stating that this License and any | |||
|
|
220 | non-permissive terms added in accord with section 7 apply to the code; | |||
|
|
221 | keep intact all notices of the absence of any warranty; and give all | |||
|
|
222 | recipients a copy of this License along with the Program. | |||
|
|
223 | ||||
|
|
224 | You may charge any price or no price for each copy that you convey, | |||
|
|
225 | and you may offer support or warranty protection for a fee. | |||
|
|
226 | ||||
|
|
227 | 5. Conveying Modified Source Versions. | |||
|
|
228 | ||||
|
|
229 | You may convey a work based on the Program, or the modifications to | |||
|
|
230 | produce it from the Program, in the form of source code under the | |||
|
|
231 | terms of section 4, provided that you also meet all of these conditions: | |||
|
|
232 | ||||
|
|
233 | a) The work must carry prominent notices stating that you modified | |||
|
|
234 | it, and giving a relevant date. | |||
|
|
235 | ||||
|
|
236 | b) The work must carry prominent notices stating that it is | |||
|
|
237 | released under this License and any conditions added under section | |||
|
|
238 | 7. This requirement modifies the requirement in section 4 to | |||
|
|
239 | "keep intact all notices". | |||
|
|
240 | ||||
|
|
241 | c) You must license the entire work, as a whole, under this | |||
|
|
242 | License to anyone who comes into possession of a copy. This | |||
|
|
243 | License will therefore apply, along with any applicable section 7 | |||
|
|
244 | additional terms, to the whole of the work, and all its parts, | |||
|
|
245 | regardless of how they are packaged. This License gives no | |||
|
|
246 | permission to license the work in any other way, but it does not | |||
|
|
247 | invalidate such permission if you have separately received it. | |||
|
|
248 | ||||
|
|
249 | d) If the work has interactive user interfaces, each must display | |||
|
|
250 | Appropriate Legal Notices; however, if the Program has interactive | |||
|
|
251 | interfaces that do not display Appropriate Legal Notices, your | |||
|
|
252 | work need not make them do so. | |||
|
|
253 | ||||
|
|
254 | A compilation of a covered work with other separate and independent | |||
|
|
255 | works, which are not by their nature extensions of the covered work, | |||
|
|
256 | and which are not combined with it such as to form a larger program, | |||
|
|
257 | in or on a volume of a storage or distribution medium, is called an | |||
|
|
258 | "aggregate" if the compilation and its resulting copyright are not | |||
|
|
259 | used to limit the access or legal rights of the compilation's users | |||
|
|
260 | beyond what the individual works permit. Inclusion of a covered work | |||
|
|
261 | in an aggregate does not cause this License to apply to the other | |||
|
|
262 | parts of the aggregate. | |||
|
|
263 | ||||
|
|
264 | 6. Conveying Non-Source Forms. | |||
|
|
265 | ||||
|
|
266 | You may convey a covered work in object code form under the terms | |||
|
|
267 | of sections 4 and 5, provided that you also convey the | |||
|
|
268 | machine-readable Corresponding Source under the terms of this License, | |||
|
|
269 | in one of these ways: | |||
|
|
270 | ||||
|
|
271 | a) Convey the object code in, or embodied in, a physical product | |||
|
|
272 | (including a physical distribution medium), accompanied by the | |||
|
|
273 | Corresponding Source fixed on a durable physical medium | |||
|
|
274 | customarily used for software interchange. | |||
|
|
275 | ||||
|
|
276 | b) Convey the object code in, or embodied in, a physical product | |||
|
|
277 | (including a physical distribution medium), accompanied by a | |||
|
|
278 | written offer, valid for at least three years and valid for as | |||
|
|
279 | long as you offer spare parts or customer support for that product | |||
|
|
280 | model, to give anyone who possesses the object code either (1) a | |||
|
|
281 | copy of the Corresponding Source for all the software in the | |||
|
|
282 | product that is covered by this License, on a durable physical | |||
|
|
283 | medium customarily used for software interchange, for a price no | |||
|
|
284 | more than your reasonable cost of physically performing this | |||
|
|
285 | conveying of source, or (2) access to copy the | |||
|
|
286 | Corresponding Source from a network server at no charge. | |||
|
|
287 | ||||
|
|
288 | c) Convey individual copies of the object code with a copy of the | |||
|
|
289 | written offer to provide the Corresponding Source. This | |||
|
|
290 | alternative is allowed only occasionally and noncommercially, and | |||
|
|
291 | only if you received the object code with such an offer, in accord | |||
|
|
292 | with subsection 6b. | |||
|
|
293 | ||||
|
|
294 | d) Convey the object code by offering access from a designated | |||
|
|
295 | place (gratis or for a charge), and offer equivalent access to the | |||
|
|
296 | Corresponding Source in the same way through the same place at no | |||
|
|
297 | further charge. You need not require recipients to copy the | |||
|
|
298 | Corresponding Source along with the object code. If the place to | |||
|
|
299 | copy the object code is a network server, the Corresponding Source | |||
|
|
300 | may be on a different server (operated by you or a third party) | |||
|
|
301 | that supports equivalent copying facilities, provided you maintain | |||
|
|
302 | clear directions next to the object code saying where to find the | |||
|
|
303 | Corresponding Source. Regardless of what server hosts the | |||
|
|
304 | Corresponding Source, you remain obligated to ensure that it is | |||
|
|
305 | available for as long as needed to satisfy these requirements. | |||
|
|
306 | ||||
|
|
307 | e) Convey the object code using peer-to-peer transmission, provided | |||
|
|
308 | you inform other peers where the object code and Corresponding | |||
|
|
309 | Source of the work are being offered to the general public at no | |||
|
|
310 | charge under subsection 6d. | |||
|
|
311 | ||||
|
|
312 | A separable portion of the object code, whose source code is excluded | |||
|
|
313 | from the Corresponding Source as a System Library, need not be | |||
|
|
314 | included in conveying the object code work. | |||
|
|
315 | ||||
|
|
316 | A "User Product" is either (1) a "consumer product", which means any | |||
|
|
317 | tangible personal property which is normally used for personal, family, | |||
|
|
318 | or household purposes, or (2) anything designed or sold for incorporation | |||
|
|
319 | into a dwelling. In determining whether a product is a consumer product, | |||
|
|
320 | doubtful cases shall be resolved in favor of coverage. For a particular | |||
|
|
321 | product received by a particular user, "normally used" refers to a | |||
|
|
322 | typical or common use of that class of product, regardless of the status | |||
|
|
323 | of the particular user or of the way in which the particular user | |||
|
|
324 | actually uses, or expects or is expected to use, the product. A product | |||
|
|
325 | is a consumer product regardless of whether the product has substantial | |||
|
|
326 | commercial, industrial or non-consumer uses, unless such uses represent | |||
|
|
327 | the only significant mode of use of the product. | |||
|
|
328 | ||||
|
|
329 | "Installation Information" for a User Product means any methods, | |||
|
|
330 | procedures, authorization keys, or other information required to install | |||
|
|
331 | and execute modified versions of a covered work in that User Product from | |||
|
|
332 | a modified version of its Corresponding Source. The information must | |||
|
|
333 | suffice to ensure that the continued functioning of the modified object | |||
|
|
334 | code is in no case prevented or interfered with solely because | |||
|
|
335 | modification has been made. | |||
|
|
336 | ||||
|
|
337 | If you convey an object code work under this section in, or with, or | |||
|
|
338 | specifically for use in, a User Product, and the conveying occurs as | |||
|
|
339 | part of a transaction in which the right of possession and use of the | |||
|
|
340 | User Product is transferred to the recipient in perpetuity or for a | |||
|
|
341 | fixed term (regardless of how the transaction is characterized), the | |||
|
|
342 | Corresponding Source conveyed under this section must be accompanied | |||
|
|
343 | by the Installation Information. But this requirement does not apply | |||
|
|
344 | if neither you nor any third party retains the ability to install | |||
|
|
345 | modified object code on the User Product (for example, the work has | |||
|
|
346 | been installed in ROM). | |||
|
|
347 | ||||
|
|
348 | The requirement to provide Installation Information does not include a | |||
|
|
349 | requirement to continue to provide support service, warranty, or updates | |||
|
|
350 | for a work that has been modified or installed by the recipient, or for | |||
|
|
351 | the User Product in which it has been modified or installed. Access to a | |||
|
|
352 | network may be denied when the modification itself materially and | |||
|
|
353 | adversely affects the operation of the network or violates the rules and | |||
|
|
354 | protocols for communication across the network. | |||
|
|
355 | ||||
|
|
356 | Corresponding Source conveyed, and Installation Information provided, | |||
|
|
357 | in accord with this section must be in a format that is publicly | |||
|
|
358 | documented (and with an implementation available to the public in | |||
|
|
359 | source code form), and must require no special password or key for | |||
|
|
360 | unpacking, reading or copying. | |||
|
|
361 | ||||
|
|
362 | 7. Additional Terms. | |||
|
|
363 | ||||
|
|
364 | "Additional permissions" are terms that supplement the terms of this | |||
|
|
365 | License by making exceptions from one or more of its conditions. | |||
|
|
366 | Additional permissions that are applicable to the entire Program shall | |||
|
|
367 | be treated as though they were included in this License, to the extent | |||
|
|
368 | that they are valid under applicable law. If additional permissions | |||
|
|
369 | apply only to part of the Program, that part may be used separately | |||
|
|
370 | under those permissions, but the entire Program remains governed by | |||
|
|
371 | this License without regard to the additional permissions. | |||
|
|
372 | ||||
|
|
373 | When you convey a copy of a covered work, you may at your option | |||
|
|
374 | remove any additional permissions from that copy, or from any part of | |||
|
|
375 | it. (Additional permissions may be written to require their own | |||
|
|
376 | removal in certain cases when you modify the work.) You may place | |||
|
|
377 | additional permissions on material, added by you to a covered work, | |||
|
|
378 | for which you have or can give appropriate copyright permission. | |||
|
|
379 | ||||
|
|
380 | Notwithstanding any other provision of this License, for material you | |||
|
|
381 | add to a covered work, you may (if authorized by the copyright holders of | |||
|
|
382 | that material) supplement the terms of this License with terms: | |||
|
|
383 | ||||
|
|
384 | a) Disclaiming warranty or limiting liability differently from the | |||
|
|
385 | terms of sections 15 and 16 of this License; or | |||
|
|
386 | ||||
|
|
387 | b) Requiring preservation of specified reasonable legal notices or | |||
|
|
388 | author attributions in that material or in the Appropriate Legal | |||
|
|
389 | Notices displayed by works containing it; or | |||
|
|
390 | ||||
|
|
391 | c) Prohibiting misrepresentation of the origin of that material, or | |||
|
|
392 | requiring that modified versions of such material be marked in | |||
|
|
393 | reasonable ways as different from the original version; or | |||
|
|
394 | ||||
|
|
395 | d) Limiting the use for publicity purposes of names of licensors or | |||
|
|
396 | authors of the material; or | |||
|
|
397 | ||||
|
|
398 | e) Declining to grant rights under trademark law for use of some | |||
|
|
399 | trade names, trademarks, or service marks; or | |||
|
|
400 | ||||
|
|
401 | f) Requiring indemnification of licensors and authors of that | |||
|
|
402 | material by anyone who conveys the material (or modified versions of | |||
|
|
403 | it) with contractual assumptions of liability to the recipient, for | |||
|
|
404 | any liability that these contractual assumptions directly impose on | |||
|
|
405 | those licensors and authors. | |||
|
|
406 | ||||
|
|
407 | All other non-permissive additional terms are considered "further | |||
|
|
408 | restrictions" within the meaning of section 10. If the Program as you | |||
|
|
409 | received it, or any part of it, contains a notice stating that it is | |||
|
|
410 | governed by this License along with a term that is a further | |||
|
|
411 | restriction, you may remove that term. If a license document contains | |||
|
|
412 | a further restriction but permits relicensing or conveying under this | |||
|
|
413 | License, you may add to a covered work material governed by the terms | |||
|
|
414 | of that license document, provided that the further restriction does | |||
|
|
415 | not survive such relicensing or conveying. | |||
|
|
416 | ||||
|
|
417 | If you add terms to a covered work in accord with this section, you | |||
|
|
418 | must place, in the relevant source files, a statement of the | |||
|
|
419 | additional terms that apply to those files, or a notice indicating | |||
|
|
420 | where to find the applicable terms. | |||
|
|
421 | ||||
|
|
422 | Additional terms, permissive or non-permissive, may be stated in the | |||
|
|
423 | form of a separately written license, or stated as exceptions; | |||
|
|
424 | the above requirements apply either way. | |||
|
|
425 | ||||
|
|
426 | 8. Termination. | |||
|
|
427 | ||||
|
|
428 | You may not propagate or modify a covered work except as expressly | |||
|
|
429 | provided under this License. Any attempt otherwise to propagate or | |||
|
|
430 | modify it is void, and will automatically terminate your rights under | |||
|
|
431 | this License (including any patent licenses granted under the third | |||
|
|
432 | paragraph of section 11). | |||
|
|
433 | ||||
|
|
434 | However, if you cease all violation of this License, then your | |||
|
|
435 | license from a particular copyright holder is reinstated (a) | |||
|
|
436 | provisionally, unless and until the copyright holder explicitly and | |||
|
|
437 | finally terminates your license, and (b) permanently, if the copyright | |||
|
|
438 | holder fails to notify you of the violation by some reasonable means | |||
|
|
439 | prior to 60 days after the cessation. | |||
|
|
440 | ||||
|
|
441 | Moreover, your license from a particular copyright holder is | |||
|
|
442 | reinstated permanently if the copyright holder notifies you of the | |||
|
|
443 | violation by some reasonable means, this is the first time you have | |||
|
|
444 | received notice of violation of this License (for any work) from that | |||
|
|
445 | copyright holder, and you cure the violation prior to 30 days after | |||
|
|
446 | your receipt of the notice. | |||
|
|
447 | ||||
|
|
448 | Termination of your rights under this section does not terminate the | |||
|
|
449 | licenses of parties who have received copies or rights from you under | |||
|
|
450 | this License. If your rights have been terminated and not permanently | |||
|
|
451 | reinstated, you do not qualify to receive new licenses for the same | |||
|
|
452 | material under section 10. | |||
|
|
453 | ||||
|
|
454 | 9. Acceptance Not Required for Having Copies. | |||
|
|
455 | ||||
|
|
456 | You are not required to accept this License in order to receive or | |||
|
|
457 | run a copy of the Program. Ancillary propagation of a covered work | |||
|
|
458 | occurring solely as a consequence of using peer-to-peer transmission | |||
|
|
459 | to receive a copy likewise does not require acceptance. However, | |||
|
|
460 | nothing other than this License grants you permission to propagate or | |||
|
|
461 | modify any covered work. These actions infringe copyright if you do | |||
|
|
462 | not accept this License. Therefore, by modifying or propagating a | |||
|
|
463 | covered work, you indicate your acceptance of this License to do so. | |||
|
|
464 | ||||
|
|
465 | 10. Automatic Licensing of Downstream Recipients. | |||
|
|
466 | ||||
|
|
467 | Each time you convey a covered work, the recipient automatically | |||
|
|
468 | receives a license from the original licensors, to run, modify and | |||
|
|
469 | propagate that work, subject to this License. You are not responsible | |||
|
|
470 | for enforcing compliance by third parties with this License. | |||
|
|
471 | ||||
|
|
472 | An "entity transaction" is a transaction transferring control of an | |||
|
|
473 | organization, or substantially all assets of one, or subdividing an | |||
|
|
474 | organization, or merging organizations. If propagation of a covered | |||
|
|
475 | work results from an entity transaction, each party to that | |||
|
|
476 | transaction who receives a copy of the work also receives whatever | |||
|
|
477 | licenses to the work the party's predecessor in interest had or could | |||
|
|
478 | give under the previous paragraph, plus a right to possession of the | |||
|
|
479 | Corresponding Source of the work from the predecessor in interest, if | |||
|
|
480 | the predecessor has it or can get it with reasonable efforts. | |||
|
|
481 | ||||
|
|
482 | You may not impose any further restrictions on the exercise of the | |||
|
|
483 | rights granted or affirmed under this License. For example, you may | |||
|
|
484 | not impose a license fee, royalty, or other charge for exercise of | |||
|
|
485 | rights granted under this License, and you may not initiate litigation | |||
|
|
486 | (including a cross-claim or counterclaim in a lawsuit) alleging that | |||
|
|
487 | any patent claim is infringed by making, using, selling, offering for | |||
|
|
488 | sale, or importing the Program or any portion of it. | |||
|
|
489 | ||||
|
|
490 | 11. Patents. | |||
|
|
491 | ||||
|
|
492 | A "contributor" is a copyright holder who authorizes use under this | |||
|
|
493 | License of the Program or a work on which the Program is based. The | |||
|
|
494 | work thus licensed is called the contributor's "contributor version". | |||
|
|
495 | ||||
|
|
496 | A contributor's "essential patent claims" are all patent claims | |||
|
|
497 | owned or controlled by the contributor, whether already acquired or | |||
|
|
498 | hereafter acquired, that would be infringed by some manner, permitted | |||
|
|
499 | by this License, of making, using, or selling its contributor version, | |||
|
|
500 | but do not include claims that would be infringed only as a | |||
|
|
501 | consequence of further modification of the contributor version. For | |||
|
|
502 | purposes of this definition, "control" includes the right to grant | |||
|
|
503 | patent sublicenses in a manner consistent with the requirements of | |||
|
|
504 | this License. | |||
|
|
505 | ||||
|
|
506 | Each contributor grants you a non-exclusive, worldwide, royalty-free | |||
|
|
507 | patent license under the contributor's essential patent claims, to | |||
|
|
508 | make, use, sell, offer for sale, import and otherwise run, modify and | |||
|
|
509 | propagate the contents of its contributor version. | |||
|
|
510 | ||||
|
|
511 | In the following three paragraphs, a "patent license" is any express | |||
|
|
512 | agreement or commitment, however denominated, not to enforce a patent | |||
|
|
513 | (such as an express permission to practice a patent or covenant not to | |||
|
|
514 | sue for patent infringement). To "grant" such a patent license to a | |||
|
|
515 | party means to make such an agreement or commitment not to enforce a | |||
|
|
516 | patent against the party. | |||
|
|
517 | ||||
|
|
518 | If you convey a covered work, knowingly relying on a patent license, | |||
|
|
519 | and the Corresponding Source of the work is not available for anyone | |||
|
|
520 | to copy, free of charge and under the terms of this License, through a | |||
|
|
521 | publicly available network server or other readily accessible means, | |||
|
|
522 | then you must either (1) cause the Corresponding Source to be so | |||
|
|
523 | available, or (2) arrange to deprive yourself of the benefit of the | |||
|
|
524 | patent license for this particular work, or (3) arrange, in a manner | |||
|
|
525 | consistent with the requirements of this License, to extend the patent | |||
|
|
526 | license to downstream recipients. "Knowingly relying" means you have | |||
|
|
527 | actual knowledge that, but for the patent license, your conveying the | |||
|
|
528 | covered work in a country, or your recipient's use of the covered work | |||
|
|
529 | in a country, would infringe one or more identifiable patents in that | |||
|
|
530 | country that you have reason to believe are valid. | |||
|
|
531 | ||||
|
|
532 | If, pursuant to or in connection with a single transaction or | |||
|
|
533 | arrangement, you convey, or propagate by procuring conveyance of, a | |||
|
|
534 | covered work, and grant a patent license to some of the parties | |||
|
|
535 | receiving the covered work authorizing them to use, propagate, modify | |||
|
|
536 | or convey a specific copy of the covered work, then the patent license | |||
|
|
537 | you grant is automatically extended to all recipients of the covered | |||
|
|
538 | work and works based on it. | |||
|
|
539 | ||||
|
|
540 | A patent license is "discriminatory" if it does not include within | |||
|
|
541 | the scope of its coverage, prohibits the exercise of, or is | |||
|
|
542 | conditioned on the non-exercise of one or more of the rights that are | |||
|
|
543 | specifically granted under this License. You may not convey a covered | |||
|
|
544 | work if you are a party to an arrangement with a third party that is | |||
|
|
545 | in the business of distributing software, under which you make payment | |||
|
|
546 | to the third party based on the extent of your activity of conveying | |||
|
|
547 | the work, and under which the third party grants, to any of the | |||
|
|
548 | parties who would receive the covered work from you, a discriminatory | |||
|
|
549 | patent license (a) in connection with copies of the covered work | |||
|
|
550 | conveyed by you (or copies made from those copies), or (b) primarily | |||
|
|
551 | for and in connection with specific products or compilations that | |||
|
|
552 | contain the covered work, unless you entered into that arrangement, | |||
|
|
553 | or that patent license was granted, prior to 28 March 2007. | |||
|
|
554 | ||||
|
|
555 | Nothing in this License shall be construed as excluding or limiting | |||
|
|
556 | any implied license or other defenses to infringement that may | |||
|
|
557 | otherwise be available to you under applicable patent law. | |||
|
|
558 | ||||
|
|
559 | 12. No Surrender of Others' Freedom. | |||
|
|
560 | ||||
|
|
561 | If conditions are imposed on you (whether by court order, agreement or | |||
|
|
562 | otherwise) that contradict the conditions of this License, they do not | |||
|
|
563 | excuse you from the conditions of this License. If you cannot convey a | |||
|
|
564 | covered work so as to satisfy simultaneously your obligations under this | |||
|
|
565 | License and any other pertinent obligations, then as a consequence you may | |||
|
|
566 | not convey it at all. For example, if you agree to terms that obligate you | |||
|
|
567 | to collect a royalty for further conveying from those to whom you convey | |||
|
|
568 | the Program, the only way you could satisfy both those terms and this | |||
|
|
569 | License would be to refrain entirely from conveying the Program. | |||
|
|
570 | ||||
|
|
571 | 13. Use with the GNU Affero General Public License. | |||
|
|
572 | ||||
|
|
573 | Notwithstanding any other provision of this License, you have | |||
|
|
574 | permission to link or combine any covered work with a work licensed | |||
|
|
575 | under version 3 of the GNU Affero General Public License into a single | |||
|
|
576 | combined work, and to convey the resulting work. The terms of this | |||
|
|
577 | License will continue to apply to the part which is the covered work, | |||
|
|
578 | but the special requirements of the GNU Affero General Public License, | |||
|
|
579 | section 13, concerning interaction through a network will apply to the | |||
|
|
580 | combination as such. | |||
|
|
581 | ||||
|
|
582 | 14. Revised Versions of this License. | |||
|
|
583 | ||||
|
|
584 | The Free Software Foundation may publish revised and/or new versions of | |||
|
|
585 | the GNU General Public License from time to time. Such new versions will | |||
|
|
586 | be similar in spirit to the present version, but may differ in detail to | |||
|
|
587 | address new problems or concerns. | |||
|
|
588 | ||||
|
|
589 | Each version is given a distinguishing version number. If the | |||
|
|
590 | Program specifies that a certain numbered version of the GNU General | |||
|
|
591 | Public License "or any later version" applies to it, you have the | |||
|
|
592 | option of following the terms and conditions either of that numbered | |||
|
|
593 | version or of any later version published by the Free Software | |||
|
|
594 | Foundation. If the Program does not specify a version number of the | |||
|
|
595 | GNU General Public License, you may choose any version ever published | |||
|
|
596 | by the Free Software Foundation. | |||
|
|
597 | ||||
|
|
598 | If the Program specifies that a proxy can decide which future | |||
|
|
599 | versions of the GNU General Public License can be used, that proxy's | |||
|
|
600 | public statement of acceptance of a version permanently authorizes you | |||
|
|
601 | to choose that version for the Program. | |||
|
|
602 | ||||
|
|
603 | Later license versions may give you additional or different | |||
|
|
604 | permissions. However, no additional obligations are imposed on any | |||
|
|
605 | author or copyright holder as a result of your choosing to follow a | |||
|
|
606 | later version. | |||
|
|
607 | ||||
|
|
608 | 15. Disclaimer of Warranty. | |||
|
|
609 | ||||
|
|
610 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | |||
|
|
611 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | |||
|
|
612 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | |||
|
|
613 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | |||
|
|
614 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |||
|
|
615 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | |||
|
|
616 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | |||
|
|
617 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | |||
|
|
618 | ||||
|
|
619 | 16. Limitation of Liability. | |||
|
|
620 | ||||
|
|
621 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |||
|
|
622 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | |||
|
|
623 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | |||
|
|
624 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | |||
|
|
625 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | |||
|
|
626 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | |||
|
|
627 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | |||
|
|
628 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | |||
|
|
629 | SUCH DAMAGES. | |||
|
|
630 | ||||
|
|
631 | 17. Interpretation of Sections 15 and 16. | |||
|
|
632 | ||||
|
|
633 | If the disclaimer of warranty and limitation of liability provided | |||
|
|
634 | above cannot be given local legal effect according to their terms, | |||
|
|
635 | reviewing courts shall apply local law that most closely approximates | |||
|
|
636 | an absolute waiver of all civil liability in connection with the | |||
|
|
637 | Program, unless a warranty or assumption of liability accompanies a | |||
|
|
638 | copy of the Program in return for a fee. | |||
|
|
639 | ||||
|
|
640 | END OF TERMS AND CONDITIONS | |||
|
|
641 | ||||
|
|
642 | How to Apply These Terms to Your New Programs | |||
|
|
643 | ||||
|
|
644 | If you develop a new program, and you want it to be of the greatest | |||
|
|
645 | possible use to the public, the best way to achieve this is to make it | |||
|
|
646 | free software which everyone can redistribute and change under these terms. | |||
|
|
647 | ||||
|
|
648 | To do so, attach the following notices to the program. It is safest | |||
|
|
649 | to attach them to the start of each source file to most effectively | |||
|
|
650 | state the exclusion of warranty; and each file should have at least | |||
|
|
651 | the "copyright" line and a pointer to where the full notice is found. | |||
|
|
652 | ||||
|
|
653 | <one line to give the program's name and a brief idea of what it does.> | |||
|
|
654 | Copyright (C) <year> <name of author> | |||
|
|
655 | ||||
|
|
656 | This program is free software: you can redistribute it and/or modify | |||
|
|
657 | it under the terms of the GNU General Public License as published by | |||
|
|
658 | the Free Software Foundation, either version 3 of the License, or | |||
|
|
659 | (at your option) any later version. | |||
|
|
660 | ||||
|
|
661 | This program is distributed in the hope that it will be useful, | |||
|
|
662 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
|
663 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
|
664 | GNU General Public License for more details. | |||
|
|
665 | ||||
|
|
666 | You should have received a copy of the GNU General Public License | |||
|
|
667 | along with this program. If not, see <https://www.gnu.org/licenses/>. | |||
|
|
668 | ||||
|
|
669 | Also add information on how to contact you by electronic and paper mail. | |||
|
|
670 | ||||
|
|
671 | If the program does terminal interaction, make it output a short | |||
|
|
672 | notice like this when it starts in an interactive mode: | |||
|
|
673 | ||||
|
|
674 | <program> Copyright (C) <year> <name of author> | |||
|
|
675 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |||
|
|
676 | This is free software, and you are welcome to redistribute it | |||
|
|
677 | under certain conditions; type `show c' for details. | |||
|
|
678 | ||||
|
|
679 | The hypothetical commands `show w' and `show c' should show the appropriate | |||
|
|
680 | parts of the General Public License. Of course, your program's commands | |||
|
|
681 | might be different; for a GUI interface, you would use an "about box". | |||
|
|
682 | ||||
|
|
683 | You should also get your employer (if you work as a programmer) or school, | |||
|
|
684 | if any, to sign a "copyright disclaimer" for the program, if necessary. | |||
|
|
685 | For more information on this, and how to apply and follow the GNU GPL, see | |||
|
|
686 | <https://www.gnu.org/licenses/>. | |||
|
|
687 | ||||
|
|
688 | The GNU General Public License does not permit incorporating your program | |||
|
|
689 | into proprietary programs. If your program is a subroutine library, you | |||
|
|
690 | may consider it more useful to permit linking proprietary applications with | |||
|
|
691 | the library. If this is what you want to do, use the GNU Lesser General | |||
|
|
692 | Public License instead of this License. But first, please read | |||
|
|
693 | <https://www.gnu.org/licenses/why-not-lgpl.html>. | |||
| @@ -0,0 +1,99 | |||||
|
|
1 | .POSIX: | |||
|
|
2 | .SUFFIXES: | |||
|
|
3 | ||||
|
|
4 | include config.mk | |||
|
|
5 | ||||
|
|
6 | DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) \ | |||
|
|
7 | -I. -Iinclude -Isrc -Iparser -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" \ | |||
|
|
8 | $(XWAYLAND) -g -Wall -Wextra -Wno-unused-parameter -O1 -std=c11 $(CFLAGS) | |||
|
|
9 | LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) | |||
|
|
10 | ||||
|
|
11 | SCANNER = `$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner` | |||
|
|
12 | PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols` | |||
|
|
13 | ||||
|
|
14 | SMSG_CFLAGS = `$(PKG_CONFIG) --cflags wayland-client` -Wall -Wextra -Wno-unused-parameter | |||
|
|
15 | SMSG_LDLIBS = `$(PKG_CONFIG) --libs wayland-client` | |||
|
|
16 | ||||
|
|
17 | all: swindle smsg/smsg | |||
|
|
18 | ||||
|
|
19 | swindle: src/dwl.o src/util.o parser/parser.o dwl-ipc-unstable-v2-protocol.o src/wlr_ext_workspace_v1.o ext-workspace-v1-protocol.o | |||
|
|
20 | $(CC) $^ $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ | |||
|
|
21 | ||||
|
|
22 | src/dwl.o: src/swindle.c include/client.h config.h include/dwl-ipc.h dwl-ipc-unstable-v2-protocol.h \ | |||
|
|
23 | include/ext-workspace.h src/wlr_ext_workspace_v1.h ext-workspace-v1-protocol.h \ | |||
|
|
24 | cursor-shape-v1-protocol.h pointer-constraints-unstable-v1-protocol.h \ | |||
|
|
25 | wlr-layer-shell-unstable-v1-protocol.h \ | |||
|
|
26 | wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ | |||
|
|
27 | parser/parser.h | |||
|
|
28 | $(CC) $(DWLCFLAGS) -o $@ -c $< | |||
|
|
29 | ||||
|
|
30 | src/util.o: src/util.c src/util.h | |||
|
|
31 | $(CC) $(DWLCFLAGS) -o $@ -c $< | |||
|
|
32 | ||||
|
|
33 | src/wlr_ext_workspace_v1.o: src/wlr_ext_workspace_v1.c src/wlr_ext_workspace_v1.h ext-workspace-v1-protocol.h | |||
|
|
34 | $(CC) $(DWLCFLAGS) -o $@ -c $< | |||
|
|
35 | ||||
|
|
36 | parser/parser.o: parser/parser.c parser/parser.h | |||
|
|
37 | $(CC) $(DWLCFLAGS) -o $@ -c $< | |||
|
|
38 | ||||
|
|
39 | dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c | |||
|
|
40 | ||||
|
|
41 | ext-workspace-v1-protocol.o: ext-workspace-v1-protocol.c | |||
|
|
42 | ||||
|
|
43 | # protocol headers and glue | |||
|
|
44 | cursor-shape-v1-protocol.h: | |||
|
|
45 | $(SCANNER) enum-header $(PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@ | |||
|
|
46 | pointer-constraints-unstable-v1-protocol.h: | |||
|
|
47 | $(SCANNER) enum-header $(PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@ | |||
|
|
48 | wlr-layer-shell-unstable-v1-protocol.h: | |||
|
|
49 | $(SCANNER) enum-header protocols/wlr-layer-shell-unstable-v1.xml $@ | |||
|
|
50 | wlr-output-power-management-unstable-v1-protocol.h: | |||
|
|
51 | $(SCANNER) server-header protocols/wlr-output-power-management-unstable-v1.xml $@ | |||
|
|
52 | xdg-shell-protocol.h: | |||
|
|
53 | $(SCANNER) server-header $(PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ | |||
|
|
54 | dwl-ipc-unstable-v2-protocol.h: | |||
|
|
55 | $(SCANNER) server-header protocols/dwl-ipc-unstable-v2.xml $@ | |||
|
|
56 | dwl-ipc-unstable-v2-protocol.c: | |||
|
|
57 | $(SCANNER) private-code protocols/dwl-ipc-unstable-v2.xml $@ | |||
|
|
58 | ||||
|
|
59 | # smsg | |||
|
|
60 | smsg/dwl-ipc-unstable-v2-protocol.h: | |||
|
|
61 | $(SCANNER) client-header protocols/dwl-ipc-unstable-v2.xml $@ | |||
|
|
62 | ||||
|
|
63 | smsg/dwl-ipc-unstable-v2-protocol.c: | |||
|
|
64 | $(SCANNER) private-code protocols/dwl-ipc-unstable-v2.xml $@ | |||
|
|
65 | ||||
|
|
66 | smsg/dwl-ipc-unstable-v2-protocol.o: smsg/dwl-ipc-unstable-v2-protocol.c | |||
|
|
67 | $(CC) $(SMSG_CFLAGS) -o $@ -c $< | |||
|
|
68 | ||||
|
|
69 | smsg/smsg.o: smsg/smsg.c smsg/dwl-ipc-unstable-v2-protocol.h | |||
|
|
70 | $(CC) $(SMSG_CFLAGS) -o $@ -c $< | |||
|
|
71 | ||||
|
|
72 | smsg/smsg: smsg/smsg.o smsg/dwl-ipc-unstable-v2-protocol.o | |||
|
|
73 | $(CC) $^ $(SMSG_CFLAGS) $(SMSG_LDLIBS) -o $@ | |||
|
|
74 | ||||
|
|
75 | ext-workspace-v1-protocol.h: | |||
|
|
76 | $(SCANNER) server-header protocols/ext-workspace-v1.xml $@ | |||
|
|
77 | ext-workspace-v1-protocol.c: | |||
|
|
78 | $(SCANNER) private-code protocols/ext-workspace-v1.xml $@ | |||
|
|
79 | ||||
|
|
80 | clean: | |||
|
|
81 | rm -f swindle smsg/smsg src/*.o parser/*.o smsg/*.o smsg/*-protocol.h smsg/*-protocol.c *.o *-protocol.h *-protocol.c | |||
|
|
82 | ||||
|
|
83 | install: swindle smsg/smsg | |||
|
|
84 | mkdir -p $(DESTDIR)$(PREFIX)/bin | |||
|
|
85 | cp -f swindle $(DESTDIR)$(PREFIX)/bin | |||
|
|
86 | chmod 755 $(DESTDIR)$(PREFIX)/bin/swindle | |||
|
|
87 | cp -f smsg/smsg $(DESTDIR)$(PREFIX)/bin | |||
|
|
88 | chmod 755 $(DESTDIR)$(PREFIX)/bin/smsg | |||
|
|
89 | mkdir -p $(DESTDIR)/etc/swindle | |||
|
|
90 | cp -r example/* $(DESTDIR)/etc/swindle | |||
|
|
91 | ||||
|
|
92 | uninstall: | |||
|
|
93 | rm -f $(DESTDIR)$(PREFIX)/bin/swindle | |||
|
|
94 | rm -f $(DESTDIR)$(PREFIX)/bin/smsg | |||
|
|
95 | rm -rf $(DESTDIR)/etc/swindle | |||
|
|
96 | ||||
|
|
97 | .SUFFIXES: .c .o | |||
|
|
98 | .c.o: | |||
|
|
99 | $(CC) $(DWLCFLAGS) -o $@ -c $< | |||
| @@ -0,0 +1,56 | |||||
|
|
1 | ## Swindle | |||
|
|
2 | ||||
|
|
3 | ||||
|
|
4 | ||||
|
|
5 | > "It's so bad it's not even funny!" -kantiankant, 2026 | |||
|
|
6 | ||||
|
|
7 | ||||
|
|
8 | Swindle is a fork of [dwl](https://codeberg.org/dwl/dwl) that was designed from the start with one goal: to make it feel like the poor man's Hyprland. It has the following: | |||
|
|
9 | ||||
|
|
10 | * NO cool vfx | |||
|
|
11 | * nearly NO documentation | |||
|
|
12 | * ZERO good original code | |||
|
|
13 | * only ONE tiling layout (dwindle) | |||
|
|
14 | ||||
|
|
15 | > Fun fact: I actually took some parts of [MangoWM](https://github.com/mangowm/mango) (such as the ext-workspaces implementation). hence why the LICENSE.mangowm exists | |||
|
|
16 | ||||
|
|
17 | ## Dependencies | |||
|
|
18 | ||||
|
|
19 | * libinput | |||
|
|
20 | * wayland | |||
|
|
21 | * wlroots-0.20 (compiled with the libinput backend) | |||
|
|
22 | * xkbcommon | |||
|
|
23 | * wayland-protocols (compile-time only) | |||
|
|
24 | * pkg-config (compile-time only) | |||
|
|
25 | ||||
|
|
26 | Install these (and their `-devel` versions if your distro has separate | |||
|
|
27 | development packages) and run `make` followed by `doas/sudo make install`, if you wish to install it (installs to /usr/local/bin/ by default). | |||
|
|
28 | ||||
|
|
29 | ## Things that are good to have | |||
|
|
30 | ||||
|
|
31 | * [foot](https://codeberg.org/dnkl/foot) | |||
|
|
32 | ||||
|
|
33 | ||||
|
|
34 | ## Known Issues | |||
|
|
35 | ||||
|
|
36 | N/A | |||
|
|
37 | ||||
|
|
38 | ## Configuration | |||
|
|
39 | ||||
|
|
40 | read example/config.lua. It should give you a basic idea of how configuring swindle works. | |||
|
|
41 | ||||
|
|
42 | > note: it's best to copy /etc/swindle/config.lua into XDG_HOME_DIR/.config/swindle/ before you attempt to | |||
|
|
43 | start swindle because it (swindle) won't start without it | |||
|
|
44 | ||||
|
|
45 | ## Checklist | |||
|
|
46 | ||||
|
|
47 | - [ ] separate the trackpad scroll from the mousee scroll | |||
|
|
48 | - [x] Add ability to change your keymap | |||
|
|
49 | - [ ] Add animations via [scenefx](https://github.com/wlrfx/scenefx) when 0.5 drops (in another branch) | |||
|
|
50 | - [ ] Add the ability for the compositor to read a custom path for configs | |||
|
|
51 | - [ ] Add /etc/swindle/config.lua as a fallback path if $HOME/.config/swindle/config.lua is not found | |||
|
|
52 | ||||
|
|
53 | ||||
|
|
54 | ## License | |||
|
|
55 | ||||
|
|
56 | GPL-v3, probably | |||
| @@ -0,0 +1,4 | |||||
|
|
1 | ββββββββ ββ ββ | |||
|
|
2 | ββ ββ ββ ββ | |||
|
|
3 | ββ ββ ββ ββ | |||
|
|
4 | ββββ ββββββββββ | |||
| @@ -0,0 +1,6 | |||||
|
|
1 | static void dwindle(Monitor *m); | |||
|
|
2 | ||||
|
|
3 | static const Layout layouts[] = { | |||
|
|
4 | {"><>", NULL}, | |||
|
|
5 | {"[T]", dwindle}, | |||
|
|
6 | }; | |||
| @@ -0,0 +1,31 | |||||
|
|
1 | _VERSION = 0.1 | |||
|
|
2 | VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)` | |||
|
|
3 | ||||
|
|
4 | PKG_CONFIG = pkg-config | |||
|
|
5 | ||||
|
|
6 | # paths | |||
|
|
7 | PREFIX = /usr/local | |||
|
|
8 | MANDIR = $(PREFIX)/share/man | |||
|
|
9 | DATDIR = $(PREFIX)/share | |||
|
|
10 | ||||
|
|
11 | WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.20` | |||
|
|
12 | WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.20` | |||
|
|
13 | ||||
|
|
14 | # Allow using an alternative wlroots installation | |||
|
|
15 | #WLR_INCS = -I/usr/include/pixman-1 -I/usr/include/elogind -I/usr/include/libdrm \ | |||
|
|
16 | # -I$(PWD)/wlroots/include | |||
|
|
17 | #WLR_LIBS = -Wl,-rpath,$(PWD)/wlroots/build -L$(PWD)/wlroots/build -lwlroots-0.19 | |||
|
|
18 | ||||
|
|
19 | #XWAYLAND = | |||
|
|
20 | #XLIBS = | |||
|
|
21 | # Uncomment to build XWayland support | |||
|
|
22 | XWAYLAND = -DXWAYLAND | |||
|
|
23 | XLIBS = xcb xcb-icccm | |||
|
|
24 | ||||
|
|
25 | # lua | |||
|
|
26 | LUA_PKG = lua5.4 | |||
|
|
27 | ||||
|
|
28 | PKGS = wayland-server xkbcommon libinput $(LUA_PKG) $(XLIBS) | |||
|
|
29 | ||||
|
|
30 | # Requires C11: _Alignas and anonymous unions in wlroots headers. | |||
|
|
31 | CC = cc | |||
| @@ -0,0 +1,101 | |||||
|
|
1 | sloppy_focus = true | |||
|
|
2 | bypass_surface_visibility = false | |||
|
|
3 | log_level = "error" -- "silent", "error", "info", "debug" | |||
|
|
4 | ||||
|
|
5 | appearance = { | |||
|
|
6 | border_px = 2, | |||
|
|
7 | gaps = 10, -- 0 = no gaps. simple as. | |||
|
|
8 | smart_gaps = false, | |||
|
|
9 | ||||
|
|
10 | root_color = 0x222222ff, | |||
|
|
11 | border_color = 0x00000000, | |||
|
|
12 | focus_color = 0xe8e8e8ff, | |||
|
|
13 | urgent_color = 0xff0000ff, | |||
|
|
14 | ||||
|
|
15 | fullscreen_bg = 0x000000ff, | |||
|
|
16 | } | |||
|
|
17 | ||||
|
|
18 | ||||
|
|
19 | -- Note: the entire input section requires you to restart | |||
|
|
20 | -- the compositor once changed | |||
|
|
21 | input = { | |||
|
|
22 | repeat_rate = 50, | |||
|
|
23 | repeat_delay = 150, | |||
|
|
24 | tap_to_click = true, | |||
|
|
25 | tap_and_drag = true, | |||
|
|
26 | drag_lock = true, | |||
|
|
27 | natural_scrolling = false, | |||
|
|
28 | disable_while_typing = true, | |||
|
|
29 | left_handed = false, | |||
|
|
30 | middle_button_emulation = false, | |||
|
|
31 | scroll_method = "2fg", -- "2fg", "edge", "button" | |||
|
|
32 | click_method = "button_areas", -- "button_areas", "clickfinger" | |||
|
|
33 | accel_profile = "adaptive", -- "adaptive", "flat" | |||
|
|
34 | accel_speed = 0.0, | |||
|
|
35 | ||||
|
|
36 | layout = "us", -- "us", "de", "de,us", ... | |||
|
|
37 | -- variant = "nodeadkeys", | |||
|
|
38 | -- options = "grp:alt_shift_toggle", | |||
|
|
39 | } | |||
|
|
40 | ||||
|
|
41 | rules = { | |||
|
|
42 | -- { app_id = "Gimp", floating = true, monitor = -1 }, | |||
|
|
43 | -- { app_id = "firefox", tags = 1 << 8, floating = false, monitor = -1 }, | |||
|
|
44 | } | |||
|
|
45 | ||||
|
|
46 | monitors = { | |||
|
|
47 | -- catch-all rule; name = nil means match anything | |||
|
|
48 | { name = nil, mfact = 0.55, nmaster = 1, scale = 1.0, | |||
|
|
49 | layout = "dwindle", x = -1, y = -1 }, | |||
|
|
50 | ||||
|
|
51 | -- HiDPI laptop example: | |||
|
|
52 | -- { name = "eDP-1", mfact = 0.5, nmaster = 1, scale = 2.0, | |||
|
|
53 | -- layout = "dwindle", x = -1, y = -1 }, | |||
|
|
54 | } | |||
|
|
55 | ||||
|
|
56 | -- autostart: it starts stuff in sequence | |||
|
|
57 | autostart = { | |||
|
|
58 | "dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP", | |||
|
|
59 | "systemctl --user import-environment WAYLAND_DISPLAY XDG_SESSION_TYPE XDG_CURRENT_DESKTOP", | |||
|
|
60 | "waybar", | |||
|
|
61 | } | |||
|
|
62 | ||||
|
|
63 | ||||
|
|
64 | keybinds = { | |||
|
|
65 | { mods = {"logo"}, key = "q", action = "spawn", args = {"alacritty"} }, | |||
|
|
66 | { mods = {"logo"}, key = "space", action = "spawn", args = {"wofi", "--show", "drun"} }, | |||
|
|
67 | { mods = {"logo"}, key = "w", action = "killclient" }, | |||
|
|
68 | { mods = {"logo"}, key = "v", action = "togglefloating" }, | |||
|
|
69 | { mods = {"logo"}, key = "f", action = "togglefullscreen" }, | |||
|
|
70 | { mods = {"logo"}, key = "g", action = "togglegaps" }, | |||
|
|
71 | { mods = {"logo"}, key = "h", action = "focusdir", args = {"left"} }, | |||
|
|
72 | { mods = {"logo"}, key = "j", action = "focusdir", args = {"down"} }, | |||
|
|
73 | { mods = {"logo"}, key = "k", action = "focusdir", args = {"up"} }, | |||
|
|
74 | { mods = {"logo"}, key = "l", action = "focusdir", args = {"right"} }, | |||
|
|
75 | { mods = {"logo", "shift"}, key = "H", action = "swapdir", args = {"left"} }, | |||
|
|
76 | { mods = {"logo", "shift"}, key = "J", action = "swapdir", args = {"down"} }, | |||
|
|
77 | { mods = {"logo", "shift"}, key = "K", action = "swapdir", args = {"up"} }, | |||
|
|
78 | { mods = {"logo", "shift"}, key = "L", action = "swapdir", args = {"right"} }, | |||
|
|
79 | { mods = {"logo"}, key = "Tab", action = "view" }, | |||
|
|
80 | { mods = {"logo"}, key = "0", action = "view", args = {"all"} }, | |||
|
|
81 | { mods = {"logo"}, key = "comma", action = "focusmon", args = {"left"} }, | |||
|
|
82 | { mods = {"logo"}, key = "period", action = "focusmon", args = {"right"} }, | |||
|
|
83 | { mods = {"logo", "shift"}, key = "less", action = "tagmon", args = {"left"} }, | |||
|
|
84 | { mods = {"logo", "shift"}, key = "greater", action = "tagmon", args = {"right"} }, | |||
|
|
85 | { mods = {"logo", "shift"}, key = "e", action = "quit" }, | |||
|
|
86 | } | |||
|
|
87 | ||||
|
|
88 | for i = 1, 9 do | |||
|
|
89 | local key = tostring(i) | |||
|
|
90 | local mask = 1 << (i - 1) | |||
|
|
91 | table.insert(keybinds, { mods = {"logo"}, key = key, action = "view", args = {tostring(mask)} }) | |||
|
|
92 | table.insert(keybinds, { mods = {"logo", "ctrl"}, key = key, action = "toggleview", args = {tostring(mask)} }) | |||
|
|
93 | table.insert(keybinds, { mods = {"logo", "shift"}, key = key, action = "tag", args = {tostring(mask)} }) | |||
|
|
94 | table.insert(keybinds, { mods = {"logo", "ctrl", "shift"}, key = key, action = "toggletag", args = {tostring(mask)} }) | |||
|
|
95 | end | |||
|
|
96 | ||||
|
|
97 | buttons = { | |||
|
|
98 | { mods = {"logo"}, button = "left", action = "moveresize", args = {"move"} }, | |||
|
|
99 | { mods = {"logo"}, button = "middle", action = "togglefloating" }, | |||
|
|
100 | { mods = {"logo"}, button = "right", action = "moveresize", args = {"resize"} }, | |||
|
|
101 | } | |||
| @@ -0,0 +1,61 | |||||
|
|
1 | { | |||
|
|
2 | "nodes": { | |||
|
|
3 | "flake-utils": { | |||
|
|
4 | "inputs": { | |||
|
|
5 | "systems": "systems" | |||
|
|
6 | }, | |||
|
|
7 | "locked": { | |||
|
|
8 | "lastModified": 1731533236, | |||
|
|
9 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", | |||
|
|
10 | "owner": "numtide", | |||
|
|
11 | "repo": "flake-utils", | |||
|
|
12 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", | |||
|
|
13 | "type": "github" | |||
|
|
14 | }, | |||
|
|
15 | "original": { | |||
|
|
16 | "owner": "numtide", | |||
|
|
17 | "repo": "flake-utils", | |||
|
|
18 | "type": "github" | |||
|
|
19 | } | |||
|
|
20 | }, | |||
|
|
21 | "nixpkgs": { | |||
|
|
22 | "locked": { | |||
|
|
23 | "lastModified": 1786106723, | |||
|
|
24 | "narHash": "sha256-zDSUbpoeo/9ZmD2+wXnzxoo1+uhL8vxc0b8yuYMKYq0=", | |||
|
|
25 | "owner": "NixOS", | |||
|
|
26 | "repo": "nixpkgs", | |||
|
|
27 | "rev": "f13ff45afd1bb73e640eaa08a7066dbed07e3238", | |||
|
|
28 | "type": "github" | |||
|
|
29 | }, | |||
|
|
30 | "original": { | |||
|
|
31 | "owner": "NixOS", | |||
|
|
32 | "ref": "nixos-unstable", | |||
|
|
33 | "repo": "nixpkgs", | |||
|
|
34 | "type": "github" | |||
|
|
35 | } | |||
|
|
36 | }, | |||
|
|
37 | "root": { | |||
|
|
38 | "inputs": { | |||
|
|
39 | "flake-utils": "flake-utils", | |||
|
|
40 | "nixpkgs": "nixpkgs" | |||
|
|
41 | } | |||
|
|
42 | }, | |||
|
|
43 | "systems": { | |||
|
|
44 | "locked": { | |||
|
|
45 | "lastModified": 1681028828, | |||
|
|
46 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", | |||
|
|
47 | "owner": "nix-systems", | |||
|
|
48 | "repo": "default", | |||
|
|
49 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", | |||
|
|
50 | "type": "github" | |||
|
|
51 | }, | |||
|
|
52 | "original": { | |||
|
|
53 | "owner": "nix-systems", | |||
|
|
54 | "repo": "default", | |||
|
|
55 | "type": "github" | |||
|
|
56 | } | |||
|
|
57 | } | |||
|
|
58 | }, | |||
|
|
59 | "root": "root", | |||
|
|
60 | "version": 7 | |||
|
|
61 | } | |||
| @@ -0,0 +1,65 | |||||
|
|
1 | { | |||
|
|
2 | description = "swindle: a dwl fork with Lua config, IPC, and ext-workspace support, plus its smsg client"; | |||
|
|
3 | ||||
|
|
4 | inputs = { | |||
|
|
5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable"; | |||
|
|
6 | flake-utils.url = "github:numtide/flake-utils"; | |||
|
|
7 | }; | |||
|
|
8 | ||||
|
|
9 | outputs = { self, nixpkgs, flake-utils }: | |||
|
|
10 | flake-utils.lib.eachDefaultSystem (system: | |||
|
|
11 | let | |||
|
|
12 | pkgs = nixpkgs.legacyPackages.${system}; | |||
|
|
13 | in | |||
|
|
14 | { | |||
|
|
15 | packages.default = pkgs.stdenv.mkDerivation rec { | |||
|
|
16 | pname = "swindle"; | |||
|
|
17 | version = "0.1"; | |||
|
|
18 | ||||
|
|
19 | src = self; | |||
|
|
20 | ||||
|
|
21 | nativeBuildInputs = with pkgs; [ | |||
|
|
22 | pkg-config | |||
|
|
23 | wayland-scanner | |||
|
|
24 | wayland-protocols | |||
|
|
25 | ]; | |||
|
|
26 | ||||
|
|
27 | buildInputs = with pkgs; [ | |||
|
|
28 | wayland | |||
|
|
29 | wlroots_0_20 | |||
|
|
30 | pixman | |||
|
|
31 | libxkbcommon | |||
|
|
32 | libinput | |||
|
|
33 | lua5_4 | |||
|
|
34 | libxcb | |||
|
|
35 | libxcb-wm | |||
|
|
36 | ]; | |||
|
|
37 | ||||
|
|
38 | enableParallelBuilding = true; | |||
|
|
39 | ||||
|
|
40 | postPatch = '' | |||
|
|
41 | substituteInPlace Makefile \ | |||
|
|
42 | --replace '$(DESTDIR)/etc/swindle' '$(DESTDIR)$(PREFIX)/etc/swindle' | |||
|
|
43 | ''; | |||
|
|
44 | ||||
|
|
45 | makeFlags = [ "PREFIX=${placeholder "out"}" ]; | |||
|
|
46 | ||||
|
|
47 | meta = with pkgs.lib; { | |||
|
|
48 | description = "A dwl fork with Lua configuration, IPC support, and ext-workspace-v1"; | |||
|
|
49 | license = with licenses; [ gpl3Only mit isc ]; | |||
|
|
50 | platforms = platforms.linux; | |||
|
|
51 | mainProgram = "swindle"; | |||
|
|
52 | }; | |||
|
|
53 | }; | |||
|
|
54 | ||||
|
|
55 | devShells.default = pkgs.mkShell { | |||
|
|
56 | inputsFrom = [ self.packages.${system}.default ]; | |||
|
|
57 | packages = with pkgs; [ pkg-config gdb ]; | |||
|
|
58 | }; | |||
|
|
59 | ||||
|
|
60 | apps.default = flake-utils.lib.mkApp { | |||
|
|
61 | drv = self.packages.${system}.default; | |||
|
|
62 | }; | |||
|
|
63 | }); | |||
|
|
64 | } | |||
|
|
65 | ||||
| @@ -0,0 +1,447 | |||||
|
|
1 | /* | |||
|
|
2 | * Attempt to consolidate unavoidable suck into one file, away from src/swindle.c. This | |||
|
|
3 | * file is not meant to be pretty. We use a .h file with static inline | |||
|
|
4 | * functions instead of a separate .c module, or function pointers like sway, so | |||
|
|
5 | * that they will simply compile out if the chosen #defines leave them unused. | |||
|
|
6 | */ | |||
|
|
7 | ||||
|
|
8 | /* Leave these functions first; they're used in the others */ | |||
|
|
9 | static inline int | |||
|
|
10 | client_is_x11(Client *c) | |||
|
|
11 | { | |||
|
|
12 | #ifdef XWAYLAND | |||
|
|
13 | return c->type == X11; | |||
|
|
14 | #endif | |||
|
|
15 | return 0; | |||
|
|
16 | } | |||
|
|
17 | ||||
|
|
18 | static inline struct wlr_surface * | |||
|
|
19 | client_surface(Client *c) | |||
|
|
20 | { | |||
|
|
21 | #ifdef XWAYLAND | |||
|
|
22 | if (client_is_x11(c)) | |||
|
|
23 | return c->surface.xwayland->surface; | |||
|
|
24 | #endif | |||
|
|
25 | return c->surface.xdg->surface; | |||
|
|
26 | } | |||
|
|
27 | ||||
|
|
28 | static inline int | |||
|
|
29 | toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) | |||
|
|
30 | { | |||
|
|
31 | struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; | |||
|
|
32 | struct wlr_surface *root_surface; | |||
|
|
33 | struct wlr_layer_surface_v1 *layer_surface; | |||
|
|
34 | Client *c = NULL; | |||
|
|
35 | LayerSurface *l = NULL; | |||
|
|
36 | int type = -1; | |||
|
|
37 | #ifdef XWAYLAND | |||
|
|
38 | struct wlr_xwayland_surface *xsurface; | |||
|
|
39 | #endif | |||
|
|
40 | ||||
|
|
41 | if (!s) | |||
|
|
42 | return -1; | |||
|
|
43 | root_surface = wlr_surface_get_root_surface(s); | |||
|
|
44 | ||||
|
|
45 | #ifdef XWAYLAND | |||
|
|
46 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) { | |||
|
|
47 | c = xsurface->data; | |||
|
|
48 | type = c->type; | |||
|
|
49 | goto end; | |||
|
|
50 | } | |||
|
|
51 | #endif | |||
|
|
52 | ||||
|
|
53 | if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) { | |||
|
|
54 | l = layer_surface->data; | |||
|
|
55 | type = LayerShell; | |||
|
|
56 | goto end; | |||
|
|
57 | } | |||
|
|
58 | ||||
|
|
59 | xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); | |||
|
|
60 | while (xdg_surface) { | |||
|
|
61 | tmp_xdg_surface = NULL; | |||
|
|
62 | switch (xdg_surface->role) { | |||
|
|
63 | case WLR_XDG_SURFACE_ROLE_POPUP: | |||
|
|
64 | if (!xdg_surface->popup || !xdg_surface->popup->parent) | |||
|
|
65 | return -1; | |||
|
|
66 | ||||
|
|
67 | tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); | |||
|
|
68 | ||||
|
|
69 | if (!tmp_xdg_surface) | |||
|
|
70 | return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); | |||
|
|
71 | ||||
|
|
72 | xdg_surface = tmp_xdg_surface; | |||
|
|
73 | break; | |||
|
|
74 | case WLR_XDG_SURFACE_ROLE_TOPLEVEL: | |||
|
|
75 | c = xdg_surface->data; | |||
|
|
76 | type = c->type; | |||
|
|
77 | goto end; | |||
|
|
78 | case WLR_XDG_SURFACE_ROLE_NONE: | |||
|
|
79 | return -1; | |||
|
|
80 | } | |||
|
|
81 | } | |||
|
|
82 | ||||
|
|
83 | end: | |||
|
|
84 | if (pl) | |||
|
|
85 | *pl = l; | |||
|
|
86 | if (pc) | |||
|
|
87 | *pc = c; | |||
|
|
88 | return type; | |||
|
|
89 | } | |||
|
|
90 | ||||
|
|
91 | /* The others */ | |||
|
|
92 | static inline void | |||
|
|
93 | client_activate_surface(struct wlr_surface *s, int activated) | |||
|
|
94 | { | |||
|
|
95 | struct wlr_xdg_toplevel *toplevel; | |||
|
|
96 | #ifdef XWAYLAND | |||
|
|
97 | struct wlr_xwayland_surface *xsurface; | |||
|
|
98 | if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { | |||
|
|
99 | wlr_xwayland_surface_activate(xsurface, activated); | |||
|
|
100 | return; | |||
|
|
101 | } | |||
|
|
102 | #endif | |||
|
|
103 | if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) | |||
|
|
104 | wlr_xdg_toplevel_set_activated(toplevel, activated); | |||
|
|
105 | } | |||
|
|
106 | ||||
|
|
107 | static inline uint32_t | |||
|
|
108 | client_set_bounds(Client *c, int32_t width, int32_t height) | |||
|
|
109 | { | |||
|
|
110 | #ifdef XWAYLAND | |||
|
|
111 | if (client_is_x11(c)) | |||
|
|
112 | return 0; | |||
|
|
113 | #endif | |||
|
|
114 | if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= | |||
|
|
115 | XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION && width >= 0 && height >= 0 | |||
|
|
116 | && (c->bounds.width != width || c->bounds.height != height)) { | |||
|
|
117 | c->bounds.width = width; | |||
|
|
118 | c->bounds.height = height; | |||
|
|
119 | return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); | |||
|
|
120 | } | |||
|
|
121 | return 0; | |||
|
|
122 | } | |||
|
|
123 | ||||
|
|
124 | static inline const char * | |||
|
|
125 | client_get_appid(Client *c) | |||
|
|
126 | { | |||
|
|
127 | #ifdef XWAYLAND | |||
|
|
128 | if (client_is_x11(c)) | |||
|
|
129 | return c->surface.xwayland->class ? c->surface.xwayland->class : "broken"; | |||
|
|
130 | #endif | |||
|
|
131 | return c->surface.xdg->toplevel->app_id ? c->surface.xdg->toplevel->app_id : "broken"; | |||
|
|
132 | } | |||
|
|
133 | ||||
|
|
134 | static inline void | |||
|
|
135 | client_get_clip(Client *c, struct wlr_box *clip) | |||
|
|
136 | { | |||
|
|
137 | *clip = (struct wlr_box){ | |||
|
|
138 | .x = 0, | |||
|
|
139 | .y = 0, | |||
|
|
140 | .width = c->geom.width - c->bw, | |||
|
|
141 | .height = c->geom.height - c->bw, | |||
|
|
142 | }; | |||
|
|
143 | ||||
|
|
144 | #ifdef XWAYLAND | |||
|
|
145 | if (client_is_x11(c)) | |||
|
|
146 | return; | |||
|
|
147 | #endif | |||
|
|
148 | ||||
|
|
149 | clip->x = c->surface.xdg->geometry.x; | |||
|
|
150 | clip->y = c->surface.xdg->geometry.y; | |||
|
|
151 | } | |||
|
|
152 | ||||
|
|
153 | static inline void | |||
|
|
154 | client_get_geometry(Client *c, struct wlr_box *geom) | |||
|
|
155 | { | |||
|
|
156 | #ifdef XWAYLAND | |||
|
|
157 | if (client_is_x11(c)) { | |||
|
|
158 | geom->x = c->surface.xwayland->x; | |||
|
|
159 | geom->y = c->surface.xwayland->y; | |||
|
|
160 | geom->width = c->surface.xwayland->width; | |||
|
|
161 | geom->height = c->surface.xwayland->height; | |||
|
|
162 | return; | |||
|
|
163 | } | |||
|
|
164 | #endif | |||
|
|
165 | *geom = c->surface.xdg->geometry; | |||
|
|
166 | } | |||
|
|
167 | ||||
|
|
168 | static inline Client * | |||
|
|
169 | client_get_parent(Client *c) | |||
|
|
170 | { | |||
|
|
171 | Client *p = NULL; | |||
|
|
172 | #ifdef XWAYLAND | |||
|
|
173 | if (client_is_x11(c)) { | |||
|
|
174 | if (c->surface.xwayland->parent) | |||
|
|
175 | toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL); | |||
|
|
176 | return p; | |||
|
|
177 | } | |||
|
|
178 | #endif | |||
|
|
179 | if (c->surface.xdg->toplevel->parent) | |||
|
|
180 | toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); | |||
|
|
181 | return p; | |||
|
|
182 | } | |||
|
|
183 | ||||
|
|
184 | static inline int | |||
|
|
185 | client_has_children(Client *c) | |||
|
|
186 | { | |||
|
|
187 | #ifdef XWAYLAND | |||
|
|
188 | if (client_is_x11(c)) | |||
|
|
189 | return !wl_list_empty(&c->surface.xwayland->children); | |||
|
|
190 | #endif | |||
|
|
191 | /* surface.xdg->link is never empty because it always contains at least the | |||
|
|
192 | * surface itself. */ | |||
|
|
193 | return wl_list_length(&c->surface.xdg->link) > 1; | |||
|
|
194 | } | |||
|
|
195 | ||||
|
|
196 | static inline const char * | |||
|
|
197 | client_get_title(Client *c) | |||
|
|
198 | { | |||
|
|
199 | #ifdef XWAYLAND | |||
|
|
200 | if (client_is_x11(c)) | |||
|
|
201 | return c->surface.xwayland->title ? c->surface.xwayland->title : "broken"; | |||
|
|
202 | #endif | |||
|
|
203 | return c->surface.xdg->toplevel->title ? c->surface.xdg->toplevel->title : "broken"; | |||
|
|
204 | } | |||
|
|
205 | ||||
|
|
206 | static inline int | |||
|
|
207 | client_is_float_type(Client *c) | |||
|
|
208 | { | |||
|
|
209 | struct wlr_xdg_toplevel *toplevel; | |||
|
|
210 | struct wlr_xdg_toplevel_state state; | |||
|
|
211 | ||||
|
|
212 | #ifdef XWAYLAND | |||
|
|
213 | if (client_is_x11(c)) { | |||
|
|
214 | struct wlr_xwayland_surface *surface = c->surface.xwayland; | |||
|
|
215 | xcb_size_hints_t *size_hints = surface->size_hints; | |||
|
|
216 | if (surface->modal) | |||
|
|
217 | return 1; | |||
|
|
218 | ||||
|
|
219 | if (wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_DIALOG) | |||
|
|
220 | || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_SPLASH) | |||
|
|
221 | || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_TOOLBAR) | |||
|
|
222 | || wlr_xwayland_surface_has_window_type(surface, WLR_XWAYLAND_NET_WM_WINDOW_TYPE_UTILITY)) { | |||
|
|
223 | return 1; | |||
|
|
224 | } | |||
|
|
225 | ||||
|
|
226 | return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 | |||
|
|
227 | && (size_hints->max_width == size_hints->min_width | |||
|
|
228 | || size_hints->max_height == size_hints->min_height); | |||
|
|
229 | } | |||
|
|
230 | #endif | |||
|
|
231 | ||||
|
|
232 | toplevel = c->surface.xdg->toplevel; | |||
|
|
233 | state = toplevel->current; | |||
|
|
234 | return toplevel->parent || (state.min_width != 0 && state.min_height != 0 | |||
|
|
235 | && (state.min_width == state.max_width | |||
|
|
236 | || state.min_height == state.max_height)); | |||
|
|
237 | } | |||
|
|
238 | ||||
|
|
239 | static inline int | |||
|
|
240 | client_is_rendered_on_mon(Client *c, Monitor *m) | |||
|
|
241 | { | |||
|
|
242 | /* This is needed for when you don't want to check formal assignment, | |||
|
|
243 | * but rather actual displaying of the pixels. | |||
|
|
244 | * Usually VISIBLEON suffices and is also faster. */ | |||
|
|
245 | struct wlr_surface_output *s; | |||
|
|
246 | int unused_lx, unused_ly; | |||
|
|
247 | if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) | |||
|
|
248 | return 0; | |||
|
|
249 | wl_list_for_each(s, &client_surface(c)->current_outputs, link) | |||
|
|
250 | if (s->output == m->wlr_output) | |||
|
|
251 | return 1; | |||
|
|
252 | return 0; | |||
|
|
253 | } | |||
|
|
254 | ||||
|
|
255 | static inline int | |||
|
|
256 | client_is_stopped(Client *c) | |||
|
|
257 | { | |||
|
|
258 | int pid; | |||
|
|
259 | siginfo_t in = {0}; | |||
|
|
260 | #ifdef XWAYLAND | |||
|
|
261 | if (client_is_x11(c)) | |||
|
|
262 | return 0; | |||
|
|
263 | #endif | |||
|
|
264 | ||||
|
|
265 | wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); | |||
|
|
266 | if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) { | |||
|
|
267 | /* This process is not our child process, while is very unlikely that | |||
|
|
268 | * it is stopped, in order to do not skip frames, assume that it is. */ | |||
|
|
269 | if (errno == ECHILD) | |||
|
|
270 | return 1; | |||
|
|
271 | } else if (in.si_pid) { | |||
|
|
272 | if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) | |||
|
|
273 | return 1; | |||
|
|
274 | if (in.si_code == CLD_CONTINUED) | |||
|
|
275 | return 0; | |||
|
|
276 | } | |||
|
|
277 | ||||
|
|
278 | return 0; | |||
|
|
279 | } | |||
|
|
280 | ||||
|
|
281 | static inline int | |||
|
|
282 | client_is_unmanaged(Client *c) | |||
|
|
283 | { | |||
|
|
284 | #ifdef XWAYLAND | |||
|
|
285 | if (client_is_x11(c)) | |||
|
|
286 | return c->surface.xwayland->override_redirect; | |||
|
|
287 | #endif | |||
|
|
288 | return 0; | |||
|
|
289 | } | |||
|
|
290 | ||||
|
|
291 | static inline void | |||
|
|
292 | client_notify_enter(struct wlr_surface *s, struct wlr_keyboard *kb) | |||
|
|
293 | { | |||
|
|
294 | if (kb) | |||
|
|
295 | wlr_seat_keyboard_notify_enter(seat, s, kb->keycodes, | |||
|
|
296 | kb->num_keycodes, &kb->modifiers); | |||
|
|
297 | else | |||
|
|
298 | wlr_seat_keyboard_notify_enter(seat, s, NULL, 0, NULL); | |||
|
|
299 | } | |||
|
|
300 | ||||
|
|
301 | static inline void | |||
|
|
302 | client_send_close(Client *c) | |||
|
|
303 | { | |||
|
|
304 | #ifdef XWAYLAND | |||
|
|
305 | if (client_is_x11(c)) { | |||
|
|
306 | wlr_xwayland_surface_close(c->surface.xwayland); | |||
|
|
307 | return; | |||
|
|
308 | } | |||
|
|
309 | #endif | |||
|
|
310 | wlr_xdg_toplevel_send_close(c->surface.xdg->toplevel); | |||
|
|
311 | } | |||
|
|
312 | ||||
|
|
313 | static inline void | |||
|
|
314 | client_set_border_color(Client *c, const float inner[static 4], const float outer[static 4]) | |||
|
|
315 | { | |||
|
|
316 | int i; | |||
|
|
317 | for (i = 0; i < 4; i++) { | |||
|
|
318 | wlr_scene_rect_set_color(c->inner_border[i], inner); | |||
|
|
319 | wlr_scene_rect_set_color(c->outer_border[i], outer); | |||
|
|
320 | } | |||
|
|
321 | } | |||
|
|
322 | ||||
|
|
323 | ||||
|
|
324 | /* Opacity black magic I cooked up half asleep. Idk how this works so uhm | |||
|
|
325 | * baii */ | |||
|
|
326 | ||||
|
|
327 | static inline void | |||
|
|
328 | client_node_apply_opacity(struct wlr_scene_node *node, float opacity) | |||
|
|
329 | { | |||
|
|
330 | struct wlr_scene_tree *tree; | |||
|
|
331 | struct wlr_scene_node *child; | |||
|
|
332 | ||||
|
|
333 | if (!node->enabled && node->type != WLR_SCENE_NODE_TREE) | |||
|
|
334 | return; | |||
|
|
335 | ||||
|
|
336 | switch (node->type) { | |||
|
|
337 | case WLR_SCENE_NODE_BUFFER: | |||
|
|
338 | wlr_scene_buffer_set_opacity(wlr_scene_buffer_from_node(node), opacity); | |||
|
|
339 | break; | |||
|
|
340 | case WLR_SCENE_NODE_TREE: | |||
|
|
341 | tree = wlr_scene_tree_from_node(node); | |||
|
|
342 | wl_list_for_each(child, &tree->children, link) | |||
|
|
343 | client_node_apply_opacity(child, opacity); | |||
|
|
344 | break; | |||
|
|
345 | case WLR_SCENE_NODE_RECT: | |||
|
|
346 | default: | |||
|
|
347 | break; | |||
|
|
348 | } | |||
|
|
349 | } | |||
|
|
350 | ||||
|
|
351 | static inline void | |||
|
|
352 | client_apply_opacity(Client *c) | |||
|
|
353 | { | |||
|
|
354 | client_node_apply_opacity(&c->scene_surface->node, c->isfullscreen ? 1.0f : c->opacity); | |||
|
|
355 | } | |||
|
|
356 | ||||
|
|
357 | static inline void | |||
|
|
358 | client_set_opacity(Client *c, float opacity) | |||
|
|
359 | { | |||
|
|
360 | c->opacity = opacity; | |||
|
|
361 | client_apply_opacity(c); | |||
|
|
362 | } | |||
|
|
363 | ||||
|
|
364 | static inline void | |||
|
|
365 | client_set_fullscreen(Client *c, int fullscreen) | |||
|
|
366 | { | |||
|
|
367 | #ifdef XWAYLAND | |||
|
|
368 | if (client_is_x11(c)) { | |||
|
|
369 | wlr_xwayland_surface_set_fullscreen(c->surface.xwayland, fullscreen); | |||
|
|
370 | return; | |||
|
|
371 | } | |||
|
|
372 | #endif | |||
|
|
373 | wlr_xdg_toplevel_set_fullscreen(c->surface.xdg->toplevel, fullscreen); | |||
|
|
374 | } | |||
|
|
375 | ||||
|
|
376 | static inline void | |||
|
|
377 | client_set_scale(struct wlr_surface *s, float scale) | |||
|
|
378 | { | |||
|
|
379 | wlr_fractional_scale_v1_notify_scale(s, scale); | |||
|
|
380 | wlr_surface_set_preferred_buffer_scale(s, (int32_t)ceilf(scale)); | |||
|
|
381 | } | |||
|
|
382 | ||||
|
|
383 | static inline uint32_t | |||
|
|
384 | client_set_size(Client *c, uint32_t width, uint32_t height) | |||
|
|
385 | { | |||
|
|
386 | #ifdef XWAYLAND | |||
|
|
387 | if (client_is_x11(c)) { | |||
|
|
388 | wlr_xwayland_surface_configure(c->surface.xwayland, | |||
|
|
389 | c->geom.x + c->bw, c->geom.y + c->bw, width, height); | |||
|
|
390 | return 0; | |||
|
|
391 | } | |||
|
|
392 | #endif | |||
|
|
393 | if ((int32_t)width == c->surface.xdg->toplevel->current.width | |||
|
|
394 | && (int32_t)height == c->surface.xdg->toplevel->current.height) | |||
|
|
395 | return 0; | |||
|
|
396 | return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, (int32_t)width, (int32_t)height); | |||
|
|
397 | } | |||
|
|
398 | ||||
|
|
399 | static inline void | |||
|
|
400 | client_set_tiled(Client *c, uint32_t edges) | |||
|
|
401 | { | |||
|
|
402 | #ifdef XWAYLAND | |||
|
|
403 | if (client_is_x11(c)) { | |||
|
|
404 | wlr_xwayland_surface_set_maximized(c->surface.xwayland, | |||
|
|
405 | edges != WLR_EDGE_NONE, edges != WLR_EDGE_NONE); | |||
|
|
406 | return; | |||
|
|
407 | } | |||
|
|
408 | #endif | |||
|
|
409 | if (wl_resource_get_version(c->surface.xdg->toplevel->resource) | |||
|
|
410 | >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { | |||
|
|
411 | wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); | |||
|
|
412 | } else { | |||
|
|
413 | wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE); | |||
|
|
414 | } | |||
|
|
415 | } | |||
|
|
416 | ||||
|
|
417 | static inline void | |||
|
|
418 | client_set_suspended(Client *c, int suspended) | |||
|
|
419 | { | |||
|
|
420 | #ifdef XWAYLAND | |||
|
|
421 | if (client_is_x11(c)) | |||
|
|
422 | return; | |||
|
|
423 | #endif | |||
|
|
424 | ||||
|
|
425 | wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); | |||
|
|
426 | } | |||
|
|
427 | ||||
|
|
428 | static inline int | |||
|
|
429 | client_wants_focus(Client *c) | |||
|
|
430 | { | |||
|
|
431 | #ifdef XWAYLAND | |||
|
|
432 | return client_is_unmanaged(c) | |||
|
|
433 | && wlr_xwayland_surface_override_redirect_wants_focus(c->surface.xwayland) | |||
|
|
434 | && wlr_xwayland_surface_icccm_input_model(c->surface.xwayland) != WLR_ICCCM_INPUT_MODEL_NONE; | |||
|
|
435 | #endif | |||
|
|
436 | return 0; | |||
|
|
437 | } | |||
|
|
438 | ||||
|
|
439 | static inline int | |||
|
|
440 | client_wants_fullscreen(Client *c) | |||
|
|
441 | { | |||
|
|
442 | #ifdef XWAYLAND | |||
|
|
443 | if (client_is_x11(c)) | |||
|
|
444 | return c->surface.xwayland->fullscreen; | |||
|
|
445 | #endif | |||
|
|
446 | return c->surface.xdg->toplevel->requested.fullscreen; | |||
|
|
447 | } | |||
| @@ -0,0 +1,284 | |||||
|
|
1 | #include "dwl-ipc-unstable-v2-protocol.h" | |||
|
|
2 | ||||
|
|
3 | /* Forward declarations for dwl.c static functions */ | |||
|
|
4 | ||||
|
|
5 | static void arrange(Monitor *m); | |||
|
|
6 | static void focusclient(Client *c, int lift); | |||
|
|
7 | static Client *focustop(Monitor *m); | |||
|
|
8 | static void printstatus(void); | |||
|
|
9 | ||||
|
|
10 | /* globals */ | |||
|
|
11 | ||||
|
|
12 | static struct wl_global *ipc_global = NULL; | |||
|
|
13 | /* All bound manager objects */ | |||
|
|
14 | static struct wl_list ipc_managers; /* IpcManager.link */ | |||
|
|
15 | ||||
|
|
16 | typedef struct { | |||
|
|
17 | struct wl_resource *resource; | |||
|
|
18 | struct wl_list link; /* ipc_managers */ | |||
|
|
19 | struct wl_list outputs; /* IpcOutput.link */ | |||
|
|
20 | } IpcManager; | |||
|
|
21 | ||||
|
|
22 | typedef struct { | |||
|
|
23 | struct wl_resource *resource; | |||
|
|
24 | Monitor *mon; | |||
|
|
25 | struct wl_list link; /* IpcManager.outputs */ | |||
|
|
26 | } IpcOutput; | |||
|
|
27 | ||||
|
|
28 | /* helpers */ | |||
|
|
29 | ||||
|
|
30 | static IpcOutput * | |||
|
|
31 | ipc_output_for_mon(IpcManager *mgr, Monitor *m) | |||
|
|
32 | { | |||
|
|
33 | IpcOutput *o; | |||
|
|
34 | wl_list_for_each(o, &mgr->outputs, link) | |||
|
|
35 | if (o->mon == m) | |||
|
|
36 | return o; | |||
|
|
37 | return NULL; | |||
|
|
38 | } | |||
|
|
39 | ||||
|
|
40 | /* Send the full state of monitor m to one IpcOutput resource */ | |||
|
|
41 | static void | |||
|
|
42 | ipc_send_output_state(IpcOutput *ipc_out) | |||
|
|
43 | { | |||
|
|
44 | Monitor *m = ipc_out->mon; | |||
|
|
45 | struct wl_resource *res = ipc_out->resource; | |||
|
|
46 | Client *c; | |||
|
|
47 | uint32_t occ = 0, urg = 0, sel_tags = 0; | |||
|
|
48 | int tag, focused; | |||
|
|
49 | ||||
|
|
50 | /* Compute occupancy/urgency (same logic as printstatus, I think) */ | |||
|
|
51 | wl_list_for_each(c, &clients, link) { | |||
|
|
52 | if (c->mon != m) | |||
|
|
53 | continue; | |||
|
|
54 | occ |= c->tags; | |||
|
|
55 | if (c->isurgent) | |||
|
|
56 | urg |= c->tags; | |||
|
|
57 | } | |||
|
|
58 | ||||
|
|
59 | c = focustop(m); | |||
|
|
60 | sel_tags = c ? c->tags : 0; | |||
|
|
61 | ||||
|
|
62 | /* active */ | |||
|
|
63 | zdwl_ipc_output_v2_send_active(res, m == selmon ? 1 : 0); | |||
|
|
64 | ||||
|
|
65 | /* per-tag state */ | |||
|
|
66 | for (tag = 0; tag < TAGCOUNT; tag++) { | |||
|
|
67 | uint32_t mask = 1u << tag; | |||
|
|
68 | uint32_t state = ZDWL_IPC_OUTPUT_V2_TAG_STATE_NONE; | |||
|
|
69 | uint32_t clients_on_tag = 0; | |||
|
|
70 | ||||
|
|
71 | if (m->tagset[m->seltags] & mask) | |||
|
|
72 | state = ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; | |||
|
|
73 | if (urg & mask) | |||
|
|
74 | state = ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; | |||
|
|
75 | if (occ & mask) | |||
|
|
76 | clients_on_tag = 1; /* report β₯1, not exact count */ | |||
|
|
77 | ||||
|
|
78 | focused = (sel_tags & mask) ? 1 : 0; | |||
|
|
79 | zdwl_ipc_output_v2_send_tag(res, tag, state, clients_on_tag, focused); | |||
|
|
80 | } | |||
|
|
81 | ||||
|
|
82 | /* layout index */ | |||
|
|
83 | { | |||
|
|
84 | uint32_t i; | |||
|
|
85 | for (i = 0; i < LENGTH(layouts); i++) | |||
|
|
86 | if (&layouts[i] == m->lt[m->sellt]) | |||
|
|
87 | break; | |||
|
|
88 | zdwl_ipc_output_v2_send_layout(res, i); | |||
|
|
89 | } | |||
|
|
90 | ||||
|
|
91 | /* layout symbol */ | |||
|
|
92 | zdwl_ipc_output_v2_send_layout_symbol(res, m->ltsymbol); | |||
|
|
93 | ||||
|
|
94 | /* title/appid/fullscreen/floating */ | |||
|
|
95 | if (c) { | |||
|
|
96 | zdwl_ipc_output_v2_send_title(res, client_get_title(c)); | |||
|
|
97 | zdwl_ipc_output_v2_send_appid(res, client_get_appid(c)); | |||
|
|
98 | if (wl_resource_get_version(res) >= 2) { | |||
|
|
99 | zdwl_ipc_output_v2_send_fullscreen(res, c->isfullscreen ? 1 : 0); | |||
|
|
100 | zdwl_ipc_output_v2_send_floating(res, c->isfloating ? 1 : 0); | |||
|
|
101 | } | |||
|
|
102 | } else { | |||
|
|
103 | zdwl_ipc_output_v2_send_title(res, ""); | |||
|
|
104 | zdwl_ipc_output_v2_send_appid(res, ""); | |||
|
|
105 | if (wl_resource_get_version(res) >= 2) { | |||
|
|
106 | zdwl_ipc_output_v2_send_fullscreen(res, 0); | |||
|
|
107 | zdwl_ipc_output_v2_send_floating(res, 0); | |||
|
|
108 | } | |||
|
|
109 | } | |||
|
|
110 | ||||
|
|
111 | /* frame β signals end of this batch of events */ | |||
|
|
112 | zdwl_ipc_output_v2_send_frame(res); | |||
|
|
113 | } | |||
|
|
114 | ||||
|
|
115 | /* Called instead of (or alongside) the old printstatus() */ | |||
|
|
116 | static void | |||
|
|
117 | ipc_printstatus(void) | |||
|
|
118 | { | |||
|
|
119 | IpcManager *mgr; | |||
|
|
120 | Monitor *m; | |||
|
|
121 | ||||
|
|
122 | wl_list_for_each(mgr, &ipc_managers, link) { | |||
|
|
123 | wl_list_for_each(m, &mons, link) { | |||
|
|
124 | IpcOutput *out = ipc_output_for_mon(mgr, m); | |||
|
|
125 | if (out) | |||
|
|
126 | ipc_send_output_state(out); | |||
|
|
127 | } | |||
|
|
128 | } | |||
|
|
129 | } | |||
|
|
130 | ||||
|
|
131 | /* zdwl_ipc_output_v2 requests */ | |||
|
|
132 | ||||
|
|
133 | static void | |||
|
|
134 | ipc_output_handle_release(struct wl_client *client, struct wl_resource *resource) | |||
|
|
135 | { | |||
|
|
136 | wl_resource_destroy(resource); | |||
|
|
137 | } | |||
|
|
138 | ||||
|
|
139 | static void | |||
|
|
140 | ipc_output_handle_set_tags(struct wl_client *client, struct wl_resource *resource, | |||
|
|
141 | uint32_t tagmask, uint32_t toggle_tagset) | |||
|
|
142 | { | |||
|
|
143 | IpcOutput *out = wl_resource_get_user_data(resource); | |||
|
|
144 | Monitor *m = out->mon; | |||
|
|
145 | if (!m) return; | |||
|
|
146 | ||||
|
|
147 | if (toggle_tagset) | |||
|
|
148 | m->seltags ^= 1; | |||
|
|
149 | m->tagset[m->seltags] = tagmask & TAGMASK; | |||
|
|
150 | focusclient(focustop(m), 1); | |||
|
|
151 | arrange(m); | |||
|
|
152 | printstatus(); | |||
|
|
153 | } | |||
|
|
154 | ||||
|
|
155 | static void | |||
|
|
156 | ipc_output_handle_set_client_tags(struct wl_client *client, | |||
|
|
157 | struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) | |||
|
|
158 | { | |||
|
|
159 | IpcOutput *out = wl_resource_get_user_data(resource); | |||
|
|
160 | Monitor *m = out->mon; | |||
|
|
161 | Client *sel; | |||
|
|
162 | uint32_t newtags; | |||
|
|
163 | if (!m) return; | |||
|
|
164 | ||||
|
|
165 | sel = focustop(m); | |||
|
|
166 | if (!sel) return; | |||
|
|
167 | ||||
|
|
168 | newtags = (sel->tags & and_tags) ^ xor_tags; | |||
|
|
169 | if (!newtags) return; | |||
|
|
170 | sel->tags = newtags; | |||
|
|
171 | focusclient(focustop(m), 1); | |||
|
|
172 | arrange(m); | |||
|
|
173 | printstatus(); | |||
|
|
174 | } | |||
|
|
175 | ||||
|
|
176 | static void | |||
|
|
177 | ipc_output_handle_set_layout(struct wl_client *client, | |||
|
|
178 | struct wl_resource *resource, uint32_t idx) | |||
|
|
179 | { | |||
|
|
180 | IpcOutput *out = wl_resource_get_user_data(resource); | |||
|
|
181 | Monitor *m = out->mon; | |||
|
|
182 | if (!m || idx >= LENGTH(layouts)) return; | |||
|
|
183 | ||||
|
|
184 | m->lt[m->sellt] = &layouts[idx]; | |||
|
|
185 | strncpy(m->ltsymbol, layouts[idx].symbol, LENGTH(m->ltsymbol)); | |||
|
|
186 | arrange(m); | |||
|
|
187 | printstatus(); | |||
|
|
188 | } | |||
|
|
189 | ||||
|
|
190 | static const struct zdwl_ipc_output_v2_interface ipc_output_impl = { | |||
|
|
191 | .release = ipc_output_handle_release, | |||
|
|
192 | .set_tags = ipc_output_handle_set_tags, | |||
|
|
193 | .set_client_tags = ipc_output_handle_set_client_tags, | |||
|
|
194 | .set_layout = ipc_output_handle_set_layout, | |||
|
|
195 | }; | |||
|
|
196 | ||||
|
|
197 | static void | |||
|
|
198 | ipc_output_destroy(struct wl_resource *resource) | |||
|
|
199 | { | |||
|
|
200 | IpcOutput *out = wl_resource_get_user_data(resource); | |||
|
|
201 | wl_list_remove(&out->link); | |||
|
|
202 | free(out); | |||
|
|
203 | } | |||
|
|
204 | ||||
|
|
205 | /* zdwl_ipc_manager_v2 requests */ | |||
|
|
206 | ||||
|
|
207 | static void | |||
|
|
208 | ipc_manager_handle_release(struct wl_client *client, struct wl_resource *resource) | |||
|
|
209 | { | |||
|
|
210 | wl_resource_destroy(resource); | |||
|
|
211 | } | |||
|
|
212 | ||||
|
|
213 | static void | |||
|
|
214 | ipc_manager_handle_get_output(struct wl_client *client, struct wl_resource *resource, | |||
|
|
215 | uint32_t id, struct wl_resource *output_resource) | |||
|
|
216 | { | |||
|
|
217 | IpcManager *mgr = wl_resource_get_user_data(resource); | |||
|
|
218 | struct wlr_output *wlr_out = wlr_output_from_resource(output_resource); | |||
|
|
219 | Monitor *m = (wlr_out && wlr_out->data) ? wlr_out->data : NULL; | |||
|
|
220 | ||||
|
|
221 | IpcOutput *out = ecalloc(1, sizeof(*out)); | |||
|
|
222 | out->mon = m; | |||
|
|
223 | out->resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, | |||
|
|
224 | wl_resource_get_version(resource), id); | |||
|
|
225 | if (!out->resource) { | |||
|
|
226 | free(out); | |||
|
|
227 | wl_client_post_no_memory(client); | |||
|
|
228 | return; | |||
|
|
229 | } | |||
|
|
230 | wl_resource_set_implementation(out->resource, &ipc_output_impl, | |||
|
|
231 | out, ipc_output_destroy); | |||
|
|
232 | wl_list_insert(&mgr->outputs, &out->link); | |||
|
|
233 | ||||
|
|
234 | if (m) | |||
|
|
235 | ipc_send_output_state(out); | |||
|
|
236 | } | |||
|
|
237 | ||||
|
|
238 | static const struct zdwl_ipc_manager_v2_interface ipc_manager_impl = { | |||
|
|
239 | .release = ipc_manager_handle_release, | |||
|
|
240 | .get_output = ipc_manager_handle_get_output, | |||
|
|
241 | }; | |||
|
|
242 | ||||
|
|
243 | static void | |||
|
|
244 | ipc_manager_destroy(struct wl_resource *resource) | |||
|
|
245 | { | |||
|
|
246 | IpcManager *mgr = wl_resource_get_user_data(resource); | |||
|
|
247 | IpcOutput *out, *tmp; | |||
|
|
248 | wl_list_for_each_safe(out, tmp, &mgr->outputs, link) | |||
|
|
249 | wl_resource_destroy(out->resource); | |||
|
|
250 | wl_list_remove(&mgr->link); | |||
|
|
251 | free(mgr); | |||
|
|
252 | } | |||
|
|
253 | ||||
|
|
254 | /* global bind */ | |||
|
|
255 | ||||
|
|
256 | static void | |||
|
|
257 | ipc_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) | |||
|
|
258 | { | |||
|
|
259 | IpcManager *mgr = ecalloc(1, sizeof(*mgr)); | |||
|
|
260 | wl_list_init(&mgr->outputs); | |||
|
|
261 | mgr->resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, | |||
|
|
262 | version, id); | |||
|
|
263 | if (!mgr->resource) { | |||
|
|
264 | free(mgr); | |||
|
|
265 | wl_client_post_no_memory(client); | |||
|
|
266 | return; | |||
|
|
267 | } | |||
|
|
268 | wl_resource_set_implementation(mgr->resource, &ipc_manager_impl, | |||
|
|
269 | mgr, ipc_manager_destroy); | |||
|
|
270 | wl_list_insert(&ipc_managers, &mgr->link); | |||
|
|
271 | ||||
|
|
272 | /* Announce tags and layouts */ | |||
|
|
273 | zdwl_ipc_manager_v2_send_tags(mgr->resource, TAGCOUNT); | |||
|
|
274 | for (uint32_t i = 0; i < LENGTH(layouts); i++) | |||
|
|
275 | zdwl_ipc_manager_v2_send_layout(mgr->resource, layouts[i].symbol); | |||
|
|
276 | } | |||
|
|
277 | ||||
|
|
278 | static void | |||
|
|
279 | ipc_init(void) | |||
|
|
280 | { | |||
|
|
281 | wl_list_init(&ipc_managers); | |||
|
|
282 | ipc_global = wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, | |||
|
|
283 | 2, NULL, ipc_bind); | |||
|
|
284 | } | |||
| @@ -0,0 +1,210 | |||||
|
|
1 | /* My shitty port of Mango's ext-workspace.h for my compositor; | |||
|
|
2 | * thanks DreamMaoMao for doing most of the heavy lifting | |||
|
|
3 | * by actually writing the thing, it didn't work the first | |||
|
|
4 | * few tries and I barely have any idea how this code works anyway. | |||
|
|
5 | * To say that it's the eighth wonder of the world | |||
|
|
6 | * would be an understatement | |||
|
|
7 | */ | |||
|
|
8 | ||||
|
|
9 | #include "wlr_ext_workspace_v1.h" | |||
|
|
10 | ||||
|
|
11 | #define EXT_WORKSPACE_CAPS \ | |||
|
|
12 | (EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_ACTIVATE | \ | |||
|
|
13 | EXT_WORKSPACE_HANDLE_V1_WORKSPACE_CAPABILITIES_DEACTIVATE) | |||
|
|
14 | ||||
|
|
15 | /* types */ | |||
|
|
16 | ||||
|
|
17 | struct ext_workspace { | |||
|
|
18 | struct wl_list link; | |||
|
|
19 | uint32_t tag; | |||
|
|
20 | Monitor *m; | |||
|
|
21 | struct wlr_ext_workspace_handle_v1 *handle; | |||
|
|
22 | }; | |||
|
|
23 | ||||
|
|
24 | /* global */ | |||
|
|
25 | ||||
|
|
26 | static struct wlr_ext_workspace_manager_v1 *ext_workspace_manager; | |||
|
|
27 | static struct wl_list ext_workspaces; | |||
|
|
28 | static struct wl_listener ext_commit_listener; | |||
|
|
29 | static struct wl_listener ext_destroy_listener; | |||
|
|
30 | ||||
|
|
31 | /* helpers */ | |||
|
|
32 | ||||
|
|
33 | ||||
|
|
34 | static int | |||
|
|
35 | get_tag_status(uint32_t tag, Monitor *m) | |||
|
|
36 | { | |||
|
|
37 | Client *c; | |||
|
|
38 | uint32_t mask = 1 << (tag - 1); | |||
|
|
39 | ||||
|
|
40 | wl_list_for_each(c, &clients, link) { | |||
|
|
41 | if (c->mon != m) | |||
|
|
42 | continue; | |||
|
|
43 | if (!(c->tags & mask)) | |||
|
|
44 | continue; | |||
|
|
45 | if (c->isurgent) | |||
|
|
46 | return 2; | |||
|
|
47 | return 1; | |||
|
|
48 | } | |||
|
|
49 | return 0; | |||
|
|
50 | } | |||
|
|
51 | ||||
|
|
52 | static const char * | |||
|
|
53 | tag_name(uint32_t tag) | |||
|
|
54 | { | |||
|
|
55 | static const char *names[] = { | |||
|
|
56 | "1", "2", "3", "4", "5", "6", "7", "8", "9", | |||
|
|
57 | }; | |||
|
|
58 | if (tag >= 1 && tag <= LENGTH(names)) | |||
|
|
59 | return names[tag - 1]; | |||
|
|
60 | return "?"; | |||
|
|
61 | } | |||
|
|
62 | ||||
|
|
63 | /* workspace lifecycle */ | |||
|
|
64 | ||||
|
|
65 | static void | |||
|
|
66 | ext_workspace_destroy(struct ext_workspace *ws) | |||
|
|
67 | { | |||
|
|
68 | wlr_ext_workspace_handle_v1_destroy(ws->handle); | |||
|
|
69 | wl_list_remove(&ws->link); | |||
|
|
70 | free(ws); | |||
|
|
71 | } | |||
|
|
72 | ||||
|
|
73 | static void | |||
|
|
74 | ext_workspace_create(uint32_t tag, Monitor *m) | |||
|
|
75 | { | |||
|
|
76 | const char *name = tag_name(tag); | |||
|
|
77 | ||||
|
|
78 | struct ext_workspace *ws = ecalloc(1, sizeof(*ws)); | |||
|
|
79 | ws->tag = tag; | |||
|
|
80 | ws->m = m; | |||
|
|
81 | ws->handle = wlr_ext_workspace_handle_v1_create( | |||
|
|
82 | ext_workspace_manager, name, EXT_WORKSPACE_CAPS); | |||
|
|
83 | ws->handle->data = ws; | |||
|
|
84 | ||||
|
|
85 | wlr_ext_workspace_handle_v1_set_group(ws->handle, m->ext_group); | |||
|
|
86 | wlr_ext_workspace_handle_v1_set_name(ws->handle, name); | |||
|
|
87 | ||||
|
|
88 | wl_list_insert(ext_workspaces.prev, &ws->link); | |||
|
|
89 | } | |||
|
|
90 | ||||
|
|
91 | /* monitor integration */ | |||
|
|
92 | ||||
|
|
93 | static void | |||
|
|
94 | ext_workspace_createmon(Monitor *m) | |||
|
|
95 | { | |||
|
|
96 | uint32_t i; | |||
|
|
97 | ||||
|
|
98 | m->ext_group = wlr_ext_workspace_group_handle_v1_create( | |||
|
|
99 | ext_workspace_manager, 0); | |||
|
|
100 | wlr_ext_workspace_group_handle_v1_output_enter( | |||
|
|
101 | m->ext_group, m->wlr_output); | |||
|
|
102 | ||||
|
|
103 | for (i = 1; i <= TAGCOUNT; i++) | |||
|
|
104 | ext_workspace_create(i, m); | |||
|
|
105 | } | |||
|
|
106 | ||||
|
|
107 | static void | |||
|
|
108 | ext_workspace_cleanupmon(Monitor *m) | |||
|
|
109 | { | |||
|
|
110 | struct ext_workspace *ws, *tmp; | |||
|
|
111 | wl_list_for_each_safe(ws, tmp, &ext_workspaces, link) | |||
|
|
112 | if (ws->m == m) | |||
|
|
113 | ext_workspace_destroy(ws); | |||
|
|
114 | ||||
|
|
115 | wlr_ext_workspace_group_handle_v1_output_leave( | |||
|
|
116 | m->ext_group, m->wlr_output); | |||
|
|
117 | wlr_ext_workspace_group_handle_v1_destroy(m->ext_group); | |||
|
|
118 | m->ext_group = NULL; | |||
|
|
119 | } | |||
|
|
120 | ||||
|
|
121 | /* status */ | |||
|
|
122 | ||||
|
|
123 | static void | |||
|
|
124 | ext_workspace_printstatus(Monitor *m) | |||
|
|
125 | { | |||
|
|
126 | struct ext_workspace *ws; | |||
|
|
127 | ||||
|
|
128 | wl_list_for_each(ws, &ext_workspaces, link) { | |||
|
|
129 | if (ws->m != m) | |||
|
|
130 | continue; | |||
|
|
131 | ||||
|
|
132 | int status = get_tag_status(ws->tag, m); | |||
|
|
133 | int active = !!(m->tagset[m->seltags] & (1 << (ws->tag - 1)) & TAGMASK); | |||
|
|
134 | ||||
|
|
135 | wlr_ext_workspace_handle_v1_set_urgent(ws->handle, status == 2); | |||
|
|
136 | wlr_ext_workspace_handle_v1_set_hidden(ws->handle, 0); | |||
|
|
137 | wlr_ext_workspace_handle_v1_set_active(ws->handle, active); | |||
|
|
138 | } | |||
|
|
139 | } | |||
|
|
140 | ||||
|
|
141 | /* commit handler */ | |||
|
|
142 | ||||
|
|
143 | static void | |||
|
|
144 | handle_ext_commit(struct wl_listener *listener, void *data) | |||
|
|
145 | { | |||
|
|
146 | struct wlr_ext_workspace_v1_commit_event *event = data; | |||
|
|
147 | struct wlr_ext_workspace_v1_request *req; | |||
|
|
148 | ||||
|
|
149 | wl_list_for_each(req, event->requests, link) { | |||
|
|
150 | struct ext_workspace *ws = NULL; | |||
|
|
151 | struct ext_workspace *w; | |||
|
|
152 | ||||
|
|
153 | switch (req->type) { | |||
|
|
154 | case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: | |||
|
|
155 | if (!req->activate.workspace) | |||
|
|
156 | break; | |||
|
|
157 | wl_list_for_each(w, &ext_workspaces, link) { | |||
|
|
158 | if (w->handle == req->activate.workspace) { | |||
|
|
159 | ws = w; | |||
|
|
160 | break; | |||
|
|
161 | } | |||
|
|
162 | } | |||
|
|
163 | if (!ws) | |||
|
|
164 | break; | |||
|
|
165 | view(&(Arg){.ui = 1 << (ws->tag - 1)}); | |||
|
|
166 | break; | |||
|
|
167 | ||||
|
|
168 | case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: | |||
|
|
169 | if (!req->deactivate.workspace) | |||
|
|
170 | break; | |||
|
|
171 | wl_list_for_each(w, &ext_workspaces, link) { | |||
|
|
172 | if (w->handle == req->deactivate.workspace) { | |||
|
|
173 | ws = w; | |||
|
|
174 | break; | |||
|
|
175 | } | |||
|
|
176 | } | |||
|
|
177 | if (!ws) | |||
|
|
178 | break; | |||
|
|
179 | toggleview(&(Arg){.ui = 1 << (ws->tag - 1)}); | |||
|
|
180 | break; | |||
|
|
181 | ||||
|
|
182 | default: | |||
|
|
183 | break; | |||
|
|
184 | } | |||
|
|
185 | } | |||
|
|
186 | } | |||
|
|
187 | ||||
|
|
188 | /* init */ | |||
|
|
189 | ||||
|
|
190 | static void | |||
|
|
191 | handle_ext_destroy(struct wl_listener *listener, void *data) | |||
|
|
192 | { | |||
|
|
193 | wl_list_remove(&ext_commit_listener.link); | |||
|
|
194 | wl_list_remove(&ext_destroy_listener.link); | |||
|
|
195 | } | |||
|
|
196 | ||||
|
|
197 | static void | |||
|
|
198 | workspaces_init(void) | |||
|
|
199 | { | |||
|
|
200 | ext_workspace_manager = wlr_ext_workspace_manager_v1_create(dpy, 1); | |||
|
|
201 | wl_list_init(&ext_workspaces); | |||
|
|
202 | ||||
|
|
203 | ext_commit_listener.notify = handle_ext_commit; | |||
|
|
204 | wl_signal_add(&ext_workspace_manager->events.commit, | |||
|
|
205 | &ext_commit_listener); | |||
|
|
206 | ||||
|
|
207 | ext_destroy_listener.notify = handle_ext_destroy; | |||
|
|
208 | wl_signal_add(&ext_workspace_manager->events.destroy, | |||
|
|
209 | &ext_destroy_listener); | |||
|
|
210 | } | |||
| @@ -0,0 +1,351 | |||||
|
|
1 | swindle - wayland compositor | |||
|
|
2 | ||||
|
|
3 | Copyright Β© 2026 kantiankant | |||
|
|
4 | ||||
|
|
5 | See also the files LICENSE.dwl, LICENSE.dwm, | |||
|
|
6 | LICENSE.tinywl, LICENSE.sway, LICENSE.wlroots, LICENSE.mangowm. | |||
|
|
7 | ||||
|
|
8 | This program is free software: you can redistribute it and/or modify | |||
|
|
9 | it under the terms of the GNU General Public License as published by | |||
|
|
10 | the Free Software Foundation, version 2 of the License. | |||
|
|
11 | ||||
|
|
12 | ---- | |||
|
|
13 | ||||
|
|
14 | GNU GENERAL PUBLIC LICENSE | |||
|
|
15 | Version 2, June 1991 | |||
|
|
16 | ||||
|
|
17 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., | |||
|
|
18 | <https://fsf.org/> | |||
|
|
19 | Everyone is permitted to copy and distribute verbatim copies | |||
|
|
20 | of this license document, but changing it is not allowed. | |||
|
|
21 | ||||
|
|
22 | Preamble | |||
|
|
23 | ||||
|
|
24 | The licenses for most software are designed to take away your | |||
|
|
25 | freedom to share and change it. By contrast, the GNU General Public | |||
|
|
26 | License is intended to guarantee your freedom to share and change free | |||
|
|
27 | software--to make sure the software is free for all its users. This | |||
|
|
28 | General Public License applies to most of the Free Software | |||
|
|
29 | Foundation's software and to any other program whose authors commit to | |||
|
|
30 | using it. (Some other Free Software Foundation software is covered by | |||
|
|
31 | the GNU Lesser General Public License instead.) You can apply it to | |||
|
|
32 | your programs, too. | |||
|
|
33 | ||||
|
|
34 | When we speak of free software, we are referring to freedom, not | |||
|
|
35 | price. Our General Public Licenses are designed to make sure that you | |||
|
|
36 | have the freedom to distribute copies of free software (and charge for | |||
|
|
37 | this service if you wish), that you receive source code or can get it | |||
|
|
38 | if you want it, that you can change the software or use pieces of it | |||
|
|
39 | in new free programs; and that you know you can do these things. | |||
|
|
40 | ||||
|
|
41 | To protect your rights, we need to make restrictions that forbid | |||
|
|
42 | anyone to deny you these rights or to ask you to surrender the rights. | |||
|
|
43 | These restrictions translate to certain responsibilities for you if you | |||
|
|
44 | distribute copies of the software, or if you modify it. | |||
|
|
45 | ||||
|
|
46 | For example, if you distribute copies of such a program, whether | |||
|
|
47 | gratis or for a fee, you must give the recipients all the rights that | |||
|
|
48 | you have. You must make sure that they, too, receive or can get the | |||
|
|
49 | source code. And you must show them these terms so they know their | |||
|
|
50 | rights. | |||
|
|
51 | ||||
|
|
52 | We protect your rights with two steps: (1) copyright the software, and | |||
|
|
53 | (2) offer you this license which gives you legal permission to copy, | |||
|
|
54 | distribute and/or modify the software. | |||
|
|
55 | ||||
|
|
56 | Also, for each author's protection and ours, we want to make certain | |||
|
|
57 | that everyone understands that there is no warranty for this free | |||
|
|
58 | software. If the software is modified by someone else and passed on, we | |||
|
|
59 | want its recipients to know that what they have is not the original, so | |||
|
|
60 | that any problems introduced by others will not reflect on the original | |||
|
|
61 | authors' reputations. | |||
|
|
62 | ||||
|
|
63 | Finally, any free program is threatened constantly by software | |||
|
|
64 | patents. We wish to avoid the danger that redistributors of a free | |||
|
|
65 | program will individually obtain patent licenses, in effect making the | |||
|
|
66 | program proprietary. To prevent this, we have made it clear that any | |||
|
|
67 | patent must be licensed for everyone's free use or not licensed at all. | |||
|
|
68 | ||||
|
|
69 | The precise terms and conditions for copying, distribution and | |||
|
|
70 | modification follow. | |||
|
|
71 | ||||
|
|
72 | GNU GENERAL PUBLIC LICENSE | |||
|
|
73 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | |||
|
|
74 | ||||
|
|
75 | 0. This License applies to any program or other work which contains | |||
|
|
76 | a notice placed by the copyright holder saying it may be distributed | |||
|
|
77 | under the terms of this General Public License. The "Program", below, | |||
|
|
78 | refers to any such program or work, and a "work based on the Program" | |||
|
|
79 | means either the Program or any derivative work under copyright law: | |||
|
|
80 | that is to say, a work containing the Program or a portion of it, | |||
|
|
81 | either verbatim or with modifications and/or translated into another | |||
|
|
82 | language. (Hereinafter, translation is included without limitation in | |||
|
|
83 | the term "modification".) Each licensee is addressed as "you". | |||
|
|
84 | ||||
|
|
85 | Activities other than copying, distribution and modification are not | |||
|
|
86 | covered by this License; they are outside its scope. The act of | |||
|
|
87 | running the Program is not restricted, and the output from the Program | |||
|
|
88 | is covered only if its contents constitute a work based on the | |||
|
|
89 | Program (independent of having been made by running the Program). | |||
|
|
90 | Whether that is true depends on what the Program does. | |||
|
|
91 | ||||
|
|
92 | 1. You may copy and distribute verbatim copies of the Program's | |||
|
|
93 | source code as you receive it, in any medium, provided that you | |||
|
|
94 | conspicuously and appropriately publish on each copy an appropriate | |||
|
|
95 | copyright notice and disclaimer of warranty; keep intact all the | |||
|
|
96 | notices that refer to this License and to the absence of any warranty; | |||
|
|
97 | and give any other recipients of the Program a copy of this License | |||
|
|
98 | along with the Program. | |||
|
|
99 | ||||
|
|
100 | You may charge a fee for the physical act of transferring a copy, and | |||
|
|
101 | you may at your option offer warranty protection in exchange for a fee. | |||
|
|
102 | ||||
|
|
103 | 2. You may modify your copy or copies of the Program or any portion | |||
|
|
104 | of it, thus forming a work based on the Program, and copy and | |||
|
|
105 | distribute such modifications or work under the terms of Section 1 | |||
|
|
106 | above, provided that you also meet all of these conditions: | |||
|
|
107 | ||||
|
|
108 | a) You must cause the modified files to carry prominent notices | |||
|
|
109 | stating that you changed the files and the date of any change. | |||
|
|
110 | ||||
|
|
111 | b) You must cause any work that you distribute or publish, that in | |||
|
|
112 | whole or in part contains or is derived from the Program or any | |||
|
|
113 | part thereof, to be licensed as a whole at no charge to all third | |||
|
|
114 | parties under the terms of this License. | |||
|
|
115 | ||||
|
|
116 | c) If the modified program normally reads commands interactively | |||
|
|
117 | when run, you must cause it, when started running for such | |||
|
|
118 | interactive use in the most ordinary way, to print or display an | |||
|
|
119 | announcement including an appropriate copyright notice and a | |||
|
|
120 | notice that there is no warranty (or else, saying that you provide | |||
|
|
121 | a warranty) and that users may redistribute the program under | |||
|
|
122 | these conditions, and telling the user how to view a copy of this | |||
|
|
123 | License. (Exception: if the Program itself is interactive but | |||
|
|
124 | does not normally print such an announcement, your work based on | |||
|
|
125 | the Program is not required to print an announcement.) | |||
|
|
126 | ||||
|
|
127 | These requirements apply to the modified work as a whole. If | |||
|
|
128 | identifiable sections of that work are not derived from the Program, | |||
|
|
129 | and can be reasonably considered independent and separate works in | |||
|
|
130 | themselves, then this License, and its terms, do not apply to those | |||
|
|
131 | sections when you distribute them as separate works. But when you | |||
|
|
132 | distribute the same sections as part of a whole which is a work based | |||
|
|
133 | on the Program, the distribution of the whole must be on the terms of | |||
|
|
134 | this License, whose permissions for other licensees extend to the | |||
|
|
135 | entire whole, and thus to each and every part regardless of who wrote it. | |||
|
|
136 | ||||
|
|
137 | Thus, it is not the intent of this section to claim rights or contest | |||
|
|
138 | your rights to work written entirely by you; rather, the intent is to | |||
|
|
139 | exercise the right to control the distribution of derivative or | |||
|
|
140 | collective works based on the Program. | |||
|
|
141 | ||||
|
|
142 | In addition, mere aggregation of another work not based on the Program | |||
|
|
143 | with the Program (or with a work based on the Program) on a volume of | |||
|
|
144 | a storage or distribution medium does not bring the other work under | |||
|
|
145 | the scope of this License. | |||
|
|
146 | ||||
|
|
147 | 3. You may copy and distribute the Program (or a work based on it, | |||
|
|
148 | under Section 2) in object code or executable form under the terms of | |||
|
|
149 | Sections 1 and 2 above provided that you also do one of the following: | |||
|
|
150 | ||||
|
|
151 | a) Accompany it with the complete corresponding machine-readable | |||
|
|
152 | source code, which must be distributed under the terms of Sections | |||
|
|
153 | 1 and 2 above on a medium customarily used for software interchange; or, | |||
|
|
154 | ||||
|
|
155 | b) Accompany it with a written offer, valid for at least three | |||
|
|
156 | years, to give any third party, for a charge no more than your | |||
|
|
157 | cost of physically performing source distribution, a complete | |||
|
|
158 | machine-readable copy of the corresponding source code, to be | |||
|
|
159 | distributed under the terms of Sections 1 and 2 above on a medium | |||
|
|
160 | customarily used for software interchange; or, | |||
|
|
161 | ||||
|
|
162 | c) Accompany it with the information you received as to the offer | |||
|
|
163 | to distribute corresponding source code. (This alternative is | |||
|
|
164 | allowed only for noncommercial distribution and only if you | |||
|
|
165 | received the program in object code or executable form with such | |||
|
|
166 | an offer, in accord with Subsection b above.) | |||
|
|
167 | ||||
|
|
168 | The source code for a work means the preferred form of the work for | |||
|
|
169 | making modifications to it. For an executable work, complete source | |||
|
|
170 | code means all the source code for all modules it contains, plus any | |||
|
|
171 | associated interface definition files, plus the scripts used to | |||
|
|
172 | control compilation and installation of the executable. However, as a | |||
|
|
173 | special exception, the source code distributed need not include | |||
|
|
174 | anything that is normally distributed (in either source or binary | |||
|
|
175 | form) with the major components (compiler, kernel, and so on) of the | |||
|
|
176 | operating system on which the executable runs, unless that component | |||
|
|
177 | itself accompanies the executable. | |||
|
|
178 | ||||
|
|
179 | If distribution of executable or object code is made by offering | |||
|
|
180 | access to copy from a designated place, then offering equivalent | |||
|
|
181 | access to copy the source code from the same place counts as | |||
|
|
182 | distribution of the source code, even though third parties are not | |||
|
|
183 | compelled to copy the source along with the object code. | |||
|
|
184 | ||||
|
|
185 | 4. You may not copy, modify, sublicense, or distribute the Program | |||
|
|
186 | except as expressly provided under this License. Any attempt | |||
|
|
187 | otherwise to copy, modify, sublicense or distribute the Program is | |||
|
|
188 | void, and will automatically terminate your rights under this License. | |||
|
|
189 | However, parties who have received copies, or rights, from you under | |||
|
|
190 | this License will not have their licenses terminated so long as such | |||
|
|
191 | parties remain in full compliance. | |||
|
|
192 | ||||
|
|
193 | 5. You are not required to accept this License, since you have not | |||
|
|
194 | signed it. However, nothing else grants you permission to modify or | |||
|
|
195 | distribute the Program or its derivative works. These actions are | |||
|
|
196 | prohibited by law if you do not accept this License. Therefore, by | |||
|
|
197 | modifying or distributing the Program (or any work based on the | |||
|
|
198 | Program), you indicate your acceptance of this License to do so, and | |||
|
|
199 | all its terms and conditions for copying, distributing or modifying | |||
|
|
200 | the Program or works based on it. | |||
|
|
201 | ||||
|
|
202 | 6. Each time you redistribute the Program (or any work based on the | |||
|
|
203 | Program), the recipient automatically receives a license from the | |||
|
|
204 | original licensor to copy, distribute or modify the Program subject to | |||
|
|
205 | these terms and conditions. You may not impose any further | |||
|
|
206 | restrictions on the recipients' exercise of the rights granted herein. | |||
|
|
207 | You are not responsible for enforcing compliance by third parties to | |||
|
|
208 | this License. | |||
|
|
209 | ||||
|
|
210 | 7. If, as a consequence of a court judgment or allegation of patent | |||
|
|
211 | infringement or for any other reason (not limited to patent issues), | |||
|
|
212 | conditions are imposed on you (whether by court order, agreement or | |||
|
|
213 | otherwise) that contradict the conditions of this License, they do not | |||
|
|
214 | excuse you from the conditions of this License. If you cannot | |||
|
|
215 | distribute so as to satisfy simultaneously your obligations under this | |||
|
|
216 | License and any other pertinent obligations, then as a consequence you | |||
|
|
217 | may not distribute the Program at all. For example, if a patent | |||
|
|
218 | license would not permit royalty-free redistribution of the Program by | |||
|
|
219 | all those who receive copies directly or indirectly through you, then | |||
|
|
220 | the only way you could satisfy both it and this License would be to | |||
|
|
221 | refrain entirely from distribution of the Program. | |||
|
|
222 | ||||
|
|
223 | If any portion of this section is held invalid or unenforceable under | |||
|
|
224 | any particular circumstance, the balance of the section is intended to | |||
|
|
225 | apply and the section as a whole is intended to apply in other | |||
|
|
226 | circumstances. | |||
|
|
227 | ||||
|
|
228 | It is not the purpose of this section to induce you to infringe any | |||
|
|
229 | patents or other property right claims or to contest validity of any | |||
|
|
230 | such claims; this section has the sole purpose of protecting the | |||
|
|
231 | integrity of the free software distribution system, which is | |||
|
|
232 | implemented by public license practices. Many people have made | |||
|
|
233 | generous contributions to the wide range of software distributed | |||
|
|
234 | through that system in reliance on consistent application of that | |||
|
|
235 | system; it is up to the author/donor to decide if he or she is willing | |||
|
|
236 | to distribute software through any other system and a licensee cannot | |||
|
|
237 | impose that choice. | |||
|
|
238 | ||||
|
|
239 | This section is intended to make thoroughly clear what is believed to | |||
|
|
240 | be a consequence of the rest of this License. | |||
|
|
241 | ||||
|
|
242 | 8. If the distribution and/or use of the Program is restricted in | |||
|
|
243 | certain countries either by patents or by copyrighted interfaces, the | |||
|
|
244 | original copyright holder who places the Program under this License | |||
|
|
245 | may add an explicit geographical distribution limitation excluding | |||
|
|
246 | those countries, so that distribution is permitted only in or among | |||
|
|
247 | countries not thus excluded. In such case, this License incorporates | |||
|
|
248 | the limitation as if written in the body of this License. | |||
|
|
249 | ||||
|
|
250 | 9. The Free Software Foundation may publish revised and/or new versions | |||
|
|
251 | of the General Public License from time to time. Such new versions will | |||
|
|
252 | be similar in spirit to the present version, but may differ in detail to | |||
|
|
253 | address new problems or concerns. | |||
|
|
254 | ||||
|
|
255 | Each version is given a distinguishing version number. If the Program | |||
|
|
256 | specifies a version number of this License which applies to it and "any | |||
|
|
257 | later version", you have the option of following the terms and conditions | |||
|
|
258 | either of that version or of any later version published by the Free | |||
|
|
259 | Software Foundation. If the Program does not specify a version number of | |||
|
|
260 | this License, you may choose any version ever published by the Free Software | |||
|
|
261 | Foundation. | |||
|
|
262 | ||||
|
|
263 | 10. If you wish to incorporate parts of the Program into other free | |||
|
|
264 | programs whose distribution conditions are different, write to the author | |||
|
|
265 | to ask for permission. For software which is copyrighted by the Free | |||
|
|
266 | Software Foundation, write to the Free Software Foundation; we sometimes | |||
|
|
267 | make exceptions for this. Our decision will be guided by the two goals | |||
|
|
268 | of preserving the free status of all derivatives of our free software and | |||
|
|
269 | of promoting the sharing and reuse of software generally. | |||
|
|
270 | ||||
|
|
271 | NO WARRANTY | |||
|
|
272 | ||||
|
|
273 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | |||
|
|
274 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN | |||
|
|
275 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES | |||
|
|
276 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED | |||
|
|
277 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |||
|
|
278 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS | |||
|
|
279 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE | |||
|
|
280 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, | |||
|
|
281 | REPAIR OR CORRECTION. | |||
|
|
282 | ||||
|
|
283 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | |||
|
|
284 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR | |||
|
|
285 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, | |||
|
|
286 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING | |||
|
|
287 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED | |||
|
|
288 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY | |||
|
|
289 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | |||
|
|
290 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | |||
|
|
291 | POSSIBILITY OF SUCH DAMAGES. | |||
|
|
292 | ||||
|
|
293 | END OF TERMS AND CONDITIONS | |||
|
|
294 | ||||
|
|
295 | How to Apply These Terms to Your New Programs | |||
|
|
296 | ||||
|
|
297 | If you develop a new program, and you want it to be of the greatest | |||
|
|
298 | possible use to the public, the best way to achieve this is to make it | |||
|
|
299 | free software which everyone can redistribute and change under these terms. | |||
|
|
300 | ||||
|
|
301 | To do so, attach the following notices to the program. It is safest | |||
|
|
302 | to attach them to the start of each source file to most effectively | |||
|
|
303 | convey the exclusion of warranty; and each file should have at least | |||
|
|
304 | the "copyright" line and a pointer to where the full notice is found. | |||
|
|
305 | ||||
|
|
306 | <one line to give the program's name and a brief idea of what it does.> | |||
|
|
307 | Copyright (C) <year> <name of author> | |||
|
|
308 | ||||
|
|
309 | This program is free software; you can redistribute it and/or modify | |||
|
|
310 | it under the terms of the GNU General Public License as published by | |||
|
|
311 | the Free Software Foundation; either version 2 of the License, or | |||
|
|
312 | (at your option) any later version. | |||
|
|
313 | ||||
|
|
314 | This program is distributed in the hope that it will be useful, | |||
|
|
315 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
|
|
316 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
|
|
317 | GNU General Public License for more details. | |||
|
|
318 | ||||
|
|
319 | You should have received a copy of the GNU General Public License along | |||
|
|
320 | with this program; if not, see <https://www.gnu.org/licenses/>. | |||
|
|
321 | ||||
|
|
322 | Also add information on how to contact you by electronic and paper mail. | |||
|
|
323 | ||||
|
|
324 | If the program is interactive, make it output a short notice like this | |||
|
|
325 | when it starts in an interactive mode: | |||
|
|
326 | ||||
|
|
327 | Gnomovision version 69, Copyright (C) year name of author | |||
|
|
328 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | |||
|
|
329 | This is free software, and you are welcome to redistribute it | |||
|
|
330 | under certain conditions; type `show c' for details. | |||
|
|
331 | ||||
|
|
332 | The hypothetical commands `show w' and `show c' should show the appropriate | |||
|
|
333 | parts of the General Public License. Of course, the commands you use may | |||
|
|
334 | be called something other than `show w' and `show c'; they could even be | |||
|
|
335 | mouse-clicks or menu items--whatever suits your program. | |||
|
|
336 | ||||
|
|
337 | You should also get your employer (if you work as a programmer) or your | |||
|
|
338 | school, if any, to sign a "copyright disclaimer" for the program, if | |||
|
|
339 | necessary. Here is a sample; alter the names: | |||
|
|
340 | ||||
|
|
341 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program | |||
|
|
342 | `Gnomovision' (which makes passes at compilers) written by James Hacker. | |||
|
|
343 | ||||
|
|
344 | <signature of Moe Ghoul>, 1 April 1989 | |||
|
|
345 | Moe Ghoul, President of Vice | |||
|
|
346 | ||||
|
|
347 | This General Public License does not permit incorporating your program into | |||
|
|
348 | proprietary programs. If your program is a subroutine library, you may | |||
|
|
349 | consider it more useful to permit linking proprietary applications with the | |||
|
|
350 | library. If this is what you want to do, use the GNU Lesser General | |||
|
|
351 | Public License instead of this License. | |||
| This diff has been collapsed as it changes many lines, (692 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,692 | |||||
|
|
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>. | |||
| @@ -0,0 +1,12 | |||||
|
|
1 | Copyright (C) 2025 by notchoc <[email protected]> | |||
|
|
2 | ||||
|
|
3 | Permission to use, copy, modify, and/or distribute this software for any | |||
|
|
4 | purpose with or without fee is hereby granted. | |||
|
|
5 | ||||
|
|
6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |||
|
|
7 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |||
|
|
8 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |||
|
|
9 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
|
|
10 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |||
|
|
11 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |||
|
|
12 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |||
| @@ -0,0 +1,39 | |||||
|
|
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 <[email protected]> | |||
|
|
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 <[email protected]> | |||
|
|
17 | Β© 2010-2012 Connor Lane Smith <[email protected]> | |||
|
|
18 | Β© 2011 Christoph Lohmann <[email protected]> | |||
|
|
19 | Β© 2015-2016 Quentin Rameau <[email protected]> | |||
|
|
20 | Β© 2015-2016 Eric Pruitt <[email protected]> | |||
|
|
21 | Β© 2016-2017 Markus Teich <[email protected]> | |||
|
|
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. | |||
| This diff has been collapsed as it changes many lines, (692 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,692 | |||||
|
|
1 | mango - wayland compositor base wlroots | |||
|
|
2 | ||||
|
|
3 | Copyright Β© 2025 DreamMaoMao | |||
|
|
4 | ||||
|
|
5 | See also the files LICENSE.dwl, 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 | 1. 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>. No newline at end of file | |||
| @@ -0,0 +1,19 | |||||
|
|
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. | |||
| @@ -0,0 +1,127 | |||||
|
|
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. | |||
| @@ -0,0 +1,21 | |||||
|
|
1 | Copyright (c) 2017, 2018 Drew DeVault | |||
|
|
2 | Copyright (c) 2014 Jari Vetoniemi | |||
|
|
3 | Copyright (c) 2023 The wlroots contributors | |||
|
|
4 | ||||
|
|
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of | |||
|
|
6 | this software and associated documentation files (the "Software"), to deal in | |||
|
|
7 | the Software without restriction, including without limitation the rights to | |||
|
|
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies | |||
|
|
9 | of the Software, and to permit persons to whom the Software is furnished to do | |||
|
|
10 | so, subject to the following conditions: | |||
|
|
11 | ||||
|
|
12 | The above copyright notice and this permission notice shall be included in all | |||
|
|
13 | copies or substantial portions of the Software. | |||
|
|
14 | ||||
|
|
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |||
|
|
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |||
|
|
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |||
|
|
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |||
|
|
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |||
|
|
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |||
|
|
21 | SOFTWARE. | |||
| This diff has been collapsed as it changes many lines, (757 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,757 | |||||
|
|
1 | /* Here be a small dragon. | |||
|
|
2 | * Not as large as the monster | |||
|
|
3 | * in src/swindle.c | |||
|
|
4 | * but you get the idea */ | |||
|
|
5 | ||||
|
|
6 | #include "parser.h" | |||
|
|
7 | ||||
|
|
8 | #include <stdio.h> | |||
|
|
9 | #include <stdlib.h> | |||
|
|
10 | #include <string.h> | |||
|
|
11 | #include <errno.h> | |||
|
|
12 | #include <time.h> | |||
|
|
13 | #include <unistd.h> | |||
|
|
14 | #include <signal.h> | |||
|
|
15 | #include <sys/wait.h> | |||
|
|
16 | #include <sys/inotify.h> | |||
|
|
17 | #include <linux/input-event-codes.h> | |||
|
|
18 | #include <xkbcommon/xkbcommon.h> | |||
|
|
19 | #include <wayland-server-core.h> | |||
|
|
20 | ||||
|
|
21 | /* Lua */ | |||
|
|
22 | #include <lua.h> | |||
|
|
23 | #include <lauxlib.h> | |||
|
|
24 | #include <lualib.h> | |||
|
|
25 | ||||
|
|
26 | /* wlroots */ | |||
|
|
27 | #include <wlr/util/log.h> | |||
|
|
28 | #include <wlr/types/wlr_keyboard.h> | |||
|
|
29 | #include <libinput.h> | |||
|
|
30 | ||||
|
|
31 | /* Forward declarations from src/swindle.c */ | |||
|
|
32 | extern struct wl_list mons; | |||
|
|
33 | extern struct wl_list clients; | |||
|
|
34 | void arrange(void *m); | |||
|
|
35 | ||||
|
|
36 | /* Internal watcher state */ | |||
|
|
37 | ||||
|
|
38 | typedef struct { | |||
|
|
39 | int inotify_fd; | |||
|
|
40 | int watch_fd; | |||
|
|
41 | char path[1024]; | |||
|
|
42 | config_reload_cb cb; | |||
|
|
43 | void *userdata; | |||
|
|
44 | struct wl_event_source *event_src; | |||
|
|
45 | } WatchState; | |||
|
|
46 | ||||
|
|
47 | /* Helpers */ | |||
|
|
48 | ||||
|
|
49 | static void | |||
|
|
50 | lua_log_error(lua_State *L, const char *context) | |||
|
|
51 | { | |||
|
|
52 | fprintf(stderr, "swindle config [%s]: %s\n", | |||
|
|
53 | context, lua_tostring(L, -1)); | |||
|
|
54 | lua_pop(L, 1); | |||
|
|
55 | } | |||
|
|
56 | ||||
|
|
57 | /* Get a string field from the table at the top of the stack | |||
|
|
58 | * Writes into buf and returns false and leaves buf untouched on failure | |||
|
|
59 | */ | |||
|
|
60 | ||||
|
|
61 | static bool | |||
|
|
62 | lua_get_string(lua_State *L, const char *key, char *buf, size_t bufsz) | |||
|
|
63 | { | |||
|
|
64 | lua_getfield(L, -1, key); | |||
|
|
65 | bool ok = lua_isstring(L, -1); | |||
|
|
66 | if (ok) | |||
|
|
67 | strncpy(buf, lua_tostring(L, -1), bufsz - 1); | |||
|
|
68 | lua_pop(L, 1); | |||
|
|
69 | return ok; | |||
|
|
70 | } | |||
|
|
71 | ||||
|
|
72 | /* Get an integer field. It falls back to `def` if missing or wrong type */ | |||
|
|
73 | ||||
|
|
74 | static int | |||
|
|
75 | lua_get_int(lua_State *L, const char *key, int def) | |||
|
|
76 | { | |||
|
|
77 | lua_getfield(L, -1, key); | |||
|
|
78 | int val = lua_isnumber(L, -1) ? (int)lua_tointeger(L, -1) : def; | |||
|
|
79 | lua_pop(L, 1); | |||
|
|
80 | return val; | |||
|
|
81 | } | |||
|
|
82 | ||||
|
|
83 | static double | |||
|
|
84 | lua_get_double(lua_State *L, const char *key, double def) | |||
|
|
85 | { | |||
|
|
86 | lua_getfield(L, -1, key); | |||
|
|
87 | double val = lua_isnumber(L, -1) ? lua_tonumber(L, -1) : def; | |||
|
|
88 | lua_pop(L, 1); | |||
|
|
89 | return val; | |||
|
|
90 | } | |||
|
|
91 | ||||
|
|
92 | static bool | |||
|
|
93 | lua_get_bool(lua_State *L, const char *key, bool def) | |||
|
|
94 | { | |||
|
|
95 | lua_getfield(L, -1, key); | |||
|
|
96 | bool val = lua_isboolean(L, -1) ? lua_toboolean(L, -1) : def; | |||
|
|
97 | lua_pop(L, 1); | |||
|
|
98 | return val; | |||
|
|
99 | } | |||
|
|
100 | ||||
|
|
101 | /* wlroots hard-asserts opacity is in [0,1] and takes the whole thing | |||
|
|
102 | * down with it if it isn't (as it should), so it might as well clamp anything | |||
|
|
103 | * a user's config throws at it because users are kinda unwise */ | |||
|
|
104 | ||||
|
|
105 | static float | |||
|
|
106 | lua_get_opacity(lua_State *L, const char *key, float def) | |||
|
|
107 | { | |||
|
|
108 | double v = lua_get_double(L, key, (double)def); | |||
|
|
109 | if (v < 0.0) { | |||
|
|
110 | fprintf(stderr, "swindle config: '%s' below 0, clamping to 0\n", key); | |||
|
|
111 | v = 0.0; | |||
|
|
112 | } else if (v > 1.0) { | |||
|
|
113 | fprintf(stderr, "swindle config: '%s' above 1, clamping to 1\n", key); | |||
|
|
114 | v = 1.0; | |||
|
|
115 | } | |||
|
|
116 | return (float)v; | |||
|
|
117 | } | |||
|
|
118 | ||||
|
|
119 | static void | |||
|
|
120 | parse_color_hex(uint32_t hex, float out[4]) | |||
|
|
121 | { | |||
|
|
122 | out[0] = ((hex >> 24) & 0xFF) / 255.0f; | |||
|
|
123 | out[1] = ((hex >> 16) & 0xFF) / 255.0f; | |||
|
|
124 | out[2] = ((hex >> 8) & 0xFF) / 255.0f; | |||
|
|
125 | out[3] = ( hex & 0xFF) / 255.0f; | |||
|
|
126 | } | |||
|
|
127 | ||||
|
|
128 | static void | |||
|
|
129 | lua_get_color(lua_State *L, const char *key, float out[4], | |||
|
|
130 | float r, float g, float b, float a) | |||
|
|
131 | { | |||
|
|
132 | lua_getfield(L, -1, key); | |||
|
|
133 | if (lua_isnumber(L, -1)) | |||
|
|
134 | parse_color_hex((uint32_t)lua_tointeger(L, -1), out); | |||
|
|
135 | else { | |||
|
|
136 | out[0] = r; out[1] = g; out[2] = b; out[3] = a; | |||
|
|
137 | } | |||
|
|
138 | lua_pop(L, 1); | |||
|
|
139 | } | |||
|
|
140 | ||||
|
|
141 | static uint32_t | |||
|
|
142 | parse_modifier(const char *name) | |||
|
|
143 | { | |||
|
|
144 | if (!strcmp(name, "shift")) return WLR_MODIFIER_SHIFT; | |||
|
|
145 | if (!strcmp(name, "ctrl")) return WLR_MODIFIER_CTRL; | |||
|
|
146 | if (!strcmp(name, "alt")) return WLR_MODIFIER_ALT; | |||
|
|
147 | if (!strcmp(name, "logo")) return WLR_MODIFIER_LOGO; | |||
|
|
148 | if (!strcmp(name, "mod2")) return WLR_MODIFIER_MOD2; | |||
|
|
149 | if (!strcmp(name, "mod3")) return WLR_MODIFIER_MOD3; | |||
|
|
150 | if (!strcmp(name, "mod5")) return WLR_MODIFIER_MOD5; | |||
|
|
151 | fprintf(stderr, "swindle config: unknown modifier '%s', ignoring\n", name); | |||
|
|
152 | return 0; | |||
|
|
153 | } | |||
|
|
154 | ||||
|
|
155 | /* Section parsers */ | |||
|
|
156 | ||||
|
|
157 | static void | |||
|
|
158 | parse_appearance(lua_State *L, CfgAppearance *a) | |||
|
|
159 | { | |||
|
|
160 | lua_getglobal(L, "appearance"); | |||
|
|
161 | if (!lua_istable(L, -1)) { | |||
|
|
162 | fprintf(stderr, "swindle config: 'appearance' table missing' \n"); | |||
|
|
163 | lua_pop(L, 1); | |||
|
|
164 | /* defaults */ | |||
|
|
165 | a->inner_border_px = 1; | |||
|
|
166 | a->outer_border_px = 1; | |||
|
|
167 | a->gaps = 10; | |||
|
|
168 | a->smart_gaps = false; | |||
|
|
169 | parse_color_hex(0x222222ff, a->root_color); | |||
|
|
170 | parse_color_hex(0x00000000, a->inner_border_color); | |||
|
|
171 | parse_color_hex(0x00000000, a->outer_border_color); | |||
|
|
172 | parse_color_hex(0xe8e8e8ff, a->inner_focus_color); | |||
|
|
173 | parse_color_hex(0xe8e8e8ff, a->outer_focus_color); | |||
|
|
174 | parse_color_hex(0xff0000ff, a->inner_urgent_color); | |||
|
|
175 | parse_color_hex(0xff0000ff, a->outer_urgent_color); | |||
|
|
176 | a->fullscreen_bg[3] = 1.0f; | |||
|
|
177 | a->active_opacity = 1.0f; | |||
|
|
178 | a->inactive_opacity = 1.0f; | |||
|
|
179 | return; | |||
|
|
180 | } | |||
|
|
181 | ||||
|
|
182 | a->inner_border_px = (unsigned int)lua_get_int(L, "inner_border_px", 1); | |||
|
|
183 | a->outer_border_px = (unsigned int)lua_get_int(L, "outer_border_px", 1); | |||
|
|
184 | a->gaps = (unsigned int)lua_get_int(L, "gaps", 10); | |||
|
|
185 | a->smart_gaps = lua_get_bool(L, "smart_gaps", false); | |||
|
|
186 | ||||
|
|
187 | lua_get_color(L, "root_color", a->root_color, 0.133f,0.133f,0.133f,1.f); | |||
|
|
188 | lua_get_color(L, "inner_border_color", a->inner_border_color, 0.f,0.f,0.f,0.f); | |||
|
|
189 | lua_get_color(L, "outer_border_color", a->outer_border_color, 0.f,0.f,0.f,0.f); | |||
|
|
190 | lua_get_color(L, "inner_focus_color", a->inner_focus_color, 0.91f,0.91f,0.91f,1.f); | |||
|
|
191 | lua_get_color(L, "outer_focus_color", a->outer_focus_color, 0.91f,0.91f,0.91f,1.f); | |||
|
|
192 | lua_get_color(L, "inner_urgent_color", a->inner_urgent_color, 1.f,0.f,0.f,1.f); | |||
|
|
193 | lua_get_color(L, "outer_urgent_color", a->outer_urgent_color, 1.f,0.f,0.f,1.f); | |||
|
|
194 | ||||
|
|
195 | a->active_opacity = lua_get_opacity(L, "active_opacity", 1.0f); | |||
|
|
196 | a->inactive_opacity = lua_get_opacity(L, "inactive_opacity", 1.0f); | |||
|
|
197 | ||||
|
|
198 | lua_getfield(L, -1, "fullscreen_bg"); | |||
|
|
199 | if (lua_isnumber(L, -1)) | |||
|
|
200 | parse_color_hex((uint32_t)lua_tointeger(L, -1), a->fullscreen_bg); | |||
|
|
201 | else { | |||
|
|
202 | a->fullscreen_bg[0] = 0.f; | |||
|
|
203 | a->fullscreen_bg[1] = 0.f; | |||
|
|
204 | a->fullscreen_bg[2] = 0.f; | |||
|
|
205 | a->fullscreen_bg[3] = 1.f; | |||
|
|
206 | } | |||
|
|
207 | lua_pop(L, 1); | |||
|
|
208 | ||||
|
|
209 | lua_pop(L, 1); /* pop appearance table */ | |||
|
|
210 | } | |||
|
|
211 | ||||
|
|
212 | static void | |||
|
|
213 | parse_input(lua_State *L, CfgInput *inp) | |||
|
|
214 | { | |||
|
|
215 | lua_getglobal(L, "input"); | |||
|
|
216 | if (!lua_istable(L, -1)) { | |||
|
|
217 | fprintf(stderr, "swindle config: 'input' table missing, " | |||
|
|
218 | "using defaults. You'll feel the repeat delay.\n"); | |||
|
|
219 | lua_pop(L, 1); | |||
|
|
220 | inp->repeat_rate = 50; | |||
|
|
221 | inp->repeat_delay = 150; | |||
|
|
222 | inp->tap_to_click = true; | |||
|
|
223 | inp->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; | |||
|
|
224 | strcpy(inp->layout, "us"); | |||
|
|
225 | return; | |||
|
|
226 | } | |||
|
|
227 | ||||
|
|
228 | inp->repeat_rate = lua_get_int (L, "repeat_rate", 50); | |||
|
|
229 | inp->repeat_delay = lua_get_int (L, "repeat_delay", 150); | |||
|
|
230 | inp->tap_to_click = lua_get_bool(L, "tap_to_click", true); | |||
|
|
231 | inp->tap_and_drag = lua_get_bool(L, "tap_and_drag", true); | |||
|
|
232 | inp->drag_lock = lua_get_bool(L, "drag_lock", true); | |||
|
|
233 | inp->natural_scrolling = lua_get_bool(L, "natural_scrolling", false); | |||
|
|
234 | inp->disable_while_typing = lua_get_bool(L, "disable_while_typing", true); | |||
|
|
235 | inp->left_handed = lua_get_bool(L, "left_handed", false); | |||
|
|
236 | inp->middle_button_emulation= lua_get_bool(L, "middle_button_emulation",false); | |||
|
|
237 | inp->accel_speed = lua_get_double(L, "accel_speed", 0.0); | |||
|
|
238 | ||||
|
|
239 | lua_get_string(L, "layout", inp->layout, sizeof(inp->layout)); | |||
|
|
240 | lua_get_string(L, "variant", inp->variant, sizeof(inp->variant)); | |||
|
|
241 | lua_get_string(L, "model", inp->model, sizeof(inp->model)); | |||
|
|
242 | lua_get_string(L, "options", inp->options, sizeof(inp->options)); | |||
|
|
243 | if (!inp->layout[0]) strcpy(inp->layout, "us"); | |||
|
|
244 | ||||
|
|
245 | char scratch[CFG_MAX_STRLEN] = {0}; | |||
|
|
246 | ||||
|
|
247 | lua_get_string(L, "scroll_method", scratch, sizeof(scratch)); | |||
|
|
248 | if (!strcmp(scratch, "2fg")) inp->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; | |||
|
|
249 | else if (!strcmp(scratch, "edge")) inp->scroll_method = LIBINPUT_CONFIG_SCROLL_EDGE; | |||
|
|
250 | else if (!strcmp(scratch, "button")) inp->scroll_method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; | |||
|
|
251 | else inp->scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; | |||
|
|
252 | ||||
|
|
253 | lua_get_string(L, "accel_profile", scratch, sizeof(scratch)); | |||
|
|
254 | if (!strcmp(scratch, "flat")) inp->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; | |||
|
|
255 | else inp->accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; | |||
|
|
256 | ||||
|
|
257 | lua_get_string(L, "click_method", scratch, sizeof(scratch)); | |||
|
|
258 | if (!strcmp(scratch, "clickfinger")) inp->click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; | |||
|
|
259 | else if (!strcmp(scratch, "button_areas")) inp->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; | |||
|
|
260 | else inp->click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; | |||
|
|
261 | ||||
|
|
262 | lua_get_string(L, "send_events_mode", scratch, sizeof(scratch)); | |||
|
|
263 | if (!strcmp(scratch, "disabled")) inp->send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; | |||
|
|
264 | else if (!strcmp(scratch, "disabled_on_external")) inp->send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; | |||
|
|
265 | else inp->send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; | |||
|
|
266 | ||||
|
|
267 | lua_get_string(L, "tap_button_map", scratch, sizeof(scratch)); | |||
|
|
268 | if (!strcmp(scratch, "lmr")) inp->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LMR; | |||
|
|
269 | else inp->tap_button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; | |||
|
|
270 | ||||
|
|
271 | lua_pop(L, 1); | |||
|
|
272 | } | |||
|
|
273 | ||||
|
|
274 | static void | |||
|
|
275 | parse_rules(lua_State *L, Config *cfg) | |||
|
|
276 | { | |||
|
|
277 | lua_getglobal(L, "rules"); | |||
|
|
278 | if (!lua_istable(L, -1)) { | |||
|
|
279 | lua_pop(L, 1); | |||
|
|
280 | return; | |||
|
|
281 | } | |||
|
|
282 | ||||
|
|
283 | cfg->nrules = 0; | |||
|
|
284 | int n = (int)lua_rawlen(L, -1); | |||
|
|
285 | for (int i = 1; i <= n && cfg->nrules < CFG_MAX_RULES; i++) { | |||
|
|
286 | lua_rawgeti(L, -1, i); | |||
|
|
287 | if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; } | |||
|
|
288 | ||||
|
|
289 | CfgRule *r = &cfg->rules[cfg->nrules++]; | |||
|
|
290 | memset(r, 0, sizeof(*r)); | |||
|
|
291 | lua_get_string(L, "app_id", r->app_id, sizeof(r->app_id)); | |||
|
|
292 | lua_get_string(L, "title", r->title, sizeof(r->title)); | |||
|
|
293 | r->tags = (uint32_t)lua_get_int(L, "tags", 0); | |||
|
|
294 | r->floating = lua_get_bool(L, "floating", false); | |||
|
|
295 | r->monitor = lua_get_int(L, "monitor", -1); | |||
|
|
296 | ||||
|
|
297 | lua_pop(L, 1); | |||
|
|
298 | } | |||
|
|
299 | lua_pop(L, 1); | |||
|
|
300 | } | |||
|
|
301 | ||||
|
|
302 | static void | |||
|
|
303 | parse_monitors(lua_State *L, Config *cfg) | |||
|
|
304 | { | |||
|
|
305 | lua_getglobal(L, "monitors"); | |||
|
|
306 | if (!lua_istable(L, -1)) { | |||
|
|
307 | lua_pop(L, 1); | |||
|
|
308 | cfg->nmonitors = 1; | |||
|
|
309 | CfgMonitorRule *m = &cfg->monitors[0]; | |||
|
|
310 | memset(m, 0, sizeof(*m)); | |||
|
|
311 | m->mfact = 0.55f; | |||
|
|
312 | m->nmaster = 1; | |||
|
|
313 | m->scale = 1.0f; | |||
|
|
314 | strcpy(m->layout, "dwindle"); | |||
|
|
315 | m->x = m->y = -1; | |||
|
|
316 | return; | |||
|
|
317 | } | |||
|
|
318 | ||||
|
|
319 | cfg->nmonitors = 0; | |||
|
|
320 | int n = (int)lua_rawlen(L, -1); | |||
|
|
321 | for (int i = 1; i <= n && cfg->nmonitors < CFG_MAX_MONITORS; i++) { | |||
|
|
322 | lua_rawgeti(L, -1, i); | |||
|
|
323 | if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; } | |||
|
|
324 | ||||
|
|
325 | CfgMonitorRule *m = &cfg->monitors[cfg->nmonitors++]; | |||
|
|
326 | memset(m, 0, sizeof(*m)); | |||
|
|
327 | ||||
|
|
328 | lua_getfield(L, -1, "name"); | |||
|
|
329 | if (lua_isstring(L, -1)) | |||
|
|
330 | strncpy(m->name, lua_tostring(L, -1), sizeof(m->name)-1); | |||
|
|
331 | lua_pop(L, 1); | |||
|
|
332 | ||||
|
|
333 | m->mfact = (float)lua_get_double(L, "mfact", 0.55); | |||
|
|
334 | m->nmaster = lua_get_int (L, "nmaster", 1); | |||
|
|
335 | m->scale = (float)lua_get_double(L, "scale", 1.0); | |||
|
|
336 | m->x = lua_get_int (L, "x", -1); | |||
|
|
337 | m->y = lua_get_int (L, "y", -1); | |||
|
|
338 | m->transform = lua_get_int (L, "transform", 0); | |||
|
|
339 | lua_get_string(L, "layout", m->layout, sizeof(m->layout)); | |||
|
|
340 | if (!m->layout[0]) strcpy(m->layout, "dwindle"); | |||
|
|
341 | ||||
|
|
342 | lua_pop(L, 1); | |||
|
|
343 | } | |||
|
|
344 | lua_pop(L, 1); | |||
|
|
345 | } | |||
|
|
346 | ||||
|
|
347 | static void | |||
|
|
348 | parse_keybinds(lua_State *L, Config *cfg) | |||
|
|
349 | { | |||
|
|
350 | lua_getglobal(L, "keybinds"); | |||
|
|
351 | if (!lua_istable(L, -1)) { | |||
|
|
352 | lua_pop(L, 1); | |||
|
|
353 | return; | |||
|
|
354 | } | |||
|
|
355 | ||||
|
|
356 | cfg->nkeybinds = 0; | |||
|
|
357 | struct xkb_context *xkb_ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | |||
|
|
358 | ||||
|
|
359 | int n = (int)lua_rawlen(L, -1); | |||
|
|
360 | for (int i = 1; i <= n && cfg->nkeybinds < CFG_MAX_KEYBINDS; i++) { | |||
|
|
361 | lua_rawgeti(L, -1, i); | |||
|
|
362 | if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; } | |||
|
|
363 | ||||
|
|
364 | CfgKeybind *kb = &cfg->keybinds[cfg->nkeybinds]; | |||
|
|
365 | memset(kb, 0, sizeof(*kb)); | |||
|
|
366 | ||||
|
|
367 | /* mods: array of strings */ | |||
|
|
368 | kb->mods = 0; | |||
|
|
369 | lua_getfield(L, -1, "mods"); | |||
|
|
370 | if (lua_istable(L, -1)) { | |||
|
|
371 | int nm = (int)lua_rawlen(L, -1); | |||
|
|
372 | for (int m = 1; m <= nm; m++) { | |||
|
|
373 | lua_rawgeti(L, -1, m); | |||
|
|
374 | if (lua_isstring(L, -1)) | |||
|
|
375 | kb->mods |= parse_modifier(lua_tostring(L, -1)); | |||
|
|
376 | lua_pop(L, 1); | |||
|
|
377 | } | |||
|
|
378 | } | |||
|
|
379 | lua_pop(L, 1); | |||
|
|
380 | ||||
|
|
381 | /* key: string keysym name, e.g. "q", "Return", "XF86AudioMute" */ | |||
|
|
382 | char keyname[CFG_MAX_STRLEN] = {0}; | |||
|
|
383 | lua_get_string(L, "key", keyname, sizeof(keyname)); | |||
|
|
384 | kb->key = xkb_keysym_from_name(keyname, XKB_KEYSYM_CASE_INSENSITIVE); | |||
|
|
385 | if (kb->key == XKB_KEY_NoSymbol) { | |||
|
|
386 | fprintf(stderr, "swindle config: unknown key '%s', skipping bind\n", | |||
|
|
387 | keyname); | |||
|
|
388 | lua_pop(L, 1); /* keybind entry table */ | |||
|
|
389 | continue; | |||
|
|
390 | } | |||
|
|
391 | ||||
|
|
392 | lua_get_string(L, "action", kb->action, sizeof(kb->action)); | |||
|
|
393 | ||||
|
|
394 | /* args: optional array of strings */ | |||
|
|
395 | kb->nargs = 0; | |||
|
|
396 | lua_getfield(L, -1, "args"); | |||
|
|
397 | if (lua_istable(L, -1)) { | |||
|
|
398 | int na = (int)lua_rawlen(L, -1); | |||
|
|
399 | for (int a = 1; a <= na && kb->nargs < CFG_MAX_ARGS; a++) { | |||
|
|
400 | lua_rawgeti(L, -1, a); | |||
|
|
401 | if (lua_isstring(L, -1)) | |||
|
|
402 | strncpy(kb->args[kb->nargs++], | |||
|
|
403 | lua_tostring(L, -1), | |||
|
|
404 | CFG_MAX_STRLEN - 1); | |||
|
|
405 | lua_pop(L, 1); | |||
|
|
406 | } | |||
|
|
407 | } | |||
|
|
408 | lua_pop(L, 1); /* args */ | |||
|
|
409 | ||||
|
|
410 | cfg->nkeybinds++; | |||
|
|
411 | lua_pop(L, 1); /* keybind entry */ | |||
|
|
412 | } | |||
|
|
413 | ||||
|
|
414 | xkb_context_unref(xkb_ctx); | |||
|
|
415 | lua_pop(L, 1); /* keybinds table */ | |||
|
|
416 | } | |||
|
|
417 | ||||
|
|
418 | static void | |||
|
|
419 | parse_buttons(lua_State *L, Config *cfg) | |||
|
|
420 | { | |||
|
|
421 | lua_getglobal(L, "buttons"); | |||
|
|
422 | if (!lua_istable(L, -1)) { | |||
|
|
423 | lua_pop(L, 1); | |||
|
|
424 | return; | |||
|
|
425 | } | |||
|
|
426 | ||||
|
|
427 | cfg->nbuttons = 0; | |||
|
|
428 | int n = (int)lua_rawlen(L, -1); | |||
|
|
429 | for (int i = 1; i <= n && cfg->nbuttons < CFG_MAX_KEYBINDS; i++) { | |||
|
|
430 | lua_rawgeti(L, -1, i); | |||
|
|
431 | if (!lua_istable(L, -1)) { lua_pop(L, 1); continue; } | |||
|
|
432 | ||||
|
|
433 | CfgButton *b = &cfg->buttons[cfg->nbuttons++]; | |||
|
|
434 | memset(b, 0, sizeof(*b)); | |||
|
|
435 | ||||
|
|
436 | b->mods = 0; | |||
|
|
437 | lua_getfield(L, -1, "mods"); | |||
|
|
438 | if (lua_istable(L, -1)) { | |||
|
|
439 | int nm = (int)lua_rawlen(L, -1); | |||
|
|
440 | for (int m = 1; m <= nm; m++) { | |||
|
|
441 | lua_rawgeti(L, -1, m); | |||
|
|
442 | if (lua_isstring(L, -1)) | |||
|
|
443 | b->mods |= parse_modifier(lua_tostring(L, -1)); | |||
|
|
444 | lua_pop(L, 1); | |||
|
|
445 | } | |||
|
|
446 | } | |||
|
|
447 | lua_pop(L, 1); | |||
|
|
448 | ||||
|
|
449 | char btnname[CFG_MAX_STRLEN] = {0}; | |||
|
|
450 | lua_get_string(L, "button", btnname, sizeof(btnname)); | |||
|
|
451 | if (!strcmp(btnname, "left")) b->button = BTN_LEFT; | |||
|
|
452 | else if (!strcmp(btnname, "right")) b->button = BTN_RIGHT; | |||
|
|
453 | else if (!strcmp(btnname, "middle")) b->button = BTN_MIDDLE; | |||
|
|
454 | else b->button = (uint32_t)atoi(btnname); | |||
|
|
455 | ||||
|
|
456 | lua_get_string(L, "action", b->action, sizeof(b->action)); | |||
|
|
457 | ||||
|
|
458 | b->nargs = 0; | |||
|
|
459 | lua_getfield(L, -1, "args"); | |||
|
|
460 | if (lua_istable(L, -1)) { | |||
|
|
461 | int na = (int)lua_rawlen(L, -1); | |||
|
|
462 | for (int a = 1; a <= na && b->nargs < CFG_MAX_ARGS; a++) { | |||
|
|
463 | lua_rawgeti(L, -1, a); | |||
|
|
464 | if (lua_isstring(L, -1)) | |||
|
|
465 | strncpy(b->args[b->nargs++], | |||
|
|
466 | lua_tostring(L, -1), | |||
|
|
467 | CFG_MAX_STRLEN - 1); | |||
|
|
468 | lua_pop(L, 1); | |||
|
|
469 | } | |||
|
|
470 | } | |||
|
|
471 | lua_pop(L, 1); /* args */ | |||
|
|
472 | ||||
|
|
473 | lua_pop(L, 1); | |||
|
|
474 | } | |||
|
|
475 | lua_pop(L, 1); | |||
|
|
476 | } | |||
|
|
477 | ||||
|
|
478 | static void | |||
|
|
479 | parse_autostart(lua_State *L, Config *cfg) | |||
|
|
480 | { | |||
|
|
481 | lua_getglobal(L, "autostart"); | |||
|
|
482 | if (!lua_istable(L, -1)) { | |||
|
|
483 | lua_pop(L, 1); | |||
|
|
484 | return; | |||
|
|
485 | } | |||
|
|
486 | ||||
|
|
487 | cfg->nautostart = 0; | |||
|
|
488 | int n = (int)lua_rawlen(L, -1); | |||
|
|
489 | for (int i = 1; i <= n && cfg->nautostart < CFG_MAX_AUTOSTART; i++) { | |||
|
|
490 | lua_rawgeti(L, -1, i); | |||
|
|
491 | if (!lua_isstring(L, -1)) { | |||
|
|
492 | lua_pop(L, 1); | |||
|
|
493 | continue; | |||
|
|
494 | } | |||
|
|
495 | strncpy(cfg->autostart[cfg->nautostart].cmd, | |||
|
|
496 | lua_tostring(L, -1), | |||
|
|
497 | CFG_MAX_AUTOSTART_CMD - 1); | |||
|
|
498 | cfg->nautostart++; | |||
|
|
499 | lua_pop(L, 1); | |||
|
|
500 | } | |||
|
|
501 | lua_pop(L, 1); | |||
|
|
502 | } | |||
|
|
503 | ||||
|
|
504 | /* Public API */ | |||
|
|
505 | ||||
|
|
506 | char * | |||
|
|
507 | config_get_path(char *buf, size_t bufsz) | |||
|
|
508 | { | |||
|
|
509 | const char *xdg = getenv("XDG_CONFIG_HOME"); | |||
|
|
510 | if (xdg && *xdg) | |||
|
|
511 | snprintf(buf, bufsz, "%s/swindle/config.lua", xdg); | |||
|
|
512 | else { | |||
|
|
513 | const char *home = getenv("HOME"); | |||
|
|
514 | if (!home) home = "/root"; /* you're probably root, aren't you */ | |||
|
|
515 | snprintf(buf, bufsz, "%s/.config/swindle/config.lua", home); | |||
|
|
516 | } | |||
|
|
517 | return buf; | |||
|
|
518 | } | |||
|
|
519 | ||||
|
|
520 | int | |||
|
|
521 | config_load(const char *path, Config *cfg) | |||
|
|
522 | { | |||
|
|
523 | memset(cfg, 0, sizeof(*cfg)); | |||
|
|
524 | ||||
|
|
525 | lua_State *L = luaL_newstate(); | |||
|
|
526 | if (!L) { | |||
|
|
527 | fprintf(stderr, "swindle config: failed to create Lua state. " | |||
|
|
528 | "Truly rock bottom.\n"); | |||
|
|
529 | return -1; | |||
|
|
530 | } | |||
|
|
531 | luaL_openlibs(L); | |||
|
|
532 | ||||
|
|
533 | if (luaL_dofile(L, path) != LUA_OK) { | |||
|
|
534 | lua_log_error(L, "load"); | |||
|
|
535 | lua_close(L); | |||
|
|
536 | return -1; | |||
|
|
537 | } | |||
|
|
538 | ||||
|
|
539 | /* Top-level booleans */ | |||
|
|
540 | lua_getglobal(L, "sloppy_focus"); | |||
|
|
541 | cfg->sloppyfocus = lua_isboolean(L, -1) ? lua_toboolean(L, -1) : true; | |||
|
|
542 | lua_pop(L, 1); | |||
|
|
543 | ||||
|
|
544 | lua_getglobal(L, "bypass_surface_visibility"); | |||
|
|
545 | cfg->bypass_surface_visibility = lua_isboolean(L, -1) | |||
|
|
546 | ? lua_toboolean(L, -1) : false; | |||
|
|
547 | lua_pop(L, 1); | |||
|
|
548 | ||||
|
|
549 | lua_getglobal(L, "log_level"); | |||
|
|
550 | if (lua_isstring(L, -1)) { | |||
|
|
551 | const char *ll = lua_tostring(L, -1); | |||
|
|
552 | if (!strcmp(ll, "silent")) cfg->log_level = WLR_SILENT; | |||
|
|
553 | else if (!strcmp(ll, "info")) cfg->log_level = WLR_INFO; | |||
|
|
554 | else if (!strcmp(ll, "debug")) cfg->log_level = WLR_DEBUG; | |||
|
|
555 | else cfg->log_level = WLR_ERROR; | |||
|
|
556 | } else { | |||
|
|
557 | cfg->log_level = WLR_ERROR; | |||
|
|
558 | } | |||
|
|
559 | lua_pop(L, 1); | |||
|
|
560 | ||||
|
|
561 | parse_appearance(L, &cfg->appearance); | |||
|
|
562 | parse_input (L, &cfg->input); | |||
|
|
563 | parse_rules (L, cfg); | |||
|
|
564 | parse_monitors (L, cfg); | |||
|
|
565 | parse_keybinds (L, cfg); | |||
|
|
566 | parse_buttons (L, cfg); | |||
|
|
567 | parse_autostart (L, cfg); | |||
|
|
568 | ||||
|
|
569 | lua_close(L); | |||
|
|
570 | return 0; | |||
|
|
571 | } | |||
|
|
572 | ||||
|
|
573 | /* Apply functions */ | |||
|
|
574 | ||||
|
|
575 | void | |||
|
|
576 | config_apply_appearance(const Config *cfg) | |||
|
|
577 | { | |||
|
|
578 | (void)cfg; | |||
|
|
579 | } | |||
|
|
580 | ||||
|
|
581 | void | |||
|
|
582 | config_apply_input(const Config *cfg) | |||
|
|
583 | { | |||
|
|
584 | (void)cfg; | |||
|
|
585 | } | |||
|
|
586 | ||||
|
|
587 | void | |||
|
|
588 | config_apply_keybinds(const Config *cfg) | |||
|
|
589 | { | |||
|
|
590 | (void)cfg; | |||
|
|
591 | } | |||
|
|
592 | ||||
|
|
593 | void | |||
|
|
594 | config_autostart_run(const Config *cfg) | |||
|
|
595 | { | |||
|
|
596 | if (cfg->nautostart == 0) | |||
|
|
597 | return; | |||
|
|
598 | ||||
|
|
599 | pid_t pid = fork(); | |||
|
|
600 | if (pid < 0) { | |||
|
|
601 | perror("swindle autostart: fork"); | |||
|
|
602 | return; | |||
|
|
603 | } | |||
|
|
604 | if (pid != 0) | |||
|
|
605 | return; /* compositor continues (sequencer runs in child) */ | |||
|
|
606 | ||||
|
|
607 | /* The sequencer child resets any inherited signal handlers and becomes | |||
|
|
608 | * a new session so the compositor's SIGCHLD handling doesn't | |||
|
|
609 | * interfere with our waitpid calls. */ | |||
|
|
610 | signal(SIGCHLD, SIG_DFL); | |||
|
|
611 | setsid(); | |||
|
|
612 | ||||
|
|
613 | for (int i = 0; i < cfg->nautostart; i++) { | |||
|
|
614 | const char *cmd = cfg->autostart[i].cmd; | |||
|
|
615 | ||||
|
|
616 | pid_t cpid = fork(); | |||
|
|
617 | if (cpid < 0) { | |||
|
|
618 | fprintf(stderr, "swindle autostart: fork for '%s': %s\n", | |||
|
|
619 | cmd, strerror(errno)); | |||
|
|
620 | continue; | |||
|
|
621 | } | |||
|
|
622 | if (cpid == 0) { | |||
|
|
623 | execl("/bin/sh", "sh", "-c", cmd, NULL); | |||
|
|
624 | fprintf(stderr, "swindle autostart: exec '%s': %s\n", | |||
|
|
625 | cmd, strerror(errno)); | |||
|
|
626 | _exit(1); | |||
|
|
627 | } | |||
|
|
628 | ||||
|
|
629 | /* wait a second because one shotters will fly past, but | |||
|
|
630 | * daemon processes (some, like swaybg) won't because of | |||
|
|
631 | * daemon black magic. To say the least, it's good for the soul */ | |||
|
|
632 | struct timespec deadline, now; | |||
|
|
633 | clock_gettime(CLOCK_MONOTONIC, &deadline); | |||
|
|
634 | deadline.tv_sec += 0.5; | |||
|
|
635 | ||||
|
|
636 | int status; | |||
|
|
637 | int done = 0; | |||
|
|
638 | while (!done) { | |||
|
|
639 | pid_t r = waitpid(cpid, &status, WNOHANG); | |||
|
|
640 | if (r == cpid) { | |||
|
|
641 | done = 1; | |||
|
|
642 | if (WIFEXITED(status) && WEXITSTATUS(status) != 0) | |||
|
|
643 | fprintf(stderr, "swindle autostart: '%s' exited with status %d\n", | |||
|
|
644 | cmd, WEXITSTATUS(status)); | |||
|
|
645 | } else { | |||
|
|
646 | clock_gettime(CLOCK_MONOTONIC, &now); | |||
|
|
647 | if (now.tv_sec > deadline.tv_sec || | |||
|
|
648 | (now.tv_sec == deadline.tv_sec && | |||
|
|
649 | now.tv_nsec >= deadline.tv_nsec)) | |||
|
|
650 | break; /* looks like a daemon, move on */ | |||
|
|
651 | struct timespec sl = { .tv_sec = 0, .tv_nsec = 50 * 1000 * 1000 }; | |||
|
|
652 | nanosleep(&sl, NULL); | |||
|
|
653 | } | |||
|
|
654 | } | |||
|
|
655 | } | |||
|
|
656 | ||||
|
|
657 | _exit(0); | |||
|
|
658 | } | |||
|
|
659 | ||||
|
|
660 | /* inotify watcher */ | |||
|
|
661 | ||||
|
|
662 | static int | |||
|
|
663 | watch_dispatch(int fd, uint32_t mask, void *data) | |||
|
|
664 | { | |||
|
|
665 | WatchState *ws = data; | |||
|
|
666 | ||||
|
|
667 | /* Drain the inotify events */ | |||
|
|
668 | _Alignas(struct inotify_event) char buf[4096]; | |||
|
|
669 | ssize_t len = read(fd, buf, sizeof(buf)); | |||
|
|
670 | if (len < 0) { | |||
|
|
671 | if (errno != EAGAIN) | |||
|
|
672 | perror("swindle config: inotify read"); | |||
|
|
673 | return 0; | |||
|
|
674 | } | |||
|
|
675 | ||||
|
|
676 | /* Re-parse */ | |||
|
|
677 | Config *fresh = calloc(1, sizeof(Config)); | |||
|
|
678 | if (!fresh) return 0; | |||
|
|
679 | ||||
|
|
680 | if (config_load(ws->path, fresh) == 0) { | |||
|
|
681 | fprintf(stderr, "swindle config: reloaded '%s'\n", ws->path); | |||
|
|
682 | if (ws->cb) | |||
|
|
683 | ws->cb(fresh, ws->userdata); | |||
|
|
684 | } else { | |||
|
|
685 | fprintf(stderr, "swindle config: reload failed, keeping old config. " | |||
|
|
686 | "Story of my life.\n"); | |||
|
|
687 | } | |||
|
|
688 | ||||
|
|
689 | free(fresh); | |||
|
|
690 | ||||
|
|
691 | /* | |||
|
|
692 | * Some editors (vim, nano) delete-and-recreate files on save, | |||
|
|
693 | * which removes our watch (which is a pain in the ass. | |||
|
|
694 | * Hence, re-add it defensively. It's good for the soul) | |||
|
|
695 | */ | |||
|
|
696 | inotify_rm_watch(ws->inotify_fd, ws->watch_fd); | |||
|
|
697 | ws->watch_fd = inotify_add_watch(ws->inotify_fd, ws->path, | |||
|
|
698 | IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE); | |||
|
|
699 | ||||
|
|
700 | return 0; | |||
|
|
701 | } | |||
|
|
702 | ||||
|
|
703 | struct wl_event_source * | |||
|
|
704 | config_watch_start(struct wl_event_loop *loop, const char *path, | |||
|
|
705 | config_reload_cb cb, void *userdata) | |||
|
|
706 | { | |||
|
|
707 | WatchState *ws = calloc(1, sizeof(*ws)); | |||
|
|
708 | if (!ws) return NULL; | |||
|
|
709 | ||||
|
|
710 | ws->inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); | |||
|
|
711 | if (ws->inotify_fd < 0) { | |||
|
|
712 | perror("swindle config: inotify_init1"); | |||
|
|
713 | free(ws); | |||
|
|
714 | return NULL; | |||
|
|
715 | } | |||
|
|
716 | ||||
|
|
717 | ws->watch_fd = inotify_add_watch(ws->inotify_fd, path, | |||
|
|
718 | IN_CLOSE_WRITE | IN_MOVED_TO | IN_CREATE); | |||
|
|
719 | if (ws->watch_fd < 0) { | |||
|
|
720 | perror("swindle config: inotify_add_watch"); | |||
|
|
721 | close(ws->inotify_fd); | |||
|
|
722 | free(ws); | |||
|
|
723 | return NULL; | |||
|
|
724 | } | |||
|
|
725 | ||||
|
|
726 | strncpy(ws->path, path, sizeof(ws->path) - 1); | |||
|
|
727 | ws->cb = cb; | |||
|
|
728 | ws->userdata = userdata; | |||
|
|
729 | ||||
|
|
730 | struct wl_event_source *src = wl_event_loop_add_fd( | |||
|
|
731 | loop, ws->inotify_fd, | |||
|
|
732 | WL_EVENT_READABLE, | |||
|
|
733 | watch_dispatch, ws | |||
|
|
734 | ); | |||
|
|
735 | ||||
|
|
736 | if (!src) { | |||
|
|
737 | fprintf(stderr, "swindle config: failed to add inotify to event loop. " | |||
|
|
738 | "Classic.\n"); | |||
|
|
739 | close(ws->inotify_fd); | |||
|
|
740 | free(ws); | |||
|
|
741 | return NULL; | |||
|
|
742 | } | |||
|
|
743 | ||||
|
|
744 | ws->event_src = src; | |||
|
|
745 | fprintf(stderr, "swindle config: watching '%s'\n", path); | |||
|
|
746 | return (struct wl_event_source *)ws; | |||
|
|
747 | } | |||
|
|
748 | ||||
|
|
749 | void | |||
|
|
750 | config_watch_stop(struct wl_event_source *src) | |||
|
|
751 | { | |||
|
|
752 | if (!src) return; | |||
|
|
753 | WatchState *ws = (WatchState *)src; | |||
|
|
754 | wl_event_source_remove(ws->event_src); | |||
|
|
755 | close(ws->inotify_fd); | |||
|
|
756 | free(ws); | |||
|
|
757 | } | |||
| @@ -0,0 +1,160 | |||||
|
|
1 | #ifndef SWINDLE_PARSER_H | |||
|
|
2 | #define SWINDLE_PARSER_H | |||
|
|
3 | ||||
|
|
4 | #include <stdint.h> | |||
|
|
5 | #include <stdbool.h> | |||
|
|
6 | #include <wayland-server-core.h> | |||
|
|
7 | ||||
|
|
8 | /* Maximum lengths. Good for the soul, they say */ | |||
|
|
9 | #define CFG_MAX_RULES 64 | |||
|
|
10 | #define CFG_MAX_MONITORS 16 | |||
|
|
11 | #define CFG_MAX_KEYBINDS 256 | |||
|
|
12 | #define CFG_MAX_ARGS 16 | |||
|
|
13 | #define CFG_MAX_STRLEN 256 | |||
|
|
14 | #define CFG_MAX_AUTOSTART 32 | |||
|
|
15 | #define CFG_MAX_AUTOSTART_CMD 512 | |||
|
|
16 | ||||
|
|
17 | /* Appearance */ | |||
|
|
18 | ||||
|
|
19 | typedef struct { | |||
|
|
20 | unsigned int inner_border_px; | |||
|
|
21 | unsigned int outer_border_px; | |||
|
|
22 | unsigned int gaps; | |||
|
|
23 | bool smart_gaps; | |||
|
|
24 | float inner_border_color[4]; | |||
|
|
25 | float outer_border_color[4]; | |||
|
|
26 | float inner_focus_color[4]; | |||
|
|
27 | float outer_focus_color[4]; | |||
|
|
28 | float inner_urgent_color[4]; | |||
|
|
29 | float outer_urgent_color[4]; | |||
|
|
30 | float root_color[4]; | |||
|
|
31 | float fullscreen_bg[4]; | |||
|
|
32 | float active_opacity; | |||
|
|
33 | float inactive_opacity; | |||
|
|
34 | } CfgAppearance; | |||
|
|
35 | ||||
|
|
36 | /* Input */ | |||
|
|
37 | ||||
|
|
38 | typedef struct { | |||
|
|
39 | int repeat_rate; | |||
|
|
40 | int repeat_delay; | |||
|
|
41 | bool tap_to_click; | |||
|
|
42 | bool tap_and_drag; | |||
|
|
43 | bool drag_lock; | |||
|
|
44 | bool natural_scrolling; | |||
|
|
45 | bool disable_while_typing; | |||
|
|
46 | bool left_handed; | |||
|
|
47 | bool middle_button_emulation; | |||
|
|
48 | int scroll_method; /* libinput enum value */ | |||
|
|
49 | int click_method; /* libinput enum value */ | |||
|
|
50 | int send_events_mode; | |||
|
|
51 | int accel_profile; /* libinput enum value */ | |||
|
|
52 | double accel_speed; | |||
|
|
53 | int tap_button_map; /* libinput enum value */ | |||
|
|
54 | char layout[CFG_MAX_STRLEN]; /* "us", "de", "de,us", ... */ | |||
|
|
55 | char variant[CFG_MAX_STRLEN]; /* "nodeadkeys", "colemak", ... */ | |||
|
|
56 | char model[CFG_MAX_STRLEN]; /* "pc105", ... */ | |||
|
|
57 | char options[CFG_MAX_STRLEN]; /* "grp:alt_shift_toggle", ... */ | |||
|
|
58 | } CfgInput; | |||
|
|
59 | ||||
|
|
60 | /* Window Rules */ | |||
|
|
61 | ||||
|
|
62 | typedef struct { | |||
|
|
63 | char app_id[CFG_MAX_STRLEN]; | |||
|
|
64 | char title[CFG_MAX_STRLEN]; /* empty string = wildcard */ | |||
|
|
65 | uint32_t tags; | |||
|
|
66 | bool floating; | |||
|
|
67 | int monitor; /* -1 = current */ | |||
|
|
68 | } CfgRule; | |||
|
|
69 | ||||
|
|
70 | /* Monitor Rules */ | |||
|
|
71 | ||||
|
|
72 | typedef struct { | |||
|
|
73 | char name[CFG_MAX_STRLEN]; /* empty = catch-all */ | |||
|
|
74 | float mfact; | |||
|
|
75 | int nmaster; | |||
|
|
76 | float scale; | |||
|
|
77 | char layout[CFG_MAX_STRLEN]; /* "dwindle", "floating", etc. */ | |||
|
|
78 | int transform; /* WL_OUTPUT_TRANSFORM_* */ | |||
|
|
79 | int x, y; /* -1,-1 = autoconfigure */ | |||
|
|
80 | } CfgMonitorRule; | |||
|
|
81 | ||||
|
|
82 | /* keybinds */ | |||
|
|
83 | ||||
|
|
84 | typedef struct { | |||
|
|
85 | uint32_t mods; /* WLR_MODIFIER_* bitmask */ | |||
|
|
86 | uint32_t key; /* XKB keysym */ | |||
|
|
87 | char action[CFG_MAX_STRLEN]; /* "spawn", "killclient", etc. */ | |||
|
|
88 | char args[CFG_MAX_ARGS][CFG_MAX_STRLEN]; | |||
|
|
89 | int nargs; | |||
|
|
90 | } CfgKeybind; | |||
|
|
91 | ||||
|
|
92 | /* mouse buttons */ | |||
|
|
93 | ||||
|
|
94 | typedef struct { | |||
|
|
95 | uint32_t mods; | |||
|
|
96 | uint32_t button; | |||
|
|
97 | char action[CFG_MAX_STRLEN]; | |||
|
|
98 | char args[CFG_MAX_ARGS][CFG_MAX_STRLEN]; | |||
|
|
99 | int nargs; | |||
|
|
100 | } CfgButton; | |||
|
|
101 | ||||
|
|
102 | /* Autostart */ | |||
|
|
103 | ||||
|
|
104 | typedef struct { | |||
|
|
105 | char cmd[CFG_MAX_AUTOSTART_CMD]; | |||
|
|
106 | } CfgAutostart; | |||
|
|
107 | ||||
|
|
108 | /* the whole shebang */ | |||
|
|
109 | ||||
|
|
110 | typedef struct { | |||
|
|
111 | CfgAppearance appearance; | |||
|
|
112 | ||||
|
|
113 | CfgInput input; | |||
|
|
114 | ||||
|
|
115 | CfgRule rules[CFG_MAX_RULES]; | |||
|
|
116 | int nrules; | |||
|
|
117 | ||||
|
|
118 | CfgMonitorRule monitors[CFG_MAX_MONITORS]; | |||
|
|
119 | int nmonitors; | |||
|
|
120 | ||||
|
|
121 | CfgKeybind keybinds[CFG_MAX_KEYBINDS]; | |||
|
|
122 | int nkeybinds; | |||
|
|
123 | ||||
|
|
124 | CfgButton buttons[CFG_MAX_KEYBINDS]; | |||
|
|
125 | int nbuttons; | |||
|
|
126 | ||||
|
|
127 | bool sloppyfocus; | |||
|
|
128 | bool bypass_surface_visibility; | |||
|
|
129 | int log_level; /* WLR_* log level */ | |||
|
|
130 | ||||
|
|
131 | CfgAutostart autostart[CFG_MAX_AUTOSTART]; | |||
|
|
132 | int nautostart; | |||
|
|
133 | } Config; | |||
|
|
134 | ||||
|
|
135 | /* Public API */ | |||
|
|
136 | ||||
|
|
137 | int config_load(const char *path, Config *cfg); | |||
|
|
138 | ||||
|
|
139 | char *config_get_path(char *buf, size_t bufsz); | |||
|
|
140 | ||||
|
|
141 | void config_apply_appearance(const Config *cfg); | |||
|
|
142 | ||||
|
|
143 | void config_apply_input(const Config *cfg); | |||
|
|
144 | ||||
|
|
145 | void config_apply_keybinds(const Config *cfg); | |||
|
|
146 | ||||
|
|
147 | void config_autostart_run(const Config *cfg); | |||
|
|
148 | ||||
|
|
149 | typedef void (*config_reload_cb)(const Config *cfg, void *userdata); | |||
|
|
150 | ||||
|
|
151 | struct wl_event_source *config_watch_start( | |||
|
|
152 | struct wl_event_loop *loop, | |||
|
|
153 | const char *path, | |||
|
|
154 | config_reload_cb cb, | |||
|
|
155 | void *userdata | |||
|
|
156 | ); | |||
|
|
157 | ||||
|
|
158 | void config_watch_stop(struct wl_event_source *src); | |||
|
|
159 | ||||
|
|
160 | #endif | |||
| @@ -0,0 +1,184 | |||||
|
|
1 | <?xml version="1.0" encoding="UTF-8"?> | |||
|
|
2 | <protocol name="dwl_ipc_unstable_v2"> | |||
|
|
3 | <copyright> | |||
|
|
4 | Copyright (C) 2022 Russell Newcomb | |||
|
|
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 | <interface name="zdwl_ipc_manager_v2" version="2"> | |||
|
|
27 | <description summary="manage dwl state"> | |||
|
|
28 | This interface is exposed as a global in the Wayland registry and allows | |||
|
|
29 | clients to query and set dwl state. | |||
|
|
30 | ||||
|
|
31 | Warning! The protocol described in this file is experimental and | |||
|
|
32 | backward incompatible changes may be made. Backward compatible changes | |||
|
|
33 | may be added together with the corresponding interface version bump. | |||
|
|
34 | Backward incompatible changes are done by bumping the version number in | |||
|
|
35 | the protocol and interface names and resetting the interface version. | |||
|
|
36 | Once the protocol is to be declared stable, the 'z' prefix and the | |||
|
|
37 | version number in the protocol and interface names are removed and the | |||
|
|
38 | interface version is reset. | |||
|
|
39 | </description> | |||
|
|
40 | ||||
|
|
41 | <request name="release" type="destructor"> | |||
|
|
42 | <description summary="release dwl_ipc_manager"> | |||
|
|
43 | Indicates that the client will not use the manager object anymore. | |||
|
|
44 | Objects created through this interface remain valid. | |||
|
|
45 | </description> | |||
|
|
46 | </request> | |||
|
|
47 | ||||
|
|
48 | <request name="get_output"> | |||
|
|
49 | <description summary="get a dwl_ipc_output for a wl_output"> | |||
|
|
50 | Get a dwl_ipc_output for the given wl_output. | |||
|
|
51 | </description> | |||
|
|
52 | <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/> | |||
|
|
53 | <arg name="output" type="object" interface="wl_output"/> | |||
|
|
54 | </request> | |||
|
|
55 | ||||
|
|
56 | <event name="tags"> | |||
|
|
57 | <description summary="announces number of tags"> | |||
|
|
58 | This event is sent after binding and announces the number of tags. | |||
|
|
59 | </description> | |||
|
|
60 | <arg name="amount" type="uint"/> | |||
|
|
61 | </event> | |||
|
|
62 | ||||
|
|
63 | <event name="layout"> | |||
|
|
64 | <description summary="announces a layout"> | |||
|
|
65 | This event is sent after binding and announces a layout. | |||
|
|
66 | Layouts are announced in order. | |||
|
|
67 | </description> | |||
|
|
68 | <arg name="name" type="string"/> | |||
|
|
69 | </event> | |||
|
|
70 | </interface> | |||
|
|
71 | ||||
|
|
72 | <interface name="zdwl_ipc_output_v2" version="2"> | |||
|
|
73 | <description summary="interact with dwl output state"> | |||
|
|
74 | Observe and control a dwl output. | |||
|
|
75 | </description> | |||
|
|
76 | ||||
|
|
77 | <enum name="tag_state"> | |||
|
|
78 | <entry name="none" value="0" summary="no state"/> | |||
|
|
79 | <entry name="active" value="1" summary="tag is active"/> | |||
|
|
80 | <entry name="urgent" value="2" summary="tag has urgent client"/> | |||
|
|
81 | </enum> | |||
|
|
82 | ||||
|
|
83 | <request name="release" type="destructor"> | |||
|
|
84 | <description summary="release dwl_ipc_output"> | |||
|
|
85 | Indicates that the client will not use the dwl_ipc_output object | |||
|
|
86 | anymore. Objects created through this interface remain valid. | |||
|
|
87 | </description> | |||
|
|
88 | </request> | |||
|
|
89 | ||||
|
|
90 | <request name="set_tags"> | |||
|
|
91 | <description summary="set the active tags of this output"> | |||
|
|
92 | set the active tags of this output. | |||
|
|
93 | </description> | |||
|
|
94 | <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set"/> | |||
|
|
95 | <arg name="toggle_tagset" type="uint" summary="whether to toggle the tagset"/> | |||
|
|
96 | </request> | |||
|
|
97 | ||||
|
|
98 | <request name="set_client_tags"> | |||
|
|
99 | <description summary="set the tags of the focused client"> | |||
|
|
100 | Set the tags of the focused client. | |||
|
|
101 | </description> | |||
|
|
102 | <arg name="and_tags" type="uint"/> | |||
|
|
103 | <arg name="xor_tags" type="uint"/> | |||
|
|
104 | </request> | |||
|
|
105 | ||||
|
|
106 | <request name="set_layout"> | |||
|
|
107 | <description summary="set the layout of this output"> | |||
|
|
108 | Set the layout of this output. | |||
|
|
109 | </description> | |||
|
|
110 | <arg name="index" type="uint"/> | |||
|
|
111 | </request> | |||
|
|
112 | ||||
|
|
113 | <event name="toggle_visibility"> | |||
|
|
114 | <description summary="dwl visibility has been toggled"> | |||
|
|
115 | Indicates that dwl visibility has been toggled. | |||
|
|
116 | </description> | |||
|
|
117 | </event> | |||
|
|
118 | ||||
|
|
119 | <event name="active"> | |||
|
|
120 | <description summary="output is active"> | |||
|
|
121 | Indicates the output is active (selected monitor). | |||
|
|
122 | </description> | |||
|
|
123 | <arg name="active" type="uint"/> | |||
|
|
124 | </event> | |||
|
|
125 | ||||
|
|
126 | <event name="tag"> | |||
|
|
127 | <description summary="tag state changed"> | |||
|
|
128 | Indicates a tag state change. | |||
|
|
129 | </description> | |||
|
|
130 | <arg name="tag" type="uint" summary="index of the tag"/> | |||
|
|
131 | <arg name="state" type="uint" enum="tag_state" summary="state of the tag"/> | |||
|
|
132 | <arg name="clients" type="uint" summary="number of clients in tag"/> | |||
|
|
133 | <arg name="focused" type="uint" summary="1 if focused client is in this tag"/> | |||
|
|
134 | </event> | |||
|
|
135 | ||||
|
|
136 | <event name="layout"> | |||
|
|
137 | <description summary="layout changed"> | |||
|
|
138 | Indicates the layout changed. | |||
|
|
139 | </description> | |||
|
|
140 | <arg name="layout" type="uint" summary="index of the layout"/> | |||
|
|
141 | </event> | |||
|
|
142 | ||||
|
|
143 | <event name="title"> | |||
|
|
144 | <description summary="title changed"> | |||
|
|
145 | Indicates the focused client title changed. | |||
|
|
146 | </description> | |||
|
|
147 | <arg name="title" type="string"/> | |||
|
|
148 | </event> | |||
|
|
149 | ||||
|
|
150 | <event name="appid"> | |||
|
|
151 | <description summary="appid changed"> | |||
|
|
152 | Indicates the focused client appid changed. | |||
|
|
153 | </description> | |||
|
|
154 | <arg name="appid" type="string"/> | |||
|
|
155 | </event> | |||
|
|
156 | ||||
|
|
157 | <event name="layout_symbol"> | |||
|
|
158 | <description summary="layout symbol changed"> | |||
|
|
159 | Indicates the layout symbol changed. | |||
|
|
160 | </description> | |||
|
|
161 | <arg name="layout" type="string"/> | |||
|
|
162 | </event> | |||
|
|
163 | ||||
|
|
164 | <event name="frame"> | |||
|
|
165 | <description summary="frame event"> | |||
|
|
166 | Sent after all state events have been sent for a given state change. | |||
|
|
167 | </description> | |||
|
|
168 | </event> | |||
|
|
169 | ||||
|
|
170 | <event name="fullscreen" since="2"> | |||
|
|
171 | <description summary="fullscreen state changed"> | |||
|
|
172 | Indicates the fullscreen state changed. | |||
|
|
173 | </description> | |||
|
|
174 | <arg name="is_fullscreen" type="uint"/> | |||
|
|
175 | </event> | |||
|
|
176 | ||||
|
|
177 | <event name="floating" since="2"> | |||
|
|
178 | <description summary="floating state changed"> | |||
|
|
179 | Indicates the floating state changed. | |||
|
|
180 | </description> | |||
|
|
181 | <arg name="is_floating" type="uint"/> | |||
|
|
182 | </event> | |||
|
|
183 | </interface> | |||
|
|
184 | </protocol> | |||
| @@ -0,0 +1,422 | |||||
|
|
1 | <?xml version="1.0" encoding="UTF-8"?> | |||
|
|
2 | <protocol name="ext_workspace_v1"> | |||
|
|
3 | <copyright> | |||
|
|
4 | Copyright Β© 2019 Christopher Billington | |||
|
|
5 | Copyright Β© 2020 Ilia Bozhinov | |||
|
|
6 | Copyright Β© 2022 Victoria Brekenfeld | |||
|
|
7 | ||||
|
|
8 | Permission to use, copy, modify, distribute, and sell this | |||
|
|
9 | software and its documentation for any purpose is hereby granted | |||
|
|
10 | without fee, provided that the above copyright notice appear in | |||
|
|
11 | all copies and that both that copyright notice and this permission | |||
|
|
12 | notice appear in supporting documentation, and that the name of | |||
|
|
13 | the copyright holders not be used in advertising or publicity | |||
|
|
14 | pertaining to distribution of the software without specific, | |||
|
|
15 | written prior permission. The copyright holders make no | |||
|
|
16 | representations about the suitability of this software for any | |||
|
|
17 | purpose. It is provided "as is" without express or implied | |||
|
|
18 | warranty. | |||
|
|
19 | ||||
|
|
20 | THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | |||
|
|
21 | SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | |||
|
|
22 | FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | |||
|
|
23 | SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |||
|
|
24 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | |||
|
|
25 | AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | |||
|
|
26 | ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | |||
|
|
27 | THIS SOFTWARE. | |||
|
|
28 | </copyright> | |||
|
|
29 | ||||
|
|
30 | <interface name="ext_workspace_manager_v1" version="1"> | |||
|
|
31 | <description summary="list and control workspaces"> | |||
|
|
32 | Workspaces, also called virtual desktops, are groups of surfaces. A | |||
|
|
33 | compositor with a concept of workspaces may only show some such groups of | |||
|
|
34 | surfaces (those of 'active' workspaces) at a time.Β 'Activating' a | |||
|
|
35 | workspace is a request for the compositor to display that workspace's | |||
|
|
36 | surfaces as normal, whereas the compositor may hide or otherwise | |||
|
|
37 | de-emphasise surfaces that are associated only with 'inactive' workspaces. | |||
|
|
38 | Workspaces are grouped by which sets of outputs they correspond to, and | |||
|
|
39 | may contain surfaces only from those outputs. In this way, it is possible | |||
|
|
40 | for each output to have its own set of workspaces, or for all outputs (or | |||
|
|
41 | any other arbitrary grouping) to share workspaces. Compositors may | |||
|
|
42 | optionally conceptually arrange each group of workspaces in an | |||
|
|
43 | N-dimensional grid. | |||
|
|
44 | ||||
|
|
45 | The purpose of this protocol is to enable the creation of taskbars and | |||
|
|
46 | docks by providing them with a list of workspaces and their properties, | |||
|
|
47 | and allowing them to activate and deactivate workspaces. | |||
|
|
48 | ||||
|
|
49 | After a client binds the ext_workspace_manager_v1, each workspace will be | |||
|
|
50 | sent via the workspace event. | |||
|
|
51 | </description> | |||
|
|
52 | ||||
|
|
53 | <event name="workspace_group"> | |||
|
|
54 | <description summary="a workspace group has been created"> | |||
|
|
55 | This event is emitted whenever a new workspace group has been created. | |||
|
|
56 | ||||
|
|
57 | All initial details of the workspace group (outputs) will be | |||
|
|
58 | sent immediately after this event via the corresponding events in | |||
|
|
59 | ext_workspace_group_handle_v1 and ext_workspace_handle_v1. | |||
|
|
60 | </description> | |||
|
|
61 | <arg name="workspace_group" type="new_id" interface="ext_workspace_group_handle_v1"/> | |||
|
|
62 | </event> | |||
|
|
63 | ||||
|
|
64 | <event name="workspace"> | |||
|
|
65 | <description summary="workspace has been created"> | |||
|
|
66 | This event is emitted whenever a new workspace has been created. | |||
|
|
67 | ||||
|
|
68 | All initial details of the workspace (name, coordinates, state) will | |||
|
|
69 | be sent immediately after this event via the corresponding events in | |||
|
|
70 | ext_workspace_handle_v1. | |||
|
|
71 | ||||
|
|
72 | Workspaces start off unassigned to any workspace group. | |||
|
|
73 | </description> | |||
|
|
74 | <arg name="workspace" type="new_id" interface="ext_workspace_handle_v1"/> | |||
|
|
75 | </event> | |||
|
|
76 | ||||
|
|
77 | <request name="commit"> | |||
|
|
78 | <description summary="all requests about the workspaces have been sent"> | |||
|
|
79 | The client must send this request after it has finished sending other | |||
|
|
80 | requests. The compositor must process a series of requests preceding a | |||
|
|
81 | commit request atomically. | |||
|
|
82 | ||||
|
|
83 | This allows changes to the workspace properties to be seen as atomic, | |||
|
|
84 | even if they happen via multiple events, and even if they involve | |||
|
|
85 | multiple ext_workspace_handle_v1 objects, for example, deactivating one | |||
|
|
86 | workspace and activating another. | |||
|
|
87 | </description> | |||
|
|
88 | </request> | |||
|
|
89 | ||||
|
|
90 | <event name="done"> | |||
|
|
91 | <description summary="all information about the workspaces and workspace groups has been sent"> | |||
|
|
92 | This event is sent after all changes in all workspaces and workspace groups have been | |||
|
|
93 | sent. | |||
|
|
94 | ||||
|
|
95 | This allows changes to one or more ext_workspace_group_handle_v1 | |||
|
|
96 | properties and ext_workspace_handle_v1 properties | |||
|
|
97 | to be seen as atomic, even if they happen via multiple events. | |||
|
|
98 | In particular, an output moving from one workspace group to | |||
|
|
99 | another sends an output_enter event and an output_leave event to the two | |||
|
|
100 | ext_workspace_group_handle_v1 objects in question. The compositor sends | |||
|
|
101 | the done event only after updating the output information in both | |||
|
|
102 | workspace groups. | |||
|
|
103 | </description> | |||
|
|
104 | </event> | |||
|
|
105 | ||||
|
|
106 | <event name="finished" type="destructor"> | |||
|
|
107 | <description summary="the compositor has finished with the workspace_manager"> | |||
|
|
108 | This event indicates that the compositor is done sending events to the | |||
|
|
109 | ext_workspace_manager_v1. The server will destroy the object | |||
|
|
110 | immediately after sending this request. | |||
|
|
111 | </description> | |||
|
|
112 | </event> | |||
|
|
113 | ||||
|
|
114 | <request name="stop"> | |||
|
|
115 | <description summary="stop sending events"> | |||
|
|
116 | Indicates the client no longer wishes to receive events for new | |||
|
|
117 | workspace groups. However the compositor may emit further workspace | |||
|
|
118 | events, until the finished event is emitted. The compositor is expected | |||
|
|
119 | to send the finished event eventually once the stop request has been processed. | |||
|
|
120 | ||||
|
|
121 | The client must not send any requests after this one, doing so will raise a wl_display | |||
|
|
122 | invalid_object error. | |||
|
|
123 | </description> | |||
|
|
124 | </request> | |||
|
|
125 | ||||
|
|
126 | </interface> | |||
|
|
127 | ||||
|
|
128 | <interface name="ext_workspace_group_handle_v1" version="1"> | |||
|
|
129 | <description summary="a workspace group assigned to a set of outputs"> | |||
|
|
130 | A ext_workspace_group_handle_v1 object represents a workspace group | |||
|
|
131 | that is assigned a set of outputs and contains a number of workspaces. | |||
|
|
132 | ||||
|
|
133 | The set of outputs assigned to the workspace group is conveyed to the client via | |||
|
|
134 | output_enter and output_leave events, and its workspaces are conveyed with | |||
|
|
135 | workspace events. | |||
|
|
136 | ||||
|
|
137 | For example, a compositor which has a set of workspaces for each output may | |||
|
|
138 | advertise a workspace group (and its workspaces) per output, whereas a compositor | |||
|
|
139 | where a workspace spans all outputs may advertise a single workspace group for all | |||
|
|
140 | outputs. | |||
|
|
141 | </description> | |||
|
|
142 | ||||
|
|
143 | <enum name="group_capabilities" bitfield="true"> | |||
|
|
144 | <entry name="create_workspace" value="1" summary="create_workspace request is available"/> | |||
|
|
145 | </enum> | |||
|
|
146 | ||||
|
|
147 | <event name="capabilities"> | |||
|
|
148 | <description summary="compositor capabilities"> | |||
|
|
149 | This event advertises the capabilities supported by the compositor. If | |||
|
|
150 | a capability isn't supported, clients should hide or disable the UI | |||
|
|
151 | elements that expose this functionality. For instance, if the | |||
|
|
152 | compositor doesn't advertise support for creating workspaces, a button | |||
|
|
153 | triggering the create_workspace request should not be displayed. | |||
|
|
154 | ||||
|
|
155 | The compositor will ignore requests it doesn't support. For instance, | |||
|
|
156 | a compositor which doesn't advertise support for creating workspaces will ignore | |||
|
|
157 | create_workspace requests. | |||
|
|
158 | ||||
|
|
159 | Compositors must send this event once after creation of an | |||
|
|
160 | ext_workspace_group_handle_v1. When the capabilities change, compositors | |||
|
|
161 | must send this event again. | |||
|
|
162 | </description> | |||
|
|
163 | <arg name="capabilities" type="uint" summary="capabilities" enum="group_capabilities"/> | |||
|
|
164 | </event> | |||
|
|
165 | ||||
|
|
166 | <event name="output_enter"> | |||
|
|
167 | <description summary="output assigned to workspace group"> | |||
|
|
168 | This event is emitted whenever an output is assigned to the workspace | |||
|
|
169 | group or a new `wl_output` object is bound by the client, which was already | |||
|
|
170 | assigned to this workspace_group. | |||
|
|
171 | </description> | |||
|
|
172 | <arg name="output" type="object" interface="wl_output"/> | |||
|
|
173 | </event> | |||
|
|
174 | ||||
|
|
175 | <event name="output_leave"> | |||
|
|
176 | <description summary="output removed from workspace group"> | |||
|
|
177 | This event is emitted whenever an output is removed from the workspace | |||
|
|
178 | group. | |||
|
|
179 | </description> | |||
|
|
180 | <arg name="output" type="object" interface="wl_output"/> | |||
|
|
181 | </event> | |||
|
|
182 | ||||
|
|
183 | <event name="workspace_enter"> | |||
|
|
184 | <description summary="workspace added to workspace group"> | |||
|
|
185 | This event is emitted whenever a workspace is assigned to this group. | |||
|
|
186 | A workspace may only ever be assigned to a single group at a single point | |||
|
|
187 | in time, but can be re-assigned during its lifetime. | |||
|
|
188 | </description> | |||
|
|
189 | <arg name="workspace" type="object" interface="ext_workspace_handle_v1"/> | |||
|
|
190 | </event> | |||
|
|
191 | ||||
|
|
192 | <event name="workspace_leave"> | |||
|
|
193 | <description summary="workspace removed from workspace group"> | |||
|
|
194 | This event is emitted whenever a workspace is removed from this group. | |||
|
|
195 | </description> | |||
|
|
196 | <arg name="workspace" type="object" interface="ext_workspace_handle_v1"/> | |||
|
|
197 | </event> | |||
|
|
198 | ||||
|
|
199 | <event name="removed"> | |||
|
|
200 | <description summary="this workspace group has been removed"> | |||
|
|
201 | This event is send when the group associated with the ext_workspace_group_handle_v1 | |||
|
|
202 | has been removed. After sending this request the compositor will immediately consider | |||
|
|
203 | the object inert. Any requests will be ignored except the destroy request. | |||
|
|
204 | It is guaranteed there won't be any more events referencing this | |||
|
|
205 | ext_workspace_group_handle_v1. | |||
|
|
206 | ||||
|
|
207 | The compositor must remove all workspaces belonging to a workspace group | |||
|
|
208 | via a workspace_leave event before removing the workspace group. | |||
|
|
209 | </description> | |||
|
|
210 | </event> | |||
|
|
211 | ||||
|
|
212 | <request name="create_workspace"> | |||
|
|
213 | <description summary="create a new workspace"> | |||
|
|
214 | Request that the compositor create a new workspace with the given name | |||
|
|
215 | and assign it to this group. | |||
|
|
216 | ||||
|
|
217 | There is no guarantee that the compositor will create a new workspace, | |||
|
|
218 | or that the created workspace will have the provided name. | |||
|
|
219 | </description> | |||
|
|
220 | <arg name="workspace" type="string"/> | |||
|
|
221 | </request> | |||
|
|
222 | ||||
|
|
223 | <request name="destroy" type="destructor"> | |||
|
|
224 | <description summary="destroy the ext_workspace_group_handle_v1 object"> | |||
|
|
225 | Destroys the ext_workspace_group_handle_v1 object. | |||
|
|
226 | ||||
|
|
227 | This request should be send either when the client does not want to | |||
|
|
228 | use the workspace group object any more or after the removed event to finalize | |||
|
|
229 | the destruction of the object. | |||
|
|
230 | </description> | |||
|
|
231 | </request> | |||
|
|
232 | </interface> | |||
|
|
233 | ||||
|
|
234 | <interface name="ext_workspace_handle_v1" version="1"> | |||
|
|
235 | <description summary="a workspace handing a group of surfaces"> | |||
|
|
236 | A ext_workspace_handle_v1 object represents a workspace that handles a | |||
|
|
237 | group of surfaces. | |||
|
|
238 | ||||
|
|
239 | Each workspace has: | |||
|
|
240 | - a name, conveyed to the client with the name event | |||
|
|
241 | - potentially an id conveyed with the id event | |||
|
|
242 | - a list of states, conveyed to the client with the state event | |||
|
|
243 | - and optionally a set of coordinates, conveyed to the client with the | |||
|
|
244 | coordinates event | |||
|
|
245 | ||||
|
|
246 | The client may request that the compositor activate or deactivate the workspace. | |||
|
|
247 | ||||
|
|
248 | Each workspace can belong to only a single workspace group. | |||
|
|
249 | Depending on the compositor policy, there might be workspaces with | |||
|
|
250 | the same name in different workspace groups, but these workspaces are still | |||
|
|
251 | separate (e.g. one of them might be active while the other is not). | |||
|
|
252 | </description> | |||
|
|
253 | ||||
|
|
254 | <event name="id"> | |||
|
|
255 | <description summary="workspace id"> | |||
|
|
256 | If this event is emitted, it will be send immediately after the | |||
|
|
257 | ext_workspace_handle_v1 is created or when an id is assigned to | |||
|
|
258 | a workspace (at most once during its lifetime). | |||
|
|
259 | ||||
|
|
260 | An id will never change during the lifetime of the `ext_workspace_handle_v1` | |||
|
|
261 | and is guaranteed to be unique during its lifetime. | |||
|
|
262 | ||||
|
|
263 | Ids are not human-readable and shouldn't be displayed, use `name` for that purpose. | |||
|
|
264 | ||||
|
|
265 | Compositors are expected to only send ids for workspaces likely stable across multiple | |||
|
|
266 | sessions and can be used by clients to store preferences for workspaces. Workspaces without | |||
|
|
267 | ids should be considered temporary and any data associated with them should be deleted once | |||
|
|
268 | the respective object is lost. | |||
|
|
269 | </description> | |||
|
|
270 | <arg name="id" type="string"/> | |||
|
|
271 | </event> | |||
|
|
272 | ||||
|
|
273 | <event name="name"> | |||
|
|
274 | <description summary="workspace name changed"> | |||
|
|
275 | This event is emitted immediately after the ext_workspace_handle_v1 is | |||
|
|
276 | created and whenever the name of the workspace changes. | |||
|
|
277 | ||||
|
|
278 | A name is meant to be human-readable and can be displayed to a user. | |||
|
|
279 | Unlike the id it is neither stable nor unique. | |||
|
|
280 | </description> | |||
|
|
281 | <arg name="name" type="string"/> | |||
|
|
282 | </event> | |||
|
|
283 | ||||
|
|
284 | <event name="coordinates"> | |||
|
|
285 | <description summary="workspace coordinates changed"> | |||
|
|
286 | This event is used to organize workspaces into an N-dimensional grid | |||
|
|
287 | within a workspace group, and if supported, is emitted immediately after | |||
|
|
288 | the ext_workspace_handle_v1 is created and whenever the coordinates of | |||
|
|
289 | the workspace change. Compositors may not send this event if they do not | |||
|
|
290 | conceptually arrange workspaces in this way. If compositors simply | |||
|
|
291 | number workspaces, without any geometric interpretation, they may send | |||
|
|
292 | 1D coordinates, which clients should not interpret as implying any | |||
|
|
293 | geometry. Sending an empty array means that the compositor no longer | |||
|
|
294 | orders the workspace geometrically. | |||
|
|
295 | ||||
|
|
296 | Coordinates have an arbitrary number of dimensions N with an uint32 | |||
|
|
297 | position along each dimension. By convention if N > 1, the first | |||
|
|
298 | dimension is X, the second Y, the third Z, and so on. The compositor may | |||
|
|
299 | chose to utilize these events for a more novel workspace layout | |||
|
|
300 | convention, however. No guarantee is made about the grid being filled or | |||
|
|
301 | bounded; there may be a workspace at coordinate 1 and another at | |||
|
|
302 | coordinate 1000 and none in between. Within a workspace group, however, | |||
|
|
303 | workspaces must have unique coordinates of equal dimensionality. | |||
|
|
304 | </description> | |||
|
|
305 | <arg name="coordinates" type="array"/> | |||
|
|
306 | </event> | |||
|
|
307 | ||||
|
|
308 | <enum name="state" bitfield="true"> | |||
|
|
309 | <description summary="types of states on the workspace"> | |||
|
|
310 | The different states that a workspace can have. | |||
|
|
311 | </description> | |||
|
|
312 | ||||
|
|
313 | <entry name="active" value="1" summary="the workspace is active"/> | |||
|
|
314 | <entry name="urgent" value="2" summary="the workspace requests attention"/> | |||
|
|
315 | <entry name="hidden" value="4"> | |||
|
|
316 | <description summary="the workspace is not visible"> | |||
|
|
317 | The workspace is not visible in its workspace group, and clients | |||
|
|
318 | attempting to visualize the compositor workspace state should not | |||
|
|
319 | display such workspaces. | |||
|
|
320 | </description> | |||
|
|
321 | </entry> | |||
|
|
322 | </enum> | |||
|
|
323 | ||||
|
|
324 | <event name="state"> | |||
|
|
325 | <description summary="the state of the workspace changed"> | |||
|
|
326 | This event is emitted immediately after the ext_workspace_handle_v1 is | |||
|
|
327 | created and each time the workspace state changes, either because of a | |||
|
|
328 | compositor action or because of a request in this protocol. | |||
|
|
329 | ||||
|
|
330 | Missing states convey the opposite meaning, e.g. an unset active bit | |||
|
|
331 | means the workspace is currently inactive. | |||
|
|
332 | </description> | |||
|
|
333 | <arg name="state" type="uint" enum="state"/> | |||
|
|
334 | </event> | |||
|
|
335 | ||||
|
|
336 | <enum name="workspace_capabilities" bitfield="true"> | |||
|
|
337 | <entry name="activate" value="1" summary="activate request is available"/> | |||
|
|
338 | <entry name="deactivate" value="2" summary="deactivate request is available"/> | |||
|
|
339 | <entry name="remove" value="4" summary="remove request is available"/> | |||
|
|
340 | <entry name="assign" value="8" summary="assign request is available"/> | |||
|
|
341 | </enum> | |||
|
|
342 | ||||
|
|
343 | <event name="capabilities"> | |||
|
|
344 | <description summary="compositor capabilities"> | |||
|
|
345 | This event advertises the capabilities supported by the compositor. If | |||
|
|
346 | a capability isn't supported, clients should hide or disable the UI | |||
|
|
347 | elements that expose this functionality. For instance, if the | |||
|
|
348 | compositor doesn't advertise support for removing workspaces, a button | |||
|
|
349 | triggering the remove request should not be displayed. | |||
|
|
350 | ||||
|
|
351 | The compositor will ignore requests it doesn't support. For instance, | |||
|
|
352 | a compositor which doesn't advertise support for remove will ignore | |||
|
|
353 | remove requests. | |||
|
|
354 | ||||
|
|
355 | Compositors must send this event once after creation of an | |||
|
|
356 | ext_workspace_handle_v1 . When the capabilities change, compositors | |||
|
|
357 | must send this event again. | |||
|
|
358 | </description> | |||
|
|
359 | <arg name="capabilities" type="uint" summary="capabilities" enum="workspace_capabilities"/> | |||
|
|
360 | </event> | |||
|
|
361 | ||||
|
|
362 | <event name="removed"> | |||
|
|
363 | <description summary="this workspace has been removed"> | |||
|
|
364 | This event is send when the workspace associated with the ext_workspace_handle_v1 | |||
|
|
365 | has been removed. After sending this request, the compositor will immediately consider | |||
|
|
366 | the object inert. Any requests will be ignored except the destroy request. | |||
|
|
367 | ||||
|
|
368 | It is guaranteed there won't be any more events referencing this | |||
|
|
369 | ext_workspace_handle_v1. | |||
|
|
370 | ||||
|
|
371 | The compositor must only remove a workspaces not currently belonging to any | |||
|
|
372 | workspace_group. | |||
|
|
373 | </description> | |||
|
|
374 | </event> | |||
|
|
375 | ||||
|
|
376 | <request name="destroy" type="destructor"> | |||
|
|
377 | <description summary="destroy the ext_workspace_handle_v1 object"> | |||
|
|
378 | Destroys the ext_workspace_handle_v1 object. | |||
|
|
379 | ||||
|
|
380 | This request should be made either when the client does not want to | |||
|
|
381 | use the workspace object any more or after the remove event to finalize | |||
|
|
382 | the destruction of the object. | |||
|
|
383 | </description> | |||
|
|
384 | </request> | |||
|
|
385 | ||||
|
|
386 | <request name="activate"> | |||
|
|
387 | <description summary="activate the workspace"> | |||
|
|
388 | Request that this workspace be activated. | |||
|
|
389 | ||||
|
|
390 | There is no guarantee the workspace will be actually activated, and | |||
|
|
391 | behaviour may be compositor-dependent. For example, activating a | |||
|
|
392 | workspace may or may not deactivate all other workspaces in the same | |||
|
|
393 | group. | |||
|
|
394 | </description> | |||
|
|
395 | </request> | |||
|
|
396 | ||||
|
|
397 | <request name="deactivate"> | |||
|
|
398 | <description summary="deactivate the workspace"> | |||
|
|
399 | Request that this workspace be deactivated. | |||
|
|
400 | ||||
|
|
401 | There is no guarantee the workspace will be actually deactivated. | |||
|
|
402 | </description> | |||
|
|
403 | </request> | |||
|
|
404 | ||||
|
|
405 | <request name="assign"> | |||
|
|
406 | <description summary="assign workspace to group"> | |||
|
|
407 | Requests that this workspace is assigned to the given workspace group. | |||
|
|
408 | ||||
|
|
409 | There is no guarantee the workspace will be assigned. | |||
|
|
410 | </description> | |||
|
|
411 | <arg name="workspace_group" type="object" interface="ext_workspace_group_handle_v1"/> | |||
|
|
412 | </request> | |||
|
|
413 | ||||
|
|
414 | <request name="remove"> | |||
|
|
415 | <description summary="remove the workspace"> | |||
|
|
416 | Request that this workspace be removed. | |||
|
|
417 | ||||
|
|
418 | There is no guarantee the workspace will be actually removed. | |||
|
|
419 | </description> | |||
|
|
420 | </request> | |||
|
|
421 | </interface> | |||
|
|
422 | </protocol> | |||
| @@ -0,0 +1,390 | |||||
|
|
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> | |||
| @@ -0,0 +1,128 | |||||
|
|
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> | |||
| @@ -0,0 +1,55 | |||||
|
|
1 | **smsg** - send ipc messages to swindle (forked from [this repository](https://codeberg.org/notchoc/dwlmsg)) | |||
|
|
2 | ||||
|
|
3 | ``` | |||
|
|
4 | usage: smsg [-OTLPF] | |||
|
|
5 | smsg [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>] | |||
|
|
6 | smsg [-o <output>] (-g | -w) [-FOotlcvmf] | |||
|
|
7 | ``` | |||
|
|
8 | ||||
|
|
9 | ``` | |||
|
|
10 | options: | |||
|
|
11 | -g get | |||
|
|
12 | -s set | |||
|
|
13 | -w watch | |||
|
|
14 | -O get all outputs | |||
|
|
15 | -T get number of tags | |||
|
|
16 | -L get all available layouts | |||
|
|
17 | -P get compositor pid | |||
|
|
18 | -F get focused output | |||
|
|
19 | -o select output | |||
|
|
20 | -t get/set selected tags (set with [+-^.], overwrite with ! prefix) | |||
|
|
21 | -l get/set current layout | |||
|
|
22 | -c get title and appid of focused client | |||
|
|
23 | -v get visibility of statusbar | |||
|
|
24 | -m get fullscreen status | |||
|
|
25 | -f get floating status | |||
|
|
26 | ``` | |||
|
|
27 | ||||
|
|
28 | ``` | |||
|
|
29 | examples: | |||
|
|
30 | # act like dwl stdout | |||
|
|
31 | smsg -w | |||
|
|
32 | # watch focused client appid and title | |||
|
|
33 | smsg -w -c | |||
|
|
34 | # get currently focused output | |||
|
|
35 | smsg -F | |||
|
|
36 | # watch for focused output changes | |||
|
|
37 | smsg -w -F | |||
|
|
38 | # get all available outputs | |||
|
|
39 | smsg -O | |||
|
|
40 | # watch available outputs | |||
|
|
41 | smsg -w -O | |||
|
|
42 | # select tag 1, deselect tag 2, toggle tag 4 on output eDP-1 | |||
|
|
43 | smsg -o eDP-1 -s -t +-.^ | |||
|
|
44 | # toggle tag 3, overwriting current tagset (yes, zero-indexed) | |||
|
|
45 | smsg -t !2^ | |||
|
|
46 | # select tag 8 on current output | |||
|
|
47 | smsg -s -t 7 | |||
|
|
48 | # deselect tag 8 on current output | |||
|
|
49 | smsg -s -t 7- | |||
|
|
50 | # switch to first layout (order given by smsg -L) | |||
|
|
51 | smsg -l 0 | |||
|
|
52 | # switch to floating layout | |||
|
|
53 | smsg -l '><>' | |||
|
|
54 | ``` | |||
|
|
55 | ||||
| @@ -0,0 +1,58 | |||||
|
|
1 | #ifndef ARG_H__ | |||
|
|
2 | #define ARG_H__ | |||
|
|
3 | ||||
|
|
4 | extern char *argv0; | |||
|
|
5 | ||||
|
|
6 | #define ARGBEGIN for (argv0 = *argv, argv++, argc--;\ | |||
|
|
7 | argv[0] && argv[0][0] == '-'\ | |||
|
|
8 | && argv[0][1];\ | |||
|
|
9 | argc--, argv++) {\ | |||
|
|
10 | char argc_;\ | |||
|
|
11 | char **argv_;\ | |||
|
|
12 | int brk_;\ | |||
|
|
13 | if (argv[0][1] == '-' && argv[0][2] == '\0') {\ | |||
|
|
14 | argv++;\ | |||
|
|
15 | argc--;\ | |||
|
|
16 | break;\ | |||
|
|
17 | }\ | |||
|
|
18 | for (brk_ = 0, argv[0]++, argv_ = argv;\ | |||
|
|
19 | argv[0][0] && !brk_;\ | |||
|
|
20 | argv[0]++) {\ | |||
|
|
21 | if (argv_ != argv)\ | |||
|
|
22 | break;\ | |||
|
|
23 | argc_ = argv[0][0];\ | |||
|
|
24 | switch (argc_) | |||
|
|
25 | ||||
|
|
26 | #define ARGNUM case '0':\ | |||
|
|
27 | case '1':\ | |||
|
|
28 | case '2':\ | |||
|
|
29 | case '3':\ | |||
|
|
30 | case '4':\ | |||
|
|
31 | case '5':\ | |||
|
|
32 | case '6':\ | |||
|
|
33 | case '7':\ | |||
|
|
34 | case '8':\ | |||
|
|
35 | case '9' | |||
|
|
36 | ||||
|
|
37 | #define ARGEND }\ | |||
|
|
38 | } | |||
|
|
39 | ||||
|
|
40 | #define ARGC() argc_ | |||
|
|
41 | ||||
|
|
42 | #define ARGNUMF() (brk_ = 1, estrtonum(argv[0], 0, INT_MAX)) | |||
|
|
43 | ||||
|
|
44 | #define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\ | |||
|
|
45 | ((x), abort(), (char *)0) :\ | |||
|
|
46 | (brk_ = 1, (argv[0][1] != '\0')?\ | |||
|
|
47 | (&argv[0][1]) :\ | |||
|
|
48 | (argc--, argv++, argv[0]))) | |||
|
|
49 | ||||
|
|
50 | #define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\ | |||
|
|
51 | (char *)0 :\ | |||
|
|
52 | (brk_ = 1, (argv[0][1] != '\0')?\ | |||
|
|
53 | (&argv[0][1]) :\ | |||
|
|
54 | (argc--, argv++, argv[0]))) | |||
|
|
55 | ||||
|
|
56 | #define LNGARG() &argv[0][0] | |||
|
|
57 | ||||
|
|
58 | #endif | |||
| @@ -0,0 +1,181 | |||||
|
|
1 | <?xml version="1.0" encoding="utf-8"?> | |||
|
|
2 | <!-- | |||
|
|
3 | This is largely ripped from somebar's ipc patchset; just with some personal modifications. | |||
|
|
4 | I would probably just submit raphi's patchset but I don't think that would be polite. | |||
|
|
5 | --> | |||
|
|
6 | <protocol name="dwl_ipc_unstable_v2"> | |||
|
|
7 | <description summary="inter-proccess-communication about dwl's state"> | |||
|
|
8 | This protocol allows clients to update and get updates from dwl. | |||
|
|
9 | ||||
|
|
10 | Warning! The protocol described in this file is experimental and | |||
|
|
11 | backward incompatible changes may be made. Backward compatible | |||
|
|
12 | changes may be added together with the corresponding interface | |||
|
|
13 | version bump. | |||
|
|
14 | Backward incompatible changes are done by bumping the version | |||
|
|
15 | number in the protocol and interface names and resetting the | |||
|
|
16 | interface version. Once the protocol is to be declared stable, | |||
|
|
17 | the 'z' prefix and the version number in the protocol and | |||
|
|
18 | interface names are removed and the interface version number is | |||
|
|
19 | reset. | |||
|
|
20 | </description> | |||
|
|
21 | ||||
|
|
22 | <interface name="zdwl_ipc_manager_v2" version="2"> | |||
|
|
23 | <description summary="manage dwl state"> | |||
|
|
24 | This interface is exposed as a global in wl_registry. | |||
|
|
25 | ||||
|
|
26 | Clients can use this interface to get a dwl_ipc_output. | |||
|
|
27 | After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. | |||
|
|
28 | The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. | |||
|
|
29 | </description> | |||
|
|
30 | ||||
|
|
31 | <request name="release" type="destructor"> | |||
|
|
32 | <description summary="release dwl_ipc_manager"> | |||
|
|
33 | Indicates that the client will not the dwl_ipc_manager object anymore. | |||
|
|
34 | Objects created through this instance are not affected. | |||
|
|
35 | </description> | |||
|
|
36 | </request> | |||
|
|
37 | ||||
|
|
38 | <request name="get_output"> | |||
|
|
39 | <description summary="get a dwl_ipc_outout for a wl_output"> | |||
|
|
40 | Get a dwl_ipc_outout for the specified wl_output. | |||
|
|
41 | </description> | |||
|
|
42 | <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/> | |||
|
|
43 | <arg name="output" type="object" interface="wl_output"/> | |||
|
|
44 | </request> | |||
|
|
45 | ||||
|
|
46 | <event name="tags"> | |||
|
|
47 | <description summary="Announces tag amount"> | |||
|
|
48 | This event is sent after binding. | |||
|
|
49 | A roundtrip after binding guarantees the client recieved all tags. | |||
|
|
50 | </description> | |||
|
|
51 | <arg name="amount" type="uint"/> | |||
|
|
52 | </event> | |||
|
|
53 | ||||
|
|
54 | <event name="layout"> | |||
|
|
55 | <description summary="Announces a layout"> | |||
|
|
56 | This event is sent after binding. | |||
|
|
57 | A roundtrip after binding guarantees the client recieved all layouts. | |||
|
|
58 | </description> | |||
|
|
59 | <arg name="name" type="string"/> | |||
|
|
60 | </event> | |||
|
|
61 | </interface> | |||
|
|
62 | ||||
|
|
63 | <interface name="zdwl_ipc_output_v2" version="2"> | |||
|
|
64 | <description summary="control dwl output"> | |||
|
|
65 | Observe and control a dwl output. | |||
|
|
66 | ||||
|
|
67 | Events are double-buffered: | |||
|
|
68 | Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. | |||
|
|
69 | ||||
|
|
70 | Request are not double-buffered: | |||
|
|
71 | The compositor will update immediately upon request. | |||
|
|
72 | </description> | |||
|
|
73 | ||||
|
|
74 | <enum name="tag_state"> | |||
|
|
75 | <entry name="none" value="0" summary="no state"/> | |||
|
|
76 | <entry name="active" value="1" summary="tag is active"/> | |||
|
|
77 | <entry name="urgent" value="2" summary="tag has at least one urgent client"/> | |||
|
|
78 | </enum> | |||
|
|
79 | ||||
|
|
80 | <request name="release" type="destructor"> | |||
|
|
81 | <description summary="release dwl_ipc_outout"> | |||
|
|
82 | Indicates to that the client no longer needs this dwl_ipc_output. | |||
|
|
83 | </description> | |||
|
|
84 | </request> | |||
|
|
85 | ||||
|
|
86 | <event name="toggle_visibility"> | |||
|
|
87 | <description summary="Toggle client visibilty"> | |||
|
|
88 | Indicates the client should hide or show themselves. | |||
|
|
89 | If the client is visible then hide, if hidden then show. | |||
|
|
90 | </description> | |||
|
|
91 | </event> | |||
|
|
92 | ||||
|
|
93 | <event name="active"> | |||
|
|
94 | <description summary="Update the selected output."> | |||
|
|
95 | Indicates if the output is active. Zero is invalid, nonzero is valid. | |||
|
|
96 | </description> | |||
|
|
97 | <arg name="active" type="uint"/> | |||
|
|
98 | </event> | |||
|
|
99 | ||||
|
|
100 | <event name="tag"> | |||
|
|
101 | <description summary="Update the state of a tag."> | |||
|
|
102 | Indicates that a tag has been updated. | |||
|
|
103 | </description> | |||
|
|
104 | <arg name="tag" type="uint" summary="Index of the tag"/> | |||
|
|
105 | <arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/> | |||
|
|
106 | <arg name="clients" type="uint" summary="The number of clients in the tag."/> | |||
|
|
107 | <arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/> | |||
|
|
108 | </event> | |||
|
|
109 | ||||
|
|
110 | <event name="layout"> | |||
|
|
111 | <description summary="Update the layout."> | |||
|
|
112 | Indicates a new layout is selected. | |||
|
|
113 | </description> | |||
|
|
114 | <arg name="layout" type="uint" summary="Index of the layout."/> | |||
|
|
115 | </event> | |||
|
|
116 | ||||
|
|
117 | <event name="title"> | |||
|
|
118 | <description summary="Update the title."> | |||
|
|
119 | Indicates the title has changed. | |||
|
|
120 | </description> | |||
|
|
121 | <arg name="title" type="string" summary="The new title name."/> | |||
|
|
122 | </event> | |||
|
|
123 | ||||
|
|
124 | <event name="appid" since="1"> | |||
|
|
125 | <description summary="Update the appid."> | |||
|
|
126 | Indicates the appid has changed. | |||
|
|
127 | </description> | |||
|
|
128 | <arg name="appid" type="string" summary="The new appid."/> | |||
|
|
129 | </event> | |||
|
|
130 | ||||
|
|
131 | <event name="layout_symbol" since="1"> | |||
|
|
132 | <description summary="Update the current layout symbol"> | |||
|
|
133 | Indicates the layout has changed. Since layout symbols are dynamic. | |||
|
|
134 | As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. | |||
|
|
135 | You can ignore the zdwl_ipc_output.layout event. | |||
|
|
136 | </description> | |||
|
|
137 | <arg name="layout" type="string" summary="The new layout"/> | |||
|
|
138 | </event> | |||
|
|
139 | ||||
|
|
140 | <event name="frame"> | |||
|
|
141 | <description summary="The update sequence is done."> | |||
|
|
142 | Indicates that a sequence of status updates have finished and the client should redraw. | |||
|
|
143 | </description> | |||
|
|
144 | </event> | |||
|
|
145 | ||||
|
|
146 | <request name="set_tags"> | |||
|
|
147 | <description summary="Set the active tags of this output"/> | |||
|
|
148 | <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/> | |||
|
|
149 | <arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/> | |||
|
|
150 | </request> | |||
|
|
151 | ||||
|
|
152 | <request name="set_client_tags"> | |||
|
|
153 | <description summary="Set the tags of the focused client."> | |||
|
|
154 | The tags are updated as follows: | |||
|
|
155 | new_tags = (current_tags AND and_tags) XOR xor_tags | |||
|
|
156 | </description> | |||
|
|
157 | <arg name="and_tags" type="uint"/> | |||
|
|
158 | <arg name="xor_tags" type="uint"/> | |||
|
|
159 | </request> | |||
|
|
160 | ||||
|
|
161 | <request name="set_layout"> | |||
|
|
162 | <description summary="Set the layout of this output"/> | |||
|
|
163 | <arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/> | |||
|
|
164 | </request> | |||
|
|
165 | ||||
|
|
166 | <!-- Version 2 --> | |||
|
|
167 | <event name="fullscreen" since="2"> | |||
|
|
168 | <description summary="Update fullscreen status"> | |||
|
|
169 | Indicates if the selected client on this output is fullscreen. | |||
|
|
170 | </description> | |||
|
|
171 | <arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/> | |||
|
|
172 | </event> | |||
|
|
173 | ||||
|
|
174 | <event name="floating" since="2"> | |||
|
|
175 | <description summary="Update the floating status"> | |||
|
|
176 | Indicates if the selected client on this output is floating. | |||
|
|
177 | </description> | |||
|
|
178 | <arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/> | |||
|
|
179 | </event> | |||
|
|
180 | </interface> | |||
|
|
181 | </protocol> | |||
| This diff has been collapsed as it changes many lines, (516 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,516 | |||||
|
|
1 | #define _GNU_SOURCE | |||
|
|
2 | #include "arg.h" | |||
|
|
3 | #include <poll.h> | |||
|
|
4 | #include <stdio.h> | |||
|
|
5 | #include <stdlib.h> | |||
|
|
6 | #include <string.h> | |||
|
|
7 | #include <sys/socket.h> | |||
|
|
8 | #include <wayland-client.h> | |||
|
|
9 | #include <wayland-util.h> | |||
|
|
10 | #include "dwl-ipc-unstable-v2-protocol.h" | |||
|
|
11 | ||||
|
|
12 | #define die(fmt, ...) do { fprintf(stderr, fmt "\n", ##__VA_ARGS__); exit(EXIT_FAILURE); } while (0) | |||
|
|
13 | ||||
|
|
14 | char *argv0; | |||
|
|
15 | ||||
|
|
16 | static enum { | |||
|
|
17 | NONE = 0, | |||
|
|
18 | SET = 1<<0, | |||
|
|
19 | GET = 1<<1, | |||
|
|
20 | WATCH = 1<<2 | GET, | |||
|
|
21 | } mode = NONE; | |||
|
|
22 | ||||
|
|
23 | static int Oflag; | |||
|
|
24 | static int Tflag; | |||
|
|
25 | static int Lflag; | |||
|
|
26 | static int Pflag; | |||
|
|
27 | static int Fflag; | |||
|
|
28 | static int oflag; | |||
|
|
29 | static int tflag; | |||
|
|
30 | static int lflag; | |||
|
|
31 | static int cflag; | |||
|
|
32 | static int vflag; | |||
|
|
33 | static int mflag; | |||
|
|
34 | static int fflag; | |||
|
|
35 | ||||
|
|
36 | static uint32_t occ, seltags, sel, urg; | |||
|
|
37 | ||||
|
|
38 | static char *output_name; | |||
|
|
39 | static char *focused_output; | |||
|
|
40 | static uint32_t tagcount; | |||
|
|
41 | static char *tagset; | |||
|
|
42 | static char *layout_name; | |||
|
|
43 | static size_t layoutcount, layout_idx; | |||
|
|
44 | static char *client_tags; | |||
|
|
45 | ||||
|
|
46 | struct output { | |||
|
|
47 | char *output_name; | |||
|
|
48 | uint32_t name; | |||
|
|
49 | }; | |||
|
|
50 | static size_t outputs_buflen = 4; | |||
|
|
51 | static struct output *outputs; | |||
|
|
52 | static size_t outputcount; | |||
|
|
53 | ||||
|
|
54 | static struct wl_display *display; | |||
|
|
55 | static struct wl_registry *registry; | |||
|
|
56 | static struct zdwl_ipc_manager_v2 *dwl_ipc_manager; | |||
|
|
57 | ||||
|
|
58 | static void | |||
|
|
59 | dwl_ipc_tags(void *data, struct zdwl_ipc_manager_v2 *dwl_ipc_manager, uint32_t count) | |||
|
|
60 | { | |||
|
|
61 | tagcount = count; | |||
|
|
62 | if (Tflag && mode & GET) printf("%d\n", tagcount); | |||
|
|
63 | } | |||
|
|
64 | ||||
|
|
65 | static void | |||
|
|
66 | dwl_ipc_layout(void *data, struct zdwl_ipc_manager_v2 *dwl_ipc_manager, const char *name) | |||
|
|
67 | { | |||
|
|
68 | if (lflag && mode & SET && strcmp(layout_name, name) == 0) | |||
|
|
69 | layout_idx = layoutcount; | |||
|
|
70 | if (Lflag && mode & GET) printf("%s\n", name); | |||
|
|
71 | layoutcount++; | |||
|
|
72 | } | |||
|
|
73 | ||||
|
|
74 | static const struct zdwl_ipc_manager_v2_listener dwl_ipc_listener = { | |||
|
|
75 | .tags = dwl_ipc_tags, | |||
|
|
76 | .layout = dwl_ipc_layout | |||
|
|
77 | }; | |||
|
|
78 | ||||
|
|
79 | static void | |||
|
|
80 | dwl_ipc_output_toggle_visibility(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output) | |||
|
|
81 | { | |||
|
|
82 | if (!vflag) return; | |||
|
|
83 | char *oname = data; | |||
|
|
84 | if (oname) printf("%s ", oname); | |||
|
|
85 | printf("toggle\n"); | |||
|
|
86 | } | |||
|
|
87 | ||||
|
|
88 | static void | |||
|
|
89 | dwl_ipc_output_active(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
90 | uint32_t active) | |||
|
|
91 | { | |||
|
|
92 | if (active && (!output_name || !*output_name)) | |||
|
|
93 | output_name = strdup(data); | |||
|
|
94 | if (active) { | |||
|
|
95 | free(focused_output); | |||
|
|
96 | focused_output = data ? strdup(data) : NULL; | |||
|
|
97 | } | |||
|
|
98 | if (!oflag) return; | |||
|
|
99 | char *oname = data; | |||
|
|
100 | if (oname) printf("%s ", oname); | |||
|
|
101 | printf("selmon %u\n", !!active); | |||
|
|
102 | } | |||
|
|
103 | ||||
|
|
104 | static void | |||
|
|
105 | dwl_ipc_output_tag(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
106 | uint32_t tag, uint32_t state, uint32_t clients, uint32_t focused) | |||
|
|
107 | { | |||
|
|
108 | if (!tflag) return; | |||
|
|
109 | if (state != ZDWL_IPC_OUTPUT_V2_TAG_STATE_NONE) seltags |= 1<<tag; | |||
|
|
110 | if (state == ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE) urg |= 1<<tag; | |||
|
|
111 | if (clients > 0) occ |= 1<<tag; | |||
|
|
112 | if (!(mode & GET)) return; | |||
|
|
113 | if (data) printf("%s ", (char *)data); | |||
|
|
114 | printf("tag %u %u %u %u\n", tag, state, clients, focused); | |||
|
|
115 | } | |||
|
|
116 | ||||
|
|
117 | static void | |||
|
|
118 | dwl_ipc_output_layout(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
119 | uint32_t layout) | |||
|
|
120 | { | |||
|
|
121 | } | |||
|
|
122 | ||||
|
|
123 | static void | |||
|
|
124 | dwl_ipc_output_layout_symbol(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
125 | const char *layout) | |||
|
|
126 | { | |||
|
|
127 | if (!(lflag && mode & GET)) return; | |||
|
|
128 | if (data) printf("%s ", (char *)data); | |||
|
|
129 | printf("layout %s\n", layout); | |||
|
|
130 | } | |||
|
|
131 | ||||
|
|
132 | static void | |||
|
|
133 | dwl_ipc_output_title(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
134 | const char *title) | |||
|
|
135 | { | |||
|
|
136 | if (!(cflag && mode & GET)) return; | |||
|
|
137 | if (data) printf("%s ", (char *)data); | |||
|
|
138 | printf("title %s\n", title); | |||
|
|
139 | } | |||
|
|
140 | ||||
|
|
141 | static void | |||
|
|
142 | dwl_ipc_output_appid(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
143 | const char *appid) | |||
|
|
144 | { | |||
|
|
145 | if (!(cflag && mode & GET)) return; | |||
|
|
146 | if (data) printf("%s ", (char *)data); | |||
|
|
147 | printf("appid %s\n", appid); | |||
|
|
148 | } | |||
|
|
149 | ||||
|
|
150 | static void | |||
|
|
151 | dwl_ipc_output_fullscreen(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
152 | uint32_t is_fullscreen) | |||
|
|
153 | { | |||
|
|
154 | if (!mflag) return; | |||
|
|
155 | if (data) printf("%s ", (char *)data); | |||
|
|
156 | printf("fullscreen %u\n", is_fullscreen); | |||
|
|
157 | } | |||
|
|
158 | ||||
|
|
159 | static void | |||
|
|
160 | dwl_ipc_output_floating(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output, | |||
|
|
161 | uint32_t is_floating) | |||
|
|
162 | { | |||
|
|
163 | if (!fflag) return; | |||
|
|
164 | if (data) printf("%s ", (char *)data); | |||
|
|
165 | printf("floating %u\n", is_floating); | |||
|
|
166 | } | |||
|
|
167 | ||||
|
|
168 | static void | |||
|
|
169 | dwl_ipc_output_frame(void *data, struct zdwl_ipc_output_v2 *dwl_ipc_output) | |||
|
|
170 | { | |||
|
|
171 | if (mode & SET) { | |||
|
|
172 | if (data && !(output_name && strcmp(output_name, (char *)data) == 0)) return; | |||
|
|
173 | if (lflag) { | |||
|
|
174 | if (layout_idx == 0) { | |||
|
|
175 | for (char *c = layout_name; *c; c++) { | |||
|
|
176 | if (*c < '0' || *c > '9') | |||
|
|
177 | die("bad layout %s", layout_name); | |||
|
|
178 | layout_idx = *c-'0' + layout_idx*10; | |||
|
|
179 | } | |||
|
|
180 | } | |||
|
|
181 | if (layout_idx >= layoutcount) | |||
|
|
182 | die("bad layout %s", layout_name); | |||
|
|
183 | zdwl_ipc_output_v2_set_layout(dwl_ipc_output, layout_idx); | |||
|
|
184 | } | |||
|
|
185 | if (tflag) { | |||
|
|
186 | uint32_t mask = seltags; | |||
|
|
187 | char *t = tagset; | |||
|
|
188 | uint32_t i = 0; | |||
|
|
189 | int toggle = 1; | |||
|
|
190 | ||||
|
|
191 | if (*t == '!') { | |||
|
|
192 | toggle = 0; | |||
|
|
193 | t++; | |||
|
|
194 | } | |||
|
|
195 | ||||
|
|
196 | for (; *t && *t >= '0' && *t <= '9'; t++) | |||
|
|
197 | i = *t-'0' + i*10; | |||
|
|
198 | ||||
|
|
199 | if (toggle && !*t) mask = 1<<i; | |||
|
|
200 | ||||
|
|
201 | for (; *t; t++, i++) { | |||
|
|
202 | switch (*t) { | |||
|
|
203 | case '-': | |||
|
|
204 | mask &= ~(1<<i); | |||
|
|
205 | break; | |||
|
|
206 | case '+': | |||
|
|
207 | mask |= 1<<i; | |||
|
|
208 | break; | |||
|
|
209 | case '^': | |||
|
|
210 | mask ^= 1<<i; | |||
|
|
211 | break; | |||
|
|
212 | } | |||
|
|
213 | } | |||
|
|
214 | ||||
|
|
215 | if (i > tagcount) die("bad tagset %s", tagset); | |||
|
|
216 | ||||
|
|
217 | zdwl_ipc_output_v2_set_tags(dwl_ipc_output, mask, toggle); | |||
|
|
218 | } | |||
|
|
219 | if (cflag) { | |||
|
|
220 | uint32_t and = ~0, xor = 0; | |||
|
|
221 | char *t = client_tags; | |||
|
|
222 | uint32_t i = 0; | |||
|
|
223 | ||||
|
|
224 | for (; *t && *t >= '0' && *t <= '9'; t++) | |||
|
|
225 | i = *t-'0' + i*10; | |||
|
|
226 | ||||
|
|
227 | if (!*t) { | |||
|
|
228 | and = 0; | |||
|
|
229 | xor = 1<<i; | |||
|
|
230 | } | |||
|
|
231 | ||||
|
|
232 | for (; *t; t++, i++) { | |||
|
|
233 | switch (*t) { | |||
|
|
234 | case '-': | |||
|
|
235 | and &= ~(1<<i); | |||
|
|
236 | break; | |||
|
|
237 | case '+': | |||
|
|
238 | and &= ~(1<<i); | |||
|
|
239 | xor |= 1<<i; | |||
|
|
240 | break; | |||
|
|
241 | case '^': | |||
|
|
242 | xor |= 1<<i; | |||
|
|
243 | break; | |||
|
|
244 | } | |||
|
|
245 | } | |||
|
|
246 | if (i > tagcount) die("bad client tagset %s", client_tags); | |||
|
|
247 | ||||
|
|
248 | zdwl_ipc_output_v2_set_client_tags(dwl_ipc_output, and, xor); | |||
|
|
249 | } | |||
|
|
250 | wl_display_flush(display); | |||
|
|
251 | exit(0); | |||
|
|
252 | } else { | |||
|
|
253 | if (Fflag && mode == WATCH && focused_output) | |||
|
|
254 | printf("focusmon %s\n", focused_output); | |||
|
|
255 | if (tflag) { | |||
|
|
256 | if (data) printf("%s ", (char *)data); | |||
|
|
257 | printf("tags %u %u %u %u\n", occ, seltags, sel, urg); | |||
|
|
258 | occ = seltags = sel = urg = 0; | |||
|
|
259 | } | |||
|
|
260 | } | |||
|
|
261 | fflush(stdout); | |||
|
|
262 | } | |||
|
|
263 | ||||
|
|
264 | static const struct zdwl_ipc_output_v2_listener dwl_ipc_output_listener = { | |||
|
|
265 | .toggle_visibility = dwl_ipc_output_toggle_visibility, | |||
|
|
266 | .active = dwl_ipc_output_active, | |||
|
|
267 | .tag = dwl_ipc_output_tag, | |||
|
|
268 | .layout = dwl_ipc_output_layout, | |||
|
|
269 | .title = dwl_ipc_output_title, | |||
|
|
270 | .appid = dwl_ipc_output_appid, | |||
|
|
271 | .layout_symbol = dwl_ipc_output_layout_symbol, | |||
|
|
272 | .fullscreen = dwl_ipc_output_fullscreen, | |||
|
|
273 | .floating = dwl_ipc_output_floating, | |||
|
|
274 | .frame = dwl_ipc_output_frame, | |||
|
|
275 | }; | |||
|
|
276 | ||||
|
|
277 | static void | |||
|
|
278 | wl_output_name(void *data, struct wl_output *output, const char *name) | |||
|
|
279 | { | |||
|
|
280 | if (outputs) { | |||
|
|
281 | struct output *o = (struct output *)data; | |||
|
|
282 | o->output_name = strdup(name); | |||
|
|
283 | printf("+ "); | |||
|
|
284 | } | |||
|
|
285 | if (Oflag) printf("%s\n", name); | |||
|
|
286 | if (output_name && *output_name && strcmp(output_name, name) != 0) { | |||
|
|
287 | wl_output_release(output); | |||
|
|
288 | return; | |||
|
|
289 | } | |||
|
|
290 | if (dwl_ipc_manager) { | |||
|
|
291 | struct zdwl_ipc_output_v2 *dwl_ipc_output = | |||
|
|
292 | zdwl_ipc_manager_v2_get_output(dwl_ipc_manager, output); | |||
|
|
293 | zdwl_ipc_output_v2_add_listener(dwl_ipc_output, &dwl_ipc_output_listener, | |||
|
|
294 | output_name && *output_name ? NULL : strdup(name)); | |||
|
|
295 | } | |||
|
|
296 | } | |||
|
|
297 | ||||
|
|
298 | static void | |||
|
|
299 | wl_output_geometry(void *data, struct wl_output *wl_output, int32_t x, int32_t y, int32_t w, int32_t h, int32_t subpixel, const char *make, const char *model, int32_t transform) { | |||
|
|
300 | } | |||
|
|
301 | ||||
|
|
302 | static void | |||
|
|
303 | wl_output_mode(void *data, struct wl_output *wl_output, uint32_t flags, int32_t width, int32_t height, int32_t refresh) | |||
|
|
304 | { | |||
|
|
305 | } | |||
|
|
306 | ||||
|
|
307 | static void | |||
|
|
308 | wl_output_done(void *data, struct wl_output *wl_output) | |||
|
|
309 | { | |||
|
|
310 | } | |||
|
|
311 | ||||
|
|
312 | static void | |||
|
|
313 | wl_output_scale(void *data, struct wl_output *wl_output, int32_t factor) | |||
|
|
314 | { | |||
|
|
315 | } | |||
|
|
316 | ||||
|
|
317 | static void | |||
|
|
318 | wl_output_description(void *data, struct wl_output *output, const char *description) | |||
|
|
319 | { | |||
|
|
320 | } | |||
|
|
321 | ||||
|
|
322 | static const struct wl_output_listener output_listener = { | |||
|
|
323 | .geometry = wl_output_geometry, | |||
|
|
324 | .mode = wl_output_mode, | |||
|
|
325 | .done = wl_output_done, | |||
|
|
326 | .scale = wl_output_scale, | |||
|
|
327 | .name = wl_output_name, | |||
|
|
328 | .description = wl_output_description, | |||
|
|
329 | }; | |||
|
|
330 | ||||
|
|
331 | static void | |||
|
|
332 | global_add(void *data, struct wl_registry *wl_registry, | |||
|
|
333 | uint32_t name, const char *interface, uint32_t version) | |||
|
|
334 | { | |||
|
|
335 | if (strcmp(interface, wl_output_interface.name) == 0) { | |||
|
|
336 | struct wl_output *o = wl_registry_bind( | |||
|
|
337 | wl_registry, name, &wl_output_interface, | |||
|
|
338 | WL_OUTPUT_NAME_SINCE_VERSION); | |||
|
|
339 | wl_output_add_listener(o, &output_listener, outputs ? &outputs[outputcount] : NULL); | |||
|
|
340 | if (!outputs) return; | |||
|
|
341 | if (outputcount >= outputs_buflen) { | |||
|
|
342 | outputs_buflen *= 2; | |||
|
|
343 | outputs = reallocarray(outputs, outputs_buflen, sizeof(struct output)); | |||
|
|
344 | } | |||
|
|
345 | outputs[outputcount].name = name; | |||
|
|
346 | outputcount++; | |||
|
|
347 | } else if (strcmp(interface, zdwl_ipc_manager_v2_interface.name) == 0) { | |||
|
|
348 | dwl_ipc_manager = wl_registry_bind(wl_registry, name, | |||
|
|
349 | &zdwl_ipc_manager_v2_interface, version < 2 ? version : 2); | |||
|
|
350 | zdwl_ipc_manager_v2_add_listener(dwl_ipc_manager, &dwl_ipc_listener, NULL); | |||
|
|
351 | } | |||
|
|
352 | } | |||
|
|
353 | ||||
|
|
354 | static void | |||
|
|
355 | global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) | |||
|
|
356 | { | |||
|
|
357 | if (!outputs) return; | |||
|
|
358 | for (size_t i = 0; i < outputcount; i++) { | |||
|
|
359 | if (outputs[i].name == name) { | |||
|
|
360 | printf("- %s\n", outputs[i].output_name); | |||
|
|
361 | free(outputs[i].output_name); | |||
|
|
362 | outputs[i] = outputs[--outputcount]; | |||
|
|
363 | } | |||
|
|
364 | } | |||
|
|
365 | } | |||
|
|
366 | ||||
|
|
367 | static const struct wl_registry_listener registry_listener = { | |||
|
|
368 | .global = global_add, | |||
|
|
369 | .global_remove = global_remove, | |||
|
|
370 | }; | |||
|
|
371 | ||||
|
|
372 | static void | |||
|
|
373 | usage(int code) | |||
|
|
374 | { | |||
|
|
375 | fprintf(code ? stderr : stdout, | |||
|
|
376 | "usage:" | |||
|
|
377 | "\t%1$s [-OTLPF]\n" | |||
|
|
378 | "\t%1$s [-o <output>] -s [-t <tags>] [-l <layout>] [-c <tags>]\n" | |||
|
|
379 | "\t%1$s [-o <output>] (-g | -w) [-FOotlcvmf]\n", | |||
|
|
380 | argv0); | |||
|
|
381 | exit(code); | |||
|
|
382 | } | |||
|
|
383 | ||||
|
|
384 | int | |||
|
|
385 | main(int argc, char *argv[]) | |||
|
|
386 | { | |||
|
|
387 | ARGBEGIN { | |||
|
|
388 | case '?': case 'h': | |||
|
|
389 | usage(0); break; | |||
|
|
390 | case 's': | |||
|
|
391 | if (mode != NONE) usage(1); | |||
|
|
392 | mode = SET; | |||
|
|
393 | break; | |||
|
|
394 | case 'g': | |||
|
|
395 | if (mode != NONE) usage(1); | |||
|
|
396 | mode = GET; | |||
|
|
397 | break; | |||
|
|
398 | case 'w': | |||
|
|
399 | if (mode != NONE) usage(1); | |||
|
|
400 | mode = WATCH; | |||
|
|
401 | break; | |||
|
|
402 | case 'o': | |||
|
|
403 | if (mode & SET) | |||
|
|
404 | output_name = EARGF(usage(1)); | |||
|
|
405 | else if (mode & GET || !(output_name = ARGF())) | |||
|
|
406 | oflag = 1; | |||
|
|
407 | break; | |||
|
|
408 | case 't': | |||
|
|
409 | tflag = 1; | |||
|
|
410 | if (mode & GET) break; | |||
|
|
411 | mode = SET; | |||
|
|
412 | tagset = EARGF(usage(1)); | |||
|
|
413 | break; | |||
|
|
414 | case 'l': | |||
|
|
415 | lflag = 1; | |||
|
|
416 | if (mode & GET) break; | |||
|
|
417 | mode = SET; | |||
|
|
418 | layout_name = EARGF(usage(1)); | |||
|
|
419 | break; | |||
|
|
420 | case 'c': | |||
|
|
421 | cflag = 1; | |||
|
|
422 | if (mode & GET) break; | |||
|
|
423 | mode = SET; | |||
|
|
424 | client_tags = EARGF(usage(1)); | |||
|
|
425 | break; | |||
|
|
426 | case 'O': | |||
|
|
427 | Oflag = 1; | |||
|
|
428 | if (mode & SET) usage(1); | |||
|
|
429 | if (mode & WATCH) | |||
|
|
430 | outputs = malloc(outputs_buflen * sizeof(struct output)); | |||
|
|
431 | else | |||
|
|
432 | mode = GET; | |||
|
|
433 | break; | |||
|
|
434 | case 'T': | |||
|
|
435 | Tflag = 1; | |||
|
|
436 | if (mode && mode != GET) usage(1); | |||
|
|
437 | mode = GET; | |||
|
|
438 | break; | |||
|
|
439 | case 'L': | |||
|
|
440 | Lflag = 1; | |||
|
|
441 | if (mode && mode != GET) usage(1); | |||
|
|
442 | mode = GET; | |||
|
|
443 | break; | |||
|
|
444 | case 'P': | |||
|
|
445 | Pflag = 1; | |||
|
|
446 | if (mode && mode != GET) usage(1); | |||
|
|
447 | mode = GET; | |||
|
|
448 | break; | |||
|
|
449 | case 'F': | |||
|
|
450 | Fflag = 1; | |||
|
|
451 | if (mode && mode != GET) usage(1); | |||
|
|
452 | mode = GET; | |||
|
|
453 | break; | |||
|
|
454 | case 'v': | |||
|
|
455 | vflag = 1; | |||
|
|
456 | if (mode & SET) usage(1); | |||
|
|
457 | mode |= GET; | |||
|
|
458 | break; | |||
|
|
459 | case 'm': | |||
|
|
460 | mflag = 1; | |||
|
|
461 | if (mode & SET) usage(1); | |||
|
|
462 | mode |= GET; | |||
|
|
463 | break; | |||
|
|
464 | case 'f': | |||
|
|
465 | fflag = 1; | |||
|
|
466 | if (mode & SET) usage(1); | |||
|
|
467 | mode |= GET; | |||
|
|
468 | break; | |||
|
|
469 | default: | |||
|
|
470 | fprintf(stderr, "%s: bad option: -%c\n", argv0, ARGC()); | |||
|
|
471 | usage(1); | |||
|
|
472 | } ARGEND | |||
|
|
473 | if (argc > 0) { | |||
|
|
474 | fprintf(stderr, "%s: bad args:", argv0); | |||
|
|
475 | for (; *argv; argv++) | |||
|
|
476 | fprintf(stderr, " %s", argv[0]); | |||
|
|
477 | fprintf(stderr, "\n"); | |||
|
|
478 | usage(1); | |||
|
|
479 | } | |||
|
|
480 | if (mode == NONE) usage(1); | |||
|
|
481 | if (mode & GET && !(oflag || tflag || lflag || Oflag || Tflag || Lflag || Pflag || Fflag || cflag || vflag || mflag || fflag)) | |||
|
|
482 | oflag = tflag = lflag = cflag = vflag = mflag = fflag = Fflag = 1; | |||
|
|
483 | ||||
|
|
484 | display = wl_display_connect(NULL); | |||
|
|
485 | if (!display) die("bad display"); | |||
|
|
486 | ||||
|
|
487 | if (Pflag) { | |||
|
|
488 | int fd = wl_display_get_fd(display); | |||
|
|
489 | struct ucred ucred; | |||
|
|
490 | socklen_t len = sizeof(struct ucred); | |||
|
|
491 | if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &len) == -1) | |||
|
|
492 | die("bad getsockopt"); | |||
|
|
493 | printf("%d\n", ucred.pid); | |||
|
|
494 | } | |||
|
|
495 | ||||
|
|
496 | registry = wl_display_get_registry(display); | |||
|
|
497 | if (!registry) die("bad registry"); | |||
|
|
498 | ||||
|
|
499 | wl_registry_add_listener(registry, ®istry_listener, NULL); | |||
|
|
500 | ||||
|
|
501 | wl_display_dispatch(display); | |||
|
|
502 | wl_display_roundtrip(display); | |||
|
|
503 | ||||
|
|
504 | if (!dwl_ipc_manager && (mode & SET || tflag || lflag || Tflag || Lflag || cflag || vflag || mflag || fflag)) | |||
|
|
505 | die("bad dwl-ipc protocol"); | |||
|
|
506 | ||||
|
|
507 | wl_display_roundtrip(display); | |||
|
|
508 | ||||
|
|
509 | if (Fflag && !(mode == WATCH) && focused_output) | |||
|
|
510 | printf("focusmon %s\n", focused_output); | |||
|
|
511 | ||||
|
|
512 | if (mode == WATCH) | |||
|
|
513 | while (wl_display_dispatch(display) != -1); | |||
|
|
514 | ||||
|
|
515 | return 0; | |||
|
|
516 | } | |||
| This diff has been collapsed as it changes many lines, (4222 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,4222 | |||||
|
|
1 | /* | |||
|
|
2 | * See LICENSE file and licenses/ directory for copyright and license details. | |||
|
|
3 | */ | |||
|
|
4 | #include <errno.h> | |||
|
|
5 | #include <getopt.h> | |||
|
|
6 | #include <libinput.h> | |||
|
|
7 | #include <linux/input-event-codes.h> | |||
|
|
8 | #include <math.h> | |||
|
|
9 | #include <signal.h> | |||
|
|
10 | #include <stdio.h> | |||
|
|
11 | #include <sys/stat.h> | |||
|
|
12 | #include <stdlib.h> | |||
|
|
13 | #include <sys/wait.h> | |||
|
|
14 | #include <time.h> | |||
|
|
15 | #include <unistd.h> | |||
|
|
16 | #include <wayland-server-core.h> | |||
|
|
17 | #include <wlr/backend.h> | |||
|
|
18 | #include <wlr/backend/libinput.h> | |||
|
|
19 | #include <wlr/render/allocator.h> | |||
|
|
20 | #include <wlr/render/wlr_renderer.h> | |||
|
|
21 | #include <wlr/types/wlr_alpha_modifier_v1.h> | |||
|
|
22 | #include <wlr/types/wlr_compositor.h> | |||
|
|
23 | #include <wlr/types/wlr_cursor.h> | |||
|
|
24 | #include <wlr/types/wlr_cursor_shape_v1.h> | |||
|
|
25 | #include <wlr/types/wlr_data_control_v1.h> | |||
|
|
26 | #include <wlr/types/wlr_data_device.h> | |||
|
|
27 | #include <wlr/types/wlr_drm.h> | |||
|
|
28 | #include <wlr/types/wlr_export_dmabuf_v1.h> | |||
|
|
29 | #include <wlr/types/wlr_ext_data_control_v1.h> | |||
|
|
30 | #include <wlr/types/wlr_foreign_toplevel_management_v1.h> | |||
|
|
31 | #include <wlr/types/wlr_fractional_scale_v1.h> | |||
|
|
32 | #include <wlr/types/wlr_gamma_control_v1.h> | |||
|
|
33 | #include <wlr/types/wlr_idle_inhibit_v1.h> | |||
|
|
34 | #include <wlr/types/wlr_idle_notify_v1.h> | |||
|
|
35 | #include <wlr/types/wlr_input_device.h> | |||
|
|
36 | #include <wlr/types/wlr_keyboard.h> | |||
|
|
37 | #include <wlr/types/wlr_keyboard_group.h> | |||
|
|
38 | #include <wlr/types/wlr_layer_shell_v1.h> | |||
|
|
39 | #include <wlr/types/wlr_linux_dmabuf_v1.h> | |||
|
|
40 | #include <wlr/types/wlr_linux_drm_syncobj_v1.h> | |||
|
|
41 | #include <wlr/types/wlr_output.h> | |||
|
|
42 | #include <wlr/types/wlr_output_layout.h> | |||
|
|
43 | #include <wlr/types/wlr_output_management_v1.h> | |||
|
|
44 | #include <wlr/types/wlr_output_power_management_v1.h> | |||
|
|
45 | #include <wlr/types/wlr_pointer.h> | |||
|
|
46 | #include <wlr/types/wlr_pointer_constraints_v1.h> | |||
|
|
47 | #include <wlr/types/wlr_presentation_time.h> | |||
|
|
48 | #include <wlr/types/wlr_primary_selection.h> | |||
|
|
49 | #include <wlr/types/wlr_primary_selection_v1.h> | |||
|
|
50 | #include <wlr/types/wlr_relative_pointer_v1.h> | |||
|
|
51 | #include <wlr/types/wlr_scene.h> | |||
|
|
52 | #include <wlr/types/wlr_screencopy_v1.h> | |||
|
|
53 | #include <wlr/types/wlr_seat.h> | |||
|
|
54 | #include <wlr/types/wlr_server_decoration.h> | |||
|
|
55 | #include <wlr/types/wlr_session_lock_v1.h> | |||
|
|
56 | #include <wlr/types/wlr_single_pixel_buffer_v1.h> | |||
|
|
57 | #include <wlr/types/wlr_subcompositor.h> | |||
|
|
58 | #include <wlr/types/wlr_viewporter.h> | |||
|
|
59 | #include <wlr/types/wlr_virtual_keyboard_v1.h> | |||
|
|
60 | #include <wlr/types/wlr_virtual_pointer_v1.h> | |||
|
|
61 | #include <wlr/types/wlr_xcursor_manager.h> | |||
|
|
62 | #include <wlr/types/wlr_xdg_activation_v1.h> | |||
|
|
63 | #include <wlr/types/wlr_xdg_decoration_v1.h> | |||
|
|
64 | #include <wlr/types/wlr_xdg_output_v1.h> | |||
|
|
65 | #include <wlr/types/wlr_xdg_shell.h> | |||
|
|
66 | #include <wlr/util/log.h> | |||
|
|
67 | #include <wlr/util/region.h> | |||
|
|
68 | #include <xkbcommon/xkbcommon.h> | |||
|
|
69 | #ifdef XWAYLAND | |||
|
|
70 | #include <wlr/xwayland.h> | |||
|
|
71 | #include <xcb/xcb.h> | |||
|
|
72 | #include <xcb/xcb_icccm.h> | |||
|
|
73 | #endif | |||
|
|
74 | ||||
|
|
75 | #include "util.h" | |||
|
|
76 | #include "parser/parser.h" | |||
|
|
77 | ||||
|
|
78 | /* macros */ | |||
|
|
79 | #define MAX(A, B) ((A) > (B) ? (A) : (B)) | |||
|
|
80 | #define MIN(A, B) ((A) < (B) ? (A) : (B)) | |||
|
|
81 | #define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS) | |||
|
|
82 | #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) | |||
|
|
83 | #define LENGTH(X) (sizeof X / sizeof X[0]) | |||
|
|
84 | #define END(A) ((A) + LENGTH(A)) | |||
|
|
85 | #define TAGMASK ((1u << TAGCOUNT) - 1) | |||
|
|
86 | #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) | |||
|
|
87 | #define LISTEN_STATIC(E, H) do { struct wl_listener *_l = ecalloc(1, sizeof(*_l)); _l->notify = (H); wl_signal_add((E), _l); } while (0) | |||
|
|
88 | ||||
|
|
89 | /* enums */ | |||
|
|
90 | enum { CurNormal, CurPressed, CurMove, CurResize, CurTileDrag }; /* cursor */ | |||
|
|
91 | enum { XDGShell, LayerShell, X11 }; /* client types */ | |||
|
|
92 | enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ | |||
|
|
93 | ||||
|
|
94 | typedef union { | |||
|
|
95 | int i; | |||
|
|
96 | uint32_t ui; | |||
|
|
97 | float f; | |||
|
|
98 | const void *v; | |||
|
|
99 | } Arg; | |||
|
|
100 | ||||
|
|
101 | typedef struct { | |||
|
|
102 | unsigned int mod; | |||
|
|
103 | unsigned int button; | |||
|
|
104 | void (*func)(const Arg *); | |||
|
|
105 | const Arg arg; | |||
|
|
106 | } Button; | |||
|
|
107 | ||||
|
|
108 | typedef struct Monitor Monitor; | |||
|
|
109 | typedef struct { | |||
|
|
110 | /* Must keep this field first */ | |||
|
|
111 | unsigned int type; /* XDGShell or X11* */ | |||
|
|
112 | ||||
|
|
113 | Monitor *mon; | |||
|
|
114 | struct wlr_scene_tree *scene; | |||
|
|
115 | struct wlr_scene_rect *inner_border[4]; /* inner top, bottom, left, right */ | |||
|
|
116 | struct wlr_scene_rect *outer_border[4]; /* outer top, bottom, left, right */ | |||
|
|
117 | struct wlr_scene_tree *scene_surface; | |||
|
|
118 | struct wl_list link; | |||
|
|
119 | struct wl_list flink; | |||
|
|
120 | struct wlr_box geom; /* layout-relative, includes border */ | |||
|
|
121 | struct wlr_box prev; /* layout-relative, includes border */ | |||
|
|
122 | struct wlr_box bounds; /* only width and height are used */ | |||
|
|
123 | union { | |||
|
|
124 | struct wlr_xdg_surface *xdg; | |||
|
|
125 | struct wlr_xwayland_surface *xwayland; | |||
|
|
126 | } surface; | |||
|
|
127 | struct wlr_xdg_toplevel_decoration_v1 *decoration; | |||
|
|
128 | struct wl_listener commit; | |||
|
|
129 | struct wl_listener map; | |||
|
|
130 | struct wl_listener maximize; | |||
|
|
131 | struct wl_listener unmap; | |||
|
|
132 | struct wl_listener destroy; | |||
|
|
133 | struct wl_listener set_title; | |||
|
|
134 | struct wl_listener fullscreen; | |||
|
|
135 | struct wl_listener set_decoration_mode; | |||
|
|
136 | struct wl_listener destroy_decoration; | |||
|
|
137 | struct wlr_foreign_toplevel_handle_v1 *foreign_toplevel; | |||
|
|
138 | struct wl_listener factivate; | |||
|
|
139 | struct wl_listener fclose; | |||
|
|
140 | struct wl_listener ffullscreen; | |||
|
|
141 | struct wl_listener fdestroy; | |||
|
|
142 | #ifdef XWAYLAND | |||
|
|
143 | struct wl_listener activate; | |||
|
|
144 | struct wl_listener associate; | |||
|
|
145 | struct wl_listener dissociate; | |||
|
|
146 | struct wl_listener configure; | |||
|
|
147 | struct wl_listener set_hints; | |||
|
|
148 | #endif | |||
|
|
149 | unsigned int bw; /* total border width = ibw + obw */ | |||
|
|
150 | unsigned int ibw; /* inner border width */ | |||
|
|
151 | unsigned int obw; /* outer border width */ | |||
|
|
152 | uint32_t tags; | |||
|
|
153 | int isfloating, isurgent, isfullscreen; | |||
|
|
154 | float opacity; /* re-asserted every frame in rendermon() */ | |||
|
|
155 | uint32_t resize; /* configure serial of a pending resize */ | |||
|
|
156 | } Client; | |||
|
|
157 | ||||
|
|
158 | typedef struct { | |||
|
|
159 | uint32_t mod; | |||
|
|
160 | xkb_keysym_t keysym; | |||
|
|
161 | void (*func)(const Arg *); | |||
|
|
162 | const Arg arg; | |||
|
|
163 | } Key; | |||
|
|
164 | ||||
|
|
165 | typedef struct { | |||
|
|
166 | struct wlr_keyboard_group *wlr_group; | |||
|
|
167 | ||||
|
|
168 | int nsyms; | |||
|
|
169 | const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ | |||
|
|
170 | uint32_t mods; /* invalid if nsyms == 0 */ | |||
|
|
171 | struct wl_event_source *key_repeat_source; | |||
|
|
172 | ||||
|
|
173 | struct wl_listener modifiers; | |||
|
|
174 | struct wl_listener key; | |||
|
|
175 | struct wl_listener destroy; | |||
|
|
176 | } KeyboardGroup; | |||
|
|
177 | ||||
|
|
178 | typedef struct { | |||
|
|
179 | /* Must keep this field first */ | |||
|
|
180 | unsigned int type; /* LayerShell */ | |||
|
|
181 | ||||
|
|
182 | Monitor *mon; | |||
|
|
183 | struct wlr_scene_tree *scene; | |||
|
|
184 | struct wlr_scene_tree *popups; | |||
|
|
185 | struct wlr_scene_layer_surface_v1 *scene_layer; | |||
|
|
186 | struct wl_list link; | |||
|
|
187 | int mapped; | |||
|
|
188 | struct wlr_layer_surface_v1 *layer_surface; | |||
|
|
189 | ||||
|
|
190 | struct wl_listener destroy; | |||
|
|
191 | struct wl_listener unmap; | |||
|
|
192 | struct wl_listener surface_commit; | |||
|
|
193 | } LayerSurface; | |||
|
|
194 | ||||
|
|
195 | typedef struct { | |||
|
|
196 | const char *symbol; | |||
|
|
197 | void (*arrange)(Monitor *); | |||
|
|
198 | } Layout; | |||
|
|
199 | ||||
|
|
200 | #ifndef TAGCOUNT | |||
|
|
201 | #define TAGCOUNT 9 | |||
|
|
202 | #endif | |||
|
|
203 | ||||
|
|
204 | /* dwindle tree */ | |||
|
|
205 | typedef struct DwindleNode DwindleNode; | |||
|
|
206 | struct DwindleNode { | |||
|
|
207 | DwindleNode *children[2]; | |||
|
|
208 | DwindleNode *parent; | |||
|
|
209 | Client *client; | |||
|
|
210 | struct wlr_box box; | |||
|
|
211 | float split_ratio; | |||
|
|
212 | int split_top; | |||
|
|
213 | int is_node; | |||
|
|
214 | }; | |||
|
|
215 | ||||
|
|
216 | /* which physical edge of its old box a leaf's geometry moved toward/from */ | |||
|
|
217 | typedef enum { EDGE_NONE, EDGE_LEFT, EDGE_RIGHT, EDGE_TOP, EDGE_BOTTOM } DwindleEdge; | |||
|
|
218 | ||||
|
|
219 | /* Filled in by dwindle_insert()/dwindle_remove() when a non-NULL hint | |||
|
|
220 | * pointer is passed in*/ | |||
|
|
221 | ||||
|
|
222 | /*Left untouched (caller should zero it first) if | |||
|
|
223 | * the operation didn't actually split/collapse anything, i.e. first | |||
|
|
224 | * insert into an empty tree. */ | |||
|
|
225 | ||||
|
|
226 | typedef struct { | |||
|
|
227 | DwindleEdge edge; | |||
|
|
228 | struct wlr_box old_box; | |||
|
|
229 | } DwindleHint; | |||
|
|
230 | ||||
|
|
231 | struct Monitor { | |||
|
|
232 | struct wl_list link; | |||
|
|
233 | struct wlr_output *wlr_output; | |||
|
|
234 | struct wlr_scene_output *scene_output; | |||
|
|
235 | struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ | |||
|
|
236 | struct wl_listener frame; | |||
|
|
237 | struct wl_listener destroy; | |||
|
|
238 | struct wl_listener request_state; | |||
|
|
239 | struct wl_listener destroy_lock_surface; | |||
|
|
240 | struct wlr_session_lock_surface_v1 *lock_surface; | |||
|
|
241 | struct wlr_box m; /* monitor area, layout-relative */ | |||
|
|
242 | struct wlr_box w; /* window area, layout-relative */ | |||
|
|
243 | struct wl_list layers[4]; /* LayerSurface.link */ | |||
|
|
244 | const Layout *lt[2]; | |||
|
|
245 | int gaps; | |||
|
|
246 | unsigned int seltags; | |||
|
|
247 | struct wlr_ext_workspace_group_handle_v1 *ext_group; | |||
|
|
248 | unsigned int sellt; | |||
|
|
249 | uint32_t tagset[2]; | |||
|
|
250 | float mfact; | |||
|
|
251 | int gamma_lut_changed; | |||
|
|
252 | int nmaster; | |||
|
|
253 | char ltsymbol[16]; | |||
|
|
254 | int asleep; | |||
|
|
255 | DwindleNode *dwindle_root[TAGCOUNT]; /* one tree per tag */ | |||
|
|
256 | Client *dwindle_focus[TAGCOUNT]; /* insertion anchor, one per tag */ | |||
|
|
257 | }; | |||
|
|
258 | ||||
|
|
259 | typedef struct { | |||
|
|
260 | const char *name; | |||
|
|
261 | float mfact; | |||
|
|
262 | int nmaster; | |||
|
|
263 | float scale; | |||
|
|
264 | const Layout *lt; | |||
|
|
265 | enum wl_output_transform rr; | |||
|
|
266 | int x, y; | |||
|
|
267 | } MonitorRule; | |||
|
|
268 | ||||
|
|
269 | typedef struct { | |||
|
|
270 | struct wlr_pointer_constraint_v1 *constraint; | |||
|
|
271 | struct wl_listener destroy; | |||
|
|
272 | } PointerConstraint; | |||
|
|
273 | ||||
|
|
274 | typedef struct { | |||
|
|
275 | const char *id; | |||
|
|
276 | const char *title; | |||
|
|
277 | uint32_t tags; | |||
|
|
278 | int isfloating; | |||
|
|
279 | int monitor; | |||
|
|
280 | } Rule; | |||
|
|
281 | ||||
|
|
282 | typedef struct { | |||
|
|
283 | struct wlr_scene_tree *scene; | |||
|
|
284 | ||||
|
|
285 | struct wlr_session_lock_v1 *lock; | |||
|
|
286 | struct wl_listener new_surface; | |||
|
|
287 | struct wl_listener unlock; | |||
|
|
288 | struct wl_listener destroy; | |||
|
|
289 | } SessionLock; | |||
|
|
290 | ||||
|
|
291 | /* function declarations */ | |||
|
|
292 | static void applybounds(Client *c, struct wlr_box *bbox); | |||
|
|
293 | static void applyrules(Client *c); | |||
|
|
294 | static void arrange(Monitor *m); | |||
|
|
295 | static void arrangelayer(Monitor *m, struct wl_list *list, | |||
|
|
296 | struct wlr_box *usable_area, int exclusive); | |||
|
|
297 | static void arrangelayers(Monitor *m); | |||
|
|
298 | static void axisnotify(struct wl_listener *listener, void *data); | |||
|
|
299 | static void buttonpress(struct wl_listener *listener, void *data); | |||
|
|
300 | static void chvt(const Arg *arg); | |||
|
|
301 | static void checkidleinhibitor(struct wlr_surface *exclude); | |||
|
|
302 | static void cleanup(void); | |||
|
|
303 | static void cleanupmon(struct wl_listener *listener, void *data); | |||
|
|
304 | static void cleanuplisteners(void); | |||
|
|
305 | static void closemon(Monitor *m); | |||
|
|
306 | static void dwindle_free_tree(DwindleNode *n); | |||
|
|
307 | static void dwindle_remove_client(Client *c); | |||
|
|
308 | static void dwindle_insert(DwindleNode **root, Client *new_c, Client *focused, DwindleHint *hint); | |||
|
|
309 | static int dwindle_insert_at_edge(DwindleNode **root, Client *new_c, Client *target, DwindleEdge edge); | |||
|
|
310 | static void dwindle_remove(DwindleNode **root, Client *c, DwindleHint *hint); | |||
|
|
311 | static DwindleEdge edge_at_point(struct wlr_box box, double x, double y); | |||
|
|
312 | static struct wlr_box preselect_indicator_box(struct wlr_box target, DwindleEdge edge); | |||
|
|
313 | static Client *tiled_client_at(double x, double y, Client *exclude); | |||
|
|
314 | static inline int current_tag_idx(Monitor *m); | |||
|
|
315 | static void commitlayersurfacenotify(struct wl_listener *listener, void *data); | |||
|
|
316 | static void commitnotify(struct wl_listener *listener, void *data); | |||
|
|
317 | static void commitpopup(struct wl_listener *listener, void *data); | |||
|
|
318 | static void createdecoration(struct wl_listener *listener, void *data); | |||
|
|
319 | static void createidleinhibitor(struct wl_listener *listener, void *data); | |||
|
|
320 | static void createkeyboard(struct wlr_keyboard *keyboard); | |||
|
|
321 | static KeyboardGroup *createkeyboardgroup(void); | |||
|
|
322 | static void createlayersurface(struct wl_listener *listener, void *data); | |||
|
|
323 | static void createforeigntoplevel(Client *c); | |||
|
|
324 | static void createlocksurface(struct wl_listener *listener, void *data); | |||
|
|
325 | static void createmon(struct wl_listener *listener, void *data); | |||
|
|
326 | static void createnotify(struct wl_listener *listener, void *data); | |||
|
|
327 | static void createpointer(struct wlr_pointer *pointer); | |||
|
|
328 | static void createpointerconstraint(struct wl_listener *listener, void *data); | |||
|
|
329 | static void createpopup(struct wl_listener *listener, void *data); | |||
|
|
330 | static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); | |||
|
|
331 | static void cursorframe(struct wl_listener *listener, void *data); | |||
|
|
332 | static void cursorwarptohint(void); | |||
|
|
333 | static void destroydecoration(struct wl_listener *listener, void *data); | |||
|
|
334 | static void destroydragicon(struct wl_listener *listener, void *data); | |||
|
|
335 | static void destroyidleinhibitor(struct wl_listener *listener, void *data); | |||
|
|
336 | static void destroylayersurfacenotify(struct wl_listener *listener, void *data); | |||
|
|
337 | static void destroylock(SessionLock *lock, int unlocked); | |||
|
|
338 | static void destroylocksurface(struct wl_listener *listener, void *data); | |||
|
|
339 | static void destroynotify(struct wl_listener *listener, void *data); | |||
|
|
340 | static void destroypointerconstraint(struct wl_listener *listener, void *data); | |||
|
|
341 | static void destroysessionlock(struct wl_listener *listener, void *data); | |||
|
|
342 | static void destroykeyboardgroup(struct wl_listener *listener, void *data); | |||
|
|
343 | static Monitor *dirtomon(enum wlr_direction dir); | |||
|
|
344 | static void factivatenotify(struct wl_listener *listener, void *data); | |||
|
|
345 | static void fclosenotify(struct wl_listener *listener, void *data); | |||
|
|
346 | static void fdestroynotify(struct wl_listener *listener, void *data); | |||
|
|
347 | static void ffullscreennotify(struct wl_listener *listener, void *data); | |||
|
|
348 | static void focusclient(Client *c, int lift); | |||
|
|
349 | static void focusmon(const Arg *arg); | |||
|
|
350 | static void focusdir(const Arg *arg); | |||
|
|
351 | static void swapdir(const Arg *arg); | |||
|
|
352 | static void swaptiled(Client *a, Client *b); | |||
|
|
353 | static void dwindle_move_to_edge(Client *a, Client *b, DwindleEdge edge); | |||
|
|
354 | static Client *focustop(Monitor *m); | |||
|
|
355 | static void fullscreennotify(struct wl_listener *listener, void *data); | |||
|
|
356 | static void gpureset(struct wl_listener *listener, void *data); | |||
|
|
357 | static void handlesig(int signo); | |||
|
|
358 | static void inputdevice(struct wl_listener *listener, void *data); | |||
|
|
359 | static int keybinding(uint32_t mods, xkb_keysym_t sym); | |||
|
|
360 | static void keypress(struct wl_listener *listener, void *data); | |||
|
|
361 | static void keypressmod(struct wl_listener *listener, void *data); | |||
|
|
362 | static int keyrepeat(void *data); | |||
|
|
363 | static void killclient(const Arg *arg); | |||
|
|
364 | static void locksession(struct wl_listener *listener, void *data); | |||
|
|
365 | static void mapnotify(struct wl_listener *listener, void *data); | |||
|
|
366 | static void maximizenotify(struct wl_listener *listener, void *data); | |||
|
|
367 | static void motionabsolute(struct wl_listener *listener, void *data); | |||
|
|
368 | static void motionnotify(uint32_t time, struct wlr_input_device *device, double sx, | |||
|
|
369 | double sy, double sx_unaccel, double sy_unaccel); | |||
|
|
370 | static void motionrelative(struct wl_listener *listener, void *data); | |||
|
|
371 | static void moveresize(const Arg *arg); | |||
|
|
372 | static void outputmgrapply(struct wl_listener *listener, void *data); | |||
|
|
373 | static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test); | |||
|
|
374 | static void outputmgrtest(struct wl_listener *listener, void *data); | |||
|
|
375 | static void pointerfocus(Client *c, struct wlr_surface *surface, | |||
|
|
376 | double sx, double sy, uint32_t time); | |||
|
|
377 | static void printstatus(void); | |||
|
|
378 | static void powermgrsetmode(struct wl_listener *listener, void *data); | |||
|
|
379 | static void quit(const Arg *arg); | |||
|
|
380 | static void rendermon(struct wl_listener *listener, void *data); | |||
|
|
381 | static void requestdecorationmode(struct wl_listener *listener, void *data); | |||
|
|
382 | static void requeststartdrag(struct wl_listener *listener, void *data); | |||
|
|
383 | static void requestmonstate(struct wl_listener *listener, void *data); | |||
|
|
384 | static void resize(Client *c, struct wlr_box geo, int interact); | |||
|
|
385 | static void run(char *startup_cmd); | |||
|
|
386 | static void setcursor(struct wl_listener *listener, void *data); | |||
|
|
387 | static void setcursorshape(struct wl_listener *listener, void *data); | |||
|
|
388 | static void setfloating(Client *c, int floating); | |||
|
|
389 | static void setfullscreen(Client *c, int fullscreen); | |||
|
|
390 | static void setlayout(const Arg *arg); | |||
|
|
391 | static void setmon(Client *c, Monitor *m, uint32_t newtags); | |||
|
|
392 | static void setpsel(struct wl_listener *listener, void *data); | |||
|
|
393 | static void setsel(struct wl_listener *listener, void *data); | |||
|
|
394 | static void setup(void); | |||
|
|
395 | static void spawn(const Arg *arg); | |||
|
|
396 | static void startdrag(struct wl_listener *listener, void *data); | |||
|
|
397 | static void tag(const Arg *arg); | |||
|
|
398 | static void tagmon(const Arg *arg); | |||
|
|
399 | static void dwindle(Monitor *m); | |||
|
|
400 | static void togglefloating(const Arg *arg); | |||
|
|
401 | static void togglefullscreen(const Arg *arg); | |||
|
|
402 | static void togglegaps(const Arg *arg); | |||
|
|
403 | static void toggletag(const Arg *arg); | |||
|
|
404 | static void toggleview(const Arg *arg); | |||
|
|
405 | static void unlocksession(struct wl_listener *listener, void *data); | |||
|
|
406 | static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); | |||
|
|
407 | static void unmapnotify(struct wl_listener *listener, void *data); | |||
|
|
408 | static void updatemons(struct wl_listener *listener, void *data); | |||
|
|
409 | static void updatetitle(struct wl_listener *listener, void *data); | |||
|
|
410 | static void urgent(struct wl_listener *listener, void *data); | |||
|
|
411 | static void view(const Arg *arg); | |||
|
|
412 | static void virtualkeyboard(struct wl_listener *listener, void *data); | |||
|
|
413 | static void virtualpointer(struct wl_listener *listener, void *data); | |||
|
|
414 | static Monitor *xytomon(double x, double y); | |||
|
|
415 | static void xytonode(double x, double y, struct wlr_surface **psurface, | |||
|
|
416 | Client **pc, LayerSurface **pl, double *nx, double *ny); | |||
|
|
417 | ||||
|
|
418 | /* variables */ | |||
|
|
419 | static pid_t child_pid = -1; | |||
|
|
420 | static int locked; | |||
|
|
421 | static void *exclusive_focus; | |||
|
|
422 | static struct wl_display *dpy; | |||
|
|
423 | static struct wl_event_loop *event_loop; | |||
|
|
424 | static struct wlr_backend *backend; | |||
|
|
425 | static struct wlr_scene *scene; | |||
|
|
426 | static struct wlr_scene_tree *layers[NUM_LAYERS]; | |||
|
|
427 | static struct wlr_scene_tree *drag_icon; | |||
|
|
428 | /* Map from ZWLR_LAYER_SHELL_* constants to Lyr* enum */ | |||
|
|
429 | static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay }; | |||
|
|
430 | static struct wlr_renderer *drw; | |||
|
|
431 | static struct wlr_allocator *alloc; | |||
|
|
432 | static struct wlr_compositor *compositor; | |||
|
|
433 | static struct wlr_session *session; | |||
|
|
434 | ||||
|
|
435 | static struct wlr_xdg_shell *xdg_shell; | |||
|
|
436 | static struct wlr_xdg_activation_v1 *activation; | |||
|
|
437 | static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; | |||
|
|
438 | static struct wl_list clients; /* tiling order */ | |||
|
|
439 | static struct wl_list fstack; /* focus order */ | |||
|
|
440 | static struct wlr_idle_notifier_v1 *idle_notifier; | |||
|
|
441 | static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; | |||
|
|
442 | static struct wlr_layer_shell_v1 *layer_shell; | |||
|
|
443 | static struct wlr_output_manager_v1 *output_mgr; | |||
|
|
444 | static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; | |||
|
|
445 | static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr; | |||
|
|
446 | static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; | |||
|
|
447 | static struct wlr_output_power_manager_v1 *power_mgr; | |||
|
|
448 | static struct wlr_foreign_toplevel_manager_v1 *foreign_toplevel_mgr; | |||
|
|
449 | ||||
|
|
450 | static struct wlr_pointer_constraints_v1 *pointer_constraints; | |||
|
|
451 | static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr; | |||
|
|
452 | static struct wlr_pointer_constraint_v1 *active_constraint; | |||
|
|
453 | ||||
|
|
454 | static struct wlr_cursor *cursor; | |||
|
|
455 | static struct wlr_xcursor_manager *cursor_mgr; | |||
|
|
456 | ||||
|
|
457 | static struct wlr_scene_rect *root_bg; | |||
|
|
458 | static struct wlr_session_lock_manager_v1 *session_lock_mgr; | |||
|
|
459 | static struct wlr_scene_rect *locked_bg; | |||
|
|
460 | static struct wlr_session_lock_v1 *cur_lock; | |||
|
|
461 | ||||
|
|
462 | static struct wlr_seat *seat; | |||
|
|
463 | static KeyboardGroup *kb_group; | |||
|
|
464 | static unsigned int cursor_mode; | |||
|
|
465 | static Client *grabc; | |||
|
|
466 | static int grabcx, grabcy; /* client-relative */ | |||
|
|
467 | ||||
|
|
468 | /* Tile-drag preselect recomputes every motion event while cursor_mode == | |||
|
|
469 | CurTileDrag edge == EDGE_NONE means "dead centre of target, do a plain | |||
|
|
470 | swap on release"." */ | |||
|
|
471 | static Client *preselect_target; | |||
|
|
472 | static DwindleEdge preselect_edge; | |||
|
|
473 | static struct wlr_scene_rect *preselect_indicator; | |||
|
|
474 | ||||
|
|
475 | /* Fixed size grabc is resized to the moment a tile-drag starts, so it | |||
|
|
476 | visually detaches and follows the cursor like a floating window rather | |||
|
|
477 | than staying pinned at full tile size the whole gesture. Deliberately | |||
|
|
478 | not "preserve original size" */ | |||
|
|
479 | #define TILEDRAG_DETACH_W 960 | |||
|
|
480 | #define TILEDRAG_DETACH_H 540 | |||
|
|
481 | static struct wlr_box tiledrag_orig_geom; | |||
|
|
482 | ||||
|
|
483 | static struct wlr_output_layout *output_layout; | |||
|
|
484 | static struct wlr_box sgeom; | |||
|
|
485 | static struct wl_list mons; | |||
|
|
486 | static Monitor *selmon; | |||
|
|
487 | ||||
|
|
488 | /* global event handlers */ | |||
|
|
489 | static struct wl_listener cursor_axis = {.notify = axisnotify}; | |||
|
|
490 | static struct wl_listener cursor_button = {.notify = buttonpress}; | |||
|
|
491 | static struct wl_listener cursor_frame = {.notify = cursorframe}; | |||
|
|
492 | static struct wl_listener cursor_motion = {.notify = motionrelative}; | |||
|
|
493 | static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; | |||
|
|
494 | static struct wl_listener gpu_reset = {.notify = gpureset}; | |||
|
|
495 | static struct wl_listener layout_change = {.notify = updatemons}; | |||
|
|
496 | static struct wl_listener new_idle_inhibitor = {.notify = createidleinhibitor}; | |||
|
|
497 | static struct wl_listener new_input_device = {.notify = inputdevice}; | |||
|
|
498 | static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; | |||
|
|
499 | static struct wl_listener new_virtual_pointer = {.notify = virtualpointer}; | |||
|
|
500 | static struct wl_listener new_pointer_constraint = {.notify = createpointerconstraint}; | |||
|
|
501 | static struct wl_listener new_output = {.notify = createmon}; | |||
|
|
502 | static struct wl_listener new_xdg_toplevel = {.notify = createnotify}; | |||
|
|
503 | static struct wl_listener new_xdg_popup = {.notify = createpopup}; | |||
|
|
504 | static struct wl_listener new_xdg_decoration = {.notify = createdecoration}; | |||
|
|
505 | static struct wl_listener new_layer_surface = {.notify = createlayersurface}; | |||
|
|
506 | static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; | |||
|
|
507 | static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; | |||
|
|
508 | static struct wl_listener output_power_mgr_set_mode = {.notify = powermgrsetmode}; | |||
|
|
509 | static struct wl_listener request_activate = {.notify = urgent}; | |||
|
|
510 | static struct wl_listener request_cursor = {.notify = setcursor}; | |||
|
|
511 | static struct wl_listener request_set_psel = {.notify = setpsel}; | |||
|
|
512 | static struct wl_listener request_set_sel = {.notify = setsel}; | |||
|
|
513 | static struct wl_listener request_set_cursor_shape = {.notify = setcursorshape}; | |||
|
|
514 | static struct wl_listener request_start_drag = {.notify = requeststartdrag}; | |||
|
|
515 | static struct wl_listener start_drag = {.notify = startdrag}; | |||
|
|
516 | static struct wl_listener new_session_lock = {.notify = locksession}; | |||
|
|
517 | ||||
|
|
518 | #ifdef XWAYLAND | |||
|
|
519 | static void activatex11(struct wl_listener *listener, void *data); | |||
|
|
520 | static void associatex11(struct wl_listener *listener, void *data); | |||
|
|
521 | static void configurex11(struct wl_listener *listener, void *data); | |||
|
|
522 | static void createnotifyx11(struct wl_listener *listener, void *data); | |||
|
|
523 | static void dissociatex11(struct wl_listener *listener, void *data); | |||
|
|
524 | static void sethints(struct wl_listener *listener, void *data); | |||
|
|
525 | static void xwaylandready(struct wl_listener *listener, void *data); | |||
|
|
526 | static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; | |||
|
|
527 | static struct wl_listener xwayland_ready = {.notify = xwaylandready}; | |||
|
|
528 | static struct wlr_xwayland *xwayland; | |||
|
|
529 | #endif | |||
|
|
530 | ||||
|
|
531 | /* Runtime config */ | |||
|
|
532 | ||||
|
|
533 | static Config cfg; | |||
|
|
534 | static struct wl_event_source *cfg_watch_src; | |||
|
|
535 | static const char *custom_cfg_path; | |||
|
|
536 | ||||
|
|
537 | #include "config.h" | |||
|
|
538 | ||||
|
|
539 | /* attempt to encapsulate suck into one file */ | |||
|
|
540 | #include "client.h" | |||
|
|
541 | #include "dwl-ipc.h" | |||
|
|
542 | #include "ext-workspace.h" | |||
|
|
543 | ||||
|
|
544 | /* function implementations */ | |||
|
|
545 | void | |||
|
|
546 | applybounds(Client *c, struct wlr_box *bbox) | |||
|
|
547 | { | |||
|
|
548 | /* set minimum possible */ | |||
|
|
549 | c->geom.width = MAX(1 + 2 * (int)c->bw, c->geom.width); | |||
|
|
550 | c->geom.height = MAX(1 + 2 * (int)c->bw, c->geom.height); | |||
|
|
551 | ||||
|
|
552 | if (c->geom.x >= bbox->x + bbox->width) | |||
|
|
553 | c->geom.x = bbox->x + bbox->width - c->geom.width; | |||
|
|
554 | if (c->geom.y >= bbox->y + bbox->height) | |||
|
|
555 | c->geom.y = bbox->y + bbox->height - c->geom.height; | |||
|
|
556 | if (c->geom.x + c->geom.width <= bbox->x) | |||
|
|
557 | c->geom.x = bbox->x; | |||
|
|
558 | if (c->geom.y + c->geom.height <= bbox->y) | |||
|
|
559 | c->geom.y = bbox->y; | |||
|
|
560 | } | |||
|
|
561 | ||||
|
|
562 | void | |||
|
|
563 | applyrules(Client *c) | |||
|
|
564 | { | |||
|
|
565 | /* rule matching */ | |||
|
|
566 | const char *appid, *title; | |||
|
|
567 | uint32_t newtags = 0; | |||
|
|
568 | int i, ri; | |||
|
|
569 | Monitor *mon = selmon, *m; | |||
|
|
570 | ||||
|
|
571 | appid = client_get_appid(c); | |||
|
|
572 | title = client_get_title(c); | |||
|
|
573 | ||||
|
|
574 | if (c->foreign_toplevel) { | |||
|
|
575 | wlr_foreign_toplevel_handle_v1_set_app_id(c->foreign_toplevel, appid); | |||
|
|
576 | wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, title); | |||
|
|
577 | } | |||
|
|
578 | ||||
|
|
579 | for (ri = 0; ri < cfg.nrules; ri++) { | |||
|
|
580 | const CfgRule *r = &cfg.rules[ri]; | |||
|
|
581 | if ((!r->title[0] || strstr(title, r->title)) | |||
|
|
582 | && (!r->app_id[0] || strstr(appid, r->app_id))) { | |||
|
|
583 | c->isfloating = r->floating; | |||
|
|
584 | newtags |= r->tags; | |||
|
|
585 | i = 0; | |||
|
|
586 | wl_list_for_each(m, &mons, link) { | |||
|
|
587 | if (r->monitor == i++) | |||
|
|
588 | mon = m; | |||
|
|
589 | } | |||
|
|
590 | } | |||
|
|
591 | } | |||
|
|
592 | ||||
|
|
593 | c->isfloating |= client_is_float_type(c); | |||
|
|
594 | setmon(c, mon, newtags); | |||
|
|
595 | } | |||
|
|
596 | ||||
|
|
597 | void | |||
|
|
598 | arrange(Monitor *m) | |||
|
|
599 | { | |||
|
|
600 | Client *c; | |||
|
|
601 | ||||
|
|
602 | if (!m->wlr_output->enabled) | |||
|
|
603 | return; | |||
|
|
604 | ||||
|
|
605 | wl_list_for_each(c, &clients, link) { | |||
|
|
606 | if (c->mon == m) { | |||
|
|
607 | wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m)); | |||
|
|
608 | client_set_suspended(c, !VISIBLEON(c, m)); | |||
|
|
609 | } | |||
|
|
610 | } | |||
|
|
611 | ||||
|
|
612 | wlr_scene_node_set_enabled(&m->fullscreen_bg->node, | |||
|
|
613 | (c = focustop(m)) && c->isfullscreen); | |||
|
|
614 | ||||
|
|
615 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); | |||
|
|
616 | ||||
|
|
617 | /* We move all clients (except fullscreen and unmanaged) to LyrTile while | |||
|
|
618 | * in floating layout to avoid "real" floating clients be always on top */ | |||
|
|
619 | wl_list_for_each(c, &clients, link) { | |||
|
|
620 | if (c->mon != m || c->scene->node.parent == layers[LyrFS]) | |||
|
|
621 | continue; | |||
|
|
622 | ||||
|
|
623 | wlr_scene_node_reparent(&c->scene->node, | |||
|
|
624 | (!m->lt[m->sellt]->arrange && c->isfloating) | |||
|
|
625 | ? layers[LyrTile] | |||
|
|
626 | : (m->lt[m->sellt]->arrange && c->isfloating) | |||
|
|
627 | ? layers[LyrFloat] | |||
|
|
628 | : c->scene->node.parent); | |||
|
|
629 | } | |||
|
|
630 | ||||
|
|
631 | if (m->lt[m->sellt]->arrange) | |||
|
|
632 | m->lt[m->sellt]->arrange(m); | |||
|
|
633 | motionnotify(0, NULL, 0, 0, 0, 0); | |||
|
|
634 | checkidleinhibitor(NULL); | |||
|
|
635 | } | |||
|
|
636 | ||||
|
|
637 | void | |||
|
|
638 | arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive) | |||
|
|
639 | { | |||
|
|
640 | LayerSurface *l; | |||
|
|
641 | struct wlr_box full_area = m->m; | |||
|
|
642 | ||||
|
|
643 | wl_list_for_each(l, list, link) { | |||
|
|
644 | struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; | |||
|
|
645 | ||||
|
|
646 | if (!layer_surface->initialized) | |||
|
|
647 | continue; | |||
|
|
648 | ||||
|
|
649 | if (exclusive != (layer_surface->current.exclusive_zone > 0)) | |||
|
|
650 | continue; | |||
|
|
651 | ||||
|
|
652 | wlr_scene_layer_surface_v1_configure(l->scene_layer, &full_area, usable_area); | |||
|
|
653 | wlr_scene_node_set_position(&l->popups->node, l->scene->node.x, l->scene->node.y); | |||
|
|
654 | } | |||
|
|
655 | } | |||
|
|
656 | ||||
|
|
657 | void | |||
|
|
658 | arrangelayers(Monitor *m) | |||
|
|
659 | { | |||
|
|
660 | int i; | |||
|
|
661 | struct wlr_box usable_area = m->m; | |||
|
|
662 | LayerSurface *l; | |||
|
|
663 | uint32_t layers_above_shell[] = { | |||
|
|
664 | ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, | |||
|
|
665 | ZWLR_LAYER_SHELL_V1_LAYER_TOP, | |||
|
|
666 | }; | |||
|
|
667 | if (!m->wlr_output->enabled) | |||
|
|
668 | return; | |||
|
|
669 | ||||
|
|
670 | /* Arrange exclusive surfaces from top->bottom */ | |||
|
|
671 | for (i = 3; i >= 0; i--) | |||
|
|
672 | arrangelayer(m, &m->layers[i], &usable_area, 1); | |||
|
|
673 | ||||
|
|
674 | if (!wlr_box_equal(&usable_area, &m->w)) { | |||
|
|
675 | m->w = usable_area; | |||
|
|
676 | arrange(m); | |||
|
|
677 | } | |||
|
|
678 | ||||
|
|
679 | /* Arrange non-exclusive surfaces from top->bottom */ | |||
|
|
680 | for (i = 3; i >= 0; i--) | |||
|
|
681 | arrangelayer(m, &m->layers[i], &usable_area, 0); | |||
|
|
682 | ||||
|
|
683 | /* Find topmost keyboard interactive layer, if such a layer exists */ | |||
|
|
684 | for (i = 0; i < (int)LENGTH(layers_above_shell); i++) { | |||
|
|
685 | wl_list_for_each_reverse(l, &m->layers[layers_above_shell[i]], link) { | |||
|
|
686 | if (locked || !l->layer_surface->current.keyboard_interactive || !l->mapped) | |||
|
|
687 | continue; | |||
|
|
688 | /* Deactivate the focused client. */ | |||
|
|
689 | focusclient(NULL, 0); | |||
|
|
690 | exclusive_focus = l; | |||
|
|
691 | client_notify_enter(l->layer_surface->surface, wlr_seat_get_keyboard(seat)); | |||
|
|
692 | return; | |||
|
|
693 | } | |||
|
|
694 | } | |||
|
|
695 | } | |||
|
|
696 | ||||
|
|
697 | void | |||
|
|
698 | axisnotify(struct wl_listener *listener, void *data) | |||
|
|
699 | { | |||
|
|
700 | /* This event is forwarded by the cursor when a pointer emits an axis event, | |||
|
|
701 | * for example when you move the scroll wheel. */ | |||
|
|
702 | struct wlr_pointer_axis_event *event = data; | |||
|
|
703 | wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); | |||
|
|
704 | wlr_seat_pointer_notify_axis(seat, | |||
|
|
705 | event->time_msec, event->orientation, event->delta, | |||
|
|
706 | event->delta_discrete, event->source, event->relative_direction); | |||
|
|
707 | } | |||
|
|
708 | ||||
|
|
709 | static void dispatch_action(const char *action, const char (*args)[CFG_MAX_STRLEN], int nargs); | |||
|
|
710 | ||||
|
|
711 | void | |||
|
|
712 | buttonpress(struct wl_listener *listener, void *data) | |||
|
|
713 | { | |||
|
|
714 | struct wlr_pointer_button_event *event = data; | |||
|
|
715 | struct wlr_keyboard *keyboard; | |||
|
|
716 | uint32_t mods; | |||
|
|
717 | Client *c; | |||
|
|
718 | ||||
|
|
719 | wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); | |||
|
|
720 | ||||
|
|
721 | switch (event->state) { | |||
|
|
722 | case WL_POINTER_BUTTON_STATE_PRESSED: | |||
|
|
723 | cursor_mode = CurPressed; | |||
|
|
724 | selmon = xytomon(cursor->x, cursor->y); | |||
|
|
725 | if (locked) | |||
|
|
726 | break; | |||
|
|
727 | ||||
|
|
728 | /* Change focus if the button was _pressed_ over a client */ | |||
|
|
729 | xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); | |||
|
|
730 | if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) | |||
|
|
731 | focusclient(c, 1); | |||
|
|
732 | ||||
|
|
733 | keyboard = wlr_seat_get_keyboard(seat); | |||
|
|
734 | mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; | |||
|
|
735 | for (int i = 0; i < cfg.nbuttons; i++) { | |||
|
|
736 | const CfgButton *b = &cfg.buttons[i]; | |||
|
|
737 | if (CLEANMASK(mods) == CLEANMASK(b->mods) && | |||
|
|
738 | event->button == b->button) { | |||
|
|
739 | dispatch_action(b->action, b->args, b->nargs); | |||
|
|
740 | return; | |||
|
|
741 | } | |||
|
|
742 | } | |||
|
|
743 | break; | |||
|
|
744 | case WL_POINTER_BUTTON_STATE_RELEASED: | |||
|
|
745 | /* If you released any buttons, we exit interactive move/resize mode. */ | |||
|
|
746 | if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { | |||
|
|
747 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); | |||
|
|
748 | if (cursor_mode == CurTileDrag) { | |||
|
|
749 | Monitor *dropmon = xytomon(cursor->x, cursor->y); | |||
|
|
750 | if (dropmon && dropmon != grabc->mon) { | |||
|
|
751 | selmon = dropmon; | |||
|
|
752 | cursor_mode = CurNormal; /* so arrange() treats grabc as tiled */ | |||
|
|
753 | setmon(grabc, dropmon, 0); | |||
|
|
754 | wlr_scene_node_reparent(&grabc->scene->node, layers[LyrTile]); | |||
|
|
755 | preselect_target = NULL; | |||
|
|
756 | preselect_edge = EDGE_NONE; | |||
|
|
757 | wlr_scene_node_set_enabled(&preselect_indicator->node, false); | |||
|
|
758 | grabc = NULL; | |||
|
|
759 | return; | |||
|
|
760 | } | |||
|
|
761 | if (preselect_target && preselect_edge != EDGE_NONE) { | |||
|
|
762 | dwindle_move_to_edge(grabc, preselect_target, preselect_edge); | |||
|
|
763 | } else { | |||
|
|
764 | Client *target = tiled_client_at(cursor->x, cursor->y, grabc); | |||
|
|
765 | if (target) | |||
|
|
766 | swaptiled(grabc, target); | |||
|
|
767 | else | |||
|
|
768 | resize(grabc, tiledrag_orig_geom, 0); | |||
|
|
769 | } | |||
|
|
770 | wlr_scene_node_reparent(&grabc->scene->node, layers[LyrTile]); | |||
|
|
771 | preselect_target = NULL; | |||
|
|
772 | preselect_edge = EDGE_NONE; | |||
|
|
773 | wlr_scene_node_set_enabled(&preselect_indicator->node, false); | |||
|
|
774 | cursor_mode = CurNormal; | |||
|
|
775 | grabc = NULL; | |||
|
|
776 | return; | |||
|
|
777 | } | |||
|
|
778 | cursor_mode = CurNormal; | |||
|
|
779 | /* Drop the window off on its new monitor */ | |||
|
|
780 | selmon = xytomon(cursor->x, cursor->y); | |||
|
|
781 | setmon(grabc, selmon, 0); | |||
|
|
782 | grabc = NULL; | |||
|
|
783 | return; | |||
|
|
784 | } | |||
|
|
785 | cursor_mode = CurNormal; | |||
|
|
786 | break; | |||
|
|
787 | } | |||
|
|
788 | /* If the event wasn't handled by the compositor, notify the client with | |||
|
|
789 | * pointer focus that a button press has occurred */ | |||
|
|
790 | wlr_seat_pointer_notify_button(seat, | |||
|
|
791 | event->time_msec, event->button, event->state); | |||
|
|
792 | } | |||
|
|
793 | ||||
|
|
794 | void | |||
|
|
795 | chvt(const Arg *arg) | |||
|
|
796 | { | |||
|
|
797 | if (!session) { | |||
|
|
798 | fprintf(stderr, "swindle: chvt: no session (not running on DRM)\n"); | |||
|
|
799 | return; | |||
|
|
800 | } | |||
|
|
801 | wlr_session_change_vt(session, arg->ui); | |||
|
|
802 | } | |||
|
|
803 | ||||
|
|
804 | void | |||
|
|
805 | checkidleinhibitor(struct wlr_surface *exclude) | |||
|
|
806 | { | |||
|
|
807 | int inhibited = 0, unused_lx, unused_ly; | |||
|
|
808 | struct wlr_idle_inhibitor_v1 *inhibitor; | |||
|
|
809 | wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { | |||
|
|
810 | struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); | |||
|
|
811 | struct wlr_scene_tree *tree = surface->data; | |||
|
|
812 | if (exclude != surface && (cfg.bypass_surface_visibility || (!tree | |||
|
|
813 | || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) { | |||
|
|
814 | inhibited = 1; | |||
|
|
815 | break; | |||
|
|
816 | } | |||
|
|
817 | } | |||
|
|
818 | ||||
|
|
819 | wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); | |||
|
|
820 | } | |||
|
|
821 | ||||
|
|
822 | void | |||
|
|
823 | cleanup(void) | |||
|
|
824 | { | |||
|
|
825 | config_watch_stop(cfg_watch_src); | |||
|
|
826 | cfg_watch_src = NULL; | |||
|
|
827 | cleanuplisteners(); | |||
|
|
828 | #ifdef XWAYLAND | |||
|
|
829 | wlr_xwayland_destroy(xwayland); | |||
|
|
830 | xwayland = NULL; | |||
|
|
831 | #endif | |||
|
|
832 | wl_display_destroy_clients(dpy); | |||
|
|
833 | if (child_pid > 0) { | |||
|
|
834 | kill(-child_pid, SIGTERM); | |||
|
|
835 | waitpid(child_pid, NULL, 0); | |||
|
|
836 | } | |||
|
|
837 | wlr_xcursor_manager_destroy(cursor_mgr); | |||
|
|
838 | ||||
|
|
839 | destroykeyboardgroup(&kb_group->destroy, NULL); | |||
|
|
840 | ||||
|
|
841 | /* If it's not destroyed manually, it will cause a use-after-free of wlr_seat. | |||
|
|
842 | * Destroy it until it's fixed on the wlroots side */ | |||
|
|
843 | wlr_backend_destroy(backend); | |||
|
|
844 | ||||
|
|
845 | wl_display_destroy(dpy); | |||
|
|
846 | /* Destroy after the wayland display (when the monitors are already destroyed) | |||
|
|
847 | to avoid destroying them with an invalid scene output. */ | |||
|
|
848 | wlr_scene_node_destroy(&scene->tree.node); | |||
|
|
849 | } | |||
|
|
850 | ||||
|
|
851 | void | |||
|
|
852 | cleanupmon(struct wl_listener *listener, void *data) | |||
|
|
853 | { | |||
|
|
854 | Monitor *m = wl_container_of(listener, m, destroy); | |||
|
|
855 | LayerSurface *l, *tmp; | |||
|
|
856 | size_t i; | |||
|
|
857 | ||||
|
|
858 | /* m->layers[i] are intentionally not unlinked */ | |||
|
|
859 | for (i = 0; i < LENGTH(m->layers); i++) { | |||
|
|
860 | wl_list_for_each_safe(l, tmp, &m->layers[i], link) | |||
|
|
861 | wlr_layer_surface_v1_destroy(l->layer_surface); | |||
|
|
862 | } | |||
|
|
863 | ||||
|
|
864 | wl_list_remove(&m->destroy.link); | |||
|
|
865 | wl_list_remove(&m->frame.link); | |||
|
|
866 | wl_list_remove(&m->link); | |||
|
|
867 | wl_list_remove(&m->request_state.link); | |||
|
|
868 | if (m->lock_surface) | |||
|
|
869 | destroylocksurface(&m->destroy_lock_surface, NULL); | |||
|
|
870 | m->wlr_output->data = NULL; | |||
|
|
871 | wlr_output_layout_remove(output_layout, m->wlr_output); | |||
|
|
872 | wlr_scene_output_destroy(m->scene_output); | |||
|
|
873 | ||||
|
|
874 | closemon(m); | |||
|
|
875 | ext_workspace_cleanupmon(m); | |||
|
|
876 | for (int i = 0; i < TAGCOUNT; i++) { | |||
|
|
877 | dwindle_free_tree(m->dwindle_root[i]); | |||
|
|
878 | m->dwindle_root[i] = NULL; | |||
|
|
879 | } | |||
|
|
880 | wlr_scene_node_destroy(&m->fullscreen_bg->node); | |||
|
|
881 | free(m); | |||
|
|
882 | } | |||
|
|
883 | ||||
|
|
884 | void | |||
|
|
885 | cleanuplisteners(void) | |||
|
|
886 | { | |||
|
|
887 | wl_list_remove(&cursor_axis.link); | |||
|
|
888 | wl_list_remove(&cursor_button.link); | |||
|
|
889 | wl_list_remove(&cursor_frame.link); | |||
|
|
890 | wl_list_remove(&cursor_motion.link); | |||
|
|
891 | wl_list_remove(&cursor_motion_absolute.link); | |||
|
|
892 | wl_list_remove(&gpu_reset.link); | |||
|
|
893 | wl_list_remove(&new_idle_inhibitor.link); | |||
|
|
894 | wl_list_remove(&layout_change.link); | |||
|
|
895 | wl_list_remove(&new_input_device.link); | |||
|
|
896 | wl_list_remove(&new_virtual_keyboard.link); | |||
|
|
897 | wl_list_remove(&new_virtual_pointer.link); | |||
|
|
898 | wl_list_remove(&new_pointer_constraint.link); | |||
|
|
899 | wl_list_remove(&new_output.link); | |||
|
|
900 | wl_list_remove(&new_xdg_toplevel.link); | |||
|
|
901 | wl_list_remove(&new_xdg_decoration.link); | |||
|
|
902 | wl_list_remove(&new_xdg_popup.link); | |||
|
|
903 | wl_list_remove(&new_layer_surface.link); | |||
|
|
904 | wl_list_remove(&output_mgr_apply.link); | |||
|
|
905 | wl_list_remove(&output_mgr_test.link); | |||
|
|
906 | wl_list_remove(&output_power_mgr_set_mode.link); | |||
|
|
907 | wl_list_remove(&request_activate.link); | |||
|
|
908 | wl_list_remove(&request_cursor.link); | |||
|
|
909 | wl_list_remove(&request_set_psel.link); | |||
|
|
910 | wl_list_remove(&request_set_sel.link); | |||
|
|
911 | wl_list_remove(&request_set_cursor_shape.link); | |||
|
|
912 | wl_list_remove(&request_start_drag.link); | |||
|
|
913 | wl_list_remove(&start_drag.link); | |||
|
|
914 | wl_list_remove(&new_session_lock.link); | |||
|
|
915 | #ifdef XWAYLAND | |||
|
|
916 | wl_list_remove(&new_xwayland_surface.link); | |||
|
|
917 | wl_list_remove(&xwayland_ready.link); | |||
|
|
918 | #endif | |||
|
|
919 | } | |||
|
|
920 | ||||
|
|
921 | void | |||
|
|
922 | closemon(Monitor *m) | |||
|
|
923 | { | |||
|
|
924 | /* update selmon if needed and | |||
|
|
925 | * move closed monitor's clients to the focused one */ | |||
|
|
926 | Client *c; | |||
|
|
927 | int i = 0, nmons = wl_list_length(&mons); | |||
|
|
928 | if (!nmons) { | |||
|
|
929 | selmon = NULL; | |||
|
|
930 | } else if (m == selmon) { | |||
|
|
931 | do /* don't switch to disabled mons */ | |||
|
|
932 | selmon = wl_container_of(mons.next, selmon, link); | |||
|
|
933 | while (!selmon->wlr_output->enabled && i++ < nmons); | |||
|
|
934 | ||||
|
|
935 | if (!selmon->wlr_output->enabled) | |||
|
|
936 | selmon = NULL; | |||
|
|
937 | } | |||
|
|
938 | ||||
|
|
939 | wl_list_for_each(c, &clients, link) { | |||
|
|
940 | if (c->isfloating && c->geom.x > m->m.width) | |||
|
|
941 | resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y, | |||
|
|
942 | .width = c->geom.width, .height = c->geom.height}, 0); | |||
|
|
943 | if (c->mon == m) | |||
|
|
944 | setmon(c, selmon, c->tags); | |||
|
|
945 | } | |||
|
|
946 | focusclient(focustop(selmon), 1); | |||
|
|
947 | printstatus(); | |||
|
|
948 | } | |||
|
|
949 | ||||
|
|
950 | void | |||
|
|
951 | commitlayersurfacenotify(struct wl_listener *listener, void *data) | |||
|
|
952 | { | |||
|
|
953 | LayerSurface *l = wl_container_of(listener, l, surface_commit); | |||
|
|
954 | struct wlr_layer_surface_v1 *layer_surface = l->layer_surface; | |||
|
|
955 | struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->current.layer]]; | |||
|
|
956 | struct wlr_layer_surface_v1_state old_state; | |||
|
|
957 | ||||
|
|
958 | if (l->layer_surface->initial_commit) { | |||
|
|
959 | client_set_scale(layer_surface->surface, l->mon->wlr_output->scale); | |||
|
|
960 | ||||
|
|
961 | /* Temporarily set the layer's current state to pending | |||
|
|
962 | * so that we can easily arrange it */ | |||
|
|
963 | old_state = l->layer_surface->current; | |||
|
|
964 | l->layer_surface->current = l->layer_surface->pending; | |||
|
|
965 | arrangelayers(l->mon); | |||
|
|
966 | l->layer_surface->current = old_state; | |||
|
|
967 | return; | |||
|
|
968 | } | |||
|
|
969 | ||||
|
|
970 | if (layer_surface->current.committed == 0 && l->mapped == layer_surface->surface->mapped) | |||
|
|
971 | return; | |||
|
|
972 | l->mapped = layer_surface->surface->mapped; | |||
|
|
973 | ||||
|
|
974 | if (scene_layer != l->scene->node.parent) { | |||
|
|
975 | wlr_scene_node_reparent(&l->scene->node, scene_layer); | |||
|
|
976 | wl_list_remove(&l->link); | |||
|
|
977 | wl_list_insert(&l->mon->layers[layer_surface->current.layer], &l->link); | |||
|
|
978 | wlr_scene_node_reparent(&l->popups->node, (layer_surface->current.layer | |||
|
|
979 | < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer)); | |||
|
|
980 | } | |||
|
|
981 | ||||
|
|
982 | arrangelayers(l->mon); | |||
|
|
983 | } | |||
|
|
984 | ||||
|
|
985 | void | |||
|
|
986 | commitnotify(struct wl_listener *listener, void *data) | |||
|
|
987 | { | |||
|
|
988 | Client *c = wl_container_of(listener, c, commit); | |||
|
|
989 | ||||
|
|
990 | if (c->surface.xdg->initial_commit) { | |||
|
|
991 | /* | |||
|
|
992 | * Get the monitor this client will be rendered on | |||
|
|
993 | * Note that if the user set a rule in which the client is placed on | |||
|
|
994 | * a different monitor based on its title, this will likely select | |||
|
|
995 | * a wrong monitor. | |||
|
|
996 | */ | |||
|
|
997 | applyrules(c); | |||
|
|
998 | if (c->mon) { | |||
|
|
999 | client_set_scale(client_surface(c), c->mon->wlr_output->scale); | |||
|
|
1000 | } | |||
|
|
1001 | setmon(c, NULL, 0); /* Make sure to reapply rules in mapnotify() */ | |||
|
|
1002 | ||||
|
|
1003 | wlr_xdg_toplevel_set_wm_capabilities(c->surface.xdg->toplevel, | |||
|
|
1004 | WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); | |||
|
|
1005 | if (c->decoration) | |||
|
|
1006 | requestdecorationmode(&c->set_decoration_mode, c->decoration); | |||
|
|
1007 | wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, 0, 0); | |||
|
|
1008 | return; | |||
|
|
1009 | } | |||
|
|
1010 | ||||
|
|
1011 | resize(c, c->geom, (c->isfloating && !c->isfullscreen)); | |||
|
|
1012 | ||||
|
|
1013 | /* mark a pending resize as completed */ | |||
|
|
1014 | if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) | |||
|
|
1015 | c->resize = 0; | |||
|
|
1016 | } | |||
|
|
1017 | ||||
|
|
1018 | void | |||
|
|
1019 | commitpopup(struct wl_listener *listener, void *data) | |||
|
|
1020 | { | |||
|
|
1021 | struct wlr_surface *surface = data; | |||
|
|
1022 | struct wlr_xdg_popup *popup = wlr_xdg_popup_try_from_wlr_surface(surface); | |||
|
|
1023 | LayerSurface *l = NULL; | |||
|
|
1024 | Client *c = NULL; | |||
|
|
1025 | struct wlr_box box; | |||
|
|
1026 | int type = -1; | |||
|
|
1027 | ||||
|
|
1028 | if (!popup->base->initial_commit) | |||
|
|
1029 | return; | |||
|
|
1030 | ||||
|
|
1031 | type = toplevel_from_wlr_surface(popup->base->surface, &c, &l); | |||
|
|
1032 | if (!popup->parent || type < 0) | |||
|
|
1033 | return; | |||
|
|
1034 | popup->base->surface->data = wlr_scene_xdg_surface_create( | |||
|
|
1035 | popup->parent->data, popup->base); | |||
|
|
1036 | if ((l && !l->mon) || (c && !c->mon)) { | |||
|
|
1037 | wlr_xdg_popup_destroy(popup); | |||
|
|
1038 | return; | |||
|
|
1039 | } | |||
|
|
1040 | box = type == LayerShell ? l->mon->m : c->mon->w; | |||
|
|
1041 | box.x -= (type == LayerShell ? l->scene->node.x : c->geom.x); | |||
|
|
1042 | box.y -= (type == LayerShell ? l->scene->node.y : c->geom.y); | |||
|
|
1043 | wlr_xdg_popup_unconstrain_from_box(popup, &box); | |||
|
|
1044 | wl_list_remove(&listener->link); | |||
|
|
1045 | free(listener); | |||
|
|
1046 | } | |||
|
|
1047 | ||||
|
|
1048 | void | |||
|
|
1049 | createdecoration(struct wl_listener *listener, void *data) | |||
|
|
1050 | { | |||
|
|
1051 | struct wlr_xdg_toplevel_decoration_v1 *deco = data; | |||
|
|
1052 | Client *c = deco->toplevel->base->data; | |||
|
|
1053 | c->decoration = deco; | |||
|
|
1054 | ||||
|
|
1055 | LISTEN(&deco->events.request_mode, &c->set_decoration_mode, requestdecorationmode); | |||
|
|
1056 | LISTEN(&deco->events.destroy, &c->destroy_decoration, destroydecoration); | |||
|
|
1057 | ||||
|
|
1058 | requestdecorationmode(&c->set_decoration_mode, deco); | |||
|
|
1059 | } | |||
|
|
1060 | ||||
|
|
1061 | void | |||
|
|
1062 | createidleinhibitor(struct wl_listener *listener, void *data) | |||
|
|
1063 | { | |||
|
|
1064 | struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; | |||
|
|
1065 | LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor); | |||
|
|
1066 | ||||
|
|
1067 | checkidleinhibitor(NULL); | |||
|
|
1068 | } | |||
|
|
1069 | ||||
|
|
1070 | void | |||
|
|
1071 | createkeyboard(struct wlr_keyboard *keyboard) | |||
|
|
1072 | { | |||
|
|
1073 | /* Set the keymap to match the group keymap */ | |||
|
|
1074 | wlr_keyboard_set_keymap(keyboard, kb_group->wlr_group->keyboard.keymap); | |||
|
|
1075 | ||||
|
|
1076 | /* Add the new keyboard to the group */ | |||
|
|
1077 | wlr_keyboard_group_add_keyboard(kb_group->wlr_group, keyboard); | |||
|
|
1078 | } | |||
|
|
1079 | ||||
|
|
1080 | KeyboardGroup * | |||
|
|
1081 | createkeyboardgroup(void) | |||
|
|
1082 | { | |||
|
|
1083 | KeyboardGroup *group = ecalloc(1, sizeof(*group)); | |||
|
|
1084 | struct xkb_context *context; | |||
|
|
1085 | struct xkb_keymap *keymap; | |||
|
|
1086 | ||||
|
|
1087 | group->wlr_group = wlr_keyboard_group_create(); | |||
|
|
1088 | group->wlr_group->data = group; | |||
|
|
1089 | ||||
|
|
1090 | /* Prepare an XKB keymap and assign it to the keyboard group. */ | |||
|
|
1091 | struct xkb_rule_names rn = { | |||
|
|
1092 | .model = cfg.input.model[0] ? cfg.input.model : NULL, | |||
|
|
1093 | .layout = cfg.input.layout[0] ? cfg.input.layout : "us", | |||
|
|
1094 | .variant = cfg.input.variant[0] ? cfg.input.variant : NULL, | |||
|
|
1095 | .options = cfg.input.options[0] ? cfg.input.options : NULL, | |||
|
|
1096 | }; | |||
|
|
1097 | context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | |||
|
|
1098 | if (!(keymap = xkb_keymap_new_from_names(context, &rn, | |||
|
|
1099 | XKB_KEYMAP_COMPILE_NO_FLAGS))) | |||
|
|
1100 | die("failed to compile keymap"); | |||
|
|
1101 | ||||
|
|
1102 | wlr_keyboard_set_keymap(&group->wlr_group->keyboard, keymap); | |||
|
|
1103 | xkb_keymap_unref(keymap); | |||
|
|
1104 | xkb_context_unref(context); | |||
|
|
1105 | ||||
|
|
1106 | wlr_keyboard_set_repeat_info(&group->wlr_group->keyboard, cfg.input.repeat_rate, cfg.input.repeat_delay); | |||
|
|
1107 | ||||
|
|
1108 | /* Set up listeners for keyboard events */ | |||
|
|
1109 | LISTEN(&group->wlr_group->keyboard.events.key, &group->key, keypress); | |||
|
|
1110 | LISTEN(&group->wlr_group->keyboard.events.modifiers, &group->modifiers, keypressmod); | |||
|
|
1111 | ||||
|
|
1112 | group->key_repeat_source = wl_event_loop_add_timer(event_loop, keyrepeat, group); | |||
|
|
1113 | ||||
|
|
1114 | /* A seat can only have one keyboard, but this is a limitation of the | |||
|
|
1115 | * Wayland protocol - not wlroots. We assign all connected keyboards to the | |||
|
|
1116 | * same wlr_keyboard_group, which provides a single wlr_keyboard interface for | |||
|
|
1117 | * all of them. Set this combined wlr_keyboard as the seat keyboard. | |||
|
|
1118 | */ | |||
|
|
1119 | wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); | |||
|
|
1120 | return group; | |||
|
|
1121 | } | |||
|
|
1122 | ||||
|
|
1123 | void | |||
|
|
1124 | createlayersurface(struct wl_listener *listener, void *data) | |||
|
|
1125 | { | |||
|
|
1126 | struct wlr_layer_surface_v1 *layer_surface = data; | |||
|
|
1127 | LayerSurface *l; | |||
|
|
1128 | struct wlr_surface *surface = layer_surface->surface; | |||
|
|
1129 | struct wlr_scene_tree *scene_layer = layers[layermap[layer_surface->pending.layer]]; | |||
|
|
1130 | ||||
|
|
1131 | if (!layer_surface->output | |||
|
|
1132 | && !(layer_surface->output = selmon ? selmon->wlr_output : NULL)) { | |||
|
|
1133 | wlr_layer_surface_v1_destroy(layer_surface); | |||
|
|
1134 | return; | |||
|
|
1135 | } | |||
|
|
1136 | ||||
|
|
1137 | l = layer_surface->data = ecalloc(1, sizeof(*l)); | |||
|
|
1138 | l->type = LayerShell; | |||
|
|
1139 | LISTEN(&surface->events.commit, &l->surface_commit, commitlayersurfacenotify); | |||
|
|
1140 | LISTEN(&surface->events.unmap, &l->unmap, unmaplayersurfacenotify); | |||
|
|
1141 | LISTEN(&layer_surface->events.destroy, &l->destroy, destroylayersurfacenotify); | |||
|
|
1142 | ||||
|
|
1143 | l->layer_surface = layer_surface; | |||
|
|
1144 | l->mon = layer_surface->output->data; | |||
|
|
1145 | l->scene_layer = wlr_scene_layer_surface_v1_create(scene_layer, layer_surface); | |||
|
|
1146 | l->scene = l->scene_layer->tree; | |||
|
|
1147 | l->popups = surface->data = wlr_scene_tree_create(layer_surface->current.layer | |||
|
|
1148 | < ZWLR_LAYER_SHELL_V1_LAYER_TOP ? layers[LyrTop] : scene_layer); | |||
|
|
1149 | l->scene->node.data = l->popups->node.data = l; | |||
|
|
1150 | ||||
|
|
1151 | wl_list_insert(&l->mon->layers[layer_surface->pending.layer],&l->link); | |||
|
|
1152 | wlr_surface_send_enter(surface, layer_surface->output); | |||
|
|
1153 | } | |||
|
|
1154 | ||||
|
|
1155 | void | |||
|
|
1156 | createlocksurface(struct wl_listener *listener, void *data) | |||
|
|
1157 | { | |||
|
|
1158 | SessionLock *lock = wl_container_of(listener, lock, new_surface); | |||
|
|
1159 | struct wlr_session_lock_surface_v1 *lock_surface = data; | |||
|
|
1160 | Monitor *m = lock_surface->output->data; | |||
|
|
1161 | struct wlr_scene_tree *scene_tree = lock_surface->surface->data | |||
|
|
1162 | = wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); | |||
|
|
1163 | m->lock_surface = lock_surface; | |||
|
|
1164 | ||||
|
|
1165 | wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); | |||
|
|
1166 | wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height); | |||
|
|
1167 | ||||
|
|
1168 | LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface); | |||
|
|
1169 | ||||
|
|
1170 | if (m == selmon) | |||
|
|
1171 | client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat)); | |||
|
|
1172 | } | |||
|
|
1173 | ||||
|
|
1174 | void | |||
|
|
1175 | createmon(struct wl_listener *listener, void *data) | |||
|
|
1176 | { | |||
|
|
1177 | /* This event is raised by the backend when a new output (aka a display or | |||
|
|
1178 | * monitor) becomes available. */ | |||
|
|
1179 | struct wlr_output *wlr_output = data; | |||
|
|
1180 | size_t i; | |||
|
|
1181 | struct wlr_output_state state; | |||
|
|
1182 | Monitor *m; | |||
|
|
1183 | ||||
|
|
1184 | if (!wlr_output_init_render(wlr_output, alloc, drw)) | |||
|
|
1185 | return; | |||
|
|
1186 | ||||
|
|
1187 | m = wlr_output->data = ecalloc(1, sizeof(*m)); | |||
|
|
1188 | m->wlr_output = wlr_output; | |||
|
|
1189 | ||||
|
|
1190 | for (i = 0; i < LENGTH(m->layers); i++) | |||
|
|
1191 | wl_list_init(&m->layers[i]); | |||
|
|
1192 | ||||
|
|
1193 | wlr_output_state_init(&state); | |||
|
|
1194 | /* Initialize monitor state using configured rules */ | |||
|
|
1195 | m->tagset[0] = m->tagset[1] = 1; | |||
|
|
1196 | m->gaps = (cfg.appearance.gaps > 0) ? 1 : 0; | |||
|
|
1197 | { | |||
|
|
1198 | int ri; | |||
|
|
1199 | for (ri = 0; ri < cfg.nmonitors; ri++) { | |||
|
|
1200 | const CfgMonitorRule *r = &cfg.monitors[ri]; | |||
|
|
1201 | if (!r->name[0] || strstr(wlr_output->name, r->name)) { | |||
|
|
1202 | m->m.x = r->x; | |||
|
|
1203 | m->m.y = r->y; | |||
|
|
1204 | m->mfact = r->mfact; | |||
|
|
1205 | m->nmaster = r->nmaster; | |||
|
|
1206 | /* Map layout name to layouts[] entry (it's not | |||
|
|
1207 | * very useful I know) */ | |||
|
|
1208 | m->lt[0] = &layouts[0]; /* default: floating */ | |||
|
|
1209 | for (int li = 0; li < (int)LENGTH(layouts); li++) { | |||
|
|
1210 | if (layouts[li].symbol && !strcmp(r->layout, layouts[li].symbol)) { | |||
|
|
1211 | m->lt[0] = &layouts[li]; | |||
|
|
1212 | break; | |||
|
|
1213 | } | |||
|
|
1214 | /* also match by arrange function name string */ | |||
|
|
1215 | if (!strcmp(r->layout, "dwindle") && layouts[li].arrange == dwindle) { | |||
|
|
1216 | m->lt[0] = &layouts[li]; | |||
|
|
1217 | break; | |||
|
|
1218 | } | |||
|
|
1219 | } | |||
|
|
1220 | m->lt[1] = &layouts[LENGTH(layouts) > 1 && m->lt[0] != &layouts[1]]; | |||
|
|
1221 | strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); | |||
|
|
1222 | wlr_output_state_set_scale(&state, r->scale); | |||
|
|
1223 | wlr_output_state_set_transform(&state, r->transform); | |||
|
|
1224 | break; | |||
|
|
1225 | } | |||
|
|
1226 | } | |||
|
|
1227 | } | |||
|
|
1228 | ||||
|
|
1229 | /* The mode is a tuple of (width, height, refresh rate), and each | |||
|
|
1230 | * monitor supports only a specific set of modes. We just pick the | |||
|
|
1231 | * monitor's preferred mode; a more sophisticated compositor would let | |||
|
|
1232 | * the user configure it. */ | |||
|
|
1233 | wlr_output_state_set_mode(&state, wlr_output_preferred_mode(wlr_output)); | |||
|
|
1234 | ||||
|
|
1235 | /* Set up event listeners */ | |||
|
|
1236 | LISTEN(&wlr_output->events.frame, &m->frame, rendermon); | |||
|
|
1237 | LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); | |||
|
|
1238 | LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); | |||
|
|
1239 | ||||
|
|
1240 | wlr_output_state_set_enabled(&state, 1); | |||
|
|
1241 | wlr_output_commit_state(wlr_output, &state); | |||
|
|
1242 | wlr_output_state_finish(&state); | |||
|
|
1243 | ||||
|
|
1244 | wl_list_insert(&mons, &m->link); | |||
|
|
1245 | ext_workspace_createmon(m); | |||
|
|
1246 | printstatus(); | |||
|
|
1247 | ||||
|
|
1248 | /* The xdg-protocol specifies: | |||
|
|
1249 | * | |||
|
|
1250 | * If the fullscreened surface is not opaque, the compositor must make | |||
|
|
1251 | * sure that other screen content not part of the same surface tree (made | |||
|
|
1252 | * up of subsurfaces, popups or similarly coupled surfaces) are not | |||
|
|
1253 | * visible below the fullscreened surface. | |||
|
|
1254 | * | |||
|
|
1255 | */ | |||
|
|
1256 | ||||
|
|
1257 | /* updatemons() will resize and set correct position */ | |||
|
|
1258 | m->fullscreen_bg = wlr_scene_rect_create(layers[LyrFS], 0, 0, cfg.appearance.fullscreen_bg); | |||
|
|
1259 | wlr_scene_node_set_enabled(&m->fullscreen_bg->node, 0); | |||
|
|
1260 | ||||
|
|
1261 | /* Adds this to the output layout in the order it was configured. | |||
|
|
1262 | * | |||
|
|
1263 | * The output layout utility automatically adds a wl_output global to the | |||
|
|
1264 | * display, which Wayland clients can see to find out information about the | |||
|
|
1265 | * output (such as DPI, scale factor, manufacturer, etc). | |||
|
|
1266 | */ | |||
|
|
1267 | m->scene_output = wlr_scene_output_create(scene, wlr_output); | |||
|
|
1268 | if (m->m.x == -1 && m->m.y == -1) | |||
|
|
1269 | wlr_output_layout_add_auto(output_layout, wlr_output); | |||
|
|
1270 | else | |||
|
|
1271 | wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); | |||
|
|
1272 | } | |||
|
|
1273 | ||||
|
|
1274 | void | |||
|
|
1275 | createnotify(struct wl_listener *listener, void *data) | |||
|
|
1276 | { | |||
|
|
1277 | /* This event is raised when a client creates a new toplevel (application window). */ | |||
|
|
1278 | struct wlr_xdg_toplevel *toplevel = data; | |||
|
|
1279 | Client *c = NULL; | |||
|
|
1280 | ||||
|
|
1281 | /* Allocate a Client for this surface */ | |||
|
|
1282 | c = toplevel->base->data = ecalloc(1, sizeof(*c)); | |||
|
|
1283 | c->surface.xdg = toplevel->base; | |||
|
|
1284 | c->ibw = cfg.appearance.inner_border_px; | |||
|
|
1285 | c->obw = cfg.appearance.outer_border_px; | |||
|
|
1286 | c->bw = c->ibw + c->obw; | |||
|
|
1287 | ||||
|
|
1288 | LISTEN(&toplevel->base->surface->events.commit, &c->commit, commitnotify); | |||
|
|
1289 | LISTEN(&toplevel->base->surface->events.map, &c->map, mapnotify); | |||
|
|
1290 | LISTEN(&toplevel->base->surface->events.unmap, &c->unmap, unmapnotify); | |||
|
|
1291 | LISTEN(&toplevel->events.destroy, &c->destroy, destroynotify); | |||
|
|
1292 | LISTEN(&toplevel->events.request_fullscreen, &c->fullscreen, fullscreennotify); | |||
|
|
1293 | LISTEN(&toplevel->events.request_maximize, &c->maximize, maximizenotify); | |||
|
|
1294 | LISTEN(&toplevel->events.set_title, &c->set_title, updatetitle); | |||
|
|
1295 | } | |||
|
|
1296 | ||||
|
|
1297 | void | |||
|
|
1298 | createpointer(struct wlr_pointer *pointer) | |||
|
|
1299 | { | |||
|
|
1300 | struct libinput_device *device; | |||
|
|
1301 | if (wlr_input_device_is_libinput(&pointer->base) | |||
|
|
1302 | && (device = wlr_libinput_get_device_handle(&pointer->base))) { | |||
|
|
1303 | ||||
|
|
1304 | if (libinput_device_config_tap_get_finger_count(device)) { | |||
|
|
1305 | libinput_device_config_tap_set_enabled(device, cfg.input.tap_to_click); | |||
|
|
1306 | libinput_device_config_tap_set_drag_enabled(device, cfg.input.tap_and_drag); | |||
|
|
1307 | libinput_device_config_tap_set_drag_lock_enabled(device, cfg.input.drag_lock); | |||
|
|
1308 | libinput_device_config_tap_set_button_map(device, cfg.input.tap_button_map); | |||
|
|
1309 | } | |||
|
|
1310 | ||||
|
|
1311 | if (libinput_device_config_scroll_has_natural_scroll(device)) | |||
|
|
1312 | libinput_device_config_scroll_set_natural_scroll_enabled(device, cfg.input.natural_scrolling); | |||
|
|
1313 | ||||
|
|
1314 | if (libinput_device_config_dwt_is_available(device)) | |||
|
|
1315 | libinput_device_config_dwt_set_enabled(device, cfg.input.disable_while_typing); | |||
|
|
1316 | ||||
|
|
1317 | if (libinput_device_config_left_handed_is_available(device)) | |||
|
|
1318 | libinput_device_config_left_handed_set(device, cfg.input.left_handed); | |||
|
|
1319 | ||||
|
|
1320 | if (libinput_device_config_middle_emulation_is_available(device)) | |||
|
|
1321 | libinput_device_config_middle_emulation_set_enabled(device, cfg.input.middle_button_emulation); | |||
|
|
1322 | ||||
|
|
1323 | if (libinput_device_config_scroll_get_methods(device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) | |||
|
|
1324 | libinput_device_config_scroll_set_method(device, cfg.input.scroll_method); | |||
|
|
1325 | ||||
|
|
1326 | if (libinput_device_config_click_get_methods(device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE) | |||
|
|
1327 | libinput_device_config_click_set_method(device, cfg.input.click_method); | |||
|
|
1328 | ||||
|
|
1329 | if (libinput_device_config_send_events_get_modes(device)) | |||
|
|
1330 | libinput_device_config_send_events_set_mode(device, cfg.input.send_events_mode); | |||
|
|
1331 | ||||
|
|
1332 | if (libinput_device_config_accel_is_available(device)) { | |||
|
|
1333 | libinput_device_config_accel_set_profile(device, cfg.input.accel_profile); | |||
|
|
1334 | libinput_device_config_accel_set_speed(device, cfg.input.accel_speed); | |||
|
|
1335 | } | |||
|
|
1336 | } | |||
|
|
1337 | ||||
|
|
1338 | wlr_cursor_attach_input_device(cursor, &pointer->base); | |||
|
|
1339 | } | |||
|
|
1340 | ||||
|
|
1341 | void | |||
|
|
1342 | createpointerconstraint(struct wl_listener *listener, void *data) | |||
|
|
1343 | { | |||
|
|
1344 | PointerConstraint *pointer_constraint = ecalloc(1, sizeof(*pointer_constraint)); | |||
|
|
1345 | pointer_constraint->constraint = data; | |||
|
|
1346 | LISTEN(&pointer_constraint->constraint->events.destroy, | |||
|
|
1347 | &pointer_constraint->destroy, destroypointerconstraint); | |||
|
|
1348 | } | |||
|
|
1349 | ||||
|
|
1350 | void | |||
|
|
1351 | createpopup(struct wl_listener *listener, void *data) | |||
|
|
1352 | { | |||
|
|
1353 | /* This event is raised when a client (either xdg-shell or layer-shell) | |||
|
|
1354 | * creates a new popup. */ | |||
|
|
1355 | struct wlr_xdg_popup *popup = data; | |||
|
|
1356 | LISTEN_STATIC(&popup->base->surface->events.commit, commitpopup); | |||
|
|
1357 | } | |||
|
|
1358 | ||||
|
|
1359 | void | |||
|
|
1360 | cursorconstrain(struct wlr_pointer_constraint_v1 *constraint) | |||
|
|
1361 | { | |||
|
|
1362 | if (active_constraint == constraint) | |||
|
|
1363 | return; | |||
|
|
1364 | ||||
|
|
1365 | if (active_constraint) | |||
|
|
1366 | wlr_pointer_constraint_v1_send_deactivated(active_constraint); | |||
|
|
1367 | ||||
|
|
1368 | active_constraint = constraint; | |||
|
|
1369 | wlr_pointer_constraint_v1_send_activated(constraint); | |||
|
|
1370 | } | |||
|
|
1371 | ||||
|
|
1372 | void | |||
|
|
1373 | cursorframe(struct wl_listener *listener, void *data) | |||
|
|
1374 | { | |||
|
|
1375 | /* This event is forwarded by the cursor when a pointer emits a frame | |||
|
|
1376 | * event. Frame events are sent after regular pointer events to group | |||
|
|
1377 | * multiple events together. For instance, two axis events may happen at the | |||
|
|
1378 | * same time, in which case a frame event won't be sent in between. */ | |||
|
|
1379 | /* Notify the client with pointer focus of the frame event. */ | |||
|
|
1380 | wlr_seat_pointer_notify_frame(seat); | |||
|
|
1381 | } | |||
|
|
1382 | ||||
|
|
1383 | void | |||
|
|
1384 | cursorwarptohint(void) | |||
|
|
1385 | { | |||
|
|
1386 | Client *c = NULL; | |||
|
|
1387 | double sx = active_constraint->current.cursor_hint.x; | |||
|
|
1388 | double sy = active_constraint->current.cursor_hint.y; | |||
|
|
1389 | ||||
|
|
1390 | toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); | |||
|
|
1391 | if (c && active_constraint->current.cursor_hint.enabled) { | |||
|
|
1392 | wlr_cursor_warp(cursor, NULL, sx + c->geom.x + c->bw, sy + c->geom.y + c->bw); | |||
|
|
1393 | wlr_seat_pointer_warp(active_constraint->seat, sx, sy); | |||
|
|
1394 | } | |||
|
|
1395 | } | |||
|
|
1396 | ||||
|
|
1397 | void | |||
|
|
1398 | destroydecoration(struct wl_listener *listener, void *data) | |||
|
|
1399 | { | |||
|
|
1400 | Client *c = wl_container_of(listener, c, destroy_decoration); | |||
|
|
1401 | ||||
|
|
1402 | wl_list_remove(&c->destroy_decoration.link); | |||
|
|
1403 | wl_list_remove(&c->set_decoration_mode.link); | |||
|
|
1404 | } | |||
|
|
1405 | ||||
|
|
1406 | void | |||
|
|
1407 | destroydragicon(struct wl_listener *listener, void *data) | |||
|
|
1408 | { | |||
|
|
1409 | /* Focus enter isn't sent during drag, so refocus the focused node. */ | |||
|
|
1410 | focusclient(focustop(selmon), 1); | |||
|
|
1411 | motionnotify(0, NULL, 0, 0, 0, 0); | |||
|
|
1412 | wl_list_remove(&listener->link); | |||
|
|
1413 | free(listener); | |||
|
|
1414 | } | |||
|
|
1415 | ||||
|
|
1416 | void | |||
|
|
1417 | destroyidleinhibitor(struct wl_listener *listener, void *data) | |||
|
|
1418 | { | |||
|
|
1419 | /* `data` is the wlr_surface of the idle inhibitor being destroyed, | |||
|
|
1420 | * at this point the idle inhibitor is still in the list of the manager */ | |||
|
|
1421 | checkidleinhibitor(wlr_surface_get_root_surface(data)); | |||
|
|
1422 | wl_list_remove(&listener->link); | |||
|
|
1423 | free(listener); | |||
|
|
1424 | } | |||
|
|
1425 | ||||
|
|
1426 | void | |||
|
|
1427 | destroylayersurfacenotify(struct wl_listener *listener, void *data) | |||
|
|
1428 | { | |||
|
|
1429 | LayerSurface *l = wl_container_of(listener, l, destroy); | |||
|
|
1430 | ||||
|
|
1431 | wl_list_remove(&l->link); | |||
|
|
1432 | wl_list_remove(&l->destroy.link); | |||
|
|
1433 | wl_list_remove(&l->unmap.link); | |||
|
|
1434 | wl_list_remove(&l->surface_commit.link); | |||
|
|
1435 | wlr_scene_node_destroy(&l->scene->node); | |||
|
|
1436 | wlr_scene_node_destroy(&l->popups->node); | |||
|
|
1437 | free(l); | |||
|
|
1438 | } | |||
|
|
1439 | ||||
|
|
1440 | void | |||
|
|
1441 | destroylock(SessionLock *lock, int unlock) | |||
|
|
1442 | { | |||
|
|
1443 | wlr_seat_keyboard_notify_clear_focus(seat); | |||
|
|
1444 | if ((locked = !unlock)) | |||
|
|
1445 | goto destroy; | |||
|
|
1446 | ||||
|
|
1447 | wlr_scene_node_set_enabled(&locked_bg->node, 0); | |||
|
|
1448 | ||||
|
|
1449 | focusclient(focustop(selmon), 0); | |||
|
|
1450 | motionnotify(0, NULL, 0, 0, 0, 0); | |||
|
|
1451 | ||||
|
|
1452 | destroy: | |||
|
|
1453 | wl_list_remove(&lock->new_surface.link); | |||
|
|
1454 | wl_list_remove(&lock->unlock.link); | |||
|
|
1455 | wl_list_remove(&lock->destroy.link); | |||
|
|
1456 | ||||
|
|
1457 | wlr_scene_node_destroy(&lock->scene->node); | |||
|
|
1458 | cur_lock = NULL; | |||
|
|
1459 | free(lock); | |||
|
|
1460 | } | |||
|
|
1461 | ||||
|
|
1462 | void | |||
|
|
1463 | destroylocksurface(struct wl_listener *listener, void *data) | |||
|
|
1464 | { | |||
|
|
1465 | Monitor *m = wl_container_of(listener, m, destroy_lock_surface); | |||
|
|
1466 | struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface; | |||
|
|
1467 | ||||
|
|
1468 | m->lock_surface = NULL; | |||
|
|
1469 | wl_list_remove(&m->destroy_lock_surface.link); | |||
|
|
1470 | ||||
|
|
1471 | if (lock_surface->surface != seat->keyboard_state.focused_surface) | |||
|
|
1472 | return; | |||
|
|
1473 | ||||
|
|
1474 | if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) { | |||
|
|
1475 | surface = wl_container_of(cur_lock->surfaces.next, surface, link); | |||
|
|
1476 | client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); | |||
|
|
1477 | } else if (!locked) { | |||
|
|
1478 | focusclient(focustop(selmon), 1); | |||
|
|
1479 | } else { | |||
|
|
1480 | wlr_seat_keyboard_clear_focus(seat); | |||
|
|
1481 | } | |||
|
|
1482 | } | |||
|
|
1483 | ||||
|
|
1484 | void | |||
|
|
1485 | destroynotify(struct wl_listener *listener, void *data) | |||
|
|
1486 | { | |||
|
|
1487 | /* Called when the xdg_toplevel is destroyed. */ | |||
|
|
1488 | Client *c = wl_container_of(listener, c, destroy); | |||
|
|
1489 | wl_list_remove(&c->destroy.link); | |||
|
|
1490 | wl_list_remove(&c->set_title.link); | |||
|
|
1491 | wl_list_remove(&c->fullscreen.link); | |||
|
|
1492 | #ifdef XWAYLAND | |||
|
|
1493 | if (c->type != XDGShell) { | |||
|
|
1494 | wl_list_remove(&c->activate.link); | |||
|
|
1495 | wl_list_remove(&c->associate.link); | |||
|
|
1496 | wl_list_remove(&c->configure.link); | |||
|
|
1497 | wl_list_remove(&c->dissociate.link); | |||
|
|
1498 | wl_list_remove(&c->set_hints.link); | |||
|
|
1499 | } else | |||
|
|
1500 | #endif | |||
|
|
1501 | { | |||
|
|
1502 | wl_list_remove(&c->commit.link); | |||
|
|
1503 | wl_list_remove(&c->map.link); | |||
|
|
1504 | wl_list_remove(&c->unmap.link); | |||
|
|
1505 | wl_list_remove(&c->maximize.link); | |||
|
|
1506 | } | |||
|
|
1507 | free(c); | |||
|
|
1508 | } | |||
|
|
1509 | ||||
|
|
1510 | void | |||
|
|
1511 | destroypointerconstraint(struct wl_listener *listener, void *data) | |||
|
|
1512 | { | |||
|
|
1513 | PointerConstraint *pointer_constraint = wl_container_of(listener, pointer_constraint, destroy); | |||
|
|
1514 | ||||
|
|
1515 | if (active_constraint == pointer_constraint->constraint) { | |||
|
|
1516 | cursorwarptohint(); | |||
|
|
1517 | active_constraint = NULL; | |||
|
|
1518 | } | |||
|
|
1519 | ||||
|
|
1520 | wl_list_remove(&pointer_constraint->destroy.link); | |||
|
|
1521 | free(pointer_constraint); | |||
|
|
1522 | } | |||
|
|
1523 | ||||
|
|
1524 | void | |||
|
|
1525 | destroysessionlock(struct wl_listener *listener, void *data) | |||
|
|
1526 | { | |||
|
|
1527 | SessionLock *lock = wl_container_of(listener, lock, destroy); | |||
|
|
1528 | destroylock(lock, 0); | |||
|
|
1529 | } | |||
|
|
1530 | ||||
|
|
1531 | void | |||
|
|
1532 | destroykeyboardgroup(struct wl_listener *listener, void *data) | |||
|
|
1533 | { | |||
|
|
1534 | KeyboardGroup *group = wl_container_of(listener, group, destroy); | |||
|
|
1535 | wl_event_source_remove(group->key_repeat_source); | |||
|
|
1536 | wl_list_remove(&group->key.link); | |||
|
|
1537 | wl_list_remove(&group->modifiers.link); | |||
|
|
1538 | wl_list_remove(&group->destroy.link); | |||
|
|
1539 | wlr_keyboard_group_destroy(group->wlr_group); | |||
|
|
1540 | free(group); | |||
|
|
1541 | } | |||
|
|
1542 | ||||
|
|
1543 | Monitor * | |||
|
|
1544 | dirtomon(enum wlr_direction dir) | |||
|
|
1545 | { | |||
|
|
1546 | struct wlr_output *next; | |||
|
|
1547 | if (!wlr_output_layout_get(output_layout, selmon->wlr_output)) | |||
|
|
1548 | return selmon; | |||
|
|
1549 | if ((next = wlr_output_layout_adjacent_output(output_layout, | |||
|
|
1550 | dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) | |||
|
|
1551 | return next->data; | |||
|
|
1552 | if ((next = wlr_output_layout_farthest_output(output_layout, | |||
|
|
1553 | dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), | |||
|
|
1554 | selmon->wlr_output, selmon->m.x, selmon->m.y))) | |||
|
|
1555 | return next->data; | |||
|
|
1556 | return selmon; | |||
|
|
1557 | } | |||
|
|
1558 | ||||
|
|
1559 | void | |||
|
|
1560 | focusclient(Client *c, int lift) | |||
|
|
1561 | { | |||
|
|
1562 | struct wlr_surface *old = seat->keyboard_state.focused_surface; | |||
|
|
1563 | int unused_lx, unused_ly, old_client_type; | |||
|
|
1564 | Client *old_c = NULL; | |||
|
|
1565 | LayerSurface *old_l = NULL; | |||
|
|
1566 | ||||
|
|
1567 | if (locked) | |||
|
|
1568 | return; | |||
|
|
1569 | ||||
|
|
1570 | /* Raise client in stacking order if requested */ | |||
|
|
1571 | if (c && lift) | |||
|
|
1572 | wlr_scene_node_raise_to_top(&c->scene->node); | |||
|
|
1573 | ||||
|
|
1574 | if (c && client_surface(c) == old) | |||
|
|
1575 | return; | |||
|
|
1576 | ||||
|
|
1577 | if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) { | |||
|
|
1578 | struct wlr_xdg_popup *popup, *tmp; | |||
|
|
1579 | wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link) | |||
|
|
1580 | wlr_xdg_popup_destroy(popup); | |||
|
|
1581 | } | |||
|
|
1582 | ||||
|
|
1583 | /* Put the new client atop the focus stack and select its monitor */ | |||
|
|
1584 | if (c && !client_is_unmanaged(c)) { | |||
|
|
1585 | wl_list_remove(&c->flink); | |||
|
|
1586 | wl_list_insert(&fstack, &c->flink); | |||
|
|
1587 | selmon = c->mon; | |||
|
|
1588 | c->isurgent = 0; | |||
|
|
1589 | ||||
|
|
1590 | /* Track focused client for dwindle insertion anchor */ | |||
|
|
1591 | if (c->mon && !c->isfloating && !c->isfullscreen) | |||
|
|
1592 | c->mon->dwindle_focus[current_tag_idx(c->mon)] = c; | |||
|
|
1593 | ||||
|
|
1594 | /* Don't change border color if there is an exclusive focus or we are | |||
|
|
1595 | * handling a drag operation */ | |||
|
|
1596 | if (!exclusive_focus && !seat->drag) { | |||
|
|
1597 | client_set_border_color(c, cfg.appearance.inner_focus_color, cfg.appearance.outer_focus_color); | |||
|
|
1598 | client_set_opacity(c, cfg.appearance.active_opacity); | |||
|
|
1599 | } | |||
|
|
1600 | } | |||
|
|
1601 | ||||
|
|
1602 | /* Deactivate old client if focus is changing */ | |||
|
|
1603 | if (old && (!c || client_surface(c) != old)) { | |||
|
|
1604 | /* If an overlay is focused, don't focus or activate the client, | |||
|
|
1605 | * but only update its position in fstack to render its border with the focus colors | |||
|
|
1606 | * and focus it after the overlay is closed. */ | |||
|
|
1607 | if (old_client_type == LayerShell && wlr_scene_node_coords( | |||
|
|
1608 | &old_l->scene->node, &unused_lx, &unused_ly) | |||
|
|
1609 | && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { | |||
|
|
1610 | return; | |||
|
|
1611 | } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { | |||
|
|
1612 | return; | |||
|
|
1613 | /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg | |||
|
|
1614 | * and probably other clients */ | |||
|
|
1615 | } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { | |||
|
|
1616 | client_set_border_color(old_c, cfg.appearance.inner_border_color, cfg.appearance.outer_border_color); | |||
|
|
1617 | client_set_opacity(old_c, cfg.appearance.inactive_opacity); | |||
|
|
1618 | ||||
|
|
1619 | client_activate_surface(old, 0); | |||
|
|
1620 | if (old_c->foreign_toplevel) | |||
|
|
1621 | wlr_foreign_toplevel_handle_v1_set_activated(old_c->foreign_toplevel, 0); | |||
|
|
1622 | } | |||
|
|
1623 | } | |||
|
|
1624 | printstatus(); | |||
|
|
1625 | ||||
|
|
1626 | if (!c) { | |||
|
|
1627 | /* With no client, all we have left is to clear focus */ | |||
|
|
1628 | wlr_seat_keyboard_notify_clear_focus(seat); | |||
|
|
1629 | return; | |||
|
|
1630 | } | |||
|
|
1631 | ||||
|
|
1632 | /* Change cursor surface */ | |||
|
|
1633 | motionnotify(0, NULL, 0, 0, 0, 0); | |||
|
|
1634 | ||||
|
|
1635 | /* Have a client, so focus its top-level wlr_surface */ | |||
|
|
1636 | client_notify_enter(client_surface(c), wlr_seat_get_keyboard(seat)); | |||
|
|
1637 | ||||
|
|
1638 | /* Activate the new client */ | |||
|
|
1639 | client_activate_surface(client_surface(c), 1); | |||
|
|
1640 | if (c->foreign_toplevel) | |||
|
|
1641 | wlr_foreign_toplevel_handle_v1_set_activated(c->foreign_toplevel, 1); | |||
|
|
1642 | } | |||
|
|
1643 | ||||
|
|
1644 | void | |||
|
|
1645 | focusmon(const Arg *arg) | |||
|
|
1646 | { | |||
|
|
1647 | int i = 0, nmons = wl_list_length(&mons); | |||
|
|
1648 | if (nmons) { | |||
|
|
1649 | do /* don't switch to disabled mons */ | |||
|
|
1650 | selmon = dirtomon(arg->i); | |||
|
|
1651 | while (!selmon->wlr_output->enabled && i++ < nmons); | |||
|
|
1652 | } | |||
|
|
1653 | focusclient(focustop(selmon), 1); | |||
|
|
1654 | } | |||
|
|
1655 | ||||
|
|
1656 | /* Return the centre point of a client's geometry. */ | |||
|
|
1657 | static void | |||
|
|
1658 | client_center(Client *c, int *cx, int *cy) | |||
|
|
1659 | { | |||
|
|
1660 | *cx = c->geom.x + c->geom.width / 2; | |||
|
|
1661 | *cy = c->geom.y + c->geom.height / 2; | |||
|
|
1662 | } | |||
|
|
1663 | ||||
|
|
1664 | /* focusdir is basically spatial focus in a cardinal direction. | |||
|
|
1665 | * arg->i: WLR_DIRECTION_LEFT, RIGHT, UP, DOWN. | |||
|
|
1666 | * It finds the nearest tiled visible client whose centre lies in the | |||
|
|
1667 | * requested half-plane relative to the focused client's centre */ | |||
|
|
1668 | void | |||
|
|
1669 | focusdir(const Arg *arg) | |||
|
|
1670 | { | |||
|
|
1671 | Client *c, *best = NULL; | |||
|
|
1672 | Client *sel = focustop(selmon); | |||
|
|
1673 | int sx, sy, cx, cy; | |||
|
|
1674 | double bestdist = 1e18, dist; | |||
|
|
1675 | ||||
|
|
1676 | if (!sel || (sel->isfullscreen && !client_has_children(sel))) | |||
|
|
1677 | return; | |||
|
|
1678 | ||||
|
|
1679 | client_center(sel, &sx, &sy); | |||
|
|
1680 | ||||
|
|
1681 | wl_list_for_each(c, &clients, link) { | |||
|
|
1682 | if (c == sel || !VISIBLEON(c, selmon) || c->isfloating) | |||
|
|
1683 | continue; | |||
|
|
1684 | ||||
|
|
1685 | client_center(c, &cx, &cy); | |||
|
|
1686 | ||||
|
|
1687 | /* the candidate must be in the requested direction. */ | |||
|
|
1688 | switch (arg->i) { | |||
|
|
1689 | case WLR_DIRECTION_LEFT: if (cx >= sx) continue; break; | |||
|
|
1690 | case WLR_DIRECTION_RIGHT: if (cx <= sx) continue; break; | |||
|
|
1691 | case WLR_DIRECTION_UP: if (cy >= sy) continue; break; | |||
|
|
1692 | case WLR_DIRECTION_DOWN: if (cy <= sy) continue; break; | |||
|
|
1693 | default: return; | |||
|
|
1694 | } | |||
|
|
1695 | ||||
|
|
1696 | dist = (double)(cx - sx) * (cx - sx) + (double)(cy - sy) * (cy - sy); | |||
|
|
1697 | if (dist < bestdist) { | |||
|
|
1698 | bestdist = dist; | |||
|
|
1699 | best = c; | |||
|
|
1700 | } | |||
|
|
1701 | } | |||
|
|
1702 | ||||
|
|
1703 | if (best) | |||
|
|
1704 | focusclient(best, 1); | |||
|
|
1705 | } | |||
|
|
1706 | ||||
|
|
1707 | /* We probably should change the name of this: it sounds like it | |||
|
|
1708 | * will focus the topmost client of this mon, when actually will | |||
|
|
1709 | * only return that client */ | |||
|
|
1710 | Client * | |||
|
|
1711 | focustop(Monitor *m) | |||
|
|
1712 | { | |||
|
|
1713 | Client *c; | |||
|
|
1714 | wl_list_for_each(c, &fstack, flink) { | |||
|
|
1715 | if (VISIBLEON(c, m)) | |||
|
|
1716 | return c; | |||
|
|
1717 | } | |||
|
|
1718 | return NULL; | |||
|
|
1719 | } | |||
|
|
1720 | ||||
|
|
1721 | void | |||
|
|
1722 | fullscreennotify(struct wl_listener *listener, void *data) | |||
|
|
1723 | { | |||
|
|
1724 | Client *c = wl_container_of(listener, c, fullscreen); | |||
|
|
1725 | setfullscreen(c, client_wants_fullscreen(c)); | |||
|
|
1726 | } | |||
|
|
1727 | ||||
|
|
1728 | void | |||
|
|
1729 | gpureset(struct wl_listener *listener, void *data) | |||
|
|
1730 | { | |||
|
|
1731 | struct wlr_renderer *old_drw = drw; | |||
|
|
1732 | struct wlr_allocator *old_alloc = alloc; | |||
|
|
1733 | struct Monitor *m; | |||
|
|
1734 | if (!(drw = wlr_renderer_autocreate(backend))) | |||
|
|
1735 | die("couldn't recreate renderer"); | |||
|
|
1736 | ||||
|
|
1737 | if (!(alloc = wlr_allocator_autocreate(backend, drw))) | |||
|
|
1738 | die("couldn't recreate allocator"); | |||
|
|
1739 | ||||
|
|
1740 | wl_list_remove(&gpu_reset.link); | |||
|
|
1741 | wl_signal_add(&drw->events.lost, &gpu_reset); | |||
|
|
1742 | ||||
|
|
1743 | wlr_compositor_set_renderer(compositor, drw); | |||
|
|
1744 | ||||
|
|
1745 | wl_list_for_each(m, &mons, link) { | |||
|
|
1746 | wlr_output_init_render(m->wlr_output, alloc, drw); | |||
|
|
1747 | } | |||
|
|
1748 | ||||
|
|
1749 | wlr_allocator_destroy(old_alloc); | |||
|
|
1750 | wlr_renderer_destroy(old_drw); | |||
|
|
1751 | } | |||
|
|
1752 | ||||
|
|
1753 | void | |||
|
|
1754 | handlesig(int signo) | |||
|
|
1755 | { | |||
|
|
1756 | if (signo == SIGCHLD) | |||
|
|
1757 | while (waitpid(-1, NULL, WNOHANG) > 0); | |||
|
|
1758 | else if (signo == SIGINT || signo == SIGTERM) | |||
|
|
1759 | quit(NULL); | |||
|
|
1760 | } | |||
|
|
1761 | ||||
|
|
1762 | ||||
|
|
1763 | void | |||
|
|
1764 | inputdevice(struct wl_listener *listener, void *data) | |||
|
|
1765 | { | |||
|
|
1766 | /* This event is raised by the backend when a new input device becomes | |||
|
|
1767 | * available. */ | |||
|
|
1768 | struct wlr_input_device *device = data; | |||
|
|
1769 | uint32_t caps; | |||
|
|
1770 | ||||
|
|
1771 | switch (device->type) { | |||
|
|
1772 | case WLR_INPUT_DEVICE_KEYBOARD: | |||
|
|
1773 | createkeyboard(wlr_keyboard_from_input_device(device)); | |||
|
|
1774 | break; | |||
|
|
1775 | case WLR_INPUT_DEVICE_POINTER: | |||
|
|
1776 | createpointer(wlr_pointer_from_input_device(device)); | |||
|
|
1777 | break; | |||
|
|
1778 | default: | |||
|
|
1779 | /* TODO handle other input device types */ | |||
|
|
1780 | break; | |||
|
|
1781 | } | |||
|
|
1782 | ||||
|
|
1783 | /* We need to let the wlr_seat know what our capabilities are, which is | |||
|
|
1784 | * communiciated to the client. In dwl we always have a cursor, even if | |||
|
|
1785 | * there are no pointer devices, so we always include that capability. */ | |||
|
|
1786 | /* TODO do we actually require a cursor? */ | |||
|
|
1787 | caps = WL_SEAT_CAPABILITY_POINTER; | |||
|
|
1788 | if (!wl_list_empty(&kb_group->wlr_group->devices)) | |||
|
|
1789 | caps |= WL_SEAT_CAPABILITY_KEYBOARD; | |||
|
|
1790 | wlr_seat_set_capabilities(seat, caps); | |||
|
|
1791 | } | |||
|
|
1792 | ||||
|
|
1793 | /* dispatch_action maps a CfgKeybind/CfgButton action string to the | |||
|
|
1794 | * corresponding compositor function and calls it. args[0] is used where | |||
|
|
1795 | * needed (spawn argv, tag mask, direction, etc). */ | |||
|
|
1796 | static void | |||
|
|
1797 | dispatch_action(const char *action, const char (*args)[CFG_MAX_STRLEN], int nargs) | |||
|
|
1798 | { | |||
|
|
1799 | Arg arg = {0}; | |||
|
|
1800 | const char *a0 = nargs > 0 ? args[0] : NULL; | |||
|
|
1801 | ||||
|
|
1802 | if (!strcmp(action, "spawn")) { | |||
|
|
1803 | /* Build a NULL-terminated argv on the stack. | |||
|
|
1804 | * CFG_MAX_ARGS is 16, which is plenty. */ | |||
|
|
1805 | const char *argv[CFG_MAX_ARGS + 1]; | |||
|
|
1806 | int i; | |||
|
|
1807 | for (i = 0; i < nargs && i < CFG_MAX_ARGS; i++) | |||
|
|
1808 | argv[i] = args[i]; | |||
|
|
1809 | argv[i] = NULL; | |||
|
|
1810 | arg.v = argv; | |||
|
|
1811 | spawn(&arg); | |||
|
|
1812 | return; | |||
|
|
1813 | } | |||
|
|
1814 | if (!strcmp(action, "killclient")) { killclient(NULL); return; } | |||
|
|
1815 | if (!strcmp(action, "togglefloating")) { togglefloating(NULL); return; } | |||
|
|
1816 | if (!strcmp(action, "togglefullscreen")){ togglefullscreen(NULL); return; } | |||
|
|
1817 | if (!strcmp(action, "togglegaps")) { togglegaps(NULL); return; } | |||
|
|
1818 | if (!strcmp(action, "quit")) { quit(NULL); return; } | |||
|
|
1819 | ||||
|
|
1820 | if (!strcmp(action, "view")) { | |||
|
|
1821 | if (a0 && strcmp(a0, "all") == 0) arg.ui = ~0u; | |||
|
|
1822 | else if (a0) arg.ui = (uint32_t)atoi(a0); | |||
|
|
1823 | view(&arg); | |||
|
|
1824 | return; | |||
|
|
1825 | } | |||
|
|
1826 | if (!strcmp(action, "toggleview")) { | |||
|
|
1827 | if (a0) arg.ui = (uint32_t)atoi(a0); | |||
|
|
1828 | toggleview(&arg); | |||
|
|
1829 | return; | |||
|
|
1830 | } | |||
|
|
1831 | if (!strcmp(action, "tag")) { | |||
|
|
1832 | if (a0) arg.ui = (uint32_t)atoi(a0); | |||
|
|
1833 | tag(&arg); | |||
|
|
1834 | return; | |||
|
|
1835 | } | |||
|
|
1836 | if (!strcmp(action, "toggletag")) { | |||
|
|
1837 | if (a0) arg.ui = (uint32_t)atoi(a0); | |||
|
|
1838 | toggletag(&arg); | |||
|
|
1839 | return; | |||
|
|
1840 | } | |||
|
|
1841 | ||||
|
|
1842 | if (!strcmp(action, "focusdir")) { | |||
|
|
1843 | if (a0 && !strcmp(a0, "left")) arg.i = WLR_DIRECTION_LEFT; | |||
|
|
1844 | else if (a0 && !strcmp(a0, "right")) arg.i = WLR_DIRECTION_RIGHT; | |||
|
|
1845 | else if (a0 && !strcmp(a0, "up")) arg.i = WLR_DIRECTION_UP; | |||
|
|
1846 | else if (a0 && !strcmp(a0, "down")) arg.i = WLR_DIRECTION_DOWN; | |||
|
|
1847 | focusdir(&arg); | |||
|
|
1848 | return; | |||
|
|
1849 | } | |||
|
|
1850 | if (!strcmp(action, "swapdir")) { | |||
|
|
1851 | if (a0 && !strcmp(a0, "left")) arg.i = WLR_DIRECTION_LEFT; | |||
|
|
1852 | else if (a0 && !strcmp(a0, "right")) arg.i = WLR_DIRECTION_RIGHT; | |||
|
|
1853 | else if (a0 && !strcmp(a0, "up")) arg.i = WLR_DIRECTION_UP; | |||
|
|
1854 | else if (a0 && !strcmp(a0, "down")) arg.i = WLR_DIRECTION_DOWN; | |||
|
|
1855 | swapdir(&arg); | |||
|
|
1856 | return; | |||
|
|
1857 | } | |||
|
|
1858 | if (!strcmp(action, "focusmon")) { | |||
|
|
1859 | if (a0 && !strcmp(a0, "left")) arg.i = WLR_DIRECTION_LEFT; | |||
|
|
1860 | else if (a0 && !strcmp(a0, "right")) arg.i = WLR_DIRECTION_RIGHT; | |||
|
|
1861 | focusmon(&arg); | |||
|
|
1862 | return; | |||
|
|
1863 | } | |||
|
|
1864 | if (!strcmp(action, "setlayout")) { | |||
|
|
1865 | /* arg is index into layouts[]; "0" = floating, "1" = dwindle and so | |||
|
|
1866 | * on and so forth */ | |||
|
|
1867 | ||||
|
|
1868 | if (a0) arg.ui = (uint32_t)atoi(a0); | |||
|
|
1869 | setlayout(&arg); | |||
|
|
1870 | return; | |||
|
|
1871 | } | |||
|
|
1872 | if (!strcmp(action, "tagmon")) { | |||
|
|
1873 | if (a0 && !strcmp(a0, "left")) arg.i = WLR_DIRECTION_LEFT; | |||
|
|
1874 | else if (a0 && !strcmp(a0, "right")) arg.i = WLR_DIRECTION_RIGHT; | |||
|
|
1875 | tagmon(&arg); | |||
|
|
1876 | return; | |||
|
|
1877 | } | |||
|
|
1878 | if (!strcmp(action, "moveresize")) { | |||
|
|
1879 | if (a0 && !strcmp(a0, "move")) arg.ui = CurMove; | |||
|
|
1880 | else if (a0 && !strcmp(a0, "resize")) arg.ui = CurResize; | |||
|
|
1881 | moveresize(&arg); | |||
|
|
1882 | return; | |||
|
|
1883 | } | |||
|
|
1884 | if (!strcmp(action, "chvt")) { | |||
|
|
1885 | if (a0) arg.ui = (uint32_t)atoi(a0); | |||
|
|
1886 | chvt(&arg); | |||
|
|
1887 | return; | |||
|
|
1888 | } | |||
|
|
1889 | ||||
|
|
1890 | fprintf(stderr, "swindle: unknown action '%s'\n", action); | |||
|
|
1891 | } | |||
|
|
1892 | ||||
|
|
1893 | int | |||
|
|
1894 | keybinding(uint32_t mods, xkb_keysym_t sym) | |||
|
|
1895 | { | |||
|
|
1896 | for (int i = 0; i < cfg.nkeybinds; i++) { | |||
|
|
1897 | const CfgKeybind *k = &cfg.keybinds[i]; | |||
|
|
1898 | if (CLEANMASK(mods) == CLEANMASK(k->mods) | |||
|
|
1899 | && xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->key)) { | |||
|
|
1900 | dispatch_action(k->action, k->args, k->nargs); | |||
|
|
1901 | return 1; | |||
|
|
1902 | } | |||
|
|
1903 | } | |||
|
|
1904 | return 0; | |||
|
|
1905 | } | |||
|
|
1906 | ||||
|
|
1907 | void | |||
|
|
1908 | keypress(struct wl_listener *listener, void *data) | |||
|
|
1909 | { | |||
|
|
1910 | int i; | |||
|
|
1911 | /* This event is raised when a key is pressed or released. */ | |||
|
|
1912 | KeyboardGroup *group = wl_container_of(listener, group, key); | |||
|
|
1913 | struct wlr_keyboard_key_event *event = data; | |||
|
|
1914 | ||||
|
|
1915 | /* Translate libinput keycode -> xkbcommon */ | |||
|
|
1916 | uint32_t keycode = event->keycode + 8; | |||
|
|
1917 | /* Get a list of keysyms based on the keymap for this keyboard */ | |||
|
|
1918 | const xkb_keysym_t *syms; | |||
|
|
1919 | int nsyms = xkb_state_key_get_syms( | |||
|
|
1920 | group->wlr_group->keyboard.xkb_state, keycode, &syms); | |||
|
|
1921 | ||||
|
|
1922 | /* | |||
|
|
1923 | * Also get the base-level (unshifted) keysyms for this key | |||
|
|
1924 | * This is necessary for binds like { mods={"logo","shift"}, key="1" }: | |||
|
|
1925 | * when Shift is held, xkb_state_key_get_syms returns "exclam" (0x21), | |||
|
|
1926 | * not "1" (0x31), so the config match would silently fail | |||
|
|
1927 | * Querying level 0 of the active layout gives it the unshifted sym | |||
|
|
1928 | * regardless of modifier state, which matches what the user wrote in | |||
|
|
1929 | * the config | |||
|
|
1930 | */ | |||
|
|
1931 | const xkb_keysym_t *base_syms; | |||
|
|
1932 | int base_nsyms = xkb_keymap_key_get_syms_by_level( | |||
|
|
1933 | xkb_state_get_keymap(group->wlr_group->keyboard.xkb_state), | |||
|
|
1934 | keycode, | |||
|
|
1935 | xkb_state_key_get_layout( | |||
|
|
1936 | group->wlr_group->keyboard.xkb_state, keycode), | |||
|
|
1937 | 0, /* level 0 = unshifted */ | |||
|
|
1938 | &base_syms); | |||
|
|
1939 | ||||
|
|
1940 | int handled = 0; | |||
|
|
1941 | uint32_t mods = wlr_keyboard_get_modifiers(&group->wlr_group->keyboard); | |||
|
|
1942 | ||||
|
|
1943 | wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); | |||
|
|
1944 | ||||
|
|
1945 | if (session && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | |||
|
|
1946 | for (i = 0; i < nsyms; i++) { | |||
|
|
1947 | if (syms[i] >= XKB_KEY_XF86Switch_VT_1 && | |||
|
|
1948 | syms[i] <= XKB_KEY_XF86Switch_VT_12) { | |||
|
|
1949 | wlr_session_change_vt(session, | |||
|
|
1950 | syms[i] - XKB_KEY_XF86Switch_VT_1 + 1); | |||
|
|
1951 | return; | |||
|
|
1952 | } | |||
|
|
1953 | } | |||
|
|
1954 | for (i = 0; i < base_nsyms; i++) { | |||
|
|
1955 | if (base_syms[i] >= XKB_KEY_XF86Switch_VT_1 && | |||
|
|
1956 | base_syms[i] <= XKB_KEY_XF86Switch_VT_12) { | |||
|
|
1957 | wlr_session_change_vt(session, | |||
|
|
1958 | base_syms[i] - XKB_KEY_XF86Switch_VT_1 + 1); | |||
|
|
1959 | return; | |||
|
|
1960 | } | |||
|
|
1961 | } | |||
|
|
1962 | } | |||
|
|
1963 | ||||
|
|
1964 | /* Try state-aware syms first, then base syms (catches shifted digit binds). */ | |||
|
|
1965 | if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { | |||
|
|
1966 | for (i = 0; i < nsyms; i++) | |||
|
|
1967 | handled = keybinding(mods, syms[i]) || handled; | |||
|
|
1968 | if (!handled) { | |||
|
|
1969 | for (i = 0; i < base_nsyms; i++) | |||
|
|
1970 | handled = keybinding(mods, base_syms[i]) || handled; | |||
|
|
1971 | } | |||
|
|
1972 | } | |||
|
|
1973 | ||||
|
|
1974 | if (handled && group->wlr_group->keyboard.repeat_info.delay > 0) { | |||
|
|
1975 | group->mods = mods; | |||
|
|
1976 | group->keysyms = base_syms; | |||
|
|
1977 | group->nsyms = base_nsyms; | |||
|
|
1978 | wl_event_source_timer_update(group->key_repeat_source, | |||
|
|
1979 | group->wlr_group->keyboard.repeat_info.delay); | |||
|
|
1980 | } else { | |||
|
|
1981 | group->nsyms = 0; | |||
|
|
1982 | wl_event_source_timer_update(group->key_repeat_source, 0); | |||
|
|
1983 | } | |||
|
|
1984 | ||||
|
|
1985 | if (handled) | |||
|
|
1986 | return; | |||
|
|
1987 | ||||
|
|
1988 | wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); | |||
|
|
1989 | /* Pass unhandled keycodes along to the client. */ | |||
|
|
1990 | wlr_seat_keyboard_notify_key(seat, event->time_msec, | |||
|
|
1991 | event->keycode, event->state); | |||
|
|
1992 | } | |||
|
|
1993 | ||||
|
|
1994 | void | |||
|
|
1995 | keypressmod(struct wl_listener *listener, void *data) | |||
|
|
1996 | { | |||
|
|
1997 | /* This event is raised when a modifier key, such as shift or alt, is | |||
|
|
1998 | * pressed. We simply communicate this to the client. */ | |||
|
|
1999 | KeyboardGroup *group = wl_container_of(listener, group, modifiers); | |||
|
|
2000 | ||||
|
|
2001 | wlr_seat_set_keyboard(seat, &group->wlr_group->keyboard); | |||
|
|
2002 | /* Send modifiers to the client. */ | |||
|
|
2003 | wlr_seat_keyboard_notify_modifiers(seat, | |||
|
|
2004 | &group->wlr_group->keyboard.modifiers); | |||
|
|
2005 | } | |||
|
|
2006 | ||||
|
|
2007 | int | |||
|
|
2008 | keyrepeat(void *data) | |||
|
|
2009 | { | |||
|
|
2010 | KeyboardGroup *group = data; | |||
|
|
2011 | int i; | |||
|
|
2012 | if (!group->nsyms || group->wlr_group->keyboard.repeat_info.rate <= 0) | |||
|
|
2013 | return 0; | |||
|
|
2014 | ||||
|
|
2015 | wl_event_source_timer_update(group->key_repeat_source, | |||
|
|
2016 | 1000 / group->wlr_group->keyboard.repeat_info.rate); | |||
|
|
2017 | ||||
|
|
2018 | for (i = 0; i < group->nsyms; i++) | |||
|
|
2019 | keybinding(group->mods, group->keysyms[i]); | |||
|
|
2020 | ||||
|
|
2021 | return 0; | |||
|
|
2022 | } | |||
|
|
2023 | ||||
|
|
2024 | void | |||
|
|
2025 | killclient(const Arg *arg) | |||
|
|
2026 | { | |||
|
|
2027 | Client *sel = focustop(selmon); | |||
|
|
2028 | if (sel) | |||
|
|
2029 | client_send_close(sel); | |||
|
|
2030 | } | |||
|
|
2031 | ||||
|
|
2032 | void | |||
|
|
2033 | locksession(struct wl_listener *listener, void *data) | |||
|
|
2034 | { | |||
|
|
2035 | struct wlr_session_lock_v1 *session_lock = data; | |||
|
|
2036 | SessionLock *lock; | |||
|
|
2037 | wlr_scene_node_set_enabled(&locked_bg->node, 1); | |||
|
|
2038 | if (cur_lock) { | |||
|
|
2039 | wlr_session_lock_v1_destroy(session_lock); | |||
|
|
2040 | return; | |||
|
|
2041 | } | |||
|
|
2042 | lock = session_lock->data = ecalloc(1, sizeof(*lock)); | |||
|
|
2043 | focusclient(NULL, 0); | |||
|
|
2044 | ||||
|
|
2045 | lock->scene = wlr_scene_tree_create(layers[LyrBlock]); | |||
|
|
2046 | cur_lock = lock->lock = session_lock; | |||
|
|
2047 | locked = 1; | |||
|
|
2048 | ||||
|
|
2049 | LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface); | |||
|
|
2050 | LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock); | |||
|
|
2051 | LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession); | |||
|
|
2052 | ||||
|
|
2053 | wlr_session_lock_v1_send_locked(session_lock); | |||
|
|
2054 | } | |||
|
|
2055 | ||||
|
|
2056 | void | |||
|
|
2057 | mapnotify(struct wl_listener *listener, void *data) | |||
|
|
2058 | { | |||
|
|
2059 | /* Called when the surface is mapped, or ready to display on-screen. */ | |||
|
|
2060 | Client *p = NULL; | |||
|
|
2061 | Client *w, *c = wl_container_of(listener, c, map); | |||
|
|
2062 | Monitor *m; | |||
|
|
2063 | int i; | |||
|
|
2064 | ||||
|
|
2065 | /* Create scene tree for this client and its border */ | |||
|
|
2066 | c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]); | |||
|
|
2067 | /* Enabled later by a call to arrange() */ | |||
|
|
2068 | wlr_scene_node_set_enabled(&c->scene->node, client_is_unmanaged(c)); | |||
|
|
2069 | c->scene_surface = c->type == XDGShell | |||
|
|
2070 | ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) | |||
|
|
2071 | : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); | |||
|
|
2072 | c->scene->node.data = c->scene_surface->node.data = c; | |||
|
|
2073 | ||||
|
|
2074 | client_get_geometry(c, &c->geom); | |||
|
|
2075 | ||||
|
|
2076 | /* Handle unmanaged clients first so we can return prior create borders */ | |||
|
|
2077 | if (client_is_unmanaged(c)) { | |||
|
|
2078 | /* Unmanaged clients always are floating */ | |||
|
|
2079 | wlr_scene_node_reparent(&c->scene->node, layers[LyrFloat]); | |||
|
|
2080 | wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); | |||
|
|
2081 | client_set_size(c, c->geom.width, c->geom.height); | |||
|
|
2082 | if (client_wants_focus(c)) { | |||
|
|
2083 | focusclient(c, 1); | |||
|
|
2084 | exclusive_focus = c; | |||
|
|
2085 | } | |||
|
|
2086 | goto unset_fullscreen; | |||
|
|
2087 | } | |||
|
|
2088 | ||||
|
|
2089 | for (i = 0; i < 4; i++) { | |||
|
|
2090 | c->inner_border[i] = wlr_scene_rect_create(c->scene, 0, 0, | |||
|
|
2091 | c->isurgent ? cfg.appearance.inner_urgent_color : cfg.appearance.inner_border_color); | |||
|
|
2092 | c->inner_border[i]->node.data = c; | |||
|
|
2093 | ||||
|
|
2094 | c->outer_border[i] = wlr_scene_rect_create(c->scene, 0, 0, | |||
|
|
2095 | c->isurgent ? cfg.appearance.outer_urgent_color : cfg.appearance.outer_border_color); | |||
|
|
2096 | c->outer_border[i]->node.data = c; | |||
|
|
2097 | } | |||
|
|
2098 | ||||
|
|
2099 | createforeigntoplevel(c); | |||
|
|
2100 | ||||
|
|
2101 | /* Initialize client geometry with room for border */ | |||
|
|
2102 | client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); | |||
|
|
2103 | c->geom.width += 2 * c->bw; | |||
|
|
2104 | c->geom.height += 2 * c->bw; | |||
|
|
2105 | ||||
|
|
2106 | /* Insert this client into client lists. */ | |||
|
|
2107 | wl_list_insert(&clients, &c->link); | |||
|
|
2108 | wl_list_insert(&fstack, &c->flink); | |||
|
|
2109 | ||||
|
|
2110 | /* Set initial monitor, tags, floating status, and focus: | |||
|
|
2111 | * we always consider floating, clients that have parent and thus | |||
|
|
2112 | * we set the same tags and monitor as its parent. | |||
|
|
2113 | * If there is no parent, apply rules */ | |||
|
|
2114 | if ((p = client_get_parent(c))) { | |||
|
|
2115 | c->isfloating = 1; | |||
|
|
2116 | setmon(c, p->mon, p->tags); | |||
|
|
2117 | } else { | |||
|
|
2118 | applyrules(c); | |||
|
|
2119 | } | |||
|
|
2120 | printstatus(); | |||
|
|
2121 | ||||
|
|
2122 | unset_fullscreen: | |||
|
|
2123 | m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); | |||
|
|
2124 | wl_list_for_each(w, &clients, link) { | |||
|
|
2125 | if (w != c && w != p && w->isfullscreen && m == w->mon && (w->tags & c->tags)) | |||
|
|
2126 | setfullscreen(w, 0); | |||
|
|
2127 | } | |||
|
|
2128 | } | |||
|
|
2129 | ||||
|
|
2130 | void | |||
|
|
2131 | maximizenotify(struct wl_listener *listener, void *data) | |||
|
|
2132 | { | |||
|
|
2133 | /* This event is raised when a client would like to maximize itself, | |||
|
|
2134 | * typically because the user clicked on the maximize button on | |||
|
|
2135 | * client-side decorations. swindle doesn't support maximization, but | |||
|
|
2136 | * to conform to xdg-shell protocol we still must send a configure. | |||
|
|
2137 | * Since xdg-shell protocol v5 we should ignore request of unsupported | |||
|
|
2138 | * capabilities, just schedule a empty configure when the client uses <5 | |||
|
|
2139 | * protocol version | |||
|
|
2140 | * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ | |||
|
|
2141 | Client *c = wl_container_of(listener, c, maximize); | |||
|
|
2142 | if (c->surface.xdg->initialized | |||
|
|
2143 | && wl_resource_get_version(c->surface.xdg->toplevel->resource) | |||
|
|
2144 | < XDG_TOPLEVEL_STATE_TILED_BOTTOM_SINCE_VERSION) | |||
|
|
2145 | wlr_xdg_surface_schedule_configure(c->surface.xdg); | |||
|
|
2146 | } | |||
|
|
2147 | ||||
|
|
2148 | ||||
|
|
2149 | void | |||
|
|
2150 | motionabsolute(struct wl_listener *listener, void *data) | |||
|
|
2151 | { | |||
|
|
2152 | /* This event is forwarded by the cursor when a pointer emits an _absolute_ | |||
|
|
2153 | * motion event, from 0..1 on each axis. This happens, for example, when | |||
|
|
2154 | * wlroots is running under a Wayland window rather than KMS+DRM, and you | |||
|
|
2155 | * move the mouse over the window. You could enter the window from any edge, | |||
|
|
2156 | * so we have to warp the mouse there. Also, some hardware emits these events. */ | |||
|
|
2157 | struct wlr_pointer_motion_absolute_event *event = data; | |||
|
|
2158 | double lx, ly, dx, dy; | |||
|
|
2159 | ||||
|
|
2160 | if (!event->time_msec) /* this is 0 with virtual pointers */ | |||
|
|
2161 | wlr_cursor_warp_absolute(cursor, &event->pointer->base, event->x, event->y); | |||
|
|
2162 | ||||
|
|
2163 | wlr_cursor_absolute_to_layout_coords(cursor, &event->pointer->base, event->x, event->y, &lx, &ly); | |||
|
|
2164 | dx = lx - cursor->x; | |||
|
|
2165 | dy = ly - cursor->y; | |||
|
|
2166 | motionnotify(event->time_msec, &event->pointer->base, dx, dy, dx, dy); | |||
|
|
2167 | } | |||
|
|
2168 | ||||
|
|
2169 | void | |||
|
|
2170 | motionnotify(uint32_t time, struct wlr_input_device *device, double dx, double dy, | |||
|
|
2171 | double dx_unaccel, double dy_unaccel) | |||
|
|
2172 | { | |||
|
|
2173 | double sx = 0, sy = 0, sx_confined, sy_confined; | |||
|
|
2174 | Client *c = NULL, *w = NULL; | |||
|
|
2175 | LayerSurface *l = NULL; | |||
|
|
2176 | struct wlr_surface *surface = NULL; | |||
|
|
2177 | struct wlr_pointer_constraint_v1 *constraint; | |||
|
|
2178 | ||||
|
|
2179 | /* Find the client under the pointer and send the event along. */ | |||
|
|
2180 | xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); | |||
|
|
2181 | ||||
|
|
2182 | if (cursor_mode == CurPressed && !seat->drag | |||
|
|
2183 | && surface != seat->pointer_state.focused_surface | |||
|
|
2184 | && toplevel_from_wlr_surface(seat->pointer_state.focused_surface, &w, &l) >= 0) { | |||
|
|
2185 | c = w; | |||
|
|
2186 | surface = seat->pointer_state.focused_surface; | |||
|
|
2187 | sx = cursor->x - (l ? l->scene->node.x : w->geom.x); | |||
|
|
2188 | sy = cursor->y - (l ? l->scene->node.y : w->geom.y); | |||
|
|
2189 | } | |||
|
|
2190 | ||||
|
|
2191 | /* time is 0 in internal calls meant to restore pointer focus. */ | |||
|
|
2192 | if (time) { | |||
|
|
2193 | wlr_relative_pointer_manager_v1_send_relative_motion( | |||
|
|
2194 | relative_pointer_mgr, seat, (uint64_t)time * 1000, | |||
|
|
2195 | dx, dy, dx_unaccel, dy_unaccel); | |||
|
|
2196 | ||||
|
|
2197 | wl_list_for_each(constraint, &pointer_constraints->constraints, link) | |||
|
|
2198 | cursorconstrain(constraint); | |||
|
|
2199 | ||||
|
|
2200 | if (active_constraint && cursor_mode != CurResize && cursor_mode != CurMove) { | |||
|
|
2201 | toplevel_from_wlr_surface(active_constraint->surface, &c, NULL); | |||
|
|
2202 | if (c && active_constraint->surface == seat->pointer_state.focused_surface) { | |||
|
|
2203 | sx = cursor->x - c->geom.x - c->bw; | |||
|
|
2204 | sy = cursor->y - c->geom.y - c->bw; | |||
|
|
2205 | if (wlr_region_confine(&active_constraint->region, sx, sy, | |||
|
|
2206 | sx + dx, sy + dy, &sx_confined, &sy_confined)) { | |||
|
|
2207 | dx = sx_confined - sx; | |||
|
|
2208 | dy = sy_confined - sy; | |||
|
|
2209 | } | |||
|
|
2210 | ||||
|
|
2211 | if (active_constraint->type == WLR_POINTER_CONSTRAINT_V1_LOCKED) | |||
|
|
2212 | return; | |||
|
|
2213 | } | |||
|
|
2214 | } | |||
|
|
2215 | ||||
|
|
2216 | wlr_cursor_move(cursor, device, dx, dy); | |||
|
|
2217 | wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); | |||
|
|
2218 | ||||
|
|
2219 | if (cfg.sloppyfocus) | |||
|
|
2220 | selmon = xytomon(cursor->x, cursor->y); | |||
|
|
2221 | } | |||
|
|
2222 | ||||
|
|
2223 | /* Update drag icon's position */ | |||
|
|
2224 | wlr_scene_node_set_position(&drag_icon->node, (int)round(cursor->x), (int)round(cursor->y)); | |||
|
|
2225 | ||||
|
|
2226 | /* If we are currently grabbing the mouse, handle and return */ | |||
|
|
2227 | if (cursor_mode == CurMove) { | |||
|
|
2228 | /* Move the grabbed client to the new position. */ | |||
|
|
2229 | resize(grabc, (struct wlr_box){.x = (int)round(cursor->x) - grabcx, .y = (int)round(cursor->y) - grabcy, | |||
|
|
2230 | .width = grabc->geom.width, .height = grabc->geom.height}, 1); | |||
|
|
2231 | return; | |||
|
|
2232 | } else if (cursor_mode == CurResize) { | |||
|
|
2233 | resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, | |||
|
|
2234 | .width = (int)round(cursor->x) - grabc->geom.x, .height = (int)round(cursor->y) - grabc->geom.y}, 1); | |||
|
|
2235 | return; | |||
|
|
2236 | } else if (cursor_mode == CurTileDrag) { | |||
|
|
2237 | resize(grabc, (struct wlr_box){ | |||
|
|
2238 | .x = (int)round(cursor->x) - grabcx, | |||
|
|
2239 | .y = (int)round(cursor->y) - grabcy, | |||
|
|
2240 | .width = TILEDRAG_DETACH_W, | |||
|
|
2241 | .height = TILEDRAG_DETACH_H, | |||
|
|
2242 | }, 1); | |||
|
|
2243 | ||||
|
|
2244 | /* tiled_client_at, not xytonode: grabc is on LyrFloat now and | |||
|
|
2245 | * covers whatever's under the cursor, so a scene hit-test would | |||
|
|
2246 | * just find grabc itself and stop. */ | |||
|
|
2247 | Client *target = tiled_client_at(cursor->x, cursor->y, grabc); | |||
|
|
2248 | ||||
|
|
2249 | if (target) { | |||
|
|
2250 | preselect_target = target; | |||
|
|
2251 | preselect_edge = edge_at_point(target->geom, cursor->x, cursor->y); | |||
|
|
2252 | } else { | |||
|
|
2253 | preselect_target = NULL; | |||
|
|
2254 | preselect_edge = EDGE_NONE; | |||
|
|
2255 | } | |||
|
|
2256 | ||||
|
|
2257 | if (preselect_target && preselect_edge != EDGE_NONE) { | |||
|
|
2258 | struct wlr_box ind = preselect_indicator_box(preselect_target->geom, preselect_edge); | |||
|
|
2259 | wlr_scene_node_set_position(&preselect_indicator->node, ind.x, ind.y); | |||
|
|
2260 | wlr_scene_rect_set_size(preselect_indicator, ind.width, ind.height); | |||
|
|
2261 | wlr_scene_node_set_enabled(&preselect_indicator->node, true); | |||
|
|
2262 | } else { | |||
|
|
2263 | wlr_scene_node_set_enabled(&preselect_indicator->node, false); | |||
|
|
2264 | } | |||
|
|
2265 | return; | |||
|
|
2266 | } | |||
|
|
2267 | ||||
|
|
2268 | /* If there's no client surface under the cursor, set the cursor image to a | |||
|
|
2269 | * default. This is what makes the cursor image appear when you move it | |||
|
|
2270 | * off of a client or over its border. */ | |||
|
|
2271 | if (!surface && !seat->drag) | |||
|
|
2272 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); | |||
|
|
2273 | ||||
|
|
2274 | pointerfocus(c, surface, sx, sy, time); | |||
|
|
2275 | } | |||
|
|
2276 | ||||
|
|
2277 | void | |||
|
|
2278 | motionrelative(struct wl_listener *listener, void *data) | |||
|
|
2279 | { | |||
|
|
2280 | /* This event is forwarded by the cursor when a pointer emits a _relative_ | |||
|
|
2281 | * pointer motion event (i.e. a delta) */ | |||
|
|
2282 | struct wlr_pointer_motion_event *event = data; | |||
|
|
2283 | /* The cursor doesn't move unless we tell it to. The cursor automatically | |||
|
|
2284 | * handles constraining the motion to the output layout, as well as any | |||
|
|
2285 | * special configuration applied for the specific input device which | |||
|
|
2286 | * generated the event. You can pass NULL for the device if you want to move | |||
|
|
2287 | * the cursor around without any input. */ | |||
|
|
2288 | motionnotify(event->time_msec, &event->pointer->base, event->delta_x, event->delta_y, | |||
|
|
2289 | event->unaccel_dx, event->unaccel_dy); | |||
|
|
2290 | } | |||
|
|
2291 | ||||
|
|
2292 | void | |||
|
|
2293 | moveresize(const Arg *arg) | |||
|
|
2294 | { | |||
|
|
2295 | if (cursor_mode != CurNormal && cursor_mode != CurPressed) | |||
|
|
2296 | return; | |||
|
|
2297 | xytonode(cursor->x, cursor->y, NULL, &grabc, NULL, NULL, NULL); | |||
|
|
2298 | if (!grabc || client_is_unmanaged(grabc) || grabc->isfullscreen) | |||
|
|
2299 | return; | |||
|
|
2300 | ||||
|
|
2301 | if (arg->ui == CurMove && !grabc->isfloating | |||
|
|
2302 | && grabc->mon->lt[grabc->mon->sellt]->arrange) { | |||
|
|
2303 | cursor_mode = CurTileDrag; | |||
|
|
2304 | tiledrag_orig_geom = grabc->geom; | |||
|
|
2305 | grabcx = TILEDRAG_DETACH_W / 2; | |||
|
|
2306 | grabcy = TILEDRAG_DETACH_H / 2; | |||
|
|
2307 | wlr_scene_node_reparent(&grabc->scene->node, layers[LyrFloat]); | |||
|
|
2308 | resize(grabc, (struct wlr_box){ | |||
|
|
2309 | .x = (int)round(cursor->x) - grabcx, | |||
|
|
2310 | .y = (int)round(cursor->y) - grabcy, | |||
|
|
2311 | .width = TILEDRAG_DETACH_W, | |||
|
|
2312 | .height = TILEDRAG_DETACH_H, | |||
|
|
2313 | }, 1); | |||
|
|
2314 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "grabbing"); | |||
|
|
2315 | return; | |||
|
|
2316 | } | |||
|
|
2317 | ||||
|
|
2318 | /* Float the window and tell motionnotify to grab it */ | |||
|
|
2319 | setfloating(grabc, 1); | |||
|
|
2320 | switch (cursor_mode = arg->ui) { | |||
|
|
2321 | case CurMove: | |||
|
|
2322 | grabcx = (int)round(cursor->x) - grabc->geom.x; | |||
|
|
2323 | grabcy = (int)round(cursor->y) - grabc->geom.y; | |||
|
|
2324 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "all-scroll"); | |||
|
|
2325 | break; | |||
|
|
2326 | case CurResize: | |||
|
|
2327 | /* Doesn't work for X11 output - the next absolute motion event | |||
|
|
2328 | * returns the cursor to where it started */ | |||
|
|
2329 | wlr_cursor_warp_closest(cursor, NULL, | |||
|
|
2330 | grabc->geom.x + grabc->geom.width, | |||
|
|
2331 | grabc->geom.y + grabc->geom.height); | |||
|
|
2332 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); | |||
|
|
2333 | break; | |||
|
|
2334 | } | |||
|
|
2335 | } | |||
|
|
2336 | ||||
|
|
2337 | void | |||
|
|
2338 | outputmgrapply(struct wl_listener *listener, void *data) | |||
|
|
2339 | { | |||
|
|
2340 | struct wlr_output_configuration_v1 *config = data; | |||
|
|
2341 | outputmgrapplyortest(config, 0); | |||
|
|
2342 | } | |||
|
|
2343 | ||||
|
|
2344 | void | |||
|
|
2345 | outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) | |||
|
|
2346 | { | |||
|
|
2347 | /* | |||
|
|
2348 | * Called when a client such as wlr-randr requests a change in output | |||
|
|
2349 | * configuration. This is only one way that the layout can be changed, | |||
|
|
2350 | * so any Monitor information should be updated by updatemons() after an | |||
|
|
2351 | * output_layout.change event, not here. | |||
|
|
2352 | */ | |||
|
|
2353 | struct wlr_output_configuration_head_v1 *config_head; | |||
|
|
2354 | int ok = 1; | |||
|
|
2355 | ||||
|
|
2356 | wl_list_for_each(config_head, &config->heads, link) { | |||
|
|
2357 | struct wlr_output *wlr_output = config_head->state.output; | |||
|
|
2358 | Monitor *m = wlr_output->data; | |||
|
|
2359 | struct wlr_output_state state; | |||
|
|
2360 | ||||
|
|
2361 | /* Ensure displays previously disabled by wlr-output-power-management-v1 | |||
|
|
2362 | * are properly handled*/ | |||
|
|
2363 | m->asleep = 0; | |||
|
|
2364 | ||||
|
|
2365 | wlr_output_state_init(&state); | |||
|
|
2366 | wlr_output_state_set_enabled(&state, config_head->state.enabled); | |||
|
|
2367 | if (!config_head->state.enabled) | |||
|
|
2368 | goto apply_or_test; | |||
|
|
2369 | ||||
|
|
2370 | if (config_head->state.mode) | |||
|
|
2371 | wlr_output_state_set_mode(&state, config_head->state.mode); | |||
|
|
2372 | else | |||
|
|
2373 | wlr_output_state_set_custom_mode(&state, | |||
|
|
2374 | config_head->state.custom_mode.width, | |||
|
|
2375 | config_head->state.custom_mode.height, | |||
|
|
2376 | config_head->state.custom_mode.refresh); | |||
|
|
2377 | ||||
|
|
2378 | wlr_output_state_set_transform(&state, config_head->state.transform); | |||
|
|
2379 | wlr_output_state_set_scale(&state, config_head->state.scale); | |||
|
|
2380 | wlr_output_state_set_adaptive_sync_enabled(&state, | |||
|
|
2381 | config_head->state.adaptive_sync_enabled); | |||
|
|
2382 | ||||
|
|
2383 | apply_or_test: | |||
|
|
2384 | ok &= test ? wlr_output_test_state(wlr_output, &state) | |||
|
|
2385 | : wlr_output_commit_state(wlr_output, &state); | |||
|
|
2386 | ||||
|
|
2387 | /* Don't move monitors if position wouldn't change. This avoids | |||
|
|
2388 | * wlroots marking the output as manually configured. | |||
|
|
2389 | * wlr_output_layout_add does not like disabled outputs */ | |||
|
|
2390 | if (!test && wlr_output->enabled && (m->m.x != config_head->state.x || m->m.y != config_head->state.y)) | |||
|
|
2391 | wlr_output_layout_add(output_layout, wlr_output, | |||
|
|
2392 | config_head->state.x, config_head->state.y); | |||
|
|
2393 | ||||
|
|
2394 | wlr_output_state_finish(&state); | |||
|
|
2395 | } | |||
|
|
2396 | ||||
|
|
2397 | if (ok) | |||
|
|
2398 | wlr_output_configuration_v1_send_succeeded(config); | |||
|
|
2399 | else | |||
|
|
2400 | wlr_output_configuration_v1_send_failed(config); | |||
|
|
2401 | wlr_output_configuration_v1_destroy(config); | |||
|
|
2402 | ||||
|
|
2403 | updatemons(NULL, NULL); | |||
|
|
2404 | } | |||
|
|
2405 | ||||
|
|
2406 | void | |||
|
|
2407 | outputmgrtest(struct wl_listener *listener, void *data) | |||
|
|
2408 | { | |||
|
|
2409 | struct wlr_output_configuration_v1 *config = data; | |||
|
|
2410 | outputmgrapplyortest(config, 1); | |||
|
|
2411 | } | |||
|
|
2412 | ||||
|
|
2413 | void | |||
|
|
2414 | pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, | |||
|
|
2415 | uint32_t time) | |||
|
|
2416 | { | |||
|
|
2417 | struct timespec now; | |||
|
|
2418 | ||||
|
|
2419 | if (surface != seat->pointer_state.focused_surface && | |||
|
|
2420 | cfg.sloppyfocus && time && c && !client_is_unmanaged(c)) | |||
|
|
2421 | focusclient(c, 0); | |||
|
|
2422 | ||||
|
|
2423 | /* If surface is NULL, clear pointer focus */ | |||
|
|
2424 | if (!surface) { | |||
|
|
2425 | wlr_seat_pointer_notify_clear_focus(seat); | |||
|
|
2426 | return; | |||
|
|
2427 | } | |||
|
|
2428 | ||||
|
|
2429 | if (!time) { | |||
|
|
2430 | clock_gettime(CLOCK_MONOTONIC, &now); | |||
|
|
2431 | time = now.tv_sec * 1000 + now.tv_nsec / 1000000; | |||
|
|
2432 | } | |||
|
|
2433 | ||||
|
|
2434 | /* Let the client know that the mouse cursor has entered one | |||
|
|
2435 | * of its surfaces, and make keyboard focus follow if desired. | |||
|
|
2436 | * wlroots makes this a no-op if surface is already focused */ | |||
|
|
2437 | wlr_seat_pointer_notify_enter(seat, surface, sx, sy); | |||
|
|
2438 | wlr_seat_pointer_notify_motion(seat, time, sx, sy); | |||
|
|
2439 | } | |||
|
|
2440 | ||||
|
|
2441 | void | |||
|
|
2442 | printstatus(void) | |||
|
|
2443 | { | |||
|
|
2444 | Monitor *m = NULL; | |||
|
|
2445 | Client *c; | |||
|
|
2446 | uint32_t occ, urg, sel; | |||
|
|
2447 | ||||
|
|
2448 | wl_list_for_each(m, &mons, link) { | |||
|
|
2449 | occ = urg = 0; | |||
|
|
2450 | wl_list_for_each(c, &clients, link) { | |||
|
|
2451 | if (c->mon != m) | |||
|
|
2452 | continue; | |||
|
|
2453 | occ |= c->tags; | |||
|
|
2454 | if (c->isurgent) | |||
|
|
2455 | urg |= c->tags; | |||
|
|
2456 | } | |||
|
|
2457 | if ((c = focustop(m))) { | |||
|
|
2458 | printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); | |||
|
|
2459 | printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c)); | |||
|
|
2460 | printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); | |||
|
|
2461 | printf("%s floating %d\n", m->wlr_output->name, c->isfloating); | |||
|
|
2462 | sel = c->tags; | |||
|
|
2463 | } else { | |||
|
|
2464 | printf("%s title \n", m->wlr_output->name); | |||
|
|
2465 | printf("%s appid \n", m->wlr_output->name); | |||
|
|
2466 | printf("%s fullscreen \n", m->wlr_output->name); | |||
|
|
2467 | printf("%s floating \n", m->wlr_output->name); | |||
|
|
2468 | sel = 0; | |||
|
|
2469 | } | |||
|
|
2470 | ||||
|
|
2471 | printf("%s selmon %u\n", m->wlr_output->name, m == selmon); | |||
|
|
2472 | printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", | |||
|
|
2473 | m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); | |||
|
|
2474 | printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); | |||
|
|
2475 | ext_workspace_printstatus(m); | |||
|
|
2476 | } | |||
|
|
2477 | fflush(stdout); | |||
|
|
2478 | ipc_printstatus(); | |||
|
|
2479 | } | |||
|
|
2480 | ||||
|
|
2481 | void | |||
|
|
2482 | powermgrsetmode(struct wl_listener *listener, void *data) | |||
|
|
2483 | { | |||
|
|
2484 | struct wlr_output_power_v1_set_mode_event *event = data; | |||
|
|
2485 | struct wlr_output_state state = {0}; | |||
|
|
2486 | Monitor *m = event->output->data; | |||
|
|
2487 | ||||
|
|
2488 | if (!m) | |||
|
|
2489 | return; | |||
|
|
2490 | ||||
|
|
2491 | m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */ | |||
|
|
2492 | wlr_output_state_set_enabled(&state, event->mode); | |||
|
|
2493 | wlr_output_commit_state(m->wlr_output, &state); | |||
|
|
2494 | ||||
|
|
2495 | m->asleep = !event->mode; | |||
|
|
2496 | updatemons(NULL, NULL); | |||
|
|
2497 | } | |||
|
|
2498 | ||||
|
|
2499 | void | |||
|
|
2500 | quit(const Arg *arg) | |||
|
|
2501 | { | |||
|
|
2502 | wl_display_terminate(dpy); | |||
|
|
2503 | } | |||
|
|
2504 | ||||
|
|
2505 | void | |||
|
|
2506 | rendermon(struct wl_listener *listener, void *data) | |||
|
|
2507 | { | |||
|
|
2508 | /* This function is called every time an output is ready to display a frame, | |||
|
|
2509 | * generally at the output's refresh rate (e.g. 60Hz). */ | |||
|
|
2510 | Monitor *m = wl_container_of(listener, m, frame); | |||
|
|
2511 | Client *c; | |||
|
|
2512 | struct wlr_output_state pending = {0}; | |||
|
|
2513 | struct timespec now; | |||
|
|
2514 | ||||
|
|
2515 | /* Render if no XDG clients have an outstanding resize and are visible on | |||
|
|
2516 | * this monitor. */ | |||
|
|
2517 | wl_list_for_each(c, &clients, link) { | |||
|
|
2518 | /* re-apply opacity every frame instead of trusting a single | |||
|
|
2519 | * set-on-focus-change call to stick */ | |||
|
|
2520 | client_apply_opacity(c); | |||
|
|
2521 | if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) | |||
|
|
2522 | goto skip; | |||
|
|
2523 | } | |||
|
|
2524 | ||||
|
|
2525 | wlr_scene_output_commit(m->scene_output, NULL); | |||
|
|
2526 | ||||
|
|
2527 | skip: | |||
|
|
2528 | /* Let clients know a frame has been rendered */ | |||
|
|
2529 | clock_gettime(CLOCK_MONOTONIC, &now); | |||
|
|
2530 | wlr_scene_output_send_frame_done(m->scene_output, &now); | |||
|
|
2531 | wlr_output_state_finish(&pending); | |||
|
|
2532 | } | |||
|
|
2533 | ||||
|
|
2534 | void | |||
|
|
2535 | requestdecorationmode(struct wl_listener *listener, void *data) | |||
|
|
2536 | { | |||
|
|
2537 | Client *c = wl_container_of(listener, c, set_decoration_mode); | |||
|
|
2538 | if (c->surface.xdg->initialized) | |||
|
|
2539 | wlr_xdg_toplevel_decoration_v1_set_mode(c->decoration, | |||
|
|
2540 | WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); | |||
|
|
2541 | } | |||
|
|
2542 | ||||
|
|
2543 | void | |||
|
|
2544 | requeststartdrag(struct wl_listener *listener, void *data) | |||
|
|
2545 | { | |||
|
|
2546 | struct wlr_seat_request_start_drag_event *event = data; | |||
|
|
2547 | ||||
|
|
2548 | if (wlr_seat_validate_pointer_grab_serial(seat, event->origin, | |||
|
|
2549 | event->serial)) | |||
|
|
2550 | wlr_seat_start_pointer_drag(seat, event->drag, event->serial); | |||
|
|
2551 | else | |||
|
|
2552 | wlr_data_source_destroy(event->drag->source); | |||
|
|
2553 | } | |||
|
|
2554 | ||||
|
|
2555 | void | |||
|
|
2556 | requestmonstate(struct wl_listener *listener, void *data) | |||
|
|
2557 | { | |||
|
|
2558 | struct wlr_output_event_request_state *event = data; | |||
|
|
2559 | wlr_output_commit_state(event->output, event->state); | |||
|
|
2560 | updatemons(NULL, NULL); | |||
|
|
2561 | } | |||
|
|
2562 | ||||
|
|
2563 | void | |||
|
|
2564 | resize(Client *c, struct wlr_box geo, int interact) | |||
|
|
2565 | { | |||
|
|
2566 | struct wlr_box *bbox; | |||
|
|
2567 | struct wlr_box clip; | |||
|
|
2568 | ||||
|
|
2569 | if (!c->mon || !client_surface(c)->mapped) | |||
|
|
2570 | return; | |||
|
|
2571 | ||||
|
|
2572 | bbox = interact ? &sgeom : &c->mon->w; | |||
|
|
2573 | ||||
|
|
2574 | client_set_bounds(c, geo.width, geo.height); | |||
|
|
2575 | c->geom = geo; | |||
|
|
2576 | applybounds(c, bbox); | |||
|
|
2577 | ||||
|
|
2578 | /* Update scene-graph, including borders. | |||
|
|
2579 | * Outer ring sits flush with the client's outer edge (0,0 .. w,h). | |||
|
|
2580 | * Inner ring is inset by obw from the outer ring. | |||
|
|
2581 | * The surface itself is inset by the full bw = ibw + obw. */ | |||
|
|
2582 | { | |||
|
|
2583 | unsigned int ib = c->ibw, ob = c->obw; | |||
|
|
2584 | int w = c->geom.width, h = c->geom.height; | |||
|
|
2585 | ||||
|
|
2586 | wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y); | |||
|
|
2587 | wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw); | |||
|
|
2588 | ||||
|
|
2589 | wlr_scene_rect_set_size(c->inner_border[0], MAX(1, w - 2 * (int)ob), (int)ib); | |||
|
|
2590 | wlr_scene_node_set_position(&c->inner_border[0]->node, ob, ob); | |||
|
|
2591 | wlr_scene_rect_set_size(c->inner_border[1], MAX(1, w - 2 * (int)ob), (int)ib); | |||
|
|
2592 | wlr_scene_node_set_position(&c->inner_border[1]->node, ob, MAX(0, h - (int)ob - (int)ib)); | |||
|
|
2593 | wlr_scene_rect_set_size(c->inner_border[2], (int)ib, MAX(1, h - 2 * (int)ob)); | |||
|
|
2594 | wlr_scene_node_set_position(&c->inner_border[2]->node, ob, ob); | |||
|
|
2595 | wlr_scene_rect_set_size(c->inner_border[3], (int)ib, MAX(1, h - 2 * (int)ob)); | |||
|
|
2596 | wlr_scene_node_set_position(&c->inner_border[3]->node, MAX(0, w - (int)ob - (int)ib), ob); | |||
|
|
2597 | ||||
|
|
2598 | wlr_scene_rect_set_size(c->outer_border[0], MAX(1, w), (int)ob); | |||
|
|
2599 | wlr_scene_node_set_position(&c->outer_border[0]->node, 0, 0); | |||
|
|
2600 | wlr_scene_rect_set_size(c->outer_border[1], MAX(1, w), (int)ob); | |||
|
|
2601 | wlr_scene_node_set_position(&c->outer_border[1]->node, 0, MAX(0, h - (int)ob)); | |||
|
|
2602 | wlr_scene_rect_set_size(c->outer_border[2], (int)ob, MAX(1, h - 2 * (int)ob)); | |||
|
|
2603 | wlr_scene_node_set_position(&c->outer_border[2]->node, 0, ob); | |||
|
|
2604 | wlr_scene_rect_set_size(c->outer_border[3], (int)ob, MAX(1, h - 2 * (int)ob)); | |||
|
|
2605 | wlr_scene_node_set_position(&c->outer_border[3]->node, MAX(0, w - (int)ob), ob); | |||
|
|
2606 | } | |||
|
|
2607 | ||||
|
|
2608 | /* this is a no-op if size hasn't changed */ | |||
|
|
2609 | c->resize = client_set_size(c, c->geom.width - 2 * c->bw, | |||
|
|
2610 | c->geom.height - 2 * c->bw); | |||
|
|
2611 | client_get_clip(c, &clip); | |||
|
|
2612 | wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); | |||
|
|
2613 | } | |||
|
|
2614 | ||||
|
|
2615 | void | |||
|
|
2616 | run(char *startup_cmd) | |||
|
|
2617 | { | |||
|
|
2618 | /* Add a Unix socket to the Wayland display. */ | |||
|
|
2619 | const char *socket = wl_display_add_socket_auto(dpy); | |||
|
|
2620 | if (!socket) | |||
|
|
2621 | die("startup: display_add_socket_auto"); | |||
|
|
2622 | setenv("WAYLAND_DISPLAY", socket, 1); | |||
|
|
2623 | ||||
|
|
2624 | /* Start the backend. This will enumerate outputs and inputs, become the DRM | |||
|
|
2625 | * master, etc */ | |||
|
|
2626 | if (!wlr_backend_start(backend)) | |||
|
|
2627 | die("startup: backend_start"); | |||
|
|
2628 | ||||
|
|
2629 | /* Now that the socket exists and the backend is started, run the startup command */ | |||
|
|
2630 | if (startup_cmd) { | |||
|
|
2631 | int piperw[2]; | |||
|
|
2632 | if (pipe(piperw) < 0) | |||
|
|
2633 | die("startup: pipe:"); | |||
|
|
2634 | if ((child_pid = fork()) < 0) | |||
|
|
2635 | die("startup: fork:"); | |||
|
|
2636 | if (child_pid == 0) { | |||
|
|
2637 | setsid(); | |||
|
|
2638 | dup2(piperw[0], STDIN_FILENO); | |||
|
|
2639 | close(piperw[0]); | |||
|
|
2640 | close(piperw[1]); | |||
|
|
2641 | execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); | |||
|
|
2642 | die("startup: execl:"); | |||
|
|
2643 | } | |||
|
|
2644 | dup2(piperw[1], STDOUT_FILENO); | |||
|
|
2645 | close(piperw[1]); | |||
|
|
2646 | close(piperw[0]); | |||
|
|
2647 | } | |||
|
|
2648 | ||||
|
|
2649 | /* Mark stdout as non-blocking as to avoid the startup script | |||
|
|
2650 | * causing swindle to freeze when a user neither closes stdin | |||
|
|
2651 | * nor consumes standard input in their startup script */ | |||
|
|
2652 | ||||
|
|
2653 | if (fd_set_nonblock(STDOUT_FILENO) < 0) | |||
|
|
2654 | close(STDOUT_FILENO); | |||
|
|
2655 | ||||
|
|
2656 | printstatus(); | |||
|
|
2657 | ||||
|
|
2658 | /* Launch autostart commands now that the socket is live and the backend | |||
|
|
2659 | * is running. Commands run sequentially in a detached child process so | |||
|
|
2660 | * the compositor never blocks. */ | |||
|
|
2661 | config_autostart_run(&cfg); | |||
|
|
2662 | ||||
|
|
2663 | /* At this point the outputs are initialized, choose initial selmon based on | |||
|
|
2664 | * cursor position, and set default cursor image */ | |||
|
|
2665 | selmon = xytomon(cursor->x, cursor->y); | |||
|
|
2666 | ||||
|
|
2667 | wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); | |||
|
|
2668 | wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); | |||
|
|
2669 | ||||
|
|
2670 | /* Run the Wayland event loop. This does not return until you exit the | |||
|
|
2671 | * compositor. Starting the backend rigged up all of the necessary event | |||
|
|
2672 | * loop configuration to listen to libinput events, DRM events, generate | |||
|
|
2673 | * frame events at the refresh rate, and so on. */ | |||
|
|
2674 | wl_display_run(dpy); | |||
|
|
2675 | } | |||
|
|
2676 | ||||
|
|
2677 | void | |||
|
|
2678 | setcursor(struct wl_listener *listener, void *data) | |||
|
|
2679 | { | |||
|
|
2680 | /* This event is raised by the seat when a client provides a cursor image */ | |||
|
|
2681 | struct wlr_seat_pointer_request_set_cursor_event *event = data; | |||
|
|
2682 | /* If we're "grabbing" the cursor, don't use the client's image, we will | |||
|
|
2683 | * restore it after "grabbing" sending a leave event, followed by a enter | |||
|
|
2684 | * event, which will result in the client requesting set the cursor surface */ | |||
|
|
2685 | if (cursor_mode != CurNormal && cursor_mode != CurPressed) | |||
|
|
2686 | return; | |||
|
|
2687 | /* This can be sent by any client, so we check to make sure this one | |||
|
|
2688 | * actually has pointer focus first. If so, we can tell the cursor to | |||
|
|
2689 | * use the provided surface as the cursor image. It will set the | |||
|
|
2690 | * hardware cursor on the output that it's currently on and continue to | |||
|
|
2691 | * do so as the cursor moves between outputs. */ | |||
|
|
2692 | if (event->seat_client == seat->pointer_state.focused_client) | |||
|
|
2693 | wlr_cursor_set_surface(cursor, event->surface, | |||
|
|
2694 | event->hotspot_x, event->hotspot_y); | |||
|
|
2695 | } | |||
|
|
2696 | ||||
|
|
2697 | void | |||
|
|
2698 | setcursorshape(struct wl_listener *listener, void *data) | |||
|
|
2699 | { | |||
|
|
2700 | struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; | |||
|
|
2701 | if (cursor_mode != CurNormal && cursor_mode != CurPressed) | |||
|
|
2702 | return; | |||
|
|
2703 | /* This can be sent by any client, so we check to make sure this one | |||
|
|
2704 | * actually has pointer focus first. If so, we can tell the cursor to | |||
|
|
2705 | * use the provided cursor shape. */ | |||
|
|
2706 | if (event->seat_client == seat->pointer_state.focused_client) | |||
|
|
2707 | wlr_cursor_set_xcursor(cursor, cursor_mgr, | |||
|
|
2708 | wlr_cursor_shape_v1_name(event->shape)); | |||
|
|
2709 | } | |||
|
|
2710 | ||||
|
|
2711 | void | |||
|
|
2712 | setfloating(Client *c, int floating) | |||
|
|
2713 | { | |||
|
|
2714 | Client *p = client_get_parent(c); | |||
|
|
2715 | c->isfloating = floating; | |||
|
|
2716 | /* If in floating layout do not change the client's layer */ | |||
|
|
2717 | if (!c->mon || !client_surface(c)->mapped || !c->mon->lt[c->mon->sellt]->arrange) | |||
|
|
2718 | return; | |||
|
|
2719 | wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen || | |||
|
|
2720 | (p && p->isfullscreen) ? LyrFS | |||
|
|
2721 | : c->isfloating ? LyrFloat : LyrTile]); | |||
|
|
2722 | arrange(c->mon); | |||
|
|
2723 | printstatus(); | |||
|
|
2724 | } | |||
|
|
2725 | ||||
|
|
2726 | void | |||
|
|
2727 | setfullscreen(Client *c, int fullscreen) | |||
|
|
2728 | { | |||
|
|
2729 | c->isfullscreen = fullscreen; | |||
|
|
2730 | if (!c->mon || !client_surface(c)->mapped) | |||
|
|
2731 | return; | |||
|
|
2732 | c->ibw = fullscreen ? 0 : cfg.appearance.inner_border_px; | |||
|
|
2733 | c->obw = fullscreen ? 0 : cfg.appearance.outer_border_px; | |||
|
|
2734 | c->bw = c->ibw + c->obw; | |||
|
|
2735 | client_set_fullscreen(c, fullscreen); | |||
|
|
2736 | if (c->foreign_toplevel) | |||
|
|
2737 | wlr_foreign_toplevel_handle_v1_set_fullscreen(c->foreign_toplevel, fullscreen); | |||
|
|
2738 | wlr_scene_node_reparent(&c->scene->node, layers[c->isfullscreen | |||
|
|
2739 | ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); | |||
|
|
2740 | ||||
|
|
2741 | if (fullscreen) { | |||
|
|
2742 | client_set_opacity(c, 1.0f); | |||
|
|
2743 | c->prev = c->geom; | |||
|
|
2744 | resize(c, c->mon->m, 0); | |||
|
|
2745 | } else { | |||
|
|
2746 | client_set_opacity(c, client_surface(c) == seat->keyboard_state.focused_surface | |||
|
|
2747 | ? cfg.appearance.active_opacity : cfg.appearance.inactive_opacity); | |||
|
|
2748 | resize(c, c->prev, 0); | |||
|
|
2749 | } | |||
|
|
2750 | arrange(c->mon); | |||
|
|
2751 | printstatus(); | |||
|
|
2752 | } | |||
|
|
2753 | ||||
|
|
2754 | void | |||
|
|
2755 | setlayout(const Arg *arg) | |||
|
|
2756 | { | |||
|
|
2757 | if (!selmon) | |||
|
|
2758 | return; | |||
|
|
2759 | /* arg->ui is an index into layouts[]. If out of range, just toggle. */ | |||
|
|
2760 | if (arg && arg->ui < LENGTH(layouts)) { | |||
|
|
2761 | if (selmon->lt[selmon->sellt] != &layouts[arg->ui]) | |||
|
|
2762 | selmon->sellt ^= 1; | |||
|
|
2763 | selmon->lt[selmon->sellt] = &layouts[arg->ui]; | |||
|
|
2764 | } else { | |||
|
|
2765 | selmon->sellt ^= 1; | |||
|
|
2766 | } | |||
|
|
2767 | strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); | |||
|
|
2768 | arrange(selmon); | |||
|
|
2769 | printstatus(); | |||
|
|
2770 | } | |||
|
|
2771 | ||||
|
|
2772 | /* arg > 1.0 will set mfact absolutely */ | |||
|
|
2773 | ||||
|
|
2774 | void | |||
|
|
2775 | setmon(Client *c, Monitor *m, uint32_t newtags) | |||
|
|
2776 | { | |||
|
|
2777 | Monitor *oldmon = c->mon; | |||
|
|
2778 | ||||
|
|
2779 | if (oldmon == m) | |||
|
|
2780 | return; | |||
|
|
2781 | c->mon = m; | |||
|
|
2782 | c->prev = c->geom; | |||
|
|
2783 | ||||
|
|
2784 | /* Scene graph sends surface leave/enter events on move and resize */ | |||
|
|
2785 | if (oldmon) { | |||
|
|
2786 | if (c->foreign_toplevel) | |||
|
|
2787 | wlr_foreign_toplevel_handle_v1_output_leave(c->foreign_toplevel, oldmon->wlr_output); | |||
|
|
2788 | arrange(oldmon); | |||
|
|
2789 | } | |||
|
|
2790 | if (m) { | |||
|
|
2791 | /* Make sure window actually overlaps with the monitor */ | |||
|
|
2792 | resize(c, c->geom, 0); | |||
|
|
2793 | c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ | |||
|
|
2794 | if (c->foreign_toplevel) | |||
|
|
2795 | wlr_foreign_toplevel_handle_v1_output_enter(c->foreign_toplevel, m->wlr_output); | |||
|
|
2796 | setfullscreen(c, c->isfullscreen); /* This will call arrange(c->mon) */ | |||
|
|
2797 | setfloating(c, c->isfloating); | |||
|
|
2798 | } | |||
|
|
2799 | focusclient(focustop(selmon), 1); | |||
|
|
2800 | } | |||
|
|
2801 | ||||
|
|
2802 | void | |||
|
|
2803 | setpsel(struct wl_listener *listener, void *data) | |||
|
|
2804 | { | |||
|
|
2805 | /* This event is raised by the seat when a client wants to set the selection, | |||
|
|
2806 | * usually when the user copies something. wlroots allows compositors to | |||
|
|
2807 | * ignore such requests if they so choose, but in dwl we always honor them | |||
|
|
2808 | */ | |||
|
|
2809 | struct wlr_seat_request_set_primary_selection_event *event = data; | |||
|
|
2810 | wlr_seat_set_primary_selection(seat, event->source, event->serial); | |||
|
|
2811 | } | |||
|
|
2812 | ||||
|
|
2813 | void | |||
|
|
2814 | setsel(struct wl_listener *listener, void *data) | |||
|
|
2815 | { | |||
|
|
2816 | /* This event is raised by the seat when a client wants to set the selection, | |||
|
|
2817 | * usually when the user copies something. wlroots allows compositors to | |||
|
|
2818 | * ignore such requests if they so choose, but in dwl we always honor them | |||
|
|
2819 | */ | |||
|
|
2820 | struct wlr_seat_request_set_selection_event *event = data; | |||
|
|
2821 | wlr_seat_set_selection(seat, event->source, event->serial); | |||
|
|
2822 | } | |||
|
|
2823 | ||||
|
|
2824 | /* Config hot reload callback is fired by the inotify watcher */ | |||
|
|
2825 | ||||
|
|
2826 | static void | |||
|
|
2827 | reapply_client_appearance(void) | |||
|
|
2828 | { | |||
|
|
2829 | Client *c; | |||
|
|
2830 | wl_list_for_each(c, &clients, link) { | |||
|
|
2831 | if (!c->mon || client_is_unmanaged(c)) | |||
|
|
2832 | continue; | |||
|
|
2833 | if (c == focustop(c->mon)) { | |||
|
|
2834 | client_set_border_color(c, cfg.appearance.inner_focus_color, cfg.appearance.outer_focus_color); | |||
|
|
2835 | client_set_opacity(c, cfg.appearance.active_opacity); | |||
|
|
2836 | } else { | |||
|
|
2837 | client_set_border_color(c, cfg.appearance.inner_border_color, cfg.appearance.outer_border_color); | |||
|
|
2838 | client_set_opacity(c, cfg.appearance.inactive_opacity); | |||
|
|
2839 | } | |||
|
|
2840 | } | |||
|
|
2841 | } | |||
|
|
2842 | ||||
|
|
2843 | static void | |||
|
|
2844 | reapply_client_borders(void) | |||
|
|
2845 | { | |||
|
|
2846 | Client *c; | |||
|
|
2847 | wl_list_for_each(c, &clients, link) { | |||
|
|
2848 | if (client_is_unmanaged(c) || c->isfullscreen) | |||
|
|
2849 | continue; | |||
|
|
2850 | unsigned int ibw = cfg.appearance.inner_border_px; | |||
|
|
2851 | unsigned int obw = cfg.appearance.outer_border_px; | |||
|
|
2852 | if (c->ibw == ibw && c->obw == obw) | |||
|
|
2853 | continue; | |||
|
|
2854 | c->ibw = ibw; | |||
|
|
2855 | c->obw = obw; | |||
|
|
2856 | c->bw = ibw + obw; | |||
|
|
2857 | if (c->isfloating) | |||
|
|
2858 | resize(c, c->geom, 0); | |||
|
|
2859 | } | |||
|
|
2860 | } | |||
|
|
2861 | ||||
|
|
2862 | static void | |||
|
|
2863 | on_config_reload(const Config *newcfg, void *ud) | |||
|
|
2864 | { | |||
|
|
2865 | Monitor *m; | |||
|
|
2866 | (void)ud; | |||
|
|
2867 | cfg = *newcfg; | |||
|
|
2868 | reapply_client_borders(); | |||
|
|
2869 | wl_list_for_each(m, &mons, link) | |||
|
|
2870 | arrange(m); | |||
|
|
2871 | reapply_client_appearance(); | |||
|
|
2872 | printstatus(); | |||
|
|
2873 | } | |||
|
|
2874 | ||||
|
|
2875 | void | |||
|
|
2876 | setup(void) | |||
|
|
2877 | { | |||
|
|
2878 | int drm_fd, i, sig[] = {SIGCHLD, SIGINT, SIGTERM, SIGPIPE}; | |||
|
|
2879 | struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = handlesig}; | |||
|
|
2880 | sigemptyset(&sa.sa_mask); | |||
|
|
2881 | ||||
|
|
2882 | for (i = 0; i < (int)LENGTH(sig); i++) | |||
|
|
2883 | sigaction(sig[i], &sa, NULL); | |||
|
|
2884 | ||||
|
|
2885 | /* Load config before anything else so all settings are ready */ | |||
|
|
2886 | { | |||
|
|
2887 | char cfgpath[1024]; | |||
|
|
2888 | const char *path; | |||
|
|
2889 | int debug_override = (cfg.log_level == WLR_DEBUG); /* set by -d flag in main() */ | |||
|
|
2890 | if (custom_cfg_path) | |||
|
|
2891 | path = custom_cfg_path; | |||
|
|
2892 | else { | |||
|
|
2893 | config_get_path(cfgpath, sizeof(cfgpath)); | |||
|
|
2894 | path = cfgpath; | |||
|
|
2895 | } | |||
|
|
2896 | if (config_load(path, &cfg) < 0) | |||
|
|
2897 | die("failed to load config from '%s'", path); | |||
|
|
2898 | if (debug_override) | |||
|
|
2899 | cfg.log_level = WLR_DEBUG; | |||
|
|
2900 | } | |||
|
|
2901 | ||||
|
|
2902 | wlr_log_init(cfg.log_level, NULL); | |||
|
|
2903 | ||||
|
|
2904 | /* The Wayland display is managed by libwayland. It handles accepting | |||
|
|
2905 | * clients from the Unix socket, managing Wayland globals, and so on and so forth */ | |||
|
|
2906 | dpy = wl_display_create(); | |||
|
|
2907 | event_loop = wl_display_get_event_loop(dpy); | |||
|
|
2908 | ipc_init(); | |||
|
|
2909 | workspaces_init(); | |||
|
|
2910 | ||||
|
|
2911 | /* Start the inotify config watcher */ | |||
|
|
2912 | ||||
|
|
2913 | { | |||
|
|
2914 | char cfgpath[1024]; | |||
|
|
2915 | const char *path = custom_cfg_path; | |||
|
|
2916 | if (!path) { | |||
|
|
2917 | config_get_path(cfgpath, sizeof(cfgpath)); | |||
|
|
2918 | path = cfgpath; | |||
|
|
2919 | } | |||
|
|
2920 | cfg_watch_src = config_watch_start(event_loop, path, | |||
|
|
2921 | on_config_reload, NULL); | |||
|
|
2922 | } | |||
|
|
2923 | ||||
|
|
2924 | /* The backend is a wlroots feature which abstracts the underlying input and | |||
|
|
2925 | * output hardware. The autocreate option will choose the most suitable | |||
|
|
2926 | * backend based on the current environment, such as opening an X11 window | |||
|
|
2927 | * if an X11 server is running. */ | |||
|
|
2928 | if (!(backend = wlr_backend_autocreate(event_loop, &session))) | |||
|
|
2929 | die("couldn't create backend"); | |||
|
|
2930 | ||||
|
|
2931 | /* Initialize the scene graph used to lay out windows */ | |||
|
|
2932 | scene = wlr_scene_create(); | |||
|
|
2933 | root_bg = wlr_scene_rect_create(&scene->tree, 0, 0, cfg.appearance.root_color); | |||
|
|
2934 | for (i = 0; i < NUM_LAYERS; i++) | |||
|
|
2935 | layers[i] = wlr_scene_tree_create(&scene->tree); | |||
|
|
2936 | drag_icon = wlr_scene_tree_create(&scene->tree); | |||
|
|
2937 | wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node); | |||
|
|
2938 | ||||
|
|
2939 | { | |||
|
|
2940 | static const float preselect_color[4] = {1.0f, 1.0f, 1.0f, 0.25f}; | |||
|
|
2941 | preselect_indicator = wlr_scene_rect_create(layers[LyrOverlay], 0, 0, preselect_color); | |||
|
|
2942 | wlr_scene_node_set_enabled(&preselect_indicator->node, false); | |||
|
|
2943 | } | |||
|
|
2944 | ||||
|
|
2945 | /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user | |||
|
|
2946 | * can also specify a renderer using the WLR_RENDERER env var. | |||
|
|
2947 | * The renderer is responsible for defining the various pixel formats it | |||
|
|
2948 | * supports for shared memory, this configures that for clients. */ | |||
|
|
2949 | if (!(drw = wlr_renderer_autocreate(backend))) | |||
|
|
2950 | die("couldn't create renderer"); | |||
|
|
2951 | wl_signal_add(&drw->events.lost, &gpu_reset); | |||
|
|
2952 | ||||
|
|
2953 | /* Create shm, drm and linux_dmabuf interfaces by ourselves. | |||
|
|
2954 | * The simplest way is to call: | |||
|
|
2955 | * wlr_renderer_init_wl_display(drw); | |||
|
|
2956 | * but we need to create the linux_dmabuf interface manually to integrate it | |||
|
|
2957 | * with wlr_scene. */ | |||
|
|
2958 | wlr_renderer_init_wl_shm(drw, dpy); | |||
|
|
2959 | ||||
|
|
2960 | if (wlr_renderer_get_texture_formats(drw, WLR_BUFFER_CAP_DMABUF)) { | |||
|
|
2961 | wlr_drm_create(dpy, drw); | |||
|
|
2962 | wlr_scene_set_linux_dmabuf_v1(scene, | |||
|
|
2963 | wlr_linux_dmabuf_v1_create_with_renderer(dpy, 5, drw)); | |||
|
|
2964 | } | |||
|
|
2965 | ||||
|
|
2966 | if ((drm_fd = wlr_renderer_get_drm_fd(drw)) >= 0 && drw->features.timeline | |||
|
|
2967 | && backend->features.timeline) | |||
|
|
2968 | wlr_linux_drm_syncobj_manager_v1_create(dpy, 1, drm_fd); | |||
|
|
2969 | ||||
|
|
2970 | /* Autocreates an allocator for us. | |||
|
|
2971 | * The allocator is the bridge between the renderer and the backend. It | |||
|
|
2972 | * handles the buffer creation, allowing wlroots to render onto the | |||
|
|
2973 | * screen */ | |||
|
|
2974 | if (!(alloc = wlr_allocator_autocreate(backend, drw))) | |||
|
|
2975 | die("couldn't create allocator"); | |||
|
|
2976 | ||||
|
|
2977 | /* This creates some hands-off wlroots interfaces. The compositor is | |||
|
|
2978 | * necessary for clients to allocate surfaces and the data device manager | |||
|
|
2979 | * handles the clipboard. Each of these wlroots interfaces has room for you | |||
|
|
2980 | * to dig your fingers in and play with their behavior if you want. Note that | |||
|
|
2981 | * the clients cannot set the selection directly without compositor approval, | |||
|
|
2982 | * see the setsel() function. */ | |||
|
|
2983 | compositor = wlr_compositor_create(dpy, 6, drw); | |||
|
|
2984 | wlr_subcompositor_create(dpy); | |||
|
|
2985 | wlr_data_device_manager_create(dpy); | |||
|
|
2986 | wlr_export_dmabuf_manager_v1_create(dpy); | |||
|
|
2987 | wlr_screencopy_manager_v1_create(dpy); | |||
|
|
2988 | wlr_data_control_manager_v1_create(dpy); | |||
|
|
2989 | wlr_ext_data_control_manager_v1_create(dpy, 1); | |||
|
|
2990 | wlr_primary_selection_v1_device_manager_create(dpy); | |||
|
|
2991 | wlr_viewporter_create(dpy); | |||
|
|
2992 | wlr_single_pixel_buffer_manager_v1_create(dpy); | |||
|
|
2993 | wlr_fractional_scale_manager_v1_create(dpy, 1); | |||
|
|
2994 | wlr_presentation_create(dpy, backend, 2); | |||
|
|
2995 | wlr_alpha_modifier_v1_create(dpy); | |||
|
|
2996 | ||||
|
|
2997 | foreign_toplevel_mgr = wlr_foreign_toplevel_manager_v1_create(dpy); | |||
|
|
2998 | ||||
|
|
2999 | /* Initializes the interface used to implement urgency hints */ | |||
|
|
3000 | activation = wlr_xdg_activation_v1_create(dpy); | |||
|
|
3001 | wl_signal_add(&activation->events.request_activate, &request_activate); | |||
|
|
3002 | ||||
|
|
3003 | wlr_scene_set_gamma_control_manager_v1(scene, wlr_gamma_control_manager_v1_create(dpy)); | |||
|
|
3004 | ||||
|
|
3005 | power_mgr = wlr_output_power_manager_v1_create(dpy); | |||
|
|
3006 | wl_signal_add(&power_mgr->events.set_mode, &output_power_mgr_set_mode); | |||
|
|
3007 | ||||
|
|
3008 | /* Creates an output layout, which is a wlroots utility for working with an | |||
|
|
3009 | * arrangement of screens in a physical layout. */ | |||
|
|
3010 | output_layout = wlr_output_layout_create(dpy); | |||
|
|
3011 | wl_signal_add(&output_layout->events.change, &layout_change); | |||
|
|
3012 | ||||
|
|
3013 | wlr_xdg_output_manager_v1_create(dpy, output_layout); | |||
|
|
3014 | ||||
|
|
3015 | /* Configure a listener to be notified when new outputs are available on the | |||
|
|
3016 | * backend. */ | |||
|
|
3017 | wl_list_init(&mons); | |||
|
|
3018 | wl_signal_add(&backend->events.new_output, &new_output); | |||
|
|
3019 | ||||
|
|
3020 | /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a | |||
|
|
3021 | * Wayland protocol which is used for application windows. For more | |||
|
|
3022 | * detail on shells, refer to the article: | |||
|
|
3023 | * | |||
|
|
3024 | * https://drewdevault.com/2018/07/29/Wayland-shells.html | |||
|
|
3025 | */ | |||
|
|
3026 | wl_list_init(&clients); | |||
|
|
3027 | wl_list_init(&fstack); | |||
|
|
3028 | ||||
|
|
3029 | xdg_shell = wlr_xdg_shell_create(dpy, 6); | |||
|
|
3030 | wl_signal_add(&xdg_shell->events.new_toplevel, &new_xdg_toplevel); | |||
|
|
3031 | wl_signal_add(&xdg_shell->events.new_popup, &new_xdg_popup); | |||
|
|
3032 | ||||
|
|
3033 | layer_shell = wlr_layer_shell_v1_create(dpy, 3); | |||
|
|
3034 | wl_signal_add(&layer_shell->events.new_surface, &new_layer_surface); | |||
|
|
3035 | ||||
|
|
3036 | idle_notifier = wlr_idle_notifier_v1_create(dpy); | |||
|
|
3037 | ||||
|
|
3038 | idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); | |||
|
|
3039 | wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &new_idle_inhibitor); | |||
|
|
3040 | ||||
|
|
3041 | session_lock_mgr = wlr_session_lock_manager_v1_create(dpy); | |||
|
|
3042 | wl_signal_add(&session_lock_mgr->events.new_lock, &new_session_lock); | |||
|
|
3043 | locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height, | |||
|
|
3044 | (float [4]){0.1f, 0.1f, 0.1f, 1.0f}); | |||
|
|
3045 | wlr_scene_node_set_enabled(&locked_bg->node, 0); | |||
|
|
3046 | ||||
|
|
3047 | /* Use decoration protocols to negotiate server-side decorations */ | |||
|
|
3048 | wlr_server_decoration_manager_set_default_mode( | |||
|
|
3049 | wlr_server_decoration_manager_create(dpy), | |||
|
|
3050 | WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); | |||
|
|
3051 | xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); | |||
|
|
3052 | wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration); | |||
|
|
3053 | ||||
|
|
3054 | pointer_constraints = wlr_pointer_constraints_v1_create(dpy); | |||
|
|
3055 | wl_signal_add(&pointer_constraints->events.new_constraint, &new_pointer_constraint); | |||
|
|
3056 | ||||
|
|
3057 | relative_pointer_mgr = wlr_relative_pointer_manager_v1_create(dpy); | |||
|
|
3058 | ||||
|
|
3059 | /* | |||
|
|
3060 | * Creates a cursor, which is a wlroots utility for tracking the cursor | |||
|
|
3061 | * image shown on screen. | |||
|
|
3062 | */ | |||
|
|
3063 | cursor = wlr_cursor_create(); | |||
|
|
3064 | wlr_cursor_attach_output_layout(cursor, output_layout); | |||
|
|
3065 | ||||
|
|
3066 | /* Creates an xcursor manager, another wlroots utility which loads up | |||
|
|
3067 | * Xcursor themes to source cursor images from and makes sure that cursor | |||
|
|
3068 | * images are available at all scale factors on the screen (necessary for | |||
|
|
3069 | * HiDPI support). Scaled cursors will be loaded with each output. */ | |||
|
|
3070 | cursor_mgr = wlr_xcursor_manager_create(NULL, 24); | |||
|
|
3071 | setenv("XCURSOR_SIZE", "24", 1); | |||
|
|
3072 | ||||
|
|
3073 | /* | |||
|
|
3074 | * wlr_cursor *only* displays an image on screen. It does not move around | |||
|
|
3075 | * when the pointer moves. However, we can attach input devices to it, and | |||
|
|
3076 | * it will generate aggregate events for all of them. In these events, we | |||
|
|
3077 | * can choose how we want to process them, forwarding them to clients and | |||
|
|
3078 | * moving the cursor around. More detail on this process is described in | |||
|
|
3079 | * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html | |||
|
|
3080 | * | |||
|
|
3081 | * And more comments are sprinkled throughout the notify functions above. | |||
|
|
3082 | */ | |||
|
|
3083 | wl_signal_add(&cursor->events.motion, &cursor_motion); | |||
|
|
3084 | wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); | |||
|
|
3085 | wl_signal_add(&cursor->events.button, &cursor_button); | |||
|
|
3086 | wl_signal_add(&cursor->events.axis, &cursor_axis); | |||
|
|
3087 | wl_signal_add(&cursor->events.frame, &cursor_frame); | |||
|
|
3088 | ||||
|
|
3089 | cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); | |||
|
|
3090 | wl_signal_add(&cursor_shape_mgr->events.request_set_shape, &request_set_cursor_shape); | |||
|
|
3091 | ||||
|
|
3092 | /* | |||
|
|
3093 | * Configures a seat, which is a single "seat" at which a user sits and | |||
|
|
3094 | * operates the computer. This conceptually includes up to one keyboard, | |||
|
|
3095 | * pointer, touch, and drawing tablet device. We also rig up a listener to | |||
|
|
3096 | * let us know when new input devices are available on the backend. | |||
|
|
3097 | */ | |||
|
|
3098 | wl_signal_add(&backend->events.new_input, &new_input_device); | |||
|
|
3099 | virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); | |||
|
|
3100 | wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, | |||
|
|
3101 | &new_virtual_keyboard); | |||
|
|
3102 | virtual_pointer_mgr = wlr_virtual_pointer_manager_v1_create(dpy); | |||
|
|
3103 | wl_signal_add(&virtual_pointer_mgr->events.new_virtual_pointer, | |||
|
|
3104 | &new_virtual_pointer); | |||
|
|
3105 | ||||
|
|
3106 | seat = wlr_seat_create(dpy, "seat0"); | |||
|
|
3107 | wl_signal_add(&seat->events.request_set_cursor, &request_cursor); | |||
|
|
3108 | wl_signal_add(&seat->events.request_set_selection, &request_set_sel); | |||
|
|
3109 | wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); | |||
|
|
3110 | wl_signal_add(&seat->events.request_start_drag, &request_start_drag); | |||
|
|
3111 | wl_signal_add(&seat->events.start_drag, &start_drag); | |||
|
|
3112 | ||||
|
|
3113 | kb_group = createkeyboardgroup(); | |||
|
|
3114 | wl_list_init(&kb_group->destroy.link); | |||
|
|
3115 | ||||
|
|
3116 | output_mgr = wlr_output_manager_v1_create(dpy); | |||
|
|
3117 | wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); | |||
|
|
3118 | wl_signal_add(&output_mgr->events.test, &output_mgr_test); | |||
|
|
3119 | ||||
|
|
3120 | /* Make sure XWayland clients don't connect to the parent X server, | |||
|
|
3121 | * e.g when running in the x11 backend or the wayland backend and the | |||
|
|
3122 | * compositor has Xwayland support */ | |||
|
|
3123 | unsetenv("DISPLAY"); | |||
|
|
3124 | #ifdef XWAYLAND | |||
|
|
3125 | /* | |||
|
|
3126 | * Initialise the XWayland X server. | |||
|
|
3127 | * It will be started when the first X client is started. | |||
|
|
3128 | */ | |||
|
|
3129 | if ((xwayland = wlr_xwayland_create(dpy, compositor, 1))) { | |||
|
|
3130 | wl_signal_add(&xwayland->events.ready, &xwayland_ready); | |||
|
|
3131 | wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); | |||
|
|
3132 | ||||
|
|
3133 | setenv("DISPLAY", xwayland->display_name, 1); | |||
|
|
3134 | } else { | |||
|
|
3135 | fprintf(stderr, "failed to setup XWayland X server, continuing without it\n"); | |||
|
|
3136 | } | |||
|
|
3137 | #endif | |||
|
|
3138 | } | |||
|
|
3139 | ||||
|
|
3140 | void | |||
|
|
3141 | spawn(const Arg *arg) | |||
|
|
3142 | { | |||
|
|
3143 | if (fork() == 0) { | |||
|
|
3144 | dup2(STDERR_FILENO, STDOUT_FILENO); | |||
|
|
3145 | setsid(); | |||
|
|
3146 | execvp(((char **)arg->v)[0], (char **)arg->v); | |||
|
|
3147 | die("dwl: execvp %s failed:", ((char **)arg->v)[0]); | |||
|
|
3148 | } | |||
|
|
3149 | } | |||
|
|
3150 | ||||
|
|
3151 | void | |||
|
|
3152 | startdrag(struct wl_listener *listener, void *data) | |||
|
|
3153 | { | |||
|
|
3154 | struct wlr_drag *drag = data; | |||
|
|
3155 | if (!drag->icon) | |||
|
|
3156 | return; | |||
|
|
3157 | ||||
|
|
3158 | drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node; | |||
|
|
3159 | LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); | |||
|
|
3160 | } | |||
|
|
3161 | ||||
|
|
3162 | void | |||
|
|
3163 | tag(const Arg *arg) | |||
|
|
3164 | { | |||
|
|
3165 | Client *sel = focustop(selmon); | |||
|
|
3166 | if (!sel || (arg->ui & TAGMASK) == 0) | |||
|
|
3167 | return; | |||
|
|
3168 | ||||
|
|
3169 | /* Remove from the source tag's dwindle tree */ | |||
|
|
3170 | dwindle_remove_client(sel); | |||
|
|
3171 | sel->tags = arg->ui & TAGMASK; | |||
|
|
3172 | focusclient(focustop(selmon), 1); | |||
|
|
3173 | arrange(selmon); | |||
|
|
3174 | printstatus(); | |||
|
|
3175 | } | |||
|
|
3176 | ||||
|
|
3177 | void | |||
|
|
3178 | tagmon(const Arg *arg) | |||
|
|
3179 | { | |||
|
|
3180 | Client *sel = focustop(selmon); | |||
|
|
3181 | if (sel) | |||
|
|
3182 | setmon(sel, dirtomon(arg->i), 0); | |||
|
|
3183 | } | |||
|
|
3184 | ||||
|
|
3185 | ||||
|
|
3186 | /* dwindle helpers */ | |||
|
|
3187 | ||||
|
|
3188 | /* | |||
|
|
3189 | * Returns the 0-based index of the lowest set tag bit for monitor m. | |||
|
|
3190 | * For single-tag views this is the exact tag. For multi-tag views it | |||
|
|
3191 | * picks the lowest bit */ | |||
|
|
3192 | ||||
|
|
3193 | static inline int | |||
|
|
3194 | current_tag_idx(Monitor *m) | |||
|
|
3195 | { | |||
|
|
3196 | uint32_t tags = m->tagset[m->seltags] & TAGMASK; | |||
|
|
3197 | if (!tags) | |||
|
|
3198 | return 0; | |||
|
|
3199 | int idx = 0; | |||
|
|
3200 | while (!(tags & 1u)) { tags >>= 1; idx++; } | |||
|
|
3201 | return idx < TAGCOUNT ? idx : 0; | |||
|
|
3202 | } | |||
|
|
3203 | ||||
|
|
3204 | static DwindleNode * | |||
|
|
3205 | dwindle_new_node(void) | |||
|
|
3206 | { | |||
|
|
3207 | DwindleNode *n = ecalloc(1, sizeof(*n)); | |||
|
|
3208 | n->split_ratio = 1.0f; | |||
|
|
3209 | return n; | |||
|
|
3210 | } | |||
|
|
3211 | ||||
|
|
3212 | void | |||
|
|
3213 | dwindle_free_tree(DwindleNode *n) | |||
|
|
3214 | { | |||
|
|
3215 | if (!n) | |||
|
|
3216 | return; | |||
|
|
3217 | dwindle_free_tree(n->children[0]); | |||
|
|
3218 | dwindle_free_tree(n->children[1]); | |||
|
|
3219 | free(n); | |||
|
|
3220 | } | |||
|
|
3221 | ||||
|
|
3222 | static DwindleNode * | |||
|
|
3223 | dwindle_find_leaf(DwindleNode *n, Client *c) | |||
|
|
3224 | { | |||
|
|
3225 | if (!n) | |||
|
|
3226 | return NULL; | |||
|
|
3227 | if (!n->is_node) | |||
|
|
3228 | return n->client == c ? n : NULL; | |||
|
|
3229 | DwindleNode *r = dwindle_find_leaf(n->children[0], c); | |||
|
|
3230 | return r ? r : dwindle_find_leaf(n->children[1], c); | |||
|
|
3231 | } | |||
|
|
3232 | ||||
|
|
3233 | static DwindleNode * | |||
|
|
3234 | dwindle_first_leaf(DwindleNode *n) | |||
|
|
3235 | { | |||
|
|
3236 | if (!n) | |||
|
|
3237 | return NULL; | |||
|
|
3238 | while (n->is_node) | |||
|
|
3239 | n = n->children[0]; | |||
|
|
3240 | return n; | |||
|
|
3241 | } | |||
|
|
3242 | ||||
|
|
3243 | /* | |||
|
|
3244 | * Recursively distribute geometry downward from node n | |||
|
|
3245 | * gap is inner gap pixels between the two halves | |||
|
|
3246 | */ | |||
|
|
3247 | ||||
|
|
3248 | static void | |||
|
|
3249 | dwindle_recalc(DwindleNode *n, int gap) | |||
|
|
3250 | { | |||
|
|
3251 | if (!n) | |||
|
|
3252 | return; | |||
|
|
3253 | ||||
|
|
3254 | if (!n->is_node) { | |||
|
|
3255 | if (n->client && !n->client->isfullscreen) | |||
|
|
3256 | resize(n->client, n->box, 0); | |||
|
|
3257 | return; | |||
|
|
3258 | } | |||
|
|
3259 | ||||
|
|
3260 | /* Wider than tall -> split left/right; taller -> split top/bottom. */ | |||
|
|
3261 | n->split_top = (n->box.height > n->box.width); | |||
|
|
3262 | ||||
|
|
3263 | if (!n->split_top) { | |||
|
|
3264 | int w1 = MAX(1, (int)(n->box.width / 2.0f * n->split_ratio) - gap / 2); | |||
|
|
3265 | n->children[0]->box = (struct wlr_box){ | |||
|
|
3266 | n->box.x, n->box.y, w1, n->box.height | |||
|
|
3267 | }; | |||
|
|
3268 | n->children[1]->box = (struct wlr_box){ | |||
|
|
3269 | n->box.x + w1 + gap, n->box.y, | |||
|
|
3270 | MAX(1, n->box.width - w1 - gap), n->box.height | |||
|
|
3271 | }; | |||
|
|
3272 | } else { | |||
|
|
3273 | int h1 = MAX(1, (int)(n->box.height / 2.0f * n->split_ratio) - gap / 2); | |||
|
|
3274 | n->children[0]->box = (struct wlr_box){ | |||
|
|
3275 | n->box.x, n->box.y, n->box.width, h1 | |||
|
|
3276 | }; | |||
|
|
3277 | n->children[1]->box = (struct wlr_box){ | |||
|
|
3278 | n->box.x, n->box.y + h1 + gap, | |||
|
|
3279 | n->box.width, MAX(1, n->box.height - h1 - gap) | |||
|
|
3280 | }; | |||
|
|
3281 | } | |||
|
|
3282 | ||||
|
|
3283 | dwindle_recalc(n->children[0], gap); | |||
|
|
3284 | dwindle_recalc(n->children[1], gap); | |||
|
|
3285 | } | |||
|
|
3286 | ||||
|
|
3287 | static int | |||
|
|
3288 | dwindle_insert_at_edge(DwindleNode **root, Client *new_c, Client *target, DwindleEdge edge) | |||
|
|
3289 | { | |||
|
|
3290 | DwindleNode *opening_on = dwindle_find_leaf(*root, target); | |||
|
|
3291 | if (!opening_on) | |||
|
|
3292 | return 0; | |||
|
|
3293 | ||||
|
|
3294 | DwindleNode *new_leaf = dwindle_new_node(); | |||
|
|
3295 | new_leaf->client = new_c; | |||
|
|
3296 | new_leaf->is_node = 0; | |||
|
|
3297 | ||||
|
|
3298 | DwindleNode *new_parent = dwindle_new_node(); | |||
|
|
3299 | new_parent->is_node = 1; | |||
|
|
3300 | new_parent->box = opening_on->box; | |||
|
|
3301 | new_parent->parent = opening_on->parent; | |||
|
|
3302 | ||||
|
|
3303 | int new_c_first = (edge == EDGE_LEFT || edge == EDGE_TOP); | |||
|
|
3304 | new_parent->split_top = (edge == EDGE_TOP || edge == EDGE_BOTTOM); | |||
|
|
3305 | new_parent->children[new_c_first ? 0 : 1] = new_leaf; | |||
|
|
3306 | new_parent->children[new_c_first ? 1 : 0] = opening_on; | |||
|
|
3307 | ||||
|
|
3308 | opening_on->parent = new_parent; | |||
|
|
3309 | new_leaf->parent = new_parent; | |||
|
|
3310 | ||||
|
|
3311 | if (new_parent->parent) { | |||
|
|
3312 | if (new_parent->parent->children[0] == opening_on) | |||
|
|
3313 | new_parent->parent->children[0] = new_parent; | |||
|
|
3314 | else | |||
|
|
3315 | new_parent->parent->children[1] = new_parent; | |||
|
|
3316 | } else { | |||
|
|
3317 | *root = new_parent; | |||
|
|
3318 | } | |||
|
|
3319 | return 1; | |||
|
|
3320 | } | |||
|
|
3321 | ||||
|
|
3322 | /* | |||
|
|
3323 | * Insert new_c into the tree, bisecting the focused client's node | |||
|
|
3324 | * It falls back to the first leaf if focused is NULL or not in the tree | |||
|
|
3325 | */ | |||
|
|
3326 | ||||
|
|
3327 | static void | |||
|
|
3328 | dwindle_insert(DwindleNode **root, Client *new_c, Client *focused, DwindleHint *hint) | |||
|
|
3329 | { | |||
|
|
3330 | DwindleNode *new_leaf = dwindle_new_node(); | |||
|
|
3331 | new_leaf->client = new_c; | |||
|
|
3332 | new_leaf->is_node = 0; | |||
|
|
3333 | ||||
|
|
3334 | if (!*root) { | |||
|
|
3335 | *root = new_leaf; | |||
|
|
3336 | return; | |||
|
|
3337 | } | |||
|
|
3338 | ||||
|
|
3339 | DwindleNode *opening_on = focused | |||
|
|
3340 | ? dwindle_find_leaf(*root, focused) : NULL; | |||
|
|
3341 | if (!opening_on) | |||
|
|
3342 | opening_on = dwindle_first_leaf(*root); | |||
|
|
3343 | ||||
|
|
3344 | if (hint) { | |||
|
|
3345 | int split_top = (opening_on->box.height > opening_on->box.width); | |||
|
|
3346 | hint->old_box = opening_on->box; | |||
|
|
3347 | hint->edge = split_top ? EDGE_BOTTOM : EDGE_RIGHT; | |||
|
|
3348 | } | |||
|
|
3349 | ||||
|
|
3350 | DwindleNode *new_parent = dwindle_new_node(); | |||
|
|
3351 | new_parent->is_node = 1; | |||
|
|
3352 | new_parent->box = opening_on->box; | |||
|
|
3353 | new_parent->parent = opening_on->parent; | |||
|
|
3354 | new_parent->split_top = (opening_on->box.height > opening_on->box.width); | |||
|
|
3355 | new_parent->children[0] = opening_on; | |||
|
|
3356 | new_parent->children[1] = new_leaf; | |||
|
|
3357 | ||||
|
|
3358 | opening_on->parent = new_parent; | |||
|
|
3359 | new_leaf->parent = new_parent; | |||
|
|
3360 | ||||
|
|
3361 | if (new_parent->parent) { | |||
|
|
3362 | if (new_parent->parent->children[0] == opening_on) | |||
|
|
3363 | new_parent->parent->children[0] = new_parent; | |||
|
|
3364 | else | |||
|
|
3365 | new_parent->parent->children[1] = new_parent; | |||
|
|
3366 | } else { | |||
|
|
3367 | *root = new_parent; | |||
|
|
3368 | } | |||
|
|
3369 | } | |||
|
|
3370 | ||||
|
|
3371 | /* | |||
|
|
3372 | * Remove client c from the tree, promoting its sibling upward. | |||
|
|
3373 | */ | |||
|
|
3374 | ||||
|
|
3375 | static void | |||
|
|
3376 | dwindle_remove(DwindleNode **root, Client *c, DwindleHint *hint) | |||
|
|
3377 | { | |||
|
|
3378 | DwindleNode *leaf = dwindle_find_leaf(*root, c); | |||
|
|
3379 | if (!leaf) | |||
|
|
3380 | return; | |||
|
|
3381 | ||||
|
|
3382 | DwindleNode *parent = leaf->parent; | |||
|
|
3383 | if (!parent) { | |||
|
|
3384 | free(leaf); | |||
|
|
3385 | *root = NULL; | |||
|
|
3386 | return; | |||
|
|
3387 | } | |||
|
|
3388 | ||||
|
|
3389 | int leaf_is_first = (parent->children[0] == leaf); | |||
|
|
3390 | DwindleNode *sibling = leaf_is_first | |||
|
|
3391 | ? parent->children[1] : parent->children[0]; | |||
|
|
3392 | DwindleNode *grandparent = parent->parent; | |||
|
|
3393 | ||||
|
|
3394 | if (hint) { | |||
|
|
3395 | hint->old_box = sibling->box; | |||
|
|
3396 | hint->edge = parent->split_top | |||
|
|
3397 | ? (leaf_is_first ? EDGE_TOP : EDGE_BOTTOM) | |||
|
|
3398 | : (leaf_is_first ? EDGE_LEFT : EDGE_RIGHT); | |||
|
|
3399 | } | |||
|
|
3400 | ||||
|
|
3401 | sibling->parent = grandparent; | |||
|
|
3402 | if (grandparent) { | |||
|
|
3403 | if (grandparent->children[0] == parent) | |||
|
|
3404 | grandparent->children[0] = sibling; | |||
|
|
3405 | else | |||
|
|
3406 | grandparent->children[1] = sibling; | |||
|
|
3407 | } else { | |||
|
|
3408 | *root = sibling; | |||
|
|
3409 | } | |||
|
|
3410 | ||||
|
|
3411 | free(leaf); | |||
|
|
3412 | free(parent); | |||
|
|
3413 | } | |||
|
|
3414 | ||||
|
|
3415 | /* Remove c from every monitor's per-tag tree and call from unmapnotify(). */ | |||
|
|
3416 | ||||
|
|
3417 | static void | |||
|
|
3418 | dwindle_remove_client(Client *c) | |||
|
|
3419 | { | |||
|
|
3420 | Monitor *m; | |||
|
|
3421 | wl_list_for_each(m, &mons, link) | |||
|
|
3422 | for (int i = 0; i < TAGCOUNT; i++) | |||
|
|
3423 | dwindle_remove(&m->dwindle_root[i], c, NULL); | |||
|
|
3424 | } | |||
|
|
3425 | ||||
|
|
3426 | #define PRESELECT_MARGIN 0.2 | |||
|
|
3427 | ||||
|
|
3428 | static DwindleEdge | |||
|
|
3429 | edge_at_point(struct wlr_box box, double x, double y) | |||
|
|
3430 | { | |||
|
|
3431 | double rel_x = (x - box.x) / MAX(1, box.width); | |||
|
|
3432 | double rel_y = (y - box.y) / MAX(1, box.height); | |||
|
|
3433 | ||||
|
|
3434 | /* Distance from each edge, in the same normalized 0..1 space. */ | |||
|
|
3435 | double d_left = rel_x; | |||
|
|
3436 | double d_right = 1.0 - rel_x; | |||
|
|
3437 | double d_top = rel_y; | |||
|
|
3438 | double d_bottom = 1.0 - rel_y; | |||
|
|
3439 | ||||
|
|
3440 | double closest = MIN(MIN(d_left, d_right), MIN(d_top, d_bottom)); | |||
|
|
3441 | if (closest > PRESELECT_MARGIN) | |||
|
|
3442 | return EDGE_NONE; | |||
|
|
3443 | ||||
|
|
3444 | if (closest == d_left) return EDGE_LEFT; | |||
|
|
3445 | if (closest == d_right) return EDGE_RIGHT; | |||
|
|
3446 | if (closest == d_top) return EDGE_TOP; | |||
|
|
3447 | return EDGE_BOTTOM; | |||
|
|
3448 | } | |||
|
|
3449 | ||||
|
|
3450 | static struct wlr_box | |||
|
|
3451 | preselect_indicator_box(struct wlr_box target, DwindleEdge edge) | |||
|
|
3452 | { | |||
|
|
3453 | struct wlr_box r = target; | |||
|
|
3454 | switch (edge) { | |||
|
|
3455 | case EDGE_LEFT: r.width /= 2; break; | |||
|
|
3456 | case EDGE_RIGHT: r.width /= 2; r.x += target.width - r.width; break; | |||
|
|
3457 | case EDGE_TOP: r.height /= 2; break; | |||
|
|
3458 | case EDGE_BOTTOM: r.height /= 2; r.y += target.height - r.height; break; | |||
|
|
3459 | default: break; | |||
|
|
3460 | } | |||
|
|
3461 | return r; | |||
|
|
3462 | } | |||
|
|
3463 | ||||
|
|
3464 | /* layout entry point */ | |||
|
|
3465 | ||||
|
|
3466 | void | |||
|
|
3467 | dwindle(Monitor *m) | |||
|
|
3468 | { | |||
|
|
3469 | Client *c; | |||
|
|
3470 | int n = 0, e; | |||
|
|
3471 | Client *dragging = (cursor_mode == CurTileDrag) ? grabc : NULL; | |||
|
|
3472 | ||||
|
|
3473 | wl_list_for_each(c, &clients, link) | |||
|
|
3474 | if (c != dragging && VISIBLEON(c, m) && !c->isfloating && !c->isfullscreen) | |||
|
|
3475 | n++; | |||
|
|
3476 | ||||
|
|
3477 | if (n == 0) | |||
|
|
3478 | return; | |||
|
|
3479 | ||||
|
|
3480 | e = (m->gaps && !(cfg.appearance.smart_gaps && n == 1)) ? (int)cfg.appearance.gaps : 0; | |||
|
|
3481 | ||||
|
|
3482 | int ti = current_tag_idx(m); | |||
|
|
3483 | DwindleNode **root = &m->dwindle_root[ti]; | |||
|
|
3484 | ||||
|
|
3485 | /* prune leaves whose clients are no longer tiled here */ | |||
|
|
3486 | { | |||
|
|
3487 | DwindleNode *stack[512]; | |||
|
|
3488 | Client *stale[512]; | |||
|
|
3489 | int sp = 0, sc = 0; | |||
|
|
3490 | ||||
|
|
3491 | if (*root) | |||
|
|
3492 | stack[sp++] = *root; | |||
|
|
3493 | ||||
|
|
3494 | while (sp > 0) { | |||
|
|
3495 | DwindleNode *nd = stack[--sp]; | |||
|
|
3496 | if (!nd->is_node) { | |||
|
|
3497 | int found = 0; | |||
|
|
3498 | if (nd->client == dragging) { | |||
|
|
3499 | /* Treat the dragged client as "still present" so it | |||
|
|
3500 | * isn't pruned out from under itself mid-gesture. */ | |||
|
|
3501 | found = 1; | |||
|
|
3502 | } else { | |||
|
|
3503 | wl_list_for_each(c, &clients, link) { | |||
|
|
3504 | if (c == nd->client | |||
|
|
3505 | && VISIBLEON(c, m) | |||
|
|
3506 | && !c->isfloating) { | |||
|
|
3507 | found = 1; | |||
|
|
3508 | break; | |||
|
|
3509 | } | |||
|
|
3510 | } | |||
|
|
3511 | } | |||
|
|
3512 | if (!found && sc < 512) | |||
|
|
3513 | stale[sc++] = nd->client; | |||
|
|
3514 | } else { | |||
|
|
3515 | if (nd->children[1]) stack[sp++] = nd->children[1]; | |||
|
|
3516 | if (nd->children[0]) stack[sp++] = nd->children[0]; | |||
|
|
3517 | } | |||
|
|
3518 | } | |||
|
|
3519 | ||||
|
|
3520 | for (int i = 0; i < sc; i++) | |||
|
|
3521 | dwindle_remove(root, stale[i], NULL); /* TODO: wire to animation (soon tm) */ | |||
|
|
3522 | } | |||
|
|
3523 | ||||
|
|
3524 | /* insert any newly visible client, splitting the focused node. | |||
|
|
3525 | * Use m->dwindle_focus[ti] so it actually splits what the user | |||
|
|
3526 | * was looking at when they spawned the window, rather than whatever | |||
|
|
3527 | * focustop() happens to return */ | |||
|
|
3528 | ||||
|
|
3529 | Client *focused = m->dwindle_focus[ti]; | |||
|
|
3530 | ||||
|
|
3531 | wl_list_for_each(c, &clients, link) { | |||
|
|
3532 | if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen || c == dragging) | |||
|
|
3533 | continue; | |||
|
|
3534 | if (!dwindle_find_leaf(*root, c)) { | |||
|
|
3535 | dwindle_insert(root, c, focused, NULL); /* TODO: wire to animation once that lands */ | |||
|
|
3536 | focused = c; | |||
|
|
3537 | } | |||
|
|
3538 | } | |||
|
|
3539 | ||||
|
|
3540 | /* assign root box and recurse */ | |||
|
|
3541 | if (*root) { | |||
|
|
3542 | (*root)->box = (struct wlr_box){ | |||
|
|
3543 | m->w.x + e, | |||
|
|
3544 | m->w.y + e, | |||
|
|
3545 | MAX(1, m->w.width - 2 * e), | |||
|
|
3546 | MAX(1, m->w.height - 2 * e), | |||
|
|
3547 | }; | |||
|
|
3548 | dwindle_recalc(*root, e); | |||
|
|
3549 | } | |||
|
|
3550 | } | |||
|
|
3551 | ||||
|
|
3552 | void | |||
|
|
3553 | togglefloating(const Arg *arg) | |||
|
|
3554 | { | |||
|
|
3555 | Client *sel = focustop(selmon); | |||
|
|
3556 | /* return if fullscreen */ | |||
|
|
3557 | if (sel && !sel->isfullscreen) | |||
|
|
3558 | setfloating(sel, !sel->isfloating); | |||
|
|
3559 | } | |||
|
|
3560 | ||||
|
|
3561 | void | |||
|
|
3562 | togglefullscreen(const Arg *arg) | |||
|
|
3563 | { | |||
|
|
3564 | Client *sel = focustop(selmon); | |||
|
|
3565 | if (sel) | |||
|
|
3566 | setfullscreen(sel, !sel->isfullscreen); | |||
|
|
3567 | } | |||
|
|
3568 | ||||
|
|
3569 | void | |||
|
|
3570 | togglegaps(const Arg *arg) | |||
|
|
3571 | { | |||
|
|
3572 | selmon->gaps = !selmon->gaps; | |||
|
|
3573 | arrange(selmon); | |||
|
|
3574 | } | |||
|
|
3575 | ||||
|
|
3576 | /* swapdir is essentially focusdir but swap */ | |||
|
|
3577 | ||||
|
|
3578 | void | |||
|
|
3579 | swapdir(const Arg *arg) | |||
|
|
3580 | { | |||
|
|
3581 | Client *c, *other = NULL; | |||
|
|
3582 | Client *sel = focustop(selmon); | |||
|
|
3583 | int sx, sy, cx, cy; | |||
|
|
3584 | double bestdist = 1e18, dist; | |||
|
|
3585 | ||||
|
|
3586 | if (!sel || sel->isfloating || sel->isfullscreen) | |||
|
|
3587 | return; | |||
|
|
3588 | ||||
|
|
3589 | client_center(sel, &sx, &sy); | |||
|
|
3590 | ||||
|
|
3591 | wl_list_for_each(c, &clients, link) { | |||
|
|
3592 | if (c == sel || !VISIBLEON(c, selmon) || c->isfloating || c->isfullscreen) | |||
|
|
3593 | continue; | |||
|
|
3594 | ||||
|
|
3595 | client_center(c, &cx, &cy); | |||
|
|
3596 | ||||
|
|
3597 | switch (arg->i) { | |||
|
|
3598 | case WLR_DIRECTION_LEFT: if (cx >= sx) continue; break; | |||
|
|
3599 | case WLR_DIRECTION_RIGHT: if (cx <= sx) continue; break; | |||
|
|
3600 | case WLR_DIRECTION_UP: if (cy >= sy) continue; break; | |||
|
|
3601 | case WLR_DIRECTION_DOWN: if (cy <= sy) continue; break; | |||
|
|
3602 | default: return; | |||
|
|
3603 | } | |||
|
|
3604 | ||||
|
|
3605 | dist = (double)(cx - sx) * (cx - sx) + (double)(cy - sy) * (cy - sy); | |||
|
|
3606 | if (dist < bestdist) { | |||
|
|
3607 | bestdist = dist; | |||
|
|
3608 | other = c; | |||
|
|
3609 | } | |||
|
|
3610 | } | |||
|
|
3611 | ||||
|
|
3612 | if (!other) | |||
|
|
3613 | return; | |||
|
|
3614 | ||||
|
|
3615 | if (selmon->lt[selmon->sellt]->arrange == dwindle) { | |||
|
|
3616 | int ti = current_tag_idx(selmon); | |||
|
|
3617 | DwindleNode **root = &selmon->dwindle_root[ti]; | |||
|
|
3618 | if (*root) { | |||
|
|
3619 | DwindleNode *leaf_sel = dwindle_find_leaf(*root, sel); | |||
|
|
3620 | DwindleNode *leaf_other = dwindle_find_leaf(*root, other); | |||
|
|
3621 | if (leaf_sel && leaf_other) { | |||
|
|
3622 | leaf_sel->client = other; | |||
|
|
3623 | leaf_other->client = sel; | |||
|
|
3624 | int e = (selmon->gaps && cfg.appearance.gaps) ? (int)cfg.appearance.gaps : 0; | |||
|
|
3625 | (*root)->box = (struct wlr_box){ | |||
|
|
3626 | selmon->w.x + e, | |||
|
|
3627 | selmon->w.y + e, | |||
|
|
3628 | MAX(1, selmon->w.width - 2 * e), | |||
|
|
3629 | MAX(1, selmon->w.height - 2 * e), | |||
|
|
3630 | }; | |||
|
|
3631 | dwindle_recalc(*root, e); | |||
|
|
3632 | } | |||
|
|
3633 | } | |||
|
|
3634 | } else { | |||
|
|
3635 | struct wlr_box tmp = sel->geom; | |||
|
|
3636 | resize(sel, other->geom, 0); | |||
|
|
3637 | resize(other, tmp, 0); | |||
|
|
3638 | } | |||
|
|
3639 | ||||
|
|
3640 | focusclient(sel, 1); | |||
|
|
3641 | printstatus(); | |||
|
|
3642 | } | |||
|
|
3643 | ||||
|
|
3644 | void | |||
|
|
3645 | swaptiled(Client *a, Client *b) | |||
|
|
3646 | { | |||
|
|
3647 | Monitor *m; | |||
|
|
3648 | if (!a || !b || a == b) | |||
|
|
3649 | return; | |||
|
|
3650 | ||||
|
|
3651 | wl_list_for_each(m, &mons, link) { | |||
|
|
3652 | for (int i = 0; i < TAGCOUNT; i++) { | |||
|
|
3653 | DwindleNode **root = &m->dwindle_root[i]; | |||
|
|
3654 | if (!*root) | |||
|
|
3655 | continue; | |||
|
|
3656 | DwindleNode *la = dwindle_find_leaf(*root, a); | |||
|
|
3657 | DwindleNode *lb = dwindle_find_leaf(*root, b); | |||
|
|
3658 | if (!la || !lb) | |||
|
|
3659 | continue; | |||
|
|
3660 | la->client = b; | |||
|
|
3661 | lb->client = a; | |||
|
|
3662 | int e = (m->gaps && cfg.appearance.gaps) ? (int)cfg.appearance.gaps : 0; | |||
|
|
3663 | (*root)->box = (struct wlr_box){ | |||
|
|
3664 | m->w.x + e, | |||
|
|
3665 | m->w.y + e, | |||
|
|
3666 | MAX(1, m->w.width - 2 * e), | |||
|
|
3667 | MAX(1, m->w.height - 2 * e), | |||
|
|
3668 | }; | |||
|
|
3669 | dwindle_recalc(*root, e); | |||
|
|
3670 | } | |||
|
|
3671 | } | |||
|
|
3672 | ||||
|
|
3673 | focusclient(a, 1); | |||
|
|
3674 | printstatus(); | |||
|
|
3675 | } | |||
|
|
3676 | ||||
|
|
3677 | void | |||
|
|
3678 | dwindle_move_to_edge(Client *a, Client *b, DwindleEdge edge) | |||
|
|
3679 | { | |||
|
|
3680 | if (!a || !b || a == b || edge == EDGE_NONE) | |||
|
|
3681 | return; | |||
|
|
3682 | ||||
|
|
3683 | DwindleNode **root = &b->mon->dwindle_root[current_tag_idx(b->mon)]; | |||
|
|
3684 | if (!dwindle_find_leaf(*root, a) || !dwindle_find_leaf(*root, b)) | |||
|
|
3685 | return; | |||
|
|
3686 | ||||
|
|
3687 | dwindle_remove(root, a, NULL); | |||
|
|
3688 | if (!dwindle_insert_at_edge(root, a, b, edge)) | |||
|
|
3689 | return; /* shouldn't happen (b was just confirmed present above) */ | |||
|
|
3690 | ||||
|
|
3691 | int e = (b->mon->gaps && cfg.appearance.gaps) ? (int)cfg.appearance.gaps : 0; | |||
|
|
3692 | (*root)->box = (struct wlr_box){ | |||
|
|
3693 | b->mon->w.x + e, | |||
|
|
3694 | b->mon->w.y + e, | |||
|
|
3695 | MAX(1, b->mon->w.width - 2 * e), | |||
|
|
3696 | MAX(1, b->mon->w.height - 2 * e), | |||
|
|
3697 | }; | |||
|
|
3698 | dwindle_recalc(*root, e); | |||
|
|
3699 | ||||
|
|
3700 | focusclient(a, 1); | |||
|
|
3701 | printstatus(); | |||
|
|
3702 | } | |||
|
|
3703 | ||||
|
|
3704 | void | |||
|
|
3705 | toggletag(const Arg *arg) | |||
|
|
3706 | { | |||
|
|
3707 | uint32_t newtags; | |||
|
|
3708 | Client *sel = focustop(selmon); | |||
|
|
3709 | if (!sel || !(newtags = sel->tags ^ (arg->ui & TAGMASK))) | |||
|
|
3710 | return; | |||
|
|
3711 | ||||
|
|
3712 | dwindle_remove_client(sel); | |||
|
|
3713 | sel->tags = newtags; | |||
|
|
3714 | focusclient(focustop(selmon), 1); | |||
|
|
3715 | arrange(selmon); | |||
|
|
3716 | printstatus(); | |||
|
|
3717 | } | |||
|
|
3718 | ||||
|
|
3719 | void | |||
|
|
3720 | toggleview(const Arg *arg) | |||
|
|
3721 | { | |||
|
|
3722 | uint32_t newtagset; | |||
|
|
3723 | if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0)) | |||
|
|
3724 | return; | |||
|
|
3725 | ||||
|
|
3726 | selmon->tagset[selmon->seltags] = newtagset; | |||
|
|
3727 | focusclient(focustop(selmon), 1); | |||
|
|
3728 | arrange(selmon); | |||
|
|
3729 | printstatus(); | |||
|
|
3730 | } | |||
|
|
3731 | ||||
|
|
3732 | void | |||
|
|
3733 | unlocksession(struct wl_listener *listener, void *data) | |||
|
|
3734 | { | |||
|
|
3735 | SessionLock *lock = wl_container_of(listener, lock, unlock); | |||
|
|
3736 | destroylock(lock, 1); | |||
|
|
3737 | } | |||
|
|
3738 | ||||
|
|
3739 | void | |||
|
|
3740 | unmaplayersurfacenotify(struct wl_listener *listener, void *data) | |||
|
|
3741 | { | |||
|
|
3742 | LayerSurface *l = wl_container_of(listener, l, unmap); | |||
|
|
3743 | ||||
|
|
3744 | l->mapped = 0; | |||
|
|
3745 | wlr_scene_node_set_enabled(&l->scene->node, 0); | |||
|
|
3746 | if (l == exclusive_focus) | |||
|
|
3747 | exclusive_focus = NULL; | |||
|
|
3748 | if (l->layer_surface->output && (l->mon = l->layer_surface->output->data)) | |||
|
|
3749 | arrangelayers(l->mon); | |||
|
|
3750 | if (l->layer_surface->surface == seat->keyboard_state.focused_surface) | |||
|
|
3751 | focusclient(focustop(selmon), 1); | |||
|
|
3752 | motionnotify(0, NULL, 0, 0, 0, 0); | |||
|
|
3753 | } | |||
|
|
3754 | ||||
|
|
3755 | void | |||
|
|
3756 | unmapnotify(struct wl_listener *listener, void *data) | |||
|
|
3757 | { | |||
|
|
3758 | /* Called when the surface is unmapped, and should no longer be shown. */ | |||
|
|
3759 | Client *c = wl_container_of(listener, c, unmap); | |||
|
|
3760 | if (c == grabc) { | |||
|
|
3761 | cursor_mode = CurNormal; | |||
|
|
3762 | grabc = NULL; | |||
|
|
3763 | } | |||
|
|
3764 | ||||
|
|
3765 | if (client_is_unmanaged(c)) { | |||
|
|
3766 | if (c == exclusive_focus) { | |||
|
|
3767 | exclusive_focus = NULL; | |||
|
|
3768 | focusclient(focustop(selmon), 1); | |||
|
|
3769 | } | |||
|
|
3770 | } else { | |||
|
|
3771 | wl_list_remove(&c->link); | |||
|
|
3772 | dwindle_remove_client(c); | |||
|
|
3773 | setmon(c, NULL, 0); | |||
|
|
3774 | wl_list_remove(&c->flink); | |||
|
|
3775 | } | |||
|
|
3776 | ||||
|
|
3777 | if (c->foreign_toplevel) { | |||
|
|
3778 | wlr_foreign_toplevel_handle_v1_destroy(c->foreign_toplevel); | |||
|
|
3779 | c->foreign_toplevel = NULL; | |||
|
|
3780 | } | |||
|
|
3781 | ||||
|
|
3782 | wlr_scene_node_destroy(&c->scene->node); | |||
|
|
3783 | printstatus(); | |||
|
|
3784 | motionnotify(0, NULL, 0, 0, 0, 0); | |||
|
|
3785 | } | |||
|
|
3786 | ||||
|
|
3787 | void | |||
|
|
3788 | updatemons(struct wl_listener *listener, void *data) | |||
|
|
3789 | { | |||
|
|
3790 | /* | |||
|
|
3791 | * Called whenever the output layout changes: adding or removing a | |||
|
|
3792 | * monitor, changing an output's mode or position, etc. This is where | |||
|
|
3793 | * the change officially happens and we update geometry, window | |||
|
|
3794 | * positions, focus, and the stored configuration in wlroots' | |||
|
|
3795 | * output-manager implementation. | |||
|
|
3796 | */ | |||
|
|
3797 | struct wlr_output_configuration_v1 *config | |||
|
|
3798 | = wlr_output_configuration_v1_create(); | |||
|
|
3799 | Client *c; | |||
|
|
3800 | struct wlr_output_configuration_head_v1 *config_head; | |||
|
|
3801 | Monitor *m; | |||
|
|
3802 | ||||
|
|
3803 | /* First remove from the layout the disabled monitors */ | |||
|
|
3804 | wl_list_for_each(m, &mons, link) { | |||
|
|
3805 | if (m->wlr_output->enabled || m->asleep) | |||
|
|
3806 | continue; | |||
|
|
3807 | config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); | |||
|
|
3808 | config_head->state.enabled = 0; | |||
|
|
3809 | /* Remove this output from the layout to avoid cursor enter inside it */ | |||
|
|
3810 | wlr_output_layout_remove(output_layout, m->wlr_output); | |||
|
|
3811 | closemon(m); | |||
|
|
3812 | m->m = m->w = (struct wlr_box){0}; | |||
|
|
3813 | } | |||
|
|
3814 | /* Insert outputs that need to */ | |||
|
|
3815 | wl_list_for_each(m, &mons, link) { | |||
|
|
3816 | if (m->wlr_output->enabled | |||
|
|
3817 | && !wlr_output_layout_get(output_layout, m->wlr_output)) | |||
|
|
3818 | wlr_output_layout_add_auto(output_layout, m->wlr_output); | |||
|
|
3819 | } | |||
|
|
3820 | ||||
|
|
3821 | /* Now that we update the output layout we can get its box */ | |||
|
|
3822 | wlr_output_layout_get_box(output_layout, NULL, &sgeom); | |||
|
|
3823 | ||||
|
|
3824 | wlr_scene_node_set_position(&root_bg->node, sgeom.x, sgeom.y); | |||
|
|
3825 | wlr_scene_rect_set_size(root_bg, sgeom.width, sgeom.height); | |||
|
|
3826 | ||||
|
|
3827 | /* Make sure the clients are hidden when dwl is locked */ | |||
|
|
3828 | wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y); | |||
|
|
3829 | wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); | |||
|
|
3830 | ||||
|
|
3831 | wl_list_for_each(m, &mons, link) { | |||
|
|
3832 | if (!m->wlr_output->enabled) | |||
|
|
3833 | continue; | |||
|
|
3834 | config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); | |||
|
|
3835 | ||||
|
|
3836 | /* Get the effective monitor geometry to use for surfaces */ | |||
|
|
3837 | wlr_output_layout_get_box(output_layout, m->wlr_output, &m->m); | |||
|
|
3838 | m->w = m->m; | |||
|
|
3839 | wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); | |||
|
|
3840 | ||||
|
|
3841 | wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y); | |||
|
|
3842 | wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height); | |||
|
|
3843 | ||||
|
|
3844 | if (m->lock_surface) { | |||
|
|
3845 | struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data; | |||
|
|
3846 | wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); | |||
|
|
3847 | wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, m->m.height); | |||
|
|
3848 | } | |||
|
|
3849 | ||||
|
|
3850 | /* Calculate the effective monitor geometry to use for clients */ | |||
|
|
3851 | arrangelayers(m); | |||
|
|
3852 | /* Don't move clients to the left output when plugging monitors */ | |||
|
|
3853 | arrange(m); | |||
|
|
3854 | /* make sure fullscreen clients have the right size */ | |||
|
|
3855 | if ((c = focustop(m)) && c->isfullscreen) | |||
|
|
3856 | resize(c, m->m, 0); | |||
|
|
3857 | ||||
|
|
3858 | /* Try to re-set the gamma LUT when updating monitors, | |||
|
|
3859 | * it's only really needed when enabling a disabled output, but meh. */ | |||
|
|
3860 | m->gamma_lut_changed = 1; | |||
|
|
3861 | ||||
|
|
3862 | config_head->state.x = m->m.x; | |||
|
|
3863 | config_head->state.y = m->m.y; | |||
|
|
3864 | ||||
|
|
3865 | if (!selmon) { | |||
|
|
3866 | selmon = m; | |||
|
|
3867 | } | |||
|
|
3868 | } | |||
|
|
3869 | ||||
|
|
3870 | if (selmon && selmon->wlr_output->enabled) { | |||
|
|
3871 | wl_list_for_each(c, &clients, link) { | |||
|
|
3872 | if (!c->mon && client_surface(c)->mapped) | |||
|
|
3873 | setmon(c, selmon, c->tags); | |||
|
|
3874 | } | |||
|
|
3875 | focusclient(focustop(selmon), 1); | |||
|
|
3876 | if (selmon->lock_surface) { | |||
|
|
3877 | client_notify_enter(selmon->lock_surface->surface, | |||
|
|
3878 | wlr_seat_get_keyboard(seat)); | |||
|
|
3879 | client_activate_surface(selmon->lock_surface->surface, 1); | |||
|
|
3880 | } | |||
|
|
3881 | } | |||
|
|
3882 | ||||
|
|
3883 | wlr_cursor_move(cursor, NULL, 0, 0); | |||
|
|
3884 | ||||
|
|
3885 | wlr_output_manager_v1_set_configuration(output_mgr, config); | |||
|
|
3886 | } | |||
|
|
3887 | ||||
|
|
3888 | void | |||
|
|
3889 | updatetitle(struct wl_listener *listener, void *data) | |||
|
|
3890 | { | |||
|
|
3891 | Client *c = wl_container_of(listener, c, set_title); | |||
|
|
3892 | if (c->foreign_toplevel) | |||
|
|
3893 | wlr_foreign_toplevel_handle_v1_set_title(c->foreign_toplevel, client_get_title(c)); | |||
|
|
3894 | if (c == focustop(c->mon)) | |||
|
|
3895 | printstatus(); | |||
|
|
3896 | } | |||
|
|
3897 | ||||
|
|
3898 | void | |||
|
|
3899 | urgent(struct wl_listener *listener, void *data) | |||
|
|
3900 | { | |||
|
|
3901 | struct wlr_xdg_activation_v1_request_activate_event *event = data; | |||
|
|
3902 | Client *c = NULL; | |||
|
|
3903 | toplevel_from_wlr_surface(event->surface, &c, NULL); | |||
|
|
3904 | if (!c || c == focustop(selmon)) | |||
|
|
3905 | return; | |||
|
|
3906 | ||||
|
|
3907 | c->isurgent = 1; | |||
|
|
3908 | printstatus(); | |||
|
|
3909 | ||||
|
|
3910 | if (client_surface(c)->mapped) | |||
|
|
3911 | client_set_border_color(c, cfg.appearance.inner_urgent_color, cfg.appearance.outer_urgent_color); | |||
|
|
3912 | } | |||
|
|
3913 | ||||
|
|
3914 | void | |||
|
|
3915 | view(const Arg *arg) | |||
|
|
3916 | { | |||
|
|
3917 | if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) | |||
|
|
3918 | return; | |||
|
|
3919 | selmon->seltags ^= 1; /* toggle sel tagset */ | |||
|
|
3920 | if (arg->ui & TAGMASK) | |||
|
|
3921 | selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; | |||
|
|
3922 | focusclient(focustop(selmon), 1); | |||
|
|
3923 | arrange(selmon); | |||
|
|
3924 | printstatus(); | |||
|
|
3925 | } | |||
|
|
3926 | ||||
|
|
3927 | void | |||
|
|
3928 | virtualkeyboard(struct wl_listener *listener, void *data) | |||
|
|
3929 | { | |||
|
|
3930 | struct wlr_virtual_keyboard_v1 *kb = data; | |||
|
|
3931 | /* virtual keyboards shouldn't share keyboard group */ | |||
|
|
3932 | KeyboardGroup *group = createkeyboardgroup(); | |||
|
|
3933 | /* Set the keymap to match the group keymap */ | |||
|
|
3934 | wlr_keyboard_set_keymap(&kb->keyboard, group->wlr_group->keyboard.keymap); | |||
|
|
3935 | LISTEN(&kb->keyboard.base.events.destroy, &group->destroy, destroykeyboardgroup); | |||
|
|
3936 | ||||
|
|
3937 | /* Add the new keyboard to the group */ | |||
|
|
3938 | wlr_keyboard_group_add_keyboard(group->wlr_group, &kb->keyboard); | |||
|
|
3939 | } | |||
|
|
3940 | ||||
|
|
3941 | void | |||
|
|
3942 | virtualpointer(struct wl_listener *listener, void *data) | |||
|
|
3943 | { | |||
|
|
3944 | struct wlr_virtual_pointer_v1_new_pointer_event *event = data; | |||
|
|
3945 | struct wlr_input_device *device = &event->new_pointer->pointer.base; | |||
|
|
3946 | ||||
|
|
3947 | wlr_cursor_attach_input_device(cursor, device); | |||
|
|
3948 | if (event->suggested_output) | |||
|
|
3949 | wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); | |||
|
|
3950 | } | |||
|
|
3951 | ||||
|
|
3952 | Monitor * | |||
|
|
3953 | xytomon(double x, double y) | |||
|
|
3954 | { | |||
|
|
3955 | struct wlr_output *o = wlr_output_layout_output_at(output_layout, x, y); | |||
|
|
3956 | return o ? o->data : NULL; | |||
|
|
3957 | } | |||
|
|
3958 | ||||
|
|
3959 | static Client * | |||
|
|
3960 | tiled_client_at(double x, double y, Client *exclude) | |||
|
|
3961 | { | |||
|
|
3962 | Client *c; | |||
|
|
3963 | wl_list_for_each(c, &clients, link) { | |||
|
|
3964 | if (c == exclude || c->isfloating || c->isfullscreen) | |||
|
|
3965 | continue; | |||
|
|
3966 | if (!c->mon || !VISIBLEON(c, c->mon)) | |||
|
|
3967 | continue; | |||
|
|
3968 | if (c->mon->lt[c->mon->sellt]->arrange != dwindle) | |||
|
|
3969 | continue; | |||
|
|
3970 | if (x >= c->geom.x && x < c->geom.x + c->geom.width | |||
|
|
3971 | && y >= c->geom.y && y < c->geom.y + c->geom.height) | |||
|
|
3972 | return c; | |||
|
|
3973 | } | |||
|
|
3974 | return NULL; | |||
|
|
3975 | } | |||
|
|
3976 | ||||
|
|
3977 | void | |||
|
|
3978 | xytonode(double x, double y, struct wlr_surface **psurface, | |||
|
|
3979 | Client **pc, LayerSurface **pl, double *nx, double *ny) | |||
|
|
3980 | { | |||
|
|
3981 | struct wlr_scene_node *node, *pnode; | |||
|
|
3982 | struct wlr_surface *surface = NULL; | |||
|
|
3983 | Client *c = NULL; | |||
|
|
3984 | LayerSurface *l = NULL; | |||
|
|
3985 | int layer; | |||
|
|
3986 | ||||
|
|
3987 | for (layer = NUM_LAYERS - 1; !surface && layer >= 0; layer--) { | |||
|
|
3988 | if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) | |||
|
|
3989 | continue; | |||
|
|
3990 | ||||
|
|
3991 | if (node->type == WLR_SCENE_NODE_BUFFER) | |||
|
|
3992 | surface = wlr_scene_surface_try_from_buffer( | |||
|
|
3993 | wlr_scene_buffer_from_node(node))->surface; | |||
|
|
3994 | /* Walk the tree to find a node that knows the client */ | |||
|
|
3995 | for (pnode = node; pnode && !c; pnode = &pnode->parent->node) | |||
|
|
3996 | c = pnode->data; | |||
|
|
3997 | if (c && c->type == LayerShell) { | |||
|
|
3998 | c = NULL; | |||
|
|
3999 | l = pnode->data; | |||
|
|
4000 | } | |||
|
|
4001 | } | |||
|
|
4002 | ||||
|
|
4003 | if (psurface) *psurface = surface; | |||
|
|
4004 | if (pc) *pc = c; | |||
|
|
4005 | if (pl) *pl = l; | |||
|
|
4006 | } | |||
|
|
4007 | ||||
|
|
4008 | ||||
|
|
4009 | void | |||
|
|
4010 | createforeigntoplevel(Client *c) | |||
|
|
4011 | { | |||
|
|
4012 | c->foreign_toplevel = wlr_foreign_toplevel_handle_v1_create(foreign_toplevel_mgr); | |||
|
|
4013 | ||||
|
|
4014 | LISTEN(&c->foreign_toplevel->events.request_activate, &c->factivate, factivatenotify); | |||
|
|
4015 | LISTEN(&c->foreign_toplevel->events.request_close, &c->fclose, fclosenotify); | |||
|
|
4016 | LISTEN(&c->foreign_toplevel->events.request_fullscreen, &c->ffullscreen, ffullscreennotify); | |||
|
|
4017 | LISTEN(&c->foreign_toplevel->events.destroy, &c->fdestroy, fdestroynotify); | |||
|
|
4018 | } | |||
|
|
4019 | ||||
|
|
4020 | void | |||
|
|
4021 | factivatenotify(struct wl_listener *listener, void *data) | |||
|
|
4022 | { | |||
|
|
4023 | Client *c = wl_container_of(listener, c, factivate); | |||
|
|
4024 | if (c->mon == selmon) | |||
|
|
4025 | c->tags = c->mon->tagset[c->mon->seltags]; | |||
|
|
4026 | else | |||
|
|
4027 | setmon(c, selmon, 0); | |||
|
|
4028 | focusclient(c, 1); | |||
|
|
4029 | arrange(c->mon); | |||
|
|
4030 | } | |||
|
|
4031 | ||||
|
|
4032 | void | |||
|
|
4033 | fclosenotify(struct wl_listener *listener, void *data) | |||
|
|
4034 | { | |||
|
|
4035 | Client *c = wl_container_of(listener, c, fclose); | |||
|
|
4036 | client_send_close(c); | |||
|
|
4037 | } | |||
|
|
4038 | ||||
|
|
4039 | void | |||
|
|
4040 | ffullscreennotify(struct wl_listener *listener, void *data) | |||
|
|
4041 | { | |||
|
|
4042 | Client *c = wl_container_of(listener, c, ffullscreen); | |||
|
|
4043 | struct wlr_foreign_toplevel_handle_v1_fullscreen_event *event = data; | |||
|
|
4044 | setfullscreen(c, event->fullscreen); | |||
|
|
4045 | } | |||
|
|
4046 | ||||
|
|
4047 | void | |||
|
|
4048 | fdestroynotify(struct wl_listener *listener, void *data) | |||
|
|
4049 | { | |||
|
|
4050 | Client *c = wl_container_of(listener, c, fdestroy); | |||
|
|
4051 | wl_list_remove(&c->factivate.link); | |||
|
|
4052 | wl_list_remove(&c->fclose.link); | |||
|
|
4053 | wl_list_remove(&c->ffullscreen.link); | |||
|
|
4054 | wl_list_remove(&c->fdestroy.link); | |||
|
|
4055 | } | |||
|
|
4056 | ||||
|
|
4057 | #ifdef XWAYLAND | |||
|
|
4058 | void | |||
|
|
4059 | activatex11(struct wl_listener *listener, void *data) | |||
|
|
4060 | { | |||
|
|
4061 | Client *c = wl_container_of(listener, c, activate); | |||
|
|
4062 | ||||
|
|
4063 | /* Only "managed" windows can be activated */ | |||
|
|
4064 | if (!client_is_unmanaged(c)) | |||
|
|
4065 | wlr_xwayland_surface_activate(c->surface.xwayland, 1); | |||
|
|
4066 | } | |||
|
|
4067 | ||||
|
|
4068 | void | |||
|
|
4069 | associatex11(struct wl_listener *listener, void *data) | |||
|
|
4070 | { | |||
|
|
4071 | Client *c = wl_container_of(listener, c, associate); | |||
|
|
4072 | ||||
|
|
4073 | LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); | |||
|
|
4074 | LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); | |||
|
|
4075 | } | |||
|
|
4076 | ||||
|
|
4077 | void | |||
|
|
4078 | configurex11(struct wl_listener *listener, void *data) | |||
|
|
4079 | { | |||
|
|
4080 | Client *c = wl_container_of(listener, c, configure); | |||
|
|
4081 | struct wlr_xwayland_surface_configure_event *event = data; | |||
|
|
4082 | if (!client_surface(c) || !client_surface(c)->mapped) { | |||
|
|
4083 | wlr_xwayland_surface_configure(c->surface.xwayland, | |||
|
|
4084 | event->x, event->y, event->width, event->height); | |||
|
|
4085 | return; | |||
|
|
4086 | } | |||
|
|
4087 | if (client_is_unmanaged(c)) { | |||
|
|
4088 | wlr_scene_node_set_position(&c->scene->node, event->x, event->y); | |||
|
|
4089 | wlr_xwayland_surface_configure(c->surface.xwayland, | |||
|
|
4090 | event->x, event->y, event->width, event->height); | |||
|
|
4091 | return; | |||
|
|
4092 | } | |||
|
|
4093 | if ((c->isfloating && c != grabc) || !c->mon->lt[c->mon->sellt]->arrange) { | |||
|
|
4094 | resize(c, (struct wlr_box){.x = event->x - c->bw, | |||
|
|
4095 | .y = event->y - c->bw, .width = event->width + c->bw * 2, | |||
|
|
4096 | .height = event->height + c->bw * 2}, 0); | |||
|
|
4097 | } else { | |||
|
|
4098 | arrange(c->mon); | |||
|
|
4099 | } | |||
|
|
4100 | } | |||
|
|
4101 | ||||
|
|
4102 | void | |||
|
|
4103 | createnotifyx11(struct wl_listener *listener, void *data) | |||
|
|
4104 | { | |||
|
|
4105 | struct wlr_xwayland_surface *xsurface = data; | |||
|
|
4106 | Client *c; | |||
|
|
4107 | ||||
|
|
4108 | /* Allocate a Client for this surface */ | |||
|
|
4109 | c = xsurface->data = ecalloc(1, sizeof(*c)); | |||
|
|
4110 | c->surface.xwayland = xsurface; | |||
|
|
4111 | c->type = X11; | |||
|
|
4112 | if (client_is_unmanaged(c)) { | |||
|
|
4113 | c->ibw = c->obw = c->bw = 0; | |||
|
|
4114 | } else { | |||
|
|
4115 | c->ibw = cfg.appearance.inner_border_px; | |||
|
|
4116 | c->obw = cfg.appearance.outer_border_px; | |||
|
|
4117 | c->bw = c->ibw + c->obw; | |||
|
|
4118 | } | |||
|
|
4119 | ||||
|
|
4120 | /* Listen to the various events it can emit */ | |||
|
|
4121 | LISTEN(&xsurface->events.associate, &c->associate, associatex11); | |||
|
|
4122 | LISTEN(&xsurface->events.destroy, &c->destroy, destroynotify); | |||
|
|
4123 | LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11); | |||
|
|
4124 | LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); | |||
|
|
4125 | LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); | |||
|
|
4126 | LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); | |||
|
|
4127 | LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); | |||
|
|
4128 | LISTEN(&xsurface->events.set_title, &c->set_title, updatetitle); | |||
|
|
4129 | } | |||
|
|
4130 | ||||
|
|
4131 | void | |||
|
|
4132 | dissociatex11(struct wl_listener *listener, void *data) | |||
|
|
4133 | { | |||
|
|
4134 | Client *c = wl_container_of(listener, c, dissociate); | |||
|
|
4135 | wl_list_remove(&c->map.link); | |||
|
|
4136 | wl_list_remove(&c->unmap.link); | |||
|
|
4137 | } | |||
|
|
4138 | ||||
|
|
4139 | void | |||
|
|
4140 | sethints(struct wl_listener *listener, void *data) | |||
|
|
4141 | { | |||
|
|
4142 | Client *c = wl_container_of(listener, c, set_hints); | |||
|
|
4143 | struct wlr_surface *surface = client_surface(c); | |||
|
|
4144 | if (c == focustop(selmon) || !c->surface.xwayland->hints) | |||
|
|
4145 | return; | |||
|
|
4146 | ||||
|
|
4147 | c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); | |||
|
|
4148 | printstatus(); | |||
|
|
4149 | ||||
|
|
4150 | if (c->isurgent && surface && surface->mapped) | |||
|
|
4151 | client_set_border_color(c, cfg.appearance.inner_urgent_color, cfg.appearance.outer_urgent_color); | |||
|
|
4152 | } | |||
|
|
4153 | ||||
|
|
4154 | void | |||
|
|
4155 | xwaylandready(struct wl_listener *listener, void *data) | |||
|
|
4156 | { | |||
|
|
4157 | struct wlr_xcursor *xcursor; | |||
|
|
4158 | struct wlr_xcursor_image *image; | |||
|
|
4159 | ||||
|
|
4160 | /* assign the one and only seat */ | |||
|
|
4161 | wlr_xwayland_set_seat(xwayland, seat); | |||
|
|
4162 | ||||
|
|
4163 | /* Set the default XWayland cursor to match the rest of swindle. */ | |||
|
|
4164 | if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) { | |||
|
|
4165 | image = xcursor->images[0]; | |||
|
|
4166 | wlr_xwayland_set_cursor(xwayland, | |||
|
|
4167 | wlr_xcursor_image_get_buffer(image), | |||
|
|
4168 | image->hotspot_x, image->hotspot_y); | |||
|
|
4169 | } | |||
|
|
4170 | } | |||
|
|
4171 | #endif | |||
|
|
4172 | ||||
|
|
4173 | int | |||
|
|
4174 | main(int argc, char *argv[]) | |||
|
|
4175 | { | |||
|
|
4176 | char *startup_cmd = NULL; | |||
|
|
4177 | int c; | |||
|
|
4178 | ||||
|
|
4179 | while ((c = getopt(argc, argv, "c:s:hdv")) != -1) { | |||
|
|
4180 | switch (c) { | |||
|
|
4181 | case 'c': | |||
|
|
4182 | { | |||
|
|
4183 | struct stat st; | |||
|
|
4184 | if (stat(optarg, &st) < 0) { | |||
|
|
4185 | if (errno == ENOENT) | |||
|
|
4186 | die("error: config file '%s' does not exist", optarg); | |||
|
|
4187 | die("error: config file '%s': %s", optarg, strerror(errno)); | |||
|
|
4188 | } | |||
|
|
4189 | if (S_ISDIR(st.st_mode)) | |||
|
|
4190 | die("error: config file '%s' is a directory, expected a regular file", optarg); | |||
|
|
4191 | if (access(optarg, R_OK) < 0) | |||
|
|
4192 | die("error: config file '%s' is not readable (permission denied)", optarg); | |||
|
|
4193 | custom_cfg_path = optarg; | |||
|
|
4194 | break; | |||
|
|
4195 | } | |||
|
|
4196 | case 's': | |||
|
|
4197 | startup_cmd = optarg; | |||
|
|
4198 | break; | |||
|
|
4199 | case 'd': | |||
|
|
4200 | cfg.log_level = WLR_DEBUG; /* override before setup() */ | |||
|
|
4201 | break; | |||
|
|
4202 | case 'v': | |||
|
|
4203 | die("swindle " VERSION); | |||
|
|
4204 | default: | |||
|
|
4205 | goto usage; | |||
|
|
4206 | } | |||
|
|
4207 | } | |||
|
|
4208 | if (optind < argc) | |||
|
|
4209 | goto usage; | |||
|
|
4210 | ||||
|
|
4211 | /* Wayland requires XDG_RUNTIME_DIR for creating its communications socket */ | |||
|
|
4212 | if (!getenv("XDG_RUNTIME_DIR")) | |||
|
|
4213 | die("XDG_RUNTIME_DIR must be set"); | |||
|
|
4214 | setup(); | |||
|
|
4215 | run(startup_cmd); | |||
|
|
4216 | cleanup(); | |||
|
|
4217 | return EXIT_SUCCESS; | |||
|
|
4218 | ||||
|
|
4219 | usage: | |||
|
|
4220 | die("Usage: %s [-v] [-d] [-s startup command] [-c config file]", argv[0]); | |||
|
|
4221 | } | |||
|
|
4222 | ||||
| @@ -0,0 +1,51 | |||||
|
|
1 | /* See LICENSE.dwm file for copyright and license details. */ | |||
|
|
2 | #include <stdarg.h> | |||
|
|
3 | #include <stdio.h> | |||
|
|
4 | #include <stdlib.h> | |||
|
|
5 | #include <string.h> | |||
|
|
6 | #include <fcntl.h> | |||
|
|
7 | ||||
|
|
8 | #include "util.h" | |||
|
|
9 | ||||
|
|
10 | void | |||
|
|
11 | die(const char *fmt, ...) { | |||
|
|
12 | va_list ap; | |||
|
|
13 | ||||
|
|
14 | va_start(ap, fmt); | |||
|
|
15 | vfprintf(stderr, fmt, ap); | |||
|
|
16 | va_end(ap); | |||
|
|
17 | ||||
|
|
18 | if (fmt[0] && fmt[strlen(fmt)-1] == ':') { | |||
|
|
19 | fputc(' ', stderr); | |||
|
|
20 | perror(NULL); | |||
|
|
21 | } else { | |||
|
|
22 | fputc('\n', stderr); | |||
|
|
23 | } | |||
|
|
24 | ||||
|
|
25 | exit(1); | |||
|
|
26 | } | |||
|
|
27 | ||||
|
|
28 | void * | |||
|
|
29 | ecalloc(size_t nmemb, size_t size) | |||
|
|
30 | { | |||
|
|
31 | void *p; | |||
|
|
32 | ||||
|
|
33 | if (!(p = calloc(nmemb, size))) | |||
|
|
34 | die("calloc:"); | |||
|
|
35 | return p; | |||
|
|
36 | } | |||
|
|
37 | ||||
|
|
38 | int | |||
|
|
39 | fd_set_nonblock(int fd) { | |||
|
|
40 | int flags = fcntl(fd, F_GETFL); | |||
|
|
41 | if (flags < 0) { | |||
|
|
42 | perror("fcntl(F_GETFL):"); | |||
|
|
43 | return -1; | |||
|
|
44 | } | |||
|
|
45 | if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { | |||
|
|
46 | perror("fcntl(F_SETFL):"); | |||
|
|
47 | return -1; | |||
|
|
48 | } | |||
|
|
49 | ||||
|
|
50 | return 0; | |||
|
|
51 | } | |||
| @@ -0,0 +1,5 | |||||
|
|
1 | /* See LICENSE.dwm file for copyright and license details. */ | |||
|
|
2 | ||||
|
|
3 | void die(const char *fmt, ...); | |||
|
|
4 | void *ecalloc(size_t nmemb, size_t size); | |||
|
|
5 | int fd_set_nonblock(int fd); | |||
| This diff has been collapsed as it changes many lines, (975 lines changed) Show them Hide them | |||||
| @@ -0,0 +1,975 | |||||
|
|
1 | #include "wlr_ext_workspace_v1.h" | |||
|
|
2 | #include "ext-workspace-v1-protocol.h" | |||
|
|
3 | #include <assert.h> | |||
|
|
4 | #include <stdlib.h> | |||
|
|
5 | #include <string.h> | |||
|
|
6 | #include <wlr/types/wlr_output.h> | |||
|
|
7 | ||||
|
|
8 | #define EXT_WORKSPACE_V1_VERSION 1 | |||
|
|
9 | ||||
|
|
10 | struct wlr_ext_workspace_v1_group_output { | |||
|
|
11 | struct wlr_output *output; | |||
|
|
12 | struct wlr_ext_workspace_group_handle_v1 *group; | |||
|
|
13 | struct wl_listener output_bind; | |||
|
|
14 | struct wl_listener output_destroy; | |||
|
|
15 | struct wl_list link; | |||
|
|
16 | }; | |||
|
|
17 | ||||
|
|
18 | // These structs wrap wl_resource of each interface to access the request queue | |||
|
|
19 | // (wlr_ext_workspace_manager_v1_resource.requests) assigned per manager | |||
|
|
20 | // resource | |||
|
|
21 | ||||
|
|
22 | struct wlr_ext_workspace_manager_v1_resource { | |||
|
|
23 | struct wl_resource *resource; | |||
|
|
24 | struct wlr_ext_workspace_manager_v1 *manager; | |||
|
|
25 | struct wl_list requests; // wlr_ext_workspace_v1_request.link | |||
|
|
26 | struct wl_list workspace_resources; // wlr_ext_workspace_v1_resource.link | |||
|
|
27 | struct wl_list group_resources; // wlr_ext_workspace_group_v1_resource.link | |||
|
|
28 | struct wl_list link; // wlr_ext_workspace_manager_v1.resources | |||
|
|
29 | }; | |||
|
|
30 | ||||
|
|
31 | struct wlr_ext_workspace_group_v1_resource { | |||
|
|
32 | struct wl_resource *resource; | |||
|
|
33 | struct wlr_ext_workspace_group_handle_v1 *group; | |||
|
|
34 | struct wlr_ext_workspace_manager_v1_resource *manager; | |||
|
|
35 | struct wl_list link; // wlr_ext_workspace_group_v1.resources | |||
|
|
36 | struct wl_list | |||
|
|
37 | manager_resource_link; // wlr_ext_workspace_manager_v1_resource.group_resources | |||
|
|
38 | }; | |||
|
|
39 | ||||
|
|
40 | struct wlr_ext_workspace_v1_resource { | |||
|
|
41 | struct wl_resource *resource; | |||
|
|
42 | struct wlr_ext_workspace_handle_v1 *workspace; | |||
|
|
43 | struct wlr_ext_workspace_manager_v1_resource *manager; | |||
|
|
44 | struct wl_list link; // wlr_ext_workspace_v1.resources | |||
|
|
45 | struct wl_list | |||
|
|
46 | manager_resource_link; // wlr_ext_workspace_manager_v1_resource.workspace_resources | |||
|
|
47 | }; | |||
|
|
48 | ||||
|
|
49 | static const struct ext_workspace_group_handle_v1_interface group_impl; | |||
|
|
50 | ||||
|
|
51 | static struct wlr_ext_workspace_group_v1_resource * | |||
|
|
52 | group_resource_from_resource(struct wl_resource *resource) { | |||
|
|
53 | assert(wl_resource_instance_of( | |||
|
|
54 | resource, &ext_workspace_group_handle_v1_interface, &group_impl)); | |||
|
|
55 | return wl_resource_get_user_data(resource); | |||
|
|
56 | } | |||
|
|
57 | ||||
|
|
58 | static const struct ext_workspace_handle_v1_interface workspace_impl; | |||
|
|
59 | ||||
|
|
60 | static struct wlr_ext_workspace_v1_resource * | |||
|
|
61 | workspace_resource_from_resource(struct wl_resource *resource) { | |||
|
|
62 | assert(wl_resource_instance_of(resource, &ext_workspace_handle_v1_interface, | |||
|
|
63 | &workspace_impl)); | |||
|
|
64 | return wl_resource_get_user_data(resource); | |||
|
|
65 | } | |||
|
|
66 | ||||
|
|
67 | static const struct ext_workspace_manager_v1_interface manager_impl; | |||
|
|
68 | ||||
|
|
69 | static struct wlr_ext_workspace_manager_v1_resource * | |||
|
|
70 | manager_resource_from_resource(struct wl_resource *resource) { | |||
|
|
71 | assert(wl_resource_instance_of( | |||
|
|
72 | resource, &ext_workspace_manager_v1_interface, &manager_impl)); | |||
|
|
73 | return wl_resource_get_user_data(resource); | |||
|
|
74 | } | |||
|
|
75 | ||||
|
|
76 | static void workspace_handle_destroy(struct wl_client *client, | |||
|
|
77 | struct wl_resource *resource) { | |||
|
|
78 | wl_resource_destroy(resource); | |||
|
|
79 | } | |||
|
|
80 | ||||
|
|
81 | static void workspace_handle_activate(struct wl_client *client, | |||
|
|
82 | struct wl_resource *workspace_resource) { | |||
|
|
83 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
84 | workspace_resource_from_resource(workspace_resource); | |||
|
|
85 | if (!workspace_res) { | |||
|
|
86 | return; | |||
|
|
87 | } | |||
|
|
88 | ||||
|
|
89 | struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); | |||
|
|
90 | if (!req) { | |||
|
|
91 | wl_resource_post_no_memory(workspace_resource); | |||
|
|
92 | return; | |||
|
|
93 | } | |||
|
|
94 | req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE; | |||
|
|
95 | req->activate.workspace = workspace_res->workspace; | |||
|
|
96 | wl_list_insert(workspace_res->manager->requests.prev, &req->link); | |||
|
|
97 | } | |||
|
|
98 | ||||
|
|
99 | static void | |||
|
|
100 | workspace_handle_deactivate(struct wl_client *client, | |||
|
|
101 | struct wl_resource *workspace_resource) { | |||
|
|
102 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
103 | workspace_resource_from_resource(workspace_resource); | |||
|
|
104 | if (!workspace_res) { | |||
|
|
105 | return; | |||
|
|
106 | } | |||
|
|
107 | ||||
|
|
108 | struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); | |||
|
|
109 | if (!req) { | |||
|
|
110 | wl_resource_post_no_memory(workspace_resource); | |||
|
|
111 | return; | |||
|
|
112 | } | |||
|
|
113 | req->type = WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE; | |||
|
|
114 | req->deactivate.workspace = workspace_res->workspace; | |||
|
|
115 | wl_list_insert(workspace_res->manager->requests.prev, &req->link); | |||
|
|
116 | } | |||
|
|
117 | ||||
|
|
118 | static void workspace_handle_assign(struct wl_client *client, | |||
|
|
119 | struct wl_resource *workspace_resource, | |||
|
|
120 | struct wl_resource *group_resource) { | |||
|
|
121 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
122 | workspace_resource_from_resource(workspace_resource); | |||
|
|
123 | struct wlr_ext_workspace_group_v1_resource *group_res = | |||
|
|
124 | group_resource_from_resource(group_resource); | |||
|
|
125 | if (!workspace_res || !group_res) { | |||
|
|
126 | return; | |||
|
|
127 | } | |||
|
|
128 | ||||
|
|
129 | struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); | |||
|
|
130 | if (!req) { | |||
|
|
131 | wl_resource_post_no_memory(workspace_resource); | |||
|
|
132 | return; | |||
|
|
133 | } | |||
|
|
134 | req->type = WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN; | |||
|
|
135 | req->assign.group = group_res->group; | |||
|
|
136 | req->assign.workspace = workspace_res->workspace; | |||
|
|
137 | wl_list_insert(workspace_res->manager->requests.prev, &req->link); | |||
|
|
138 | } | |||
|
|
139 | ||||
|
|
140 | static void workspace_handle_remove(struct wl_client *client, | |||
|
|
141 | struct wl_resource *workspace_resource) { | |||
|
|
142 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
143 | workspace_resource_from_resource(workspace_resource); | |||
|
|
144 | if (!workspace_res) { | |||
|
|
145 | return; | |||
|
|
146 | } | |||
|
|
147 | ||||
|
|
148 | struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); | |||
|
|
149 | if (!req) { | |||
|
|
150 | wl_resource_post_no_memory(workspace_resource); | |||
|
|
151 | return; | |||
|
|
152 | } | |||
|
|
153 | req->type = WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE; | |||
|
|
154 | req->remove.workspace = workspace_res->workspace; | |||
|
|
155 | wl_list_insert(workspace_res->manager->requests.prev, &req->link); | |||
|
|
156 | } | |||
|
|
157 | ||||
|
|
158 | static const struct ext_workspace_handle_v1_interface workspace_impl = { | |||
|
|
159 | .destroy = workspace_handle_destroy, | |||
|
|
160 | .activate = workspace_handle_activate, | |||
|
|
161 | .deactivate = workspace_handle_deactivate, | |||
|
|
162 | .assign = workspace_handle_assign, | |||
|
|
163 | .remove = workspace_handle_remove, | |||
|
|
164 | }; | |||
|
|
165 | ||||
|
|
166 | static void group_handle_create_workspace(struct wl_client *client, | |||
|
|
167 | struct wl_resource *group_resource, | |||
|
|
168 | const char *name) { | |||
|
|
169 | struct wlr_ext_workspace_group_v1_resource *group_res = | |||
|
|
170 | group_resource_from_resource(group_resource); | |||
|
|
171 | if (!group_res) { | |||
|
|
172 | return; | |||
|
|
173 | } | |||
|
|
174 | ||||
|
|
175 | struct wlr_ext_workspace_v1_request *req = calloc(1, sizeof(*req)); | |||
|
|
176 | if (!req) { | |||
|
|
177 | wl_resource_post_no_memory(group_resource); | |||
|
|
178 | return; | |||
|
|
179 | } | |||
|
|
180 | req->create_workspace.name = strdup(name); | |||
|
|
181 | if (!req->create_workspace.name) { | |||
|
|
182 | free(req); | |||
|
|
183 | wl_resource_post_no_memory(group_resource); | |||
|
|
184 | return; | |||
|
|
185 | } | |||
|
|
186 | req->type = WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE; | |||
|
|
187 | req->create_workspace.group = group_res->group; | |||
|
|
188 | wl_list_insert(group_res->manager->requests.prev, &req->link); | |||
|
|
189 | } | |||
|
|
190 | ||||
|
|
191 | static void group_handle_destroy(struct wl_client *client, | |||
|
|
192 | struct wl_resource *resource) { | |||
|
|
193 | wl_resource_destroy(resource); | |||
|
|
194 | } | |||
|
|
195 | ||||
|
|
196 | static const struct ext_workspace_group_handle_v1_interface group_impl = { | |||
|
|
197 | .create_workspace = group_handle_create_workspace, | |||
|
|
198 | .destroy = group_handle_destroy, | |||
|
|
199 | }; | |||
|
|
200 | ||||
|
|
201 | static void destroy_workspace_resource( | |||
|
|
202 | struct wlr_ext_workspace_v1_resource *workspace_res) { | |||
|
|
203 | wl_list_remove(&workspace_res->link); | |||
|
|
204 | wl_list_remove(&workspace_res->manager_resource_link); | |||
|
|
205 | wl_resource_set_user_data(workspace_res->resource, NULL); | |||
|
|
206 | free(workspace_res); | |||
|
|
207 | } | |||
|
|
208 | ||||
|
|
209 | static void workspace_resource_destroy(struct wl_resource *resource) { | |||
|
|
210 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
211 | workspace_resource_from_resource(resource); | |||
|
|
212 | if (workspace_res) { | |||
|
|
213 | destroy_workspace_resource(workspace_res); | |||
|
|
214 | } | |||
|
|
215 | } | |||
|
|
216 | ||||
|
|
217 | static struct wlr_ext_workspace_v1_resource *create_workspace_resource( | |||
|
|
218 | struct wlr_ext_workspace_handle_v1 *workspace, | |||
|
|
219 | struct wlr_ext_workspace_manager_v1_resource *manager_res) { | |||
|
|
220 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
221 | calloc(1, sizeof(*workspace_res)); | |||
|
|
222 | if (!workspace_res) { | |||
|
|
223 | return NULL; | |||
|
|
224 | } | |||
|
|
225 | ||||
|
|
226 | struct wl_client *client = wl_resource_get_client(manager_res->resource); | |||
|
|
227 | workspace_res->resource = | |||
|
|
228 | wl_resource_create(client, &ext_workspace_handle_v1_interface, | |||
|
|
229 | wl_resource_get_version(manager_res->resource), 0); | |||
|
|
230 | if (!workspace_res->resource) { | |||
|
|
231 | free(workspace_res); | |||
|
|
232 | return NULL; | |||
|
|
233 | } | |||
|
|
234 | wl_resource_set_implementation(workspace_res->resource, &workspace_impl, | |||
|
|
235 | workspace_res, workspace_resource_destroy); | |||
|
|
236 | ||||
|
|
237 | workspace_res->workspace = workspace; | |||
|
|
238 | workspace_res->manager = manager_res; | |||
|
|
239 | wl_list_insert(&workspace->resources, &workspace_res->link); | |||
|
|
240 | wl_list_insert(&manager_res->workspace_resources, | |||
|
|
241 | &workspace_res->manager_resource_link); | |||
|
|
242 | ||||
|
|
243 | return workspace_res; | |||
|
|
244 | } | |||
|
|
245 | ||||
|
|
246 | static void | |||
|
|
247 | destroy_group_resource(struct wlr_ext_workspace_group_v1_resource *group_res) { | |||
|
|
248 | wl_list_remove(&group_res->link); | |||
|
|
249 | wl_list_remove(&group_res->manager_resource_link); | |||
|
|
250 | wl_resource_set_user_data(group_res->resource, NULL); | |||
|
|
251 | free(group_res); | |||
|
|
252 | } | |||
|
|
253 | ||||
|
|
254 | static void group_handle_resource_destroy(struct wl_resource *resource) { | |||
|
|
255 | struct wlr_ext_workspace_group_v1_resource *group_res = | |||
|
|
256 | group_resource_from_resource(resource); | |||
|
|
257 | if (group_res) { | |||
|
|
258 | destroy_group_resource(group_res); | |||
|
|
259 | } | |||
|
|
260 | } | |||
|
|
261 | ||||
|
|
262 | static struct wlr_ext_workspace_group_v1_resource *create_group_resource( | |||
|
|
263 | struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
264 | struct wlr_ext_workspace_manager_v1_resource *manager_res) { | |||
|
|
265 | struct wlr_ext_workspace_group_v1_resource *group_res = | |||
|
|
266 | calloc(1, sizeof(*group_res)); | |||
|
|
267 | if (!group_res) { | |||
|
|
268 | return NULL; | |||
|
|
269 | } | |||
|
|
270 | ||||
|
|
271 | struct wl_client *client = wl_resource_get_client(manager_res->resource); | |||
|
|
272 | uint32_t version = wl_resource_get_version(manager_res->resource); | |||
|
|
273 | group_res->resource = wl_resource_create( | |||
|
|
274 | client, &ext_workspace_group_handle_v1_interface, version, 0); | |||
|
|
275 | if (group_res->resource == NULL) { | |||
|
|
276 | free(group_res); | |||
|
|
277 | return NULL; | |||
|
|
278 | } | |||
|
|
279 | wl_resource_set_implementation(group_res->resource, &group_impl, group_res, | |||
|
|
280 | group_handle_resource_destroy); | |||
|
|
281 | ||||
|
|
282 | group_res->group = group; | |||
|
|
283 | group_res->manager = manager_res; | |||
|
|
284 | wl_list_insert(&group->resources, &group_res->link); | |||
|
|
285 | wl_list_insert(&manager_res->group_resources, | |||
|
|
286 | &group_res->manager_resource_link); | |||
|
|
287 | ||||
|
|
288 | return group_res; | |||
|
|
289 | } | |||
|
|
290 | ||||
|
|
291 | static void | |||
|
|
292 | destroy_requests(struct wlr_ext_workspace_manager_v1_resource *manager_res) { | |||
|
|
293 | struct wlr_ext_workspace_v1_request *req, *tmp; | |||
|
|
294 | wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { | |||
|
|
295 | if (req->type == WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE) { | |||
|
|
296 | free(req->create_workspace.name); | |||
|
|
297 | } | |||
|
|
298 | wl_list_remove(&req->link); | |||
|
|
299 | free(req); | |||
|
|
300 | } | |||
|
|
301 | } | |||
|
|
302 | ||||
|
|
303 | static void | |||
|
|
304 | clear_requests_by(struct wlr_ext_workspace_manager_v1_resource *manager_res, | |||
|
|
305 | struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
306 | struct wlr_ext_workspace_handle_v1 *workspace) { | |||
|
|
307 | struct wlr_ext_workspace_v1_request *req, *tmp; | |||
|
|
308 | wl_list_for_each_safe(req, tmp, &manager_res->requests, link) { | |||
|
|
309 | switch (req->type) { | |||
|
|
310 | case WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE: | |||
|
|
311 | if (group && req->create_workspace.group == group) { | |||
|
|
312 | req->create_workspace.group = NULL; | |||
|
|
313 | } | |||
|
|
314 | break; | |||
|
|
315 | case WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE: | |||
|
|
316 | if (workspace && req->activate.workspace == workspace) { | |||
|
|
317 | req->activate.workspace = NULL; | |||
|
|
318 | } | |||
|
|
319 | break; | |||
|
|
320 | case WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE: | |||
|
|
321 | if (workspace && req->deactivate.workspace == workspace) { | |||
|
|
322 | req->deactivate.workspace = NULL; | |||
|
|
323 | } | |||
|
|
324 | break; | |||
|
|
325 | case WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN: | |||
|
|
326 | if (workspace && req->assign.workspace == workspace) { | |||
|
|
327 | req->assign.workspace = NULL; | |||
|
|
328 | } | |||
|
|
329 | if (group && req->assign.group == group) { | |||
|
|
330 | req->assign.group = NULL; | |||
|
|
331 | } | |||
|
|
332 | break; | |||
|
|
333 | case WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE: | |||
|
|
334 | if (workspace && req->remove.workspace == workspace) { | |||
|
|
335 | req->remove.workspace = NULL; | |||
|
|
336 | } | |||
|
|
337 | break; | |||
|
|
338 | } | |||
|
|
339 | } | |||
|
|
340 | } | |||
|
|
341 | ||||
|
|
342 | static void manager_handle_commit(struct wl_client *client, | |||
|
|
343 | struct wl_resource *resource) { | |||
|
|
344 | struct wlr_ext_workspace_manager_v1_resource *manager_res = | |||
|
|
345 | manager_resource_from_resource(resource); | |||
|
|
346 | if (!manager_res) { | |||
|
|
347 | return; | |||
|
|
348 | } | |||
|
|
349 | ||||
|
|
350 | struct wlr_ext_workspace_v1_commit_event commit_event = { | |||
|
|
351 | .requests = &manager_res->requests, | |||
|
|
352 | }; | |||
|
|
353 | wl_signal_emit_mutable(&manager_res->manager->events.commit, &commit_event); | |||
|
|
354 | destroy_requests(manager_res); | |||
|
|
355 | } | |||
|
|
356 | ||||
|
|
357 | static void handle_idle(void *data) { | |||
|
|
358 | struct wlr_ext_workspace_manager_v1 *manager = data; | |||
|
|
359 | ||||
|
|
360 | struct wlr_ext_workspace_manager_v1_resource *manager_res; | |||
|
|
361 | wl_list_for_each(manager_res, &manager->resources, link) { | |||
|
|
362 | ext_workspace_manager_v1_send_done(manager_res->resource); | |||
|
|
363 | } | |||
|
|
364 | manager->idle_source = NULL; | |||
|
|
365 | } | |||
|
|
366 | ||||
|
|
367 | static void | |||
|
|
368 | manager_schedule_done(struct wlr_ext_workspace_manager_v1 *manager) { | |||
|
|
369 | if (!manager->idle_source) { | |||
|
|
370 | manager->idle_source = | |||
|
|
371 | wl_event_loop_add_idle(manager->event_loop, handle_idle, manager); | |||
|
|
372 | } | |||
|
|
373 | } | |||
|
|
374 | ||||
|
|
375 | static void | |||
|
|
376 | workspace_send_details(struct wlr_ext_workspace_v1_resource *workspace_res) { | |||
|
|
377 | struct wlr_ext_workspace_handle_v1 *workspace = workspace_res->workspace; | |||
|
|
378 | struct wl_resource *resource = workspace_res->resource; | |||
|
|
379 | ||||
|
|
380 | ext_workspace_handle_v1_send_capabilities(resource, workspace->caps); | |||
|
|
381 | if (workspace->coordinates.size > 0) { | |||
|
|
382 | ext_workspace_handle_v1_send_coordinates(resource, | |||
|
|
383 | &workspace->coordinates); | |||
|
|
384 | } | |||
|
|
385 | if (workspace->name) { | |||
|
|
386 | ext_workspace_handle_v1_send_name(resource, workspace->name); | |||
|
|
387 | } | |||
|
|
388 | if (workspace->id) { | |||
|
|
389 | ext_workspace_handle_v1_send_id(resource, workspace->id); | |||
|
|
390 | } | |||
|
|
391 | ext_workspace_handle_v1_send_state(resource, workspace->state); | |||
|
|
392 | manager_schedule_done(workspace->manager); | |||
|
|
393 | } | |||
|
|
394 | ||||
|
|
395 | static void manager_handle_stop(struct wl_client *client, | |||
|
|
396 | struct wl_resource *resource) { | |||
|
|
397 | ext_workspace_manager_v1_send_finished(resource); | |||
|
|
398 | wl_resource_destroy(resource); | |||
|
|
399 | } | |||
|
|
400 | ||||
|
|
401 | static const struct ext_workspace_manager_v1_interface manager_impl = { | |||
|
|
402 | .commit = manager_handle_commit, | |||
|
|
403 | .stop = manager_handle_stop, | |||
|
|
404 | }; | |||
|
|
405 | ||||
|
|
406 | static void destroy_manager_resource( | |||
|
|
407 | struct wlr_ext_workspace_manager_v1_resource *manager_res) { | |||
|
|
408 | destroy_requests(manager_res); | |||
|
|
409 | ||||
|
|
410 | struct wlr_ext_workspace_v1_resource *workspace_res, *tmp2; | |||
|
|
411 | wl_list_for_each_safe(workspace_res, tmp2, | |||
|
|
412 | &manager_res->workspace_resources, | |||
|
|
413 | manager_resource_link) { | |||
|
|
414 | destroy_workspace_resource(workspace_res); | |||
|
|
415 | } | |||
|
|
416 | struct wlr_ext_workspace_group_v1_resource *group_res, *tmp3; | |||
|
|
417 | wl_list_for_each_safe(group_res, tmp3, &manager_res->group_resources, | |||
|
|
418 | manager_resource_link) { | |||
|
|
419 | destroy_group_resource(group_res); | |||
|
|
420 | } | |||
|
|
421 | ||||
|
|
422 | wl_list_remove(&manager_res->link); | |||
|
|
423 | wl_resource_set_user_data(manager_res->resource, NULL); | |||
|
|
424 | free(manager_res); | |||
|
|
425 | } | |||
|
|
426 | ||||
|
|
427 | static void manager_resource_destroy(struct wl_resource *resource) { | |||
|
|
428 | struct wlr_ext_workspace_manager_v1_resource *manager_res = | |||
|
|
429 | manager_resource_from_resource(resource); | |||
|
|
430 | if (manager_res) { | |||
|
|
431 | destroy_manager_resource(manager_res); | |||
|
|
432 | } | |||
|
|
433 | } | |||
|
|
434 | ||||
|
|
435 | static void | |||
|
|
436 | group_send_details(struct wlr_ext_workspace_group_v1_resource *group_res) { | |||
|
|
437 | struct wlr_ext_workspace_group_handle_v1 *group = group_res->group; | |||
|
|
438 | struct wl_resource *resource = group_res->resource; | |||
|
|
439 | struct wl_client *client = wl_resource_get_client(resource); | |||
|
|
440 | ||||
|
|
441 | ext_workspace_group_handle_v1_send_capabilities(resource, group->caps); | |||
|
|
442 | ||||
|
|
443 | struct wlr_ext_workspace_v1_group_output *group_output; | |||
|
|
444 | wl_list_for_each(group_output, &group->outputs, link) { | |||
|
|
445 | struct wl_resource *output_resource; | |||
|
|
446 | wl_resource_for_each(output_resource, | |||
|
|
447 | &group_output->output->resources) { | |||
|
|
448 | if (wl_resource_get_client(output_resource) == client) { | |||
|
|
449 | ext_workspace_group_handle_v1_send_output_enter( | |||
|
|
450 | resource, output_resource); | |||
|
|
451 | } | |||
|
|
452 | } | |||
|
|
453 | } | |||
|
|
454 | ||||
|
|
455 | manager_schedule_done(group->manager); | |||
|
|
456 | } | |||
|
|
457 | ||||
|
|
458 | static void manager_bind(struct wl_client *client, void *data, uint32_t version, | |||
|
|
459 | uint32_t id) { | |||
|
|
460 | struct wlr_ext_workspace_manager_v1 *manager = data; | |||
|
|
461 | ||||
|
|
462 | struct wlr_ext_workspace_manager_v1_resource *manager_res = | |||
|
|
463 | calloc(1, sizeof(*manager_res)); | |||
|
|
464 | if (!manager_res) { | |||
|
|
465 | wl_client_post_no_memory(client); | |||
|
|
466 | return; | |||
|
|
467 | } | |||
|
|
468 | ||||
|
|
469 | manager_res->manager = manager; | |||
|
|
470 | wl_list_init(&manager_res->requests); | |||
|
|
471 | wl_list_init(&manager_res->workspace_resources); | |||
|
|
472 | wl_list_init(&manager_res->group_resources); | |||
|
|
473 | ||||
|
|
474 | manager_res->resource = wl_resource_create( | |||
|
|
475 | client, &ext_workspace_manager_v1_interface, version, id); | |||
|
|
476 | if (!manager_res->resource) { | |||
|
|
477 | free(manager_res); | |||
|
|
478 | wl_client_post_no_memory(client); | |||
|
|
479 | return; | |||
|
|
480 | } | |||
|
|
481 | wl_resource_set_implementation(manager_res->resource, &manager_impl, | |||
|
|
482 | manager_res, manager_resource_destroy); | |||
|
|
483 | wl_list_insert(&manager->resources, &manager_res->link); | |||
|
|
484 | ||||
|
|
485 | struct wlr_ext_workspace_group_handle_v1 *group; | |||
|
|
486 | wl_list_for_each(group, &manager->groups, link) { | |||
|
|
487 | struct wlr_ext_workspace_group_v1_resource *group_res = | |||
|
|
488 | create_group_resource(group, manager_res); | |||
|
|
489 | if (!group_res) { | |||
|
|
490 | wl_resource_post_no_memory(manager_res->resource); | |||
|
|
491 | continue; | |||
|
|
492 | } | |||
|
|
493 | ext_workspace_manager_v1_send_workspace_group(manager_res->resource, | |||
|
|
494 | group_res->resource); | |||
|
|
495 | group_send_details(group_res); | |||
|
|
496 | } | |||
|
|
497 | ||||
|
|
498 | struct wlr_ext_workspace_handle_v1 *workspace; | |||
|
|
499 | wl_list_for_each(workspace, &manager->workspaces, link) { | |||
|
|
500 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
501 | create_workspace_resource(workspace, manager_res); | |||
|
|
502 | if (!workspace_res) { | |||
|
|
503 | wl_resource_post_no_memory(manager_res->resource); | |||
|
|
504 | continue; | |||
|
|
505 | } | |||
|
|
506 | ext_workspace_manager_v1_send_workspace(manager_res->resource, | |||
|
|
507 | workspace_res->resource); | |||
|
|
508 | workspace_send_details(workspace_res); | |||
|
|
509 | ||||
|
|
510 | if (!workspace->group) { | |||
|
|
511 | continue; | |||
|
|
512 | } | |||
|
|
513 | struct wlr_ext_workspace_group_v1_resource *group_res; | |||
|
|
514 | wl_list_for_each(group_res, &workspace->group->resources, link) { | |||
|
|
515 | if (group_res->manager == manager_res) { | |||
|
|
516 | ext_workspace_group_handle_v1_send_workspace_enter( | |||
|
|
517 | group_res->resource, workspace_res->resource); | |||
|
|
518 | } | |||
|
|
519 | } | |||
|
|
520 | } | |||
|
|
521 | ||||
|
|
522 | ext_workspace_manager_v1_send_done(manager_res->resource); | |||
|
|
523 | } | |||
|
|
524 | ||||
|
|
525 | static void manager_handle_display_destroy(struct wl_listener *listener, | |||
|
|
526 | void *data) { | |||
|
|
527 | struct wlr_ext_workspace_manager_v1 *manager = | |||
|
|
528 | wl_container_of(listener, manager, display_destroy); | |||
|
|
529 | ||||
|
|
530 | wl_signal_emit_mutable(&manager->events.destroy, NULL); | |||
|
|
531 | assert(wl_list_empty(&manager->events.commit.listener_list)); | |||
|
|
532 | assert(wl_list_empty(&manager->events.destroy.listener_list)); | |||
|
|
533 | ||||
|
|
534 | struct wlr_ext_workspace_group_handle_v1 *group, *tmp; | |||
|
|
535 | wl_list_for_each_safe(group, tmp, &manager->groups, link) { | |||
|
|
536 | wlr_ext_workspace_group_handle_v1_destroy(group); | |||
|
|
537 | } | |||
|
|
538 | ||||
|
|
539 | struct wlr_ext_workspace_handle_v1 *workspace, *tmp2; | |||
|
|
540 | wl_list_for_each_safe(workspace, tmp2, &manager->workspaces, link) { | |||
|
|
541 | wlr_ext_workspace_handle_v1_destroy(workspace); | |||
|
|
542 | } | |||
|
|
543 | ||||
|
|
544 | struct wlr_ext_workspace_manager_v1_resource *manager_res, *tmp3; | |||
|
|
545 | wl_list_for_each_safe(manager_res, tmp3, &manager->resources, link) { | |||
|
|
546 | destroy_manager_resource(manager_res); | |||
|
|
547 | } | |||
|
|
548 | ||||
|
|
549 | if (manager->idle_source) { | |||
|
|
550 | wl_event_source_remove(manager->idle_source); | |||
|
|
551 | } | |||
|
|
552 | ||||
|
|
553 | wl_list_remove(&manager->display_destroy.link); | |||
|
|
554 | wl_global_destroy(manager->global); | |||
|
|
555 | free(manager); | |||
|
|
556 | } | |||
|
|
557 | ||||
|
|
558 | struct wlr_ext_workspace_manager_v1 * | |||
|
|
559 | wlr_ext_workspace_manager_v1_create(struct wl_display *display, | |||
|
|
560 | uint32_t version) { | |||
|
|
561 | assert(version <= EXT_WORKSPACE_V1_VERSION); | |||
|
|
562 | ||||
|
|
563 | struct wlr_ext_workspace_manager_v1 *manager = calloc(1, sizeof(*manager)); | |||
|
|
564 | if (!manager) { | |||
|
|
565 | return NULL; | |||
|
|
566 | } | |||
|
|
567 | ||||
|
|
568 | manager->global = | |||
|
|
569 | wl_global_create(display, &ext_workspace_manager_v1_interface, version, | |||
|
|
570 | manager, manager_bind); | |||
|
|
571 | if (!manager->global) { | |||
|
|
572 | free(manager); | |||
|
|
573 | return NULL; | |||
|
|
574 | } | |||
|
|
575 | ||||
|
|
576 | manager->event_loop = wl_display_get_event_loop(display); | |||
|
|
577 | ||||
|
|
578 | manager->display_destroy.notify = manager_handle_display_destroy; | |||
|
|
579 | wl_display_add_destroy_listener(display, &manager->display_destroy); | |||
|
|
580 | ||||
|
|
581 | wl_list_init(&manager->groups); | |||
|
|
582 | wl_list_init(&manager->workspaces); | |||
|
|
583 | wl_list_init(&manager->resources); | |||
|
|
584 | wl_signal_init(&manager->events.commit); | |||
|
|
585 | wl_signal_init(&manager->events.destroy); | |||
|
|
586 | ||||
|
|
587 | return manager; | |||
|
|
588 | } | |||
|
|
589 | ||||
|
|
590 | struct wlr_ext_workspace_group_handle_v1 * | |||
|
|
591 | wlr_ext_workspace_group_handle_v1_create( | |||
|
|
592 | struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps) { | |||
|
|
593 | struct wlr_ext_workspace_group_handle_v1 *group = calloc(1, sizeof(*group)); | |||
|
|
594 | if (!group) { | |||
|
|
595 | return NULL; | |||
|
|
596 | } | |||
|
|
597 | ||||
|
|
598 | group->manager = manager; | |||
|
|
599 | group->caps = caps; | |||
|
|
600 | ||||
|
|
601 | wl_list_init(&group->outputs); | |||
|
|
602 | wl_list_init(&group->resources); | |||
|
|
603 | wl_signal_init(&group->events.destroy); | |||
|
|
604 | ||||
|
|
605 | wl_list_insert(manager->groups.prev, &group->link); | |||
|
|
606 | ||||
|
|
607 | struct wlr_ext_workspace_manager_v1_resource *manager_res; | |||
|
|
608 | wl_list_for_each(manager_res, &manager->resources, link) { | |||
|
|
609 | struct wlr_ext_workspace_group_v1_resource *group_res = | |||
|
|
610 | create_group_resource(group, manager_res); | |||
|
|
611 | if (!group_res) { | |||
|
|
612 | continue; | |||
|
|
613 | } | |||
|
|
614 | ext_workspace_manager_v1_send_workspace_group(manager_res->resource, | |||
|
|
615 | group_res->resource); | |||
|
|
616 | group_send_details(group_res); | |||
|
|
617 | } | |||
|
|
618 | ||||
|
|
619 | manager_schedule_done(manager); | |||
|
|
620 | ||||
|
|
621 | return group; | |||
|
|
622 | } | |||
|
|
623 | ||||
|
|
624 | static void | |||
|
|
625 | workspace_send_group(struct wlr_ext_workspace_handle_v1 *workspace, | |||
|
|
626 | struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
627 | bool enter) { | |||
|
|
628 | ||||
|
|
629 | struct wlr_ext_workspace_v1_resource *workspace_res; | |||
|
|
630 | wl_list_for_each(workspace_res, &workspace->resources, link) { | |||
|
|
631 | struct wlr_ext_workspace_group_v1_resource *group_res; | |||
|
|
632 | wl_list_for_each(group_res, &group->resources, link) { | |||
|
|
633 | if (group_res->manager != workspace_res->manager) { | |||
|
|
634 | continue; | |||
|
|
635 | } | |||
|
|
636 | if (enter) { | |||
|
|
637 | ext_workspace_group_handle_v1_send_workspace_enter( | |||
|
|
638 | group_res->resource, workspace_res->resource); | |||
|
|
639 | } else { | |||
|
|
640 | ext_workspace_group_handle_v1_send_workspace_leave( | |||
|
|
641 | group_res->resource, workspace_res->resource); | |||
|
|
642 | } | |||
|
|
643 | } | |||
|
|
644 | } | |||
|
|
645 | ||||
|
|
646 | manager_schedule_done(workspace->manager); | |||
|
|
647 | } | |||
|
|
648 | ||||
|
|
649 | static void | |||
|
|
650 | destroy_group_output(struct wlr_ext_workspace_v1_group_output *group_output) { | |||
|
|
651 | wl_list_remove(&group_output->output_bind.link); | |||
|
|
652 | wl_list_remove(&group_output->output_destroy.link); | |||
|
|
653 | wl_list_remove(&group_output->link); | |||
|
|
654 | free(group_output); | |||
|
|
655 | } | |||
|
|
656 | ||||
|
|
657 | static void group_send_output(struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
658 | struct wlr_output *output, bool enter) { | |||
|
|
659 | ||||
|
|
660 | struct wlr_ext_workspace_group_v1_resource *group_res; | |||
|
|
661 | wl_list_for_each(group_res, &group->resources, link) { | |||
|
|
662 | struct wl_client *client = wl_resource_get_client(group_res->resource); | |||
|
|
663 | ||||
|
|
664 | struct wl_resource *output_resource; | |||
|
|
665 | wl_resource_for_each(output_resource, &output->resources) { | |||
|
|
666 | if (wl_resource_get_client(output_resource) != client) { | |||
|
|
667 | continue; | |||
|
|
668 | } | |||
|
|
669 | if (enter) { | |||
|
|
670 | ext_workspace_group_handle_v1_send_output_enter( | |||
|
|
671 | group_res->resource, output_resource); | |||
|
|
672 | } else { | |||
|
|
673 | ext_workspace_group_handle_v1_send_output_leave( | |||
|
|
674 | group_res->resource, output_resource); | |||
|
|
675 | } | |||
|
|
676 | } | |||
|
|
677 | } | |||
|
|
678 | ||||
|
|
679 | manager_schedule_done(group->manager); | |||
|
|
680 | } | |||
|
|
681 | ||||
|
|
682 | void wlr_ext_workspace_group_handle_v1_destroy( | |||
|
|
683 | struct wlr_ext_workspace_group_handle_v1 *group) { | |||
|
|
684 | if (!group) { | |||
|
|
685 | return; | |||
|
|
686 | } | |||
|
|
687 | ||||
|
|
688 | wl_signal_emit_mutable(&group->events.destroy, NULL); | |||
|
|
689 | ||||
|
|
690 | assert(wl_list_empty(&group->events.destroy.listener_list)); | |||
|
|
691 | ||||
|
|
692 | struct wlr_ext_workspace_handle_v1 *workspace; | |||
|
|
693 | wl_list_for_each(workspace, &group->manager->workspaces, link) { | |||
|
|
694 | if (workspace->group == group) { | |||
|
|
695 | workspace_send_group(workspace, group, false); | |||
|
|
696 | workspace->group = NULL; | |||
|
|
697 | } | |||
|
|
698 | } | |||
|
|
699 | ||||
|
|
700 | struct wlr_ext_workspace_group_v1_resource *group_res, *tmp; | |||
|
|
701 | wl_list_for_each_safe(group_res, tmp, &group->resources, link) { | |||
|
|
702 | ext_workspace_group_handle_v1_send_removed(group_res->resource); | |||
|
|
703 | destroy_group_resource(group_res); | |||
|
|
704 | } | |||
|
|
705 | ||||
|
|
706 | struct wlr_ext_workspace_manager_v1_resource *manager_res; | |||
|
|
707 | wl_list_for_each(manager_res, &group->manager->resources, link) { | |||
|
|
708 | clear_requests_by(manager_res, group, NULL); | |||
|
|
709 | } | |||
|
|
710 | ||||
|
|
711 | struct wlr_ext_workspace_v1_group_output *group_output, *tmp3; | |||
|
|
712 | wl_list_for_each_safe(group_output, tmp3, &group->outputs, link) { | |||
|
|
713 | group_send_output(group, group_output->output, false); | |||
|
|
714 | destroy_group_output(group_output); | |||
|
|
715 | } | |||
|
|
716 | ||||
|
|
717 | manager_schedule_done(group->manager); | |||
|
|
718 | ||||
|
|
719 | wl_list_remove(&group->link); | |||
|
|
720 | free(group); | |||
|
|
721 | } | |||
|
|
722 | ||||
|
|
723 | static void handle_output_bind(struct wl_listener *listener, void *data) { | |||
|
|
724 | struct wlr_ext_workspace_v1_group_output *group_output = | |||
|
|
725 | wl_container_of(listener, group_output, output_bind); | |||
|
|
726 | struct wlr_output_event_bind *event = data; | |||
|
|
727 | struct wl_client *client = wl_resource_get_client(event->resource); | |||
|
|
728 | ||||
|
|
729 | struct wlr_ext_workspace_group_v1_resource *group_res; | |||
|
|
730 | wl_list_for_each(group_res, &group_output->group->resources, link) { | |||
|
|
731 | if (wl_resource_get_client(group_res->resource) == client) { | |||
|
|
732 | ext_workspace_group_handle_v1_send_output_enter(group_res->resource, | |||
|
|
733 | event->resource); | |||
|
|
734 | } | |||
|
|
735 | } | |||
|
|
736 | ||||
|
|
737 | manager_schedule_done(group_output->group->manager); | |||
|
|
738 | } | |||
|
|
739 | ||||
|
|
740 | static void handle_output_destroy(struct wl_listener *listener, void *data) { | |||
|
|
741 | struct wlr_ext_workspace_v1_group_output *group_output = | |||
|
|
742 | wl_container_of(listener, group_output, output_destroy); | |||
|
|
743 | group_send_output(group_output->group, group_output->output, false); | |||
|
|
744 | destroy_group_output(group_output); | |||
|
|
745 | } | |||
|
|
746 | ||||
|
|
747 | static struct wlr_ext_workspace_v1_group_output * | |||
|
|
748 | get_group_output(struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
749 | struct wlr_output *output) { | |||
|
|
750 | struct wlr_ext_workspace_v1_group_output *group_output; | |||
|
|
751 | wl_list_for_each(group_output, &group->outputs, link) { | |||
|
|
752 | if (group_output->output == output) { | |||
|
|
753 | return group_output; | |||
|
|
754 | } | |||
|
|
755 | } | |||
|
|
756 | return NULL; | |||
|
|
757 | } | |||
|
|
758 | ||||
|
|
759 | void wlr_ext_workspace_group_handle_v1_output_enter( | |||
|
|
760 | struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
761 | struct wlr_output *output) { | |||
|
|
762 | if (get_group_output(group, output)) { | |||
|
|
763 | return; | |||
|
|
764 | } | |||
|
|
765 | struct wlr_ext_workspace_v1_group_output *group_output = | |||
|
|
766 | calloc(1, sizeof(*group_output)); | |||
|
|
767 | if (!group_output) { | |||
|
|
768 | return; | |||
|
|
769 | } | |||
|
|
770 | group_output->output = output; | |||
|
|
771 | group_output->group = group; | |||
|
|
772 | wl_list_insert(&group->outputs, &group_output->link); | |||
|
|
773 | ||||
|
|
774 | group_output->output_bind.notify = handle_output_bind; | |||
|
|
775 | wl_signal_add(&output->events.bind, &group_output->output_bind); | |||
|
|
776 | group_output->output_destroy.notify = handle_output_destroy; | |||
|
|
777 | wl_signal_add(&output->events.destroy, &group_output->output_destroy); | |||
|
|
778 | ||||
|
|
779 | group_send_output(group, output, true); | |||
|
|
780 | } | |||
|
|
781 | ||||
|
|
782 | void wlr_ext_workspace_group_handle_v1_output_leave( | |||
|
|
783 | struct wlr_ext_workspace_group_handle_v1 *group, | |||
|
|
784 | struct wlr_output *output) { | |||
|
|
785 | struct wlr_ext_workspace_v1_group_output *group_output = | |||
|
|
786 | get_group_output(group, output); | |||
|
|
787 | if (!group_output) { | |||
|
|
788 | return; | |||
|
|
789 | } | |||
|
|
790 | ||||
|
|
791 | group_send_output(group, output, false); | |||
|
|
792 | destroy_group_output(group_output); | |||
|
|
793 | } | |||
|
|
794 | ||||
|
|
795 | struct wlr_ext_workspace_handle_v1 * | |||
|
|
796 | wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, | |||
|
|
797 | const char *id, uint32_t caps) { | |||
|
|
798 | struct wlr_ext_workspace_handle_v1 *workspace = | |||
|
|
799 | calloc(1, sizeof(*workspace)); | |||
|
|
800 | if (!workspace) { | |||
|
|
801 | return NULL; | |||
|
|
802 | } | |||
|
|
803 | ||||
|
|
804 | workspace->manager = manager; | |||
|
|
805 | workspace->caps = caps; | |||
|
|
806 | ||||
|
|
807 | if (id) { | |||
|
|
808 | workspace->id = strdup(id); | |||
|
|
809 | if (!workspace->id) { | |||
|
|
810 | free(workspace); | |||
|
|
811 | return NULL; | |||
|
|
812 | } | |||
|
|
813 | } | |||
|
|
814 | ||||
|
|
815 | wl_list_init(&workspace->resources); | |||
|
|
816 | wl_array_init(&workspace->coordinates); | |||
|
|
817 | wl_signal_init(&workspace->events.destroy); | |||
|
|
818 | ||||
|
|
819 | wl_list_insert(manager->workspaces.prev, &workspace->link); | |||
|
|
820 | ||||
|
|
821 | struct wlr_ext_workspace_manager_v1_resource *manager_res; | |||
|
|
822 | wl_list_for_each(manager_res, &manager->resources, link) { | |||
|
|
823 | struct wlr_ext_workspace_v1_resource *workspace_res = | |||
|
|
824 | create_workspace_resource(workspace, manager_res); | |||
|
|
825 | if (!workspace_res) { | |||
|
|
826 | continue; | |||
|
|
827 | } | |||
|
|
828 | ext_workspace_manager_v1_send_workspace(manager_res->resource, | |||
|
|
829 | workspace_res->resource); | |||
|
|
830 | workspace_send_details(workspace_res); | |||
|
|
831 | } | |||
|
|
832 | ||||
|
|
833 | manager_schedule_done(manager); | |||
|
|
834 | ||||
|
|
835 | return workspace; | |||
|
|
836 | } | |||
|
|
837 | ||||
|
|
838 | void wlr_ext_workspace_handle_v1_destroy( | |||
|
|
839 | struct wlr_ext_workspace_handle_v1 *workspace) { | |||
|
|
840 | if (!workspace) { | |||
|
|
841 | return; | |||
|
|
842 | } | |||
|
|
843 | ||||
|
|
844 | wl_signal_emit_mutable(&workspace->events.destroy, NULL); | |||
|
|
845 | ||||
|
|
846 | assert(wl_list_empty(&workspace->events.destroy.listener_list)); | |||
|
|
847 | ||||
|
|
848 | if (workspace->group) { | |||
|
|
849 | workspace_send_group(workspace, workspace->group, false); | |||
|
|
850 | } | |||
|
|
851 | ||||
|
|
852 | struct wlr_ext_workspace_v1_resource *workspace_res, *tmp; | |||
|
|
853 | wl_list_for_each_safe(workspace_res, tmp, &workspace->resources, link) { | |||
|
|
854 | ext_workspace_handle_v1_send_removed(workspace_res->resource); | |||
|
|
855 | destroy_workspace_resource(workspace_res); | |||
|
|
856 | } | |||
|
|
857 | ||||
|
|
858 | struct wlr_ext_workspace_manager_v1_resource *manager_res; | |||
|
|
859 | wl_list_for_each(manager_res, &workspace->manager->resources, link) { | |||
|
|
860 | clear_requests_by(manager_res, NULL, workspace); | |||
|
|
861 | } | |||
|
|
862 | ||||
|
|
863 | manager_schedule_done(workspace->manager); | |||
|
|
864 | ||||
|
|
865 | wl_list_remove(&workspace->link); | |||
|
|
866 | wl_array_release(&workspace->coordinates); | |||
|
|
867 | free(workspace->id); | |||
|
|
868 | free(workspace->name); | |||
|
|
869 | free(workspace); | |||
|
|
870 | } | |||
|
|
871 | ||||
|
|
872 | void wlr_ext_workspace_handle_v1_set_group( | |||
|
|
873 | struct wlr_ext_workspace_handle_v1 *workspace, | |||
|
|
874 | struct wlr_ext_workspace_group_handle_v1 *group) { | |||
|
|
875 | if (workspace->group == group) { | |||
|
|
876 | return; | |||
|
|
877 | } | |||
|
|
878 | ||||
|
|
879 | if (workspace->group) { | |||
|
|
880 | workspace_send_group(workspace, workspace->group, false); | |||
|
|
881 | } | |||
|
|
882 | workspace->group = group; | |||
|
|
883 | if (group) { | |||
|
|
884 | workspace_send_group(workspace, group, true); | |||
|
|
885 | } | |||
|
|
886 | } | |||
|
|
887 | ||||
|
|
888 | void wlr_ext_workspace_handle_v1_set_name( | |||
|
|
889 | struct wlr_ext_workspace_handle_v1 *workspace, const char *name) { | |||
|
|
890 | assert(name); | |||
|
|
891 | ||||
|
|
892 | if (workspace->name && strcmp(workspace->name, name) == 0) { | |||
|
|
893 | return; | |||
|
|
894 | } | |||
|
|
895 | ||||
|
|
896 | free(workspace->name); | |||
|
|
897 | workspace->name = strdup(name); | |||
|
|
898 | if (workspace->name == NULL) { | |||
|
|
899 | return; | |||
|
|
900 | } | |||
|
|
901 | ||||
|
|
902 | struct wlr_ext_workspace_v1_resource *workspace_res; | |||
|
|
903 | wl_list_for_each(workspace_res, &workspace->resources, link) { | |||
|
|
904 | ext_workspace_handle_v1_send_name(workspace_res->resource, | |||
|
|
905 | workspace->name); | |||
|
|
906 | } | |||
|
|
907 | ||||
|
|
908 | manager_schedule_done(workspace->manager); | |||
|
|
909 | } | |||
|
|
910 | ||||
|
|
911 | void wlr_ext_workspace_handle_v1_set_coordinates( | |||
|
|
912 | struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords, | |||
|
|
913 | size_t coords_len) { | |||
|
|
914 | size_t size = coords_len * sizeof(coords[0]); | |||
|
|
915 | if (size == workspace->coordinates.size && | |||
|
|
916 | (size == 0 || memcmp(workspace->coordinates.data, coords, size) == 0)) { | |||
|
|
917 | return; | |||
|
|
918 | } | |||
|
|
919 | ||||
|
|
920 | wl_array_release(&workspace->coordinates); | |||
|
|
921 | wl_array_init(&workspace->coordinates); | |||
|
|
922 | struct wl_array arr = { | |||
|
|
923 | .data = (void *)coords, | |||
|
|
924 | .size = size, | |||
|
|
925 | }; | |||
|
|
926 | wl_array_copy(&workspace->coordinates, &arr); | |||
|
|
927 | ||||
|
|
928 | struct wlr_ext_workspace_v1_resource *workspace_res; | |||
|
|
929 | wl_list_for_each(workspace_res, &workspace->resources, link) { | |||
|
|
930 | ext_workspace_handle_v1_send_coordinates(workspace_res->resource, | |||
|
|
931 | &workspace->coordinates); | |||
|
|
932 | } | |||
|
|
933 | ||||
|
|
934 | manager_schedule_done(workspace->manager); | |||
|
|
935 | } | |||
|
|
936 | ||||
|
|
937 | static void workspace_set_state(struct wlr_ext_workspace_handle_v1 *workspace, | |||
|
|
938 | enum ext_workspace_handle_v1_state state, | |||
|
|
939 | bool enabled) { | |||
|
|
940 | uint32_t old_state = workspace->state; | |||
|
|
941 | if (enabled) { | |||
|
|
942 | workspace->state |= state; | |||
|
|
943 | } else { | |||
|
|
944 | workspace->state &= ~state; | |||
|
|
945 | } | |||
|
|
946 | if (old_state == workspace->state) { | |||
|
|
947 | return; | |||
|
|
948 | } | |||
|
|
949 | ||||
|
|
950 | struct wlr_ext_workspace_v1_resource *workspace_res; | |||
|
|
951 | wl_list_for_each(workspace_res, &workspace->resources, link) { | |||
|
|
952 | ext_workspace_handle_v1_send_state(workspace_res->resource, | |||
|
|
953 | workspace->state); | |||
|
|
954 | } | |||
|
|
955 | ||||
|
|
956 | manager_schedule_done(workspace->manager); | |||
|
|
957 | } | |||
|
|
958 | ||||
|
|
959 | void wlr_ext_workspace_handle_v1_set_active( | |||
|
|
960 | struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) { | |||
|
|
961 | workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_ACTIVE, | |||
|
|
962 | enabled); | |||
|
|
963 | } | |||
|
|
964 | ||||
|
|
965 | void wlr_ext_workspace_handle_v1_set_urgent( | |||
|
|
966 | struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) { | |||
|
|
967 | workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_URGENT, | |||
|
|
968 | enabled); | |||
|
|
969 | } | |||
|
|
970 | ||||
|
|
971 | void wlr_ext_workspace_handle_v1_set_hidden( | |||
|
|
972 | struct wlr_ext_workspace_handle_v1 *workspace, bool enabled) { | |||
|
|
973 | workspace_set_state(workspace, EXT_WORKSPACE_HANDLE_V1_STATE_HIDDEN, | |||
|
|
974 | enabled); | |||
|
|
975 | } | |||
| @@ -0,0 +1,150 | |||||
|
|
1 | /* | |||
|
|
2 | * This an unstable interface of wlroots. No guarantees are made regarding the | |||
|
|
3 | * future consistency of this API. | |||
|
|
4 | */ | |||
|
|
5 | #ifndef WLR_USE_UNSTABLE | |||
|
|
6 | #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" | |||
|
|
7 | #endif | |||
|
|
8 | ||||
|
|
9 | #ifndef WLR_TYPES_WLR_EXT_WORKSPACE_V1_H | |||
|
|
10 | #define WLR_TYPES_WLR_EXT_WORKSPACE_V1_H | |||
|
|
11 | ||||
|
|
12 | #include <wayland-protocols/ext-workspace-v1-enum.h> | |||
|
|
13 | #include <wayland-server-core.h> | |||
|
|
14 | ||||
|
|
15 | struct wlr_output; | |||
|
|
16 | ||||
|
|
17 | enum wlr_ext_workspace_v1_request_type { | |||
|
|
18 | WLR_EXT_WORKSPACE_V1_REQUEST_CREATE_WORKSPACE, | |||
|
|
19 | WLR_EXT_WORKSPACE_V1_REQUEST_ACTIVATE, | |||
|
|
20 | WLR_EXT_WORKSPACE_V1_REQUEST_DEACTIVATE, | |||
|
|
21 | WLR_EXT_WORKSPACE_V1_REQUEST_ASSIGN, | |||
|
|
22 | WLR_EXT_WORKSPACE_V1_REQUEST_REMOVE, | |||
|
|
23 | }; | |||
|
|
24 | ||||
|
|
25 | struct wlr_ext_workspace_v1_request { | |||
|
|
26 | enum wlr_ext_workspace_v1_request_type type; | |||
|
|
27 | struct wl_list link; // wlr_ext_workspace_manager_v1_resource.requests | |||
|
|
28 | union { | |||
|
|
29 | struct { | |||
|
|
30 | char *name; | |||
|
|
31 | struct wlr_ext_workspace_group_handle_v1 | |||
|
|
32 | *group; // NULL if destroyed | |||
|
|
33 | } create_workspace; | |||
|
|
34 | struct { | |||
|
|
35 | struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed | |||
|
|
36 | } activate; | |||
|
|
37 | struct { | |||
|
|
38 | struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed | |||
|
|
39 | } deactivate; | |||
|
|
40 | struct { | |||
|
|
41 | struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed | |||
|
|
42 | struct wlr_ext_workspace_group_handle_v1 | |||
|
|
43 | *group; // NULL if destroyed | |||
|
|
44 | } assign; | |||
|
|
45 | struct { | |||
|
|
46 | struct wlr_ext_workspace_handle_v1 *workspace; // NULL if destroyed | |||
|
|
47 | } remove; | |||
|
|
48 | }; | |||
|
|
49 | }; | |||
|
|
50 | ||||
|
|
51 | struct wlr_ext_workspace_v1_commit_event { | |||
|
|
52 | struct wl_list *requests; // wlr_ext_workspace_v1_request.link | |||
|
|
53 | }; | |||
|
|
54 | ||||
|
|
55 | struct wlr_ext_workspace_manager_v1 { | |||
|
|
56 | struct wl_global *global; | |||
|
|
57 | struct wl_list groups; // wlr_ext_workspace_group_handle_v1.link | |||
|
|
58 | struct wl_list workspaces; // wlr_ext_workspace_handle_v1.link | |||
|
|
59 | ||||
|
|
60 | struct { | |||
|
|
61 | struct wl_signal commit; // wlr_ext_workspace_v1_commit_event | |||
|
|
62 | struct wl_signal destroy; | |||
|
|
63 | } events; | |||
|
|
64 | ||||
|
|
65 | void *data; | |||
|
|
66 | ||||
|
|
67 | struct { | |||
|
|
68 | struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link | |||
|
|
69 | struct wl_event_source *idle_source; | |||
|
|
70 | struct wl_event_loop *event_loop; | |||
|
|
71 | struct wl_listener display_destroy; | |||
|
|
72 | }; | |||
|
|
73 | }; | |||
|
|
74 | ||||
|
|
75 | struct wlr_ext_workspace_group_handle_v1 { | |||
|
|
76 | struct wlr_ext_workspace_manager_v1 *manager; | |||
|
|
77 | uint32_t caps; // ext_workspace_group_handle_v1_group_capabilities | |||
|
|
78 | struct { | |||
|
|
79 | struct wl_signal destroy; | |||
|
|
80 | } events; | |||
|
|
81 | ||||
|
|
82 | struct wl_list link; // wlr_ext_workspace_manager_v1.groups | |||
|
|
83 | ||||
|
|
84 | void *data; | |||
|
|
85 | ||||
|
|
86 | struct { | |||
|
|
87 | struct wl_list outputs; // wlr_ext_workspace_v1_group_output.link | |||
|
|
88 | struct wl_list resources; // wlr_ext_workspace_manager_v1_resource.link | |||
|
|
89 | }; | |||
|
|
90 | }; | |||
|
|
91 | ||||
|
|
92 | struct wlr_ext_workspace_handle_v1 { | |||
|
|
93 | struct wlr_ext_workspace_manager_v1 *manager; | |||
|
|
94 | struct wlr_ext_workspace_group_handle_v1 *group; // May be NULL | |||
|
|
95 | char *id; | |||
|
|
96 | char *name; | |||
|
|
97 | struct wl_array coordinates; | |||
|
|
98 | uint32_t caps; // ext_workspace_handle_v1_workspace_capabilities | |||
|
|
99 | uint32_t state; // ext_workspace_handle_v1_state | |||
|
|
100 | ||||
|
|
101 | struct { | |||
|
|
102 | struct wl_signal destroy; | |||
|
|
103 | } events; | |||
|
|
104 | ||||
|
|
105 | struct wl_list link; // wlr_ext_workspace_manager_v1.workspaces | |||
|
|
106 | ||||
|
|
107 | void *data; | |||
|
|
108 | ||||
|
|
109 | struct { | |||
|
|
110 | struct wl_list resources; // wlr_ext_workspace_v1_resource.link | |||
|
|
111 | }; | |||
|
|
112 | }; | |||
|
|
113 | ||||
|
|
114 | struct wlr_ext_workspace_manager_v1 * | |||
|
|
115 | wlr_ext_workspace_manager_v1_create(struct wl_display *display, | |||
|
|
116 | uint32_t version); | |||
|
|
117 | ||||
|
|
118 | struct wlr_ext_workspace_group_handle_v1 * | |||
|
|
119 | wlr_ext_workspace_group_handle_v1_create( | |||
|
|
120 | struct wlr_ext_workspace_manager_v1 *manager, uint32_t caps); | |||
|
|
121 | void wlr_ext_workspace_group_handle_v1_destroy( | |||
|
|
122 | struct wlr_ext_workspace_group_handle_v1 *group); | |||
|
|
123 | ||||
|
|
124 | void wlr_ext_workspace_group_handle_v1_output_enter( | |||
|
|
125 | struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output); | |||
|
|
126 | void wlr_ext_workspace_group_handle_v1_output_leave( | |||
|
|
127 | struct wlr_ext_workspace_group_handle_v1 *group, struct wlr_output *output); | |||
|
|
128 | ||||
|
|
129 | struct wlr_ext_workspace_handle_v1 * | |||
|
|
130 | wlr_ext_workspace_handle_v1_create(struct wlr_ext_workspace_manager_v1 *manager, | |||
|
|
131 | const char *id, uint32_t caps); | |||
|
|
132 | void wlr_ext_workspace_handle_v1_destroy( | |||
|
|
133 | struct wlr_ext_workspace_handle_v1 *workspace); | |||
|
|
134 | ||||
|
|
135 | void wlr_ext_workspace_handle_v1_set_group( | |||
|
|
136 | struct wlr_ext_workspace_handle_v1 *workspace, | |||
|
|
137 | struct wlr_ext_workspace_group_handle_v1 *group); | |||
|
|
138 | void wlr_ext_workspace_handle_v1_set_name( | |||
|
|
139 | struct wlr_ext_workspace_handle_v1 *workspace, const char *name); | |||
|
|
140 | void wlr_ext_workspace_handle_v1_set_coordinates( | |||
|
|
141 | struct wlr_ext_workspace_handle_v1 *workspace, const uint32_t *coords, | |||
|
|
142 | size_t coords_len); | |||
|
|
143 | void wlr_ext_workspace_handle_v1_set_active( | |||
|
|
144 | struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); | |||
|
|
145 | void wlr_ext_workspace_handle_v1_set_urgent( | |||
|
|
146 | struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); | |||
|
|
147 | void wlr_ext_workspace_handle_v1_set_hidden( | |||
|
|
148 | struct wlr_ext_workspace_handle_v1 *workspace, bool enabled); | |||
|
|
149 | ||||
|
|
150 | #endif | |||
General Comments 0
You need to be logged in to leave comments.
Login now
