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