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