]> git.lizzy.rs Git - bspwm.git/blob - types.c
Implement ICCCM's WM_TAKE_FOCUS behavior
[bspwm.git] / types.c
1 #include <stdlib.h>
2 #include <string.h>
3 #include <xcb/xcb.h>
4 #include <xcb/xcb_event.h>
5 #include "bspwm.h"
6 #include "window.h"
7 #include "rules.h"
8 #include "ewmh.h"
9 #include "settings.h"
10 #include "types.h"
11 #include "tree.h"
12
13 node_t *make_node(void)
14 {
15     node_t *n = malloc(sizeof(node_t));
16     n->parent = n->first_child = n->second_child = NULL;
17     n->split_ratio = split_ratio;
18     n->split_mode = MODE_AUTOMATIC;
19     n->split_type = TYPE_VERTICAL;
20     n->birth_rotation = 0;
21     n->client = NULL;
22     n->vacant = false;
23     return n;
24 }
25
26 monitor_t *make_monitor(xcb_rectangle_t *rect)
27 {
28     monitor_t *m = malloc(sizeof(monitor_t));
29     snprintf(m->name, sizeof(m->name), "%s%02d", DEFAULT_MON_NAME, ++monitor_uid);
30     m->prev = m->next = NULL;
31     m->desk = m->last_desk = NULL;
32     if (rect != NULL)
33         m->rectangle = *rect;
34     else
35         warn("no rectangle was given for monitor '%s'\n", m->name);
36     m->top_padding = m->right_padding = m->bottom_padding = m->left_padding = 0;
37     m->wired = true;
38     return m;
39 }
40
41 monitor_t *find_monitor(char *name)
42 {
43     for (monitor_t *m = mon_head; m != NULL; m = m->next)
44         if (streq(m->name, name))
45             return m;
46     return NULL;
47 }
48
49 monitor_t *get_monitor_by_id(xcb_randr_output_t id)
50 {
51     for (monitor_t *m = mon_head; m != NULL; m = m->next)
52         if (m->id == id)
53             return m;
54     return NULL;
55 }
56
57 monitor_t *add_monitor(xcb_rectangle_t *rect)
58 {
59     monitor_t *m = make_monitor(rect);
60     if (mon == NULL) {
61         mon = m;
62         mon_head = m;
63         mon_tail = m;
64     } else {
65         mon_tail->next = m;
66         m->prev = mon_tail;
67         mon_tail = m;
68     }
69     num_monitors++;
70     return m;
71 }
72
73 void remove_monitor(monitor_t *m)
74 {
75     while (m->desk_head != NULL)
76         remove_desktop(m, m->desk_head);
77     monitor_t *prev = m->prev;
78     monitor_t *next = m->next;
79     if (prev != NULL)
80         prev->next = next;
81     if (next != NULL)
82         next->prev = prev;
83     if (mon_head == m)
84         mon_head = next;
85     if (mon_tail == m)
86         mon_tail = prev;
87     if (last_mon == m)
88         last_mon = NULL;
89     if (mon == m) {
90         monitor_t *mm = (last_mon == NULL ? (prev == NULL ? next : prev) : last_mon);
91         if (mm != NULL) {
92             focus_node(mm, mm->desk, mm->desk->focus);
93             last_mon = NULL;
94         } else {
95             mon = NULL;
96         }
97     }
98     free(m);
99     num_monitors--;
100     put_status();
101 }
102
103 void transfer_desktop(monitor_t *ms, monitor_t *md, desktop_t *d)
104 {
105     if (ms == md)
106         return;
107     desktop_t *dd = ms->desk;
108     unlink_desktop(ms, d);
109     insert_desktop(md, d);
110     if (d == dd) {
111         if (ms->desk != NULL)
112             desktop_show(ms->desk);
113         if (md->desk != d)
114             desktop_hide(d);
115     }
116     for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n, d->root))
117         fit_monitor(md, n->client);
118     arrange(md, d);
119     if (d != dd && md->desk == d) {
120         desktop_show(d);
121     }
122     put_status();
123     ewmh_update_desktop_names();
124 }
125
126 void merge_monitors(monitor_t *ms, monitor_t *md)
127 {
128     PRINTF("merge %s into %s\n", ms->name, md->name);
129
130     desktop_t *d = ms->desk_head;
131     while (d != NULL) {
132         desktop_t *next = d->next;
133         transfer_desktop(ms, md, d);
134         d = next;
135     }
136 }
137
138 desktop_t *make_desktop(const char *name)
139 {
140     desktop_t *d = malloc(sizeof(desktop_t));
141     if (name == NULL)
142         snprintf(d->name, sizeof(d->name), "%s%02d", DEFAULT_DESK_NAME, ++desktop_uid);
143     else
144         strncpy(d->name, name, sizeof(d->name));
145     d->layout = LAYOUT_TILED;
146     d->prev = d->next = NULL;
147     d->root = d->focus = NULL;
148     d->history = make_focus_history();
149     return d;
150 }
151
152 void insert_desktop(monitor_t *m, desktop_t *d)
153 {
154     if (m->desk == NULL) {
155         m->desk = d;
156         m->desk_head = d;
157         m->desk_tail = d;
158     } else {
159         m->desk_tail->next = d;
160         d->prev = m->desk_tail;
161         m->desk_tail = d;
162     }
163 }
164
165 void add_desktop(monitor_t *m, desktop_t *d)
166 {
167     PRINTF("add desktop %s\n", d->name);
168
169     insert_desktop(m, d);
170     num_desktops++;
171     ewmh_update_number_of_desktops();
172     ewmh_update_desktop_names();
173     put_status();
174 }
175
176 void empty_desktop(desktop_t *d)
177 {
178     destroy_tree(d->root);
179     d->root = d->focus = NULL;
180     empty_history(d->history);
181 }
182
183 void unlink_desktop(monitor_t *m, desktop_t *d)
184 {
185     desktop_t *prev = d->prev;
186     desktop_t *next = d->next;
187     if (prev != NULL)
188         prev->next = next;
189     if (next != NULL)
190         next->prev = prev;
191     if (m->desk_head == d)
192         m->desk_head = next;
193     if (m->desk_tail == d)
194         m->desk_tail = prev;
195     if (m->last_desk == d)
196         m->last_desk = NULL;
197     if (m->desk == d)
198         m->desk = (m->last_desk == NULL ? (prev == NULL ? next : prev) : m->last_desk);
199     d->prev = d->next = NULL;
200 }
201
202 void remove_desktop(monitor_t *m, desktop_t *d)
203 {
204     PRINTF("remove desktop %s\n", d->name);
205
206     unlink_desktop(m, d);
207     empty_desktop(d);
208     free(d);
209     num_desktops--;
210     ewmh_update_number_of_desktops();
211     ewmh_update_desktop_names();
212     put_status();
213 }
214
215 client_t *make_client(xcb_window_t win)
216 {
217     client_t *c = malloc(sizeof(client_t));
218     strncpy(c->class_name, MISSING_VALUE, sizeof(c->class_name));
219     c->border_width = border_width;
220     c->window = win;
221     c->floating = c->transient = c->fullscreen = c->locked = c->urgent = false;
222     c->icccm_focus = false;
223     xcb_icccm_get_wm_protocols_reply_t protocols;
224     if (xcb_icccm_get_wm_protocols_reply(dpy, xcb_icccm_get_wm_protocols(dpy, win, ewmh->WM_PROTOCOLS), &protocols, NULL) == 1) {
225         if (has_proto(WM_TAKE_FOCUS, &protocols))
226             c->icccm_focus = true;
227         xcb_icccm_get_wm_protocols_reply_wipe(&protocols);
228     }
229     return c;
230 }
231
232 rule_t *make_rule(void)
233 {
234     rule_t *r = malloc(sizeof(rule_t));
235     r->uid = ++rule_uid;
236     r->effect.floating = false;
237     r->effect.follow = false;
238     r->effect.focus = false;
239     r->effect.desc[0] = '\0';
240     r->prev = NULL;
241     r->next = NULL;
242     return r;
243 }
244
245 pointer_state_t *make_pointer_state(void)
246 {
247     pointer_state_t *p = malloc(sizeof(pointer_state_t));
248     p->monitor = NULL;
249     p->desktop = NULL;
250     p->node = p->vertical_fence = p->horizontal_fence = NULL;
251     p->client = NULL;
252     p->window = XCB_NONE;
253     return p;
254 }
255
256 focus_history_t *make_focus_history(void)
257 {
258     focus_history_t *f = malloc(sizeof(focus_history_t));
259     f->head = f->tail = NULL;
260     return f;
261 }
262
263 node_list_t *make_node_list(void)
264 {
265     node_list_t *n = malloc(sizeof(node_list_t));
266     n->node = NULL;
267     n->prev = n->next = NULL;
268     n->latest = true;
269     return n;
270 }
271
272 void history_add(focus_history_t *f, node_t *n)
273 {
274     node_list_t *a = make_node_list();
275     a->node = n;
276     if (f->head == NULL) {
277         f->head = f->tail = a;
278     } else if (f->head->node != n) {
279         for (node_list_t *b = f->head; b != NULL; b = b->next)
280             if (b->node == n)
281                 b->latest = false;
282         f->head->prev = a;
283         a->next = f->head;
284         f->head = a;
285     } else {
286         free(a);
287     }
288 }
289
290 void history_remove(focus_history_t *f, node_t *n)
291 {
292     /* in order to maintain the `latest` node list state,
293        we remove node lists from head to tail */
294     node_list_t *b = f->head;
295     while (b != NULL) {
296         if (b->node == n) {
297             node_list_t *a = b->prev;
298             node_list_t *c = b->next;
299             if (a != NULL) {
300                 /* remove duplicate entries */
301                 while (c != NULL && c->node == a->node) {
302                     node_list_t *d = c->next;
303                     if (f->tail == c)
304                         f->tail = f->head;
305                     free(c);
306                     c = d;
307                 }
308                 a->next = c;
309             }
310             if (c != NULL)
311                 c->prev = a;
312             if (f->head == b)
313                 f->head = c;
314             if (f->tail == b)
315                 f->tail = a;
316             free(b);
317             b = c;
318         } else {
319             b = b->next;
320         }
321     }
322 }
323
324 void empty_history(focus_history_t *f)
325 {
326     node_list_t *a = f->head;
327     while (a != NULL) {
328         node_list_t *b = a->next;
329         free(a);
330         a = b;
331     }
332     f->head = f->tail = NULL;
333 }
334
335 node_t *history_get(focus_history_t *f, int i)
336 {
337     node_list_t *a = f->head;
338     while (a != NULL && i > 0) {
339         a = a->next;
340         i--;
341     }
342     if (a == NULL)
343         return NULL;
344     else
345         return a->node;
346 }
347
348 node_t *history_last(focus_history_t *f, node_t *n, client_select_t sel)
349 {
350     for (node_list_t *a = f->head; a != NULL; a = a->next) {
351         if (!a->latest || a->node == n || !node_matches(n, a->node, sel))
352             continue;
353         return a->node;
354     }
355     return NULL;
356 }
357
358 int history_rank(focus_history_t *f, node_t *n)
359 {
360     int i = 0;
361     node_list_t *a = f->head;
362     while (a != NULL && (!a->latest || a->node != n)) {
363         a = a->next;
364         i++;
365     }
366     if (a == NULL)
367         return -1;
368     else
369         return i;
370 }