]> git.lizzy.rs Git - bspwm.git/blob - window.c
Fix toggle_fullscreen
[bspwm.git] / window.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include <string.h>
5 #include <xcb/xcb.h>
6 #include <xcb/xcb_event.h>
7 #include "types.h"
8 #include "tree.h"
9 #include "bspwm.h"
10 #include "settings.h"
11 #include "ewmh.h"
12 #include "window.h"
13
14 #define p_clear(p, count)       ((void)memset((p), 0, sizeof(*(p)) * (count)))
15
16 bool locate_window(xcb_window_t win, window_location_t *loc)
17 {
18     monitor_t *m = mon_head;
19     while (m != NULL) {
20         desktop_t *d = m->desk_head;
21         while (d != NULL) {
22             node_t *n = first_extrema(d->root);
23             while (n != NULL) {
24                 if (n->client->window == win) {
25                     loc->monitor = m;
26                     loc->desktop = d;
27                     loc->node = n;
28                     return true;
29                 }
30                 n = next_leaf(n);
31             }
32             d = d->next;
33         }
34         m = m->next;
35     }
36     return false;
37 }
38
39 bool locate_desktop(char *name, desktop_location_t *loc)
40 {
41     monitor_t *m = mon_head;
42     while (m != NULL) {
43         desktop_t *d = m->desk_head;
44         while (d != NULL) {
45             if (strcmp(d->name, name) == 0) {
46                 loc->monitor = m;
47                 loc->desktop = d;
48                 return true;
49             }
50             d = d->next;
51         }
52         m = m->next;
53     }
54     return false;
55 }
56
57 void window_draw_border(node_t *n, bool focused_window, bool focused_monitor)
58 {
59     if (n == NULL)
60         return;
61
62     if (border_width < 1 || n->client->border_width < 1)
63         return;
64
65     xcb_window_t win = n->client->window;
66
67     xcb_rectangle_t actual_rectangle = (is_tiled(n->client) ? n->client->tiled_rectangle : n->client->floating_rectangle);
68
69     uint16_t width = actual_rectangle.width;
70     uint16_t height = actual_rectangle.height;
71
72     uint16_t full_width = width + 2 * border_width;
73     uint16_t full_height = height + 2 * border_width;
74
75     xcb_rectangle_t inner_rectangles[] =
76     {
77         { width, 0, 2 * border_width, height + 2 * border_width },
78         { 0, height, width + 2 * border_width, 2 * border_width }
79     };
80
81     xcb_rectangle_t main_rectangles[] =
82     {
83         { width + inner_border_width, 0, 2 * (main_border_width + outer_border_width), height + 2 * border_width },
84         { 0, height + inner_border_width, width + 2 * border_width, 2 * (main_border_width + outer_border_width) }
85     };
86
87     xcb_rectangle_t outer_rectangles[] =
88     {
89         { width + inner_border_width + main_border_width, 0, 2 * outer_border_width, height + 2 * border_width },
90         { 0, height + inner_border_width + main_border_width, width + 2 * border_width, 2 * outer_border_width }
91     };
92
93     xcb_rectangle_t *presel_rectangles;
94
95     xcb_pixmap_t pix = xcb_generate_id(dpy);
96     xcb_create_pixmap(dpy, root_depth, pix, win, full_width, full_height);
97
98     xcb_gcontext_t gc = xcb_generate_id(dpy);
99     xcb_create_gc(dpy, gc, pix, 0, NULL);
100
101     uint32_t main_border_color_pxl = get_main_border_color(n->client, focused_window, focused_monitor);
102
103     /* inner border */
104     if (inner_border_width > 0) {
105         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &inner_border_color_pxl);
106         xcb_poly_fill_rectangle(dpy, pix, gc, LENGTH(inner_rectangles), inner_rectangles);
107     }
108
109     /* main border */
110     if (main_border_width > 0) {
111         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &main_border_color_pxl);
112         xcb_poly_fill_rectangle(dpy, pix, gc, LENGTH(main_rectangles), main_rectangles);
113     }
114
115     /* outer border */
116     if (outer_border_width > 0) {
117         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &outer_border_color_pxl);
118         xcb_poly_fill_rectangle(dpy, pix, gc, LENGTH(outer_rectangles), outer_rectangles);
119     }
120
121     if (split_mode == MODE_MANUAL && focused_monitor && focused_window) {
122         uint16_t fence = (int16_t) (n->split_ratio * ((split_dir == DIR_UP || split_dir == DIR_DOWN) ? height : width));
123         presel_rectangles = malloc(2 * sizeof(xcb_rectangle_t));
124         switch (split_dir) {
125             case DIR_UP:
126                 presel_rectangles[0] = (xcb_rectangle_t) {width, 0, 2 * border_width, fence};
127                 presel_rectangles[1] = (xcb_rectangle_t) {0, height + border_width, full_width, border_width};
128                 break;
129             case DIR_DOWN:
130                 presel_rectangles[0] = (xcb_rectangle_t) {width, fence + 1, 2 * border_width, height + border_width - (fence + 1)};
131                 presel_rectangles[1] = (xcb_rectangle_t) {0, height, full_width, border_width};
132                 break;
133             case DIR_LEFT:
134                 presel_rectangles[0] = (xcb_rectangle_t) {0, height, fence, 2 * border_width};
135                 presel_rectangles[1] = (xcb_rectangle_t) {width + border_width, 0, border_width, full_height};
136                 break;
137             case DIR_RIGHT:
138                 presel_rectangles[0] = (xcb_rectangle_t) {fence + 1, height, width + border_width - (fence + 1), 2 * border_width};
139                 presel_rectangles[1] = (xcb_rectangle_t) {width, 0, border_width, full_height};
140                 break;
141         }
142         xcb_change_gc(dpy, gc, XCB_GC_FOREGROUND, &presel_border_color_pxl);
143         xcb_poly_fill_rectangle(dpy, pix, gc, 2, presel_rectangles);
144         free(presel_rectangles);
145     }
146
147     /* apply border pixmap */
148     xcb_change_window_attributes(dpy, win, XCB_CW_BORDER_PIXMAP, &pix);
149
150     xcb_free_gc(dpy, gc);
151     xcb_free_pixmap(dpy, pix);
152 }
153
154 void window_close(node_t *n)
155 {
156     if (n == NULL || n->client->locked)
157         return;
158
159     PRINTF("close window %X\n", n->client->window);
160
161     xcb_atom_t WM_DELETE_WINDOW;
162     xcb_window_t win = n->client->window;
163     xcb_client_message_event_t e;
164
165     xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply(dpy, xcb_intern_atom(dpy, 0, strlen("WM_DELETE_WINDOW"), "WM_DELETE_WINDOW"), NULL);
166     if (reply) {
167         WM_DELETE_WINDOW = reply->atom;
168         free(reply);
169     } else {
170         warn("close_window %X: could not acquire WM_DELETE_WINDOW atom\n", win);
171         return;
172     }
173
174     e.response_type = XCB_CLIENT_MESSAGE;
175     e.window = win;
176     e.format = 32;
177     e.sequence = 0;
178     e.type = ewmh->WM_PROTOCOLS;
179     e.data.data32[0] = WM_DELETE_WINDOW;
180     e.data.data32[1] = XCB_CURRENT_TIME;
181
182     xcb_send_event(dpy, false, win, XCB_EVENT_MASK_NO_EVENT, (char *) &e);
183 }
184
185 void window_kill(desktop_t *d, node_t *n)
186 {
187     if (n == NULL)
188         return;
189
190     PRINTF("kill window %X\n", n->client->window);
191
192     xcb_kill_client(dpy, n->client->window);
193     remove_node(d, n);
194 }
195
196 void toggle_fullscreen(monitor_t *m, client_t *c)
197 {
198     PRINTF("toggle fullscreen %X\n", c->window);
199
200     if (c->fullscreen) {
201         c->fullscreen = false;
202         xcb_atom_t values[] = {XCB_NONE};
203         xcb_ewmh_set_wm_state(ewmh, c->window, LENGTH(values), values);
204     } else {
205         c->fullscreen = true;
206         xcb_atom_t values[] = {ewmh->_NET_WM_STATE_FULLSCREEN};
207         xcb_ewmh_set_wm_state(ewmh, c->window, LENGTH(values), values);
208         window_raise(c->window);
209         window_border_width(c->window, 0);
210         xcb_rectangle_t r = m->rectangle;
211         window_move_resize(c->window, r.x, r.y, r.width, r.height);
212     }
213 }
214
215 void toggle_floating(node_t *n)
216 {
217     if (n == NULL || n->client->transient)
218         return;
219
220     PRINTF("toggle floating %X\n", n->client->window);
221
222     client_t *c = n->client;
223     c->floating = !c->floating;
224     n->vacant = !n->vacant;
225     update_vacant_state(n->parent);
226     if (c->floating)
227         window_raise(c->window);
228     else if (is_tiled(c))
229         window_lower(c->window);
230 }
231
232 void toggle_locked(client_t *c)
233 {
234     PRINTF("toggle locked %X\n", c->window);
235
236     c->locked = !c->locked;
237 }
238
239 void list_windows(char *rsp)
240 {
241     char line[MAXLEN];
242
243     for (monitor_t *m = mon_head; m != NULL; m = m->next)
244         for (desktop_t *d = m->desk_head; d != NULL; d = d->next)
245             for (node_t *n = first_extrema(d->root); n != NULL; n = next_leaf(n)) {
246                 snprintf(line, sizeof(line), "0x%X\n", n->client->window);
247                 strncat(rsp, line, REMLEN(rsp));
248             }
249 }
250
251 uint32_t get_main_border_color(client_t *c, bool focused_window, bool focused_monitor)
252 {
253     if (c == NULL)
254         return 0;
255
256     if (focused_monitor && focused_window) {
257         if (c->locked)
258             return focused_locked_border_color_pxl;
259         else
260             return focused_border_color_pxl;
261     } else if (focused_window) {
262         if (c->locked)
263             return active_locked_border_color_pxl;
264         else
265             return active_border_color_pxl;
266     } else {
267         if (c->urgent)
268             return urgent_border_color_pxl;
269         else if (c->locked)
270             return normal_locked_border_color_pxl;
271         else
272             return normal_border_color_pxl;
273     }
274 }
275
276 void update_floating_rectangle(client_t *c)
277 {
278     xcb_get_geometry_reply_t *geom = xcb_get_geometry_reply(dpy, xcb_get_geometry(dpy, c->window), NULL);
279
280     if (geom) {
281         c->floating_rectangle = (xcb_rectangle_t) {geom->x, geom->y, geom->width, geom->height};
282         free(geom);
283     } else {
284         c->floating_rectangle = (xcb_rectangle_t) {0, 0, 32, 24};
285     }
286 }
287
288 void window_border_width(xcb_window_t win, uint32_t bw)
289 {
290     uint32_t values[] = {bw};
291     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_BORDER_WIDTH, values);
292 }
293
294 void window_move(xcb_window_t win, int16_t x, int16_t y)
295 {
296     uint32_t values[] = {x, y};
297     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y, values);
298 }
299
300 void window_move_resize(xcb_window_t win, int16_t x, int16_t y, uint16_t w, uint16_t h)
301 {
302     uint32_t values[] = {x, y, w, h};
303     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_X_Y_WIDTH_HEIGHT, values);
304 }
305
306 void window_raise(xcb_window_t win)
307 {
308     uint32_t values[] = {XCB_STACK_MODE_ABOVE};
309     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
310 }
311
312 void window_lower(xcb_window_t win)
313 {
314     uint32_t values[] = {XCB_STACK_MODE_BELOW};
315     xcb_configure_window(dpy, win, XCB_CONFIG_WINDOW_STACK_MODE, values);
316 }
317
318 void window_set_visibility(xcb_window_t win, bool visible) {
319     uint32_t values_off[] = {ROOT_EVENT_MASK & ~XCB_EVENT_MASK_SUBSTRUCTURE_NOTIFY};
320     uint32_t values_on[] = {ROOT_EVENT_MASK};
321     xcb_change_window_attributes(dpy, screen->root, XCB_CW_EVENT_MASK, values_off);
322     if (visible)
323         xcb_map_window(dpy, win);
324     else
325         xcb_unmap_window(dpy, win);
326     xcb_change_window_attributes(dpy, screen->root, XCB_CW_EVENT_MASK, values_on);
327 }
328
329 void window_hide(xcb_window_t win)
330 {
331     window_set_visibility(win, false);
332 }
333
334 void window_show(xcb_window_t win)
335 {
336     window_set_visibility(win, true);
337 }