aboutsummaryrefslogtreecommitdiffstats
path: root/sway/border.c
diff options
context:
space:
mode:
Diffstat (limited to 'sway/border.c')
-rw-r--r--sway/border.c510
1 files changed, 0 insertions, 510 deletions
diff --git a/sway/border.c b/sway/border.c
deleted file mode 100644
index df0022ce..00000000
--- a/sway/border.c
+++ /dev/null
@@ -1,510 +0,0 @@
1#define _XOPEN_SOURCE 700
2#include <wlc/wlc-render.h>
3#include <cairo/cairo.h>
4#include <pango/pangocairo.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <string.h>
8#include <strings.h>
9#include <arpa/inet.h>
10#include "sway/border.h"
11#include "sway/container.h"
12#include "sway/config.h"
13#include "client/pango.h"
14
15void cairo_set_source_u32(cairo_t *cairo, uint32_t color) {
16 color = htonl(color);
17
18 cairo_set_source_rgba(cairo,
19 (color >> (2*8) & 0xFF) / 255.0,
20 (color >> (1*8) & 0xFF) / 255.0,
21 (color >> (0*8) & 0xFF) / 255.0,
22 (color >> (3*8) & 0xFF) / 255.0);
23}
24
25void border_clear(struct border *border) {
26 if (border && border->buffer) {
27 free(border->buffer);
28 border->buffer = NULL;
29 }
30}
31
32static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry g, cairo_surface_t **surface) {
33 if (view->border == NULL) {
34 view->border = malloc(sizeof(struct border));
35 if (!view->border) {
36 sway_log(L_ERROR, "Unable to allocate window border information");
37 return NULL;
38 }
39 }
40 cairo_t *cr;
41 int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, g.size.w);
42 view->border->buffer = calloc(stride * g.size.h, sizeof(unsigned char));
43 view->border->geometry = g;
44 if (!view->border->buffer) {
45 sway_log(L_ERROR, "Unable to allocate window border buffer");
46 return NULL;
47 }
48 *surface = cairo_image_surface_create_for_data(view->border->buffer,
49 CAIRO_FORMAT_ARGB32, g.size.w, g.size.h, stride);
50 if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) {
51 border_clear(view->border);
52 sway_log(L_ERROR, "Unable to allocate window border surface");
53 return NULL;
54 }
55 cr = cairo_create(*surface);
56 cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
57 if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) {
58 cairo_surface_destroy(*surface);
59 border_clear(view->border);
60 sway_log(L_ERROR, "Unable to create cairo context");
61 return NULL;
62 }
63 return cr;
64}
65
66// TODO: move to client/cairo.h when local set_source_u32 is fixed.
67/**
68 * Renders a sharp line of any width and height.
69 *
70 * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0
71 * if the line has a width/height of one pixel, respectively.
72 */
73static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) {
74 cairo_set_source_u32(cairo, color);
75
76 if (width > 1 && height > 1) {
77 cairo_rectangle(cairo, x, y, width, height);
78 cairo_fill(cairo);
79 } else {
80 if (width == 1) {
81 x += 0.5;
82 height += y;
83 width = x;
84 }
85
86 if (height == 1) {
87 y += 0.5;
88 width += x;
89 height = y;
90 }
91
92 cairo_move_to(cairo, x, y);
93 cairo_set_line_width(cairo, 1.0);
94 cairo_line_to(cairo, width, height);
95 cairo_stroke(cairo);
96 }
97}
98
99int get_font_text_height(const char *font) {
100 cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200);
101 cairo_t *cr = cairo_create(surface);
102 int width, height;
103 get_text_size(cr, font, &width, &height, 1, false, "Gg");
104 cairo_surface_destroy(surface);
105 cairo_destroy(cr);
106 return height;
107}
108
109static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors, bool top) {
110 struct wlc_geometry *g = &view->border->geometry;
111 struct wlc_geometry *b = &view->border_geometry;
112 struct wlc_geometry *v = &view->actual_geometry;
113 enum swayc_layouts layout = view->parent->layout;
114 uint32_t color;
115
116 int x = b->origin.x - g->origin.x;
117 int y = b->origin.y - g->origin.y;
118
119 // draw vertical/horizontal indicator if container is the only child of its parent container
120 bool is_only_child = view->parent && view->parent->children && view->parent->children->length == 1;
121
122 // left border
123 int left_border = v->origin.x - b->origin.x;
124 if (left_border > 0) {
125 render_sharp_line(cr,
126 colors->child_border,
127 x, y,
128 left_border,
129 b->size.h);
130 }
131
132 // right border
133 int right_border = b->size.w - v->size.w - left_border;
134 if (right_border > 0) {
135 if (is_only_child && layout == L_HORIZ && !view->is_floating) {
136 color = colors->indicator;
137 } else {
138 color = colors->child_border;
139 }
140 render_sharp_line(cr,
141 color,
142 x + b->size.w - right_border,
143 y,
144 right_border,
145 b->size.h);
146 }
147
148 // top border
149 int top_border = v->origin.y - b->origin.y;
150 if (top && top_border > 0) {
151 render_sharp_line(cr,
152 colors->child_border,
153 x, y,
154 b->size.w,
155 top_border);
156 }
157
158 // bottom border
159 int bottom_border = b->size.h - (top_border + v->size.h);
160 if (bottom_border > 0) {
161 if (is_only_child && layout == L_VERT && !view->is_floating) {
162 color = colors->indicator;
163 } else {
164 color = colors->child_border;
165 }
166 render_sharp_line(cr,
167 color,
168 x,
169 y + b->size.h - bottom_border,
170 b->size.w,
171 bottom_border);
172 }
173}
174
175static void render_title_bar(swayc_t *view, cairo_t *cr, struct wlc_geometry *b, struct border_colors *colors) {
176 struct wlc_geometry *tb = &view->title_bar_geometry;
177 int x = MIN(tb->origin.x, tb->origin.x - b->origin.x);
178 int y = MIN(tb->origin.y, tb->origin.y - b->origin.y);
179
180 // title bar background
181 cairo_set_source_u32(cr, colors->background);
182 cairo_rectangle(cr, x, y, tb->size.w, tb->size.h);
183 cairo_fill(cr);
184
185 // header top line
186 render_sharp_line(cr, colors->border, x, y, tb->size.w, 1);
187
188 // text
189 if (view->name) {
190 int width, height;
191 get_text_size(cr, config->font, &width, &height, 1, false, "%s", view->name);
192 cairo_move_to(cr, x + 2, y + 2);
193 cairo_set_source_u32(cr, colors->text);
194 pango_printf(cr, config->font, 1, false, "%s", view->name);
195 }
196 // Marks
197 if (config->show_marks && view->marks) {
198 int total_len = 0;
199
200 for(int i = view->marks->length - 1; i >= 0; --i) {
201 char *mark = (char *)view->marks->items[i];
202 if (*mark != '_') {
203 int width, height;
204 get_text_size(cr, config->font, &width, &height, 1, false, "[%s]", mark);
205 total_len += width;
206 if ((int)tb->size.w + x - (total_len + 2) < x + 2) {
207 break;
208 } else {
209 cairo_move_to(cr, (int)tb->size.w + x - (total_len + 2), y + 2);
210 cairo_set_source_u32(cr, colors->text);
211 pango_printf(cr, config->font, 1, false, "[%s]", mark);
212 }
213 }
214 }
215 }
216
217 // titlebars has a border all around for tabbed layouts
218 if (view->parent->layout == L_TABBED) {
219 // header bottom line
220 render_sharp_line(cr, colors->border, x, y + tb->size.h - 1,
221 tb->size.w, 1);
222
223 // left border
224 render_sharp_line(cr, colors->border, x, y, 1, tb->size.h);
225
226 // right border
227 render_sharp_line(cr, colors->border, x + tb->size.w - 1, y,
228 1, tb->size.h);
229
230 return;
231 }
232
233 if ((uint32_t)(view->actual_geometry.origin.y - tb->origin.y) == tb->size.h) {
234 // header bottom line
235 render_sharp_line(cr, colors->border,
236 x + view->actual_geometry.origin.x - tb->origin.x,
237 y + tb->size.h - 1,
238 view->actual_geometry.size.w, 1);
239 } else {
240 // header bottom line
241 render_sharp_line(cr, colors->border, x,
242 y + tb->size.h - 1,
243 tb->size.w, 1);
244 }
245}
246
247/**
248 * Generate nested container title for tabbed/stacked layouts
249 */
250static char *generate_container_title(swayc_t *container) {
251 char layout = 'H';
252 char *name, *prev_name = NULL;
253 switch (container->layout) {
254 case L_TABBED:
255 layout = 'T';
256 break;
257 case L_STACKED:
258 layout = 'S';
259 break;
260 case L_VERT:
261 layout = 'V';
262 break;
263 default:
264 layout = 'H';
265 }
266 int len = 9;
267 name = malloc(len * sizeof(char));
268 if (!name) {
269 sway_log(L_ERROR, "Unable to allocate container title");
270 return NULL;
271 }
272 snprintf(name, len, "sway: %c[", layout);
273
274 int i;
275 for (i = 0; i < container->children->length; ++i) {
276 prev_name = name;
277 swayc_t* child = container->children->items[i];
278 const char *title = NULL;
279 if (child->type == C_VIEW) {
280 title = child->app_id ? child->app_id :
281 (child->instance ? child->instance :
282 (child->class ? child->class :"(null)"));
283 } else { //child->type == C_CONTAINER
284 title = generate_container_title(child);
285 }
286
287 len = strlen(name) + strlen(title) + 1;
288 if (i < container->children->length-1) {
289 len++;
290 }
291
292 name = malloc(len * sizeof(char));
293 if (!name) {
294 free(prev_name);
295 sway_log(L_ERROR, "Unable to allocate container title");
296 return NULL;
297 }
298 if (i < container->children->length-1) {
299 snprintf(name, len, "%s%s ", prev_name, title);
300 } else {
301 snprintf(name, len, "%s%s", prev_name, title);
302 }
303 free(prev_name);
304 }
305
306 prev_name = name;
307 len = strlen(name) + 2;
308 name = malloc(len * sizeof(char));
309 if (!name) {
310 free(prev_name);
311 sway_log(L_ERROR, "Unable to allocate container title");
312 return NULL;
313 }
314 snprintf(name, len, "%s]", prev_name);
315 free(prev_name);
316 free(container->name);
317 container->name = name;
318 return container->name + 6; // don't include "sway: "
319}
320
321void update_tabbed_stacked_titlebars(swayc_t *c, cairo_t *cr, struct wlc_geometry *g, swayc_t *focused, swayc_t *focused_inactive) {
322 if (c->type == C_CONTAINER) {
323 if (c->parent->focused == c) {
324 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
325 } else {
326 render_title_bar(c, cr, g, &config->border_colors.unfocused);
327 }
328
329 if (!c->visible) {
330 return;
331 }
332
333 int i;
334 for (i = 0; i < c->children->length; ++i) {
335 swayc_t *child = c->children->items[i];
336 update_tabbed_stacked_titlebars(child, cr, g, focused, focused_inactive);
337 }
338 } else {
339 bool is_child_of_focused = swayc_is_child_of(c, get_focused_container(&root_container));
340
341 if (focused == c || is_child_of_focused) {
342 render_title_bar(c, cr, g, &config->border_colors.focused);
343 } else if (focused_inactive == c) {
344 render_title_bar(c, cr, g, &config->border_colors.focused_inactive);
345 } else {
346 render_title_bar(c, cr, g, &config->border_colors.unfocused);
347 }
348 }
349}
350
351static void update_view_border(swayc_t *view) {
352 if (!view->visible) {
353 return;
354 }
355
356 cairo_t *cr = NULL;
357 cairo_surface_t *surface = NULL;
358
359 // clear previous border buffer.
360 border_clear(view->border);
361
362 // get focused and focused_inactive views
363 swayc_t *focused = get_focused_view(&root_container);
364 swayc_t *container = swayc_parent_by_type(view, C_CONTAINER);
365 swayc_t *focused_inactive = NULL;
366
367 bool is_child_of_focused = swayc_is_parent_of(get_focused_container(&root_container), view);
368
369 if (container) {
370 focused_inactive = swayc_focus_by_type(container, C_VIEW);
371 } else {
372 container = swayc_parent_by_type(view, C_WORKSPACE);
373 if (container) {
374 focused_inactive = swayc_focus_by_type(container, C_VIEW);
375 }
376 }
377
378 // for tabbed/stacked layouts the focused view has to draw all the
379 // titlebars of the hidden views.
380 swayc_t *p = NULL;
381 if (view->parent->focused == view && (p = swayc_tabbed_stacked_ancestor(view))) {
382 struct wlc_geometry g = {
383 .origin = {
384 .x = p->x,
385 .y = p->y
386 },
387 .size = {
388 .w = p->width,
389 .h = p->height
390 }
391 };
392 cr = create_border_buffer(view, g, &surface);
393 if (!cr) {
394 goto cleanup;
395 }
396
397 bool render_top = !should_hide_top_border(view, view->y);
398 if (view == focused || is_child_of_focused) {
399 render_borders(view, cr, &config->border_colors.focused, render_top);
400 } else {
401 render_borders(view, cr, &config->border_colors.focused_inactive, render_top);
402 }
403
404 // generate container titles
405 int i;
406 for (i = 0; i < p->children->length; ++i) {
407 swayc_t *child = p->children->items[i];
408 if (child->type == C_CONTAINER) {
409 generate_container_title(child);
410 }
411 }
412
413 update_tabbed_stacked_titlebars(p, cr, &g, focused, focused_inactive);
414 } else {
415 switch (view->border_type) {
416 case B_NONE:
417 break;
418 case B_PIXEL:
419 cr = create_border_buffer(view, view->border_geometry, &surface);
420 if (!cr) {
421 break;
422 }
423
424 if (focused == view || is_child_of_focused) {
425 render_borders(view, cr, &config->border_colors.focused, true);
426 } else if (focused_inactive == view) {
427 render_borders(view, cr, &config->border_colors.focused_inactive, true);
428 } else {
429 render_borders(view, cr, &config->border_colors.unfocused, true);
430 }
431
432 break;
433 case B_NORMAL:
434 cr = create_border_buffer(view, view->border_geometry, &surface);
435 if (!cr) {
436 break;
437 }
438
439 if (focused == view || is_child_of_focused) {
440 render_borders(view, cr, &config->border_colors.focused, false);
441 render_title_bar(view, cr, &view->border_geometry,
442 &config->border_colors.focused);
443 } else if (focused_inactive == view) {
444 render_borders(view, cr, &config->border_colors.focused_inactive, false);
445 render_title_bar(view, cr, &view->border_geometry,
446 &config->border_colors.focused_inactive);
447 } else {
448 render_borders(view, cr, &config->border_colors.unfocused, false);
449 render_title_bar(view, cr, &view->border_geometry,
450 &config->border_colors.unfocused);
451 }
452
453 break;
454 }
455 }
456
457cleanup:
458
459 if (surface) {
460 cairo_surface_flush(surface);
461 cairo_surface_destroy(surface);
462 }
463
464 if (cr) {
465 cairo_destroy(cr);
466 }
467}
468
469void update_container_border(swayc_t *container) {
470 if (container->type == C_VIEW) {
471 update_view_border(container);
472 return;
473 } else {
474 for (int i = 0; i < container->children->length; ++i) {
475 update_container_border(container->children->items[i]);
476 }
477 }
478}
479
480void render_view_borders(wlc_handle view) {
481 swayc_t *c = swayc_by_handle(view);
482
483
484 // emulate i3 behavior for drawing borders for tabbed and stacked layouts:
485 // if we are not the only child in the container, always draw borders,
486 // regardless of the border setting on the individual view
487 if (!c || (c->border_type == B_NONE
488 && !((c->parent->layout == L_TABBED || c->parent->layout == L_STACKED)
489 && c->parent->children->length > 1))) {
490 return;
491 }
492
493 if (c->border && c->border->buffer) {
494 wlc_pixels_write(WLC_RGBA8888, &c->border->geometry, c->border->buffer);
495 }
496}
497
498bool should_hide_top_border(swayc_t *con, double y) {
499 // returns true if container is child of tabbed/stacked layout and is
500 // sharing top border with tabbed titlebar
501 swayc_t *par = con->parent;
502 while (par->type != C_WORKSPACE) {
503 if (par->layout == L_TABBED || par->layout == L_STACKED) {
504 return con->y == y;
505 }
506 con = par;
507 par = par->parent;
508 }
509 return false;
510}