]> git.lizzy.rs Git - bspwm.git/blob - desktop.c
Don't confine the CYCLE_DIR descriptor
[bspwm.git] / desktop.c
1 /* Copyright (c) 2012, Bastien Dejean
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * 1. Redistributions of source code must retain the above copyright notice, this
8  *    list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright notice,
10  *    this list of conditions and the following disclaimer in the documentation
11  *    and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
17  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
20  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <stdbool.h>
28 #include <string.h>
29 #include "bspwm.h"
30 #include "ewmh.h"
31 #include "history.h"
32 #include "monitor.h"
33 #include "query.h"
34 #include "tree.h"
35 #include "desktop.h"
36 #include "subscribe.h"
37 #include "settings.h"
38
39 void focus_desktop(monitor_t *m, desktop_t *d)
40 {
41         focus_monitor(m);
42
43         if (focus_follows_pointer) {
44                 listen_enter_notify(d->root, false);
45         }
46
47         show_desktop(d);
48         if (m->desk != d) {
49                 hide_desktop(m->desk);
50         }
51
52         if (focus_follows_pointer) {
53                 listen_enter_notify(d->root, true);
54         }
55
56         m->desk = d;
57         ewmh_update_current_desktop();
58
59         put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
60 }
61
62 void activate_desktop(monitor_t *m, desktop_t *d)
63 {
64         if (d == m->desk) {
65                 return;
66         }
67
68         show_desktop(d);
69         hide_desktop(m->desk);
70
71         m->desk = d;
72
73         put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate 0x%08X 0x%08X\n", m->id, d->id);
74         put_status(SBSC_MASK_REPORT);
75 }
76
77 bool find_closest_desktop(coordinates_t *ref, coordinates_t *dst, cycle_dir_t dir, desktop_select_t sel)
78 {
79         monitor_t *m = ref->monitor;
80         desktop_t *d = ref->desktop;
81         desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
82
83 #define HANDLE_BOUNDARIES(f)  \
84         if (f == NULL) { \
85                 m = (dir == CYCLE_PREV ? m->prev : m->next); \
86                 if (m == NULL) { \
87                         m = (dir == CYCLE_PREV ? mon_tail : mon_head); \
88                 } \
89                 f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head); \
90         }
91         HANDLE_BOUNDARIES(f)
92
93         while (f != d) {
94                 coordinates_t loc = {m, f, NULL};
95                 if (desktop_matches(&loc, &loc, sel)) {
96                         *dst = loc;
97                         return true;
98                 }
99                 f = (dir == CYCLE_PREV ? f->prev : f->next);
100                 HANDLE_BOUNDARIES(f)
101         }
102 #undef HANDLE_BOUNDARIES
103
104         return false;
105 }
106
107 void change_layout(monitor_t *m, desktop_t *d, layout_t l)
108 {
109         d->layout = l;
110         arrange(m, d);
111
112         put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(l));
113
114         if (d == m->desk) {
115                 put_status(SBSC_MASK_REPORT);
116         }
117 }
118
119 bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
120 {
121         if (ms == NULL || md == NULL || d == NULL || ms == md) {
122                 return false;
123         }
124
125         bool was_active = (d == ms->desk);
126
127         unlink_desktop(ms, d);
128
129         if (ms->sticky_count > 0 && was_active && ms->desk != NULL) {
130                 sticky_still = false;
131                 transfer_sticky_nodes(ms, d, ms->desk, d->root);
132                 sticky_still = true;
133         }
134
135         if (md->desk != NULL) {
136                 hide_desktop(d);
137         }
138
139         insert_desktop(md, d);
140
141         history_transfer_desktop(md, d);
142         adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
143         arrange(md, d);
144
145         if (was_active && ms->desk != NULL) {
146                 if (mon == ms) {
147                         focus_node(ms, ms->desk, ms->desk->focus);
148                 } else {
149                         activate_node(ms, ms->desk, ms->desk->focus);
150                 }
151         }
152
153         if (md->desk == d) {
154                 if (mon == md) {
155                         focus_node(md, d, d->focus);
156                 } else {
157                         activate_node(md, d, d->focus);
158                 }
159         }
160
161         ewmh_update_wm_desktops();
162         ewmh_update_desktop_names();
163         ewmh_update_current_desktop();
164
165         put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
166         put_status(SBSC_MASK_REPORT);
167
168         return true;
169 }
170
171 desktop_t *make_desktop(const char *name, uint32_t id)
172 {
173         desktop_t *d = malloc(sizeof(desktop_t));
174         snprintf(d->name, sizeof(d->name), "%s", name == NULL ? DEFAULT_DESK_NAME : name);
175         if (id == XCB_NONE) {
176                 d->id = xcb_generate_id(dpy);
177         }
178         d->prev = d->next = NULL;
179         d->root = d->focus = NULL;
180         d->layout = LAYOUT_TILED;
181         d->padding = (padding_t) PADDING;
182         d->window_gap = window_gap;
183         d->border_width = border_width;
184         return d;
185 }
186
187 void rename_desktop(monitor_t *m, desktop_t *d, const char *name)
188 {
189
190         put_status(SBSC_MASK_DESKTOP_RENAME, "desktop_rename 0x%08X 0x%08X %s %s\n", m->id, d->id, d->name, name);
191
192         snprintf(d->name, sizeof(d->name), "%s", name);
193
194         put_status(SBSC_MASK_REPORT);
195         ewmh_update_desktop_names();
196 }
197
198 void insert_desktop(monitor_t *m, desktop_t *d)
199 {
200         if (m->desk == NULL) {
201                 m->desk = d;
202                 m->desk_head = d;
203                 m->desk_tail = d;
204         } else {
205                 m->desk_tail->next = d;
206                 d->prev = m->desk_tail;
207                 m->desk_tail = d;
208         }
209 }
210
211 void add_desktop(monitor_t *m, desktop_t *d)
212 {
213         put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add 0x%08X %s 0x%08X\n", d->id, d->name, m->id);
214
215         d->border_width = m->border_width;
216         d->window_gap = m->window_gap;
217         insert_desktop(m, d);
218         ewmh_update_number_of_desktops();
219         ewmh_update_desktop_names();
220         ewmh_update_wm_desktops();
221         put_status(SBSC_MASK_REPORT);
222 }
223
224 desktop_t *find_desktop_in(uint32_t id, monitor_t *m)
225 {
226         if (m == NULL) {
227                 return NULL;
228         }
229
230         for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
231                 if (d->id == id) {
232                         return d;
233                 }
234         }
235
236         return NULL;
237 }
238
239 void empty_desktop(monitor_t *m, desktop_t *d)
240 {
241         destroy_tree(m, d, d->root);
242         d->root = d->focus = NULL;
243 }
244
245 void unlink_desktop(monitor_t *m, desktop_t *d)
246 {
247         desktop_t *prev = d->prev;
248         desktop_t *next = d->next;
249
250         if (prev != NULL) {
251                 prev->next = next;
252         }
253
254         if (next != NULL) {
255                 next->prev = prev;
256         }
257
258         if (m->desk_head == d) {
259                 m->desk_head = next;
260         }
261
262         if (m->desk_tail == d) {
263                 m->desk_tail = prev;
264         }
265
266         if (m->desk == d) {
267                 m->desk = history_last_desktop(m, d);
268                 if (m->desk == NULL) {
269                         m->desk = (prev == NULL ? next : prev);
270                 }
271         }
272
273         d->prev = d->next = NULL;
274 }
275
276 void remove_desktop(monitor_t *m, desktop_t *d)
277 {
278         put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);
279
280         bool was_focused = (mon != NULL && d == mon->desk);
281         bool was_active = (d == m->desk);
282         history_remove(d, NULL, false);
283         unlink_desktop(m, d);
284         empty_desktop(m, d);
285         free(d);
286
287         ewmh_update_current_desktop();
288         ewmh_update_number_of_desktops();
289         ewmh_update_desktop_names();
290
291         if (mon != NULL && m->desk != NULL) {
292                 if (was_focused) {
293                         update_focused();
294                 } else if (was_active) {
295                         activate_node(m, m->desk, m->desk->focus);
296                 }
297         }
298
299         put_status(SBSC_MASK_REPORT);
300 }
301
302 void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
303 {
304         if (ds == NULL || dd == NULL || ds == dd) {
305                 return;
306         }
307         transfer_node(ms, ds, ds->root, md, dd, dd->focus);
308 }
309
310 bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
311 {
312         if (d1 == NULL || d2 == NULL || d1 == d2 ||
313             (m1->desk == d1 && m1->sticky_count > 0) ||
314             (m2->desk == d2 && m2->sticky_count > 0)) {
315                 return false;
316         }
317
318         put_status(SBSC_MASK_DESKTOP_SWAP, "desktop_swap 0x%08X 0x%08X 0x%08X 0x%08X\n", m1->id, d1->id, m2->id, d2->id);
319
320         bool d1_focused = (m1->desk == d1);
321         bool d2_focused = (m2->desk == d2);
322
323         if (m1 != m2) {
324                 if (m1->desk == d1) {
325                         m1->desk = d2;
326                 }
327                 if (m1->desk_head == d1) {
328                         m1->desk_head = d2;
329                 }
330                 if (m1->desk_tail == d1) {
331                         m1->desk_tail = d2;
332                 }
333                 if (m2->desk == d2) {
334                         m2->desk = d1;
335                 }
336                 if (m2->desk_head == d2) {
337                         m2->desk_head = d1;
338                 }
339                 if (m2->desk_tail == d2) {
340                         m2->desk_tail = d1;
341                 }
342         } else {
343                 if (m1->desk == d1) {
344                         m1->desk = d2;
345                 } else if (m1->desk == d2) {
346                         m1->desk = d1;
347                 }
348                 if (m1->desk_head == d1) {
349                         m1->desk_head = d2;
350                 } else if (m1->desk_head == d2) {
351                         m1->desk_head = d1;
352                 }
353                 if (m1->desk_tail == d1) {
354                         m1->desk_tail = d2;
355                 } else if (m1->desk_tail == d2) {
356                         m1->desk_tail = d1;
357                 }
358         }
359
360         desktop_t *p1 = d1->prev;
361         desktop_t *n1 = d1->next;
362         desktop_t *p2 = d2->prev;
363         desktop_t *n2 = d2->next;
364
365         if (p1 != NULL && p1 != d2) {
366                 p1->next = d2;
367         }
368         if (n1 != NULL && n1 != d2) {
369                 n1->prev = d2;
370         }
371         if (p2 != NULL && p2 != d1) {
372                 p2->next = d1;
373         }
374         if (n2 != NULL && n2 != d1) {
375                 n2->prev = d1;
376         }
377
378         d1->prev = p2 == d1 ? d2 : p2;
379         d1->next = n2 == d1 ? d2 : n2;
380         d2->prev = p1 == d2 ? d1 : p1;
381         d2->next = n1 == d2 ? d1 : n1;
382
383         if (m1 != m2) {
384                 adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
385                 adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
386                 history_swap_desktops(m1, d1, m2, d2);
387                 arrange(m1, d2);
388                 arrange(m2, d1);
389         }
390
391         if (d1_focused && !d2_focused) {
392                 hide_desktop(d1);
393                 show_desktop(d2);
394         } else if (!d1_focused && d2_focused) {
395                 show_desktop(d1);
396                 hide_desktop(d2);
397         }
398
399         if (d1 == mon->desk) {
400                 focus_node(m2, d1, d1->focus);
401         } else if (d1 == m2->desk) {
402                 activate_node(m2, d1, d1->focus);
403         }
404
405         if (d2 == mon->desk) {
406                 focus_node(m1, d2, d2->focus);
407         } else if (d2 == m1->desk) {
408                 activate_node(m1, d2, d2->focus);
409         }
410
411         ewmh_update_wm_desktops();
412         ewmh_update_desktop_names();
413         ewmh_update_current_desktop();
414
415         put_status(SBSC_MASK_REPORT);
416
417         return true;
418 }
419
420 void show_desktop(desktop_t *d)
421 {
422         if (d == NULL) {
423                 return;
424         }
425         show_node(d->root);
426 }
427
428 void hide_desktop(desktop_t *d)
429 {
430         if (d == NULL) {
431                 return;
432         }
433         hide_node(d->root);
434 }
435
436 bool is_urgent(desktop_t *d)
437 {
438         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
439                 if (n->client == NULL) {
440                         continue;
441                 }
442                 if (n->client->urgent) {
443                         return true;
444                 }
445         }
446         return false;
447 }