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
