]> git.lizzy.rs Git - bspwm.git/blob - desktop.c
6aa8657fd2da123307c8544facca011527b83e51
[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         show_desktop(d);
44         if (m->desk != d) {
45                 hide_desktop(m->desk);
46         }
47
48         m->desk = d;
49         ewmh_update_current_desktop();
50
51         put_status(SBSC_MASK_DESKTOP_FOCUS, "desktop_focus 0x%08X 0x%08X\n", m->id, d->id);
52 }
53
54 void activate_desktop(monitor_t *m, desktop_t *d)
55 {
56         if (d == m->desk) {
57                 return;
58         }
59
60         show_desktop(d);
61         hide_desktop(m->desk);
62
63         m->desk = d;
64
65         put_status(SBSC_MASK_DESKTOP_ACTIVATE, "desktop_activate 0x%08X 0x%08X\n", m->id, d->id);
66         put_status(SBSC_MASK_REPORT);
67 }
68
69 desktop_t *closest_desktop(monitor_t *m, desktop_t *d, cycle_dir_t dir, desktop_select_t sel)
70 {
71         desktop_t *f = (dir == CYCLE_PREV ? d->prev : d->next);
72
73         if (f == NULL) {
74                 f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
75         }
76
77         while (f != d) {
78                 coordinates_t loc = {m, f, NULL};
79                 if (desktop_matches(&loc, &loc, sel)) {
80                         return f;
81                 }
82                 f = (dir == CYCLE_PREV ? f->prev : f->next);
83                 if (f == NULL) {
84                         f = (dir == CYCLE_PREV ? m->desk_tail : m->desk_head);
85                 }
86         }
87
88         return NULL;
89 }
90
91 void change_layout(monitor_t *m, desktop_t *d, layout_t l)
92 {
93         d->layout = l;
94         arrange(m, d);
95
96         put_status(SBSC_MASK_DESKTOP_LAYOUT, "desktop_layout 0x%08X 0x%08X %s\n", m->id, d->id, LAYOUT_STR(l));
97
98         if (d == m->desk) {
99                 put_status(SBSC_MASK_REPORT);
100         }
101 }
102
103 bool transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
104 {
105         if (ms == NULL || md == NULL || d == NULL || ms == md) {
106                 return false;
107         }
108
109         bool was_active = (d == ms->desk);
110
111         unlink_desktop(ms, d);
112
113         if (ms->sticky_count > 0 && was_active && ms->desk != NULL) {
114                 sticky_still = false;
115                 transfer_sticky_nodes(ms, d, ms->desk, d->root);
116                 sticky_still = true;
117         }
118
119         if (md->desk != NULL) {
120                 hide_desktop(d);
121         }
122
123         insert_desktop(md, d);
124
125         history_transfer_desktop(md, d);
126         adapt_geometry(&ms->rectangle, &md->rectangle, d->root);
127         arrange(md, d);
128
129         if (was_active && ms->desk != NULL) {
130                 if (mon == ms) {
131                         focus_node(ms, ms->desk, ms->desk->focus);
132                 } else {
133                         activate_node(ms, ms->desk, ms->desk->focus);
134                 }
135         }
136
137         if (md->desk == d) {
138                 if (mon == md) {
139                         focus_node(md, d, d->focus);
140                 } else {
141                         activate_node(md, d, d->focus);
142                 }
143         }
144
145         ewmh_update_wm_desktops();
146         ewmh_update_desktop_names();
147         ewmh_update_current_desktop();
148
149         put_status(SBSC_MASK_DESKTOP_TRANSFER, "desktop_transfer 0x%08X 0x%08X 0x%08X\n", ms->id, d->id, md->id);
150         put_status(SBSC_MASK_REPORT);
151
152         return true;
153 }
154
155 desktop_t *make_desktop(const char *name, uint32_t id)
156 {
157         desktop_t *d = malloc(sizeof(desktop_t));
158         snprintf(d->name, sizeof(d->name), "%s", name == NULL ? DEFAULT_DESK_NAME : name);
159         if (id == XCB_NONE) {
160                 d->id = xcb_generate_id(dpy);
161         }
162         d->prev = d->next = NULL;
163         d->root = d->focus = NULL;
164         d->layout = LAYOUT_TILED;
165         d->padding = (padding_t) PADDING;
166         d->window_gap = window_gap;
167         d->border_width = border_width;
168         return d;
169 }
170
171 void rename_desktop(monitor_t *m, desktop_t *d, const char *name)
172 {
173
174         put_status(SBSC_MASK_DESKTOP_RENAME, "desktop_rename 0x%08X 0x%08X %s %s\n", m->id, d->id, d->name, name);
175
176         snprintf(d->name, sizeof(d->name), "%s", name);
177
178         put_status(SBSC_MASK_REPORT);
179         ewmh_update_desktop_names();
180 }
181
182 void insert_desktop(monitor_t *m, desktop_t *d)
183 {
184         if (m->desk == NULL) {
185                 m->desk = d;
186                 m->desk_head = d;
187                 m->desk_tail = d;
188         } else {
189                 m->desk_tail->next = d;
190                 d->prev = m->desk_tail;
191                 m->desk_tail = d;
192         }
193 }
194
195 void add_desktop(monitor_t *m, desktop_t *d)
196 {
197         put_status(SBSC_MASK_DESKTOP_ADD, "desktop_add 0x%08X %s 0x%08X\n", d->id, d->name, m->id);
198
199         d->border_width = m->border_width;
200         d->window_gap = m->window_gap;
201         insert_desktop(m, d);
202         ewmh_update_number_of_desktops();
203         ewmh_update_desktop_names();
204         ewmh_update_wm_desktops();
205         put_status(SBSC_MASK_REPORT);
206 }
207
208 desktop_t *find_desktop_in(uint32_t id, monitor_t *m)
209 {
210         if (m == NULL) {
211                 return NULL;
212         }
213
214         for (desktop_t *d = m->desk_head; d != NULL; d = d->next) {
215                 if (d->id == id) {
216                         return d;
217                 }
218         }
219
220         return NULL;
221 }
222
223 void empty_desktop(monitor_t *m, desktop_t *d)
224 {
225         destroy_tree(m, d, d->root);
226         d->root = d->focus = NULL;
227 }
228
229 void unlink_desktop(monitor_t *m, desktop_t *d)
230 {
231         desktop_t *prev = d->prev;
232         desktop_t *next = d->next;
233
234         if (prev != NULL) {
235                 prev->next = next;
236         }
237
238         if (next != NULL) {
239                 next->prev = prev;
240         }
241
242         if (m->desk_head == d) {
243                 m->desk_head = next;
244         }
245
246         if (m->desk_tail == d) {
247                 m->desk_tail = prev;
248         }
249
250         if (m->desk == d) {
251                 m->desk = history_last_desktop(m, d);
252                 if (m->desk == NULL) {
253                         m->desk = (prev == NULL ? next : prev);
254                 }
255         }
256
257         d->prev = d->next = NULL;
258 }
259
260 void remove_desktop(monitor_t *m, desktop_t *d)
261 {
262         put_status(SBSC_MASK_DESKTOP_REMOVE, "desktop_remove 0x%08X 0x%08X\n", m->id, d->id);
263
264         bool was_focused = (mon != NULL && d == mon->desk);
265         bool was_active = (d == m->desk);
266         history_remove(d, NULL, false);
267         unlink_desktop(m, d);
268         empty_desktop(m, d);
269         free(d);
270
271         ewmh_update_current_desktop();
272         ewmh_update_number_of_desktops();
273         ewmh_update_desktop_names();
274
275         if (mon != NULL && m->desk != NULL) {
276                 if (was_focused) {
277                         update_focused();
278                 } else if (was_active) {
279                         activate_node(m, m->desk, m->desk->focus);
280                 }
281         }
282
283         put_status(SBSC_MASK_REPORT);
284 }
285
286 void merge_desktops(monitor_t *ms, desktop_t *ds, monitor_t *md, desktop_t *dd)
287 {
288         if (ds == NULL || dd == NULL || ds == dd) {
289                 return;
290         }
291         transfer_node(ms, ds, ds->root, md, dd, dd->focus);
292 }
293
294 bool swap_desktops(monitor_t *m1, desktop_t *d1, monitor_t *m2, desktop_t *d2)
295 {
296         if (d1 == NULL || d2 == NULL || d1 == d2 ||
297             (m1->desk == d1 && m1->sticky_count > 0) ||
298             (m2->desk == d2 && m2->sticky_count > 0)) {
299                 return false;
300         }
301
302         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);
303
304         bool d1_focused = (m1->desk == d1);
305         bool d2_focused = (m2->desk == d2);
306
307         if (m1 != m2) {
308                 if (m1->desk == d1) {
309                         m1->desk = d2;
310                 }
311                 if (m1->desk_head == d1) {
312                         m1->desk_head = d2;
313                 }
314                 if (m1->desk_tail == d1) {
315                         m1->desk_tail = d2;
316                 }
317                 if (m2->desk == d2) {
318                         m2->desk = d1;
319                 }
320                 if (m2->desk_head == d2) {
321                         m2->desk_head = d1;
322                 }
323                 if (m2->desk_tail == d2) {
324                         m2->desk_tail = d1;
325                 }
326         } else {
327                 if (m1->desk == d1) {
328                         m1->desk = d2;
329                 } else if (m1->desk == d2) {
330                         m1->desk = d1;
331                 }
332                 if (m1->desk_head == d1) {
333                         m1->desk_head = d2;
334                 } else if (m1->desk_head == d2) {
335                         m1->desk_head = d1;
336                 }
337                 if (m1->desk_tail == d1) {
338                         m1->desk_tail = d2;
339                 } else if (m1->desk_tail == d2) {
340                         m1->desk_tail = d1;
341                 }
342         }
343
344         desktop_t *p1 = d1->prev;
345         desktop_t *n1 = d1->next;
346         desktop_t *p2 = d2->prev;
347         desktop_t *n2 = d2->next;
348
349         if (p1 != NULL && p1 != d2) {
350                 p1->next = d2;
351         }
352         if (n1 != NULL && n1 != d2) {
353                 n1->prev = d2;
354         }
355         if (p2 != NULL && p2 != d1) {
356                 p2->next = d1;
357         }
358         if (n2 != NULL && n2 != d1) {
359                 n2->prev = d1;
360         }
361
362         d1->prev = p2 == d1 ? d2 : p2;
363         d1->next = n2 == d1 ? d2 : n2;
364         d2->prev = p1 == d2 ? d1 : p1;
365         d2->next = n1 == d2 ? d1 : n1;
366
367         if (m1 != m2) {
368                 adapt_geometry(&m1->rectangle, &m2->rectangle, d1->root);
369                 adapt_geometry(&m2->rectangle, &m1->rectangle, d2->root);
370                 history_swap_desktops(m1, d1, m2, d2);
371                 arrange(m1, d2);
372                 arrange(m2, d1);
373         }
374
375         if (d1_focused && !d2_focused) {
376                 hide_desktop(d1);
377                 show_desktop(d2);
378         } else if (!d1_focused && d2_focused) {
379                 show_desktop(d1);
380                 hide_desktop(d2);
381         }
382
383         if (d1 == mon->desk) {
384                 focus_node(m2, d1, d1->focus);
385         } else if (d1 == m2->desk) {
386                 activate_node(m2, d1, d1->focus);
387         }
388
389         if (d2 == mon->desk) {
390                 focus_node(m1, d2, d2->focus);
391         } else if (d2 == m1->desk) {
392                 activate_node(m1, d2, d2->focus);
393         }
394
395         ewmh_update_wm_desktops();
396         ewmh_update_desktop_names();
397         ewmh_update_current_desktop();
398
399         put_status(SBSC_MASK_REPORT);
400
401         return true;
402 }
403
404 void show_desktop(desktop_t *d)
405 {
406         if (d == NULL) {
407                 return;
408         }
409         show_node(d->root);
410 }
411
412 void hide_desktop(desktop_t *d)
413 {
414         if (d == NULL) {
415                 return;
416         }
417         hide_node(d->root);
418 }
419
420 bool is_urgent(desktop_t *d)
421 {
422         for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root)) {
423                 if (n->client == NULL) {
424                         continue;
425                 }
426                 if (n->client->urgent) {
427                         return true;
428                 }
429         }
430         return false;
431 }