diff options
Diffstat (limited to 'sway/border.c')
-rw-r--r-- | sway/border.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/sway/border.c b/sway/border.c new file mode 100644 index 00000000..ab4b70f6 --- /dev/null +++ b/sway/border.c | |||
@@ -0,0 +1,266 @@ | |||
1 | #include "border.h" | ||
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 "container.h" | ||
8 | #include "config.h" | ||
9 | #include "client/pango.h" | ||
10 | |||
11 | void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { | ||
12 | int endian = 1; | ||
13 | if (*(char *)&endian == 1) { // little endian | ||
14 | cairo_set_source_rgba(cairo, | ||
15 | (color >> (1*8) & 0xFF) / 255.0, | ||
16 | (color >> (2*8) & 0xFF) / 255.0, | ||
17 | (color >> (3*8) & 0xFF) / 255.0, | ||
18 | (color >> (0*8) & 0xFF) / 255.0); | ||
19 | } else { // big endian | ||
20 | cairo_set_source_rgba(cairo, | ||
21 | (color >> (0*8) & 0xFF) / 255.0, | ||
22 | (color >> (3*8) & 0xFF) / 255.0, | ||
23 | (color >> (2*8) & 0xFF) / 255.0, | ||
24 | (color >> (1*8) & 0xFF) / 255.0); | ||
25 | } | ||
26 | } | ||
27 | |||
28 | static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry geo, cairo_surface_t **surface) { | ||
29 | cairo_t *cr; | ||
30 | view->border_geometry = geo; | ||
31 | int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, geo.size.w); | ||
32 | view->border = calloc(stride * geo.size.h, sizeof(unsigned char)); | ||
33 | if (!view->border) { | ||
34 | sway_log(L_DEBUG, "Unable to allocate buffer"); | ||
35 | return NULL; | ||
36 | } | ||
37 | *surface = cairo_image_surface_create_for_data(view->border, | ||
38 | CAIRO_FORMAT_ARGB32, geo.size.w, geo.size.h, stride); | ||
39 | if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) { | ||
40 | free(view->border); | ||
41 | view->border = NULL; | ||
42 | sway_log(L_DEBUG, "Unable to allocate surface"); | ||
43 | return NULL; | ||
44 | } | ||
45 | cr = cairo_create(*surface); | ||
46 | if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) { | ||
47 | cairo_surface_destroy(*surface); | ||
48 | free(view->border); | ||
49 | view->border = NULL; | ||
50 | sway_log(L_DEBUG, "Unable to create cairo context"); | ||
51 | return NULL; | ||
52 | } | ||
53 | return cr; | ||
54 | } | ||
55 | |||
56 | // TODO: move to client/cairo.h when local set_source_u32 is fixed. | ||
57 | /** | ||
58 | * Renders a sharp line of any width and height. | ||
59 | * | ||
60 | * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 | ||
61 | * if the line has a width/height of one pixel, respectively. | ||
62 | */ | ||
63 | static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { | ||
64 | cairo_set_source_u32(cairo, color); | ||
65 | |||
66 | if (width > 1 && height > 1) { | ||
67 | cairo_rectangle(cairo, x, y, width, height); | ||
68 | cairo_fill(cairo); | ||
69 | } else { | ||
70 | if (width == 1) { | ||
71 | x += 0.5; | ||
72 | height += y; | ||
73 | width = x; | ||
74 | } | ||
75 | |||
76 | if (height == 1) { | ||
77 | y += 0.5; | ||
78 | width += x; | ||
79 | height = y; | ||
80 | } | ||
81 | |||
82 | cairo_move_to(cairo, x, y); | ||
83 | cairo_set_line_width(cairo, 1.0); | ||
84 | cairo_line_to(cairo, width, height); | ||
85 | cairo_stroke(cairo); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | int get_font_text_height(const char *font) { | ||
90 | cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200); | ||
91 | cairo_t *cr = cairo_create(surface); | ||
92 | int width, height; | ||
93 | get_text_size(cr, font, &width, &height, "Gg"); | ||
94 | return height; | ||
95 | } | ||
96 | |||
97 | static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors) { | ||
98 | struct wlc_geometry *b = &view->border_geometry; | ||
99 | struct wlc_geometry *v = &view->actual_geometry; | ||
100 | |||
101 | // left border | ||
102 | int left_border = v->origin.x - b->origin.x; | ||
103 | if (left_border > 0) { | ||
104 | render_sharp_line(cr, | ||
105 | colors->child_border, | ||
106 | 0, 0, | ||
107 | left_border, | ||
108 | b->size.h); | ||
109 | } | ||
110 | |||
111 | // right border | ||
112 | int right_border = b->size.w - v->size.w - left_border; | ||
113 | if (right_border > 0) { | ||
114 | render_sharp_line(cr, | ||
115 | colors->child_border, | ||
116 | b->size.w - right_border, | ||
117 | 0, | ||
118 | right_border, | ||
119 | b->size.h); | ||
120 | } | ||
121 | |||
122 | // top border | ||
123 | int top_border = v->origin.y - b->origin.y; | ||
124 | if (top_border > 0) { | ||
125 | render_sharp_line(cr, | ||
126 | colors->child_border, | ||
127 | 0, 0, | ||
128 | b->size.w, | ||
129 | top_border); | ||
130 | } | ||
131 | |||
132 | // bottom border | ||
133 | int bottom_border = b->size.h - (top_border + v->size.h); | ||
134 | if (bottom_border > 0) { | ||
135 | render_sharp_line(cr, | ||
136 | colors->child_border, | ||
137 | 0, | ||
138 | b->size.h - bottom_border, | ||
139 | b->size.w, | ||
140 | bottom_border); | ||
141 | } | ||
142 | } | ||
143 | |||
144 | static void render_with_title_bar(swayc_t *view, cairo_t *cr, struct border_colors *colors) { | ||
145 | struct wlc_geometry *tb = &view->title_bar_geometry; | ||
146 | struct wlc_geometry *b = &view->border_geometry; | ||
147 | |||
148 | // borders | ||
149 | render_borders(view, cr, colors); | ||
150 | |||
151 | // title bar background | ||
152 | cairo_set_source_u32(cr, colors->child_border); | ||
153 | cairo_rectangle(cr, 0, 0, tb->size.w, tb->size.h); | ||
154 | cairo_fill(cr); | ||
155 | |||
156 | // header top line | ||
157 | render_sharp_line(cr, colors->border, 0, 0, tb->size.w, 1); | ||
158 | |||
159 | // text | ||
160 | if (view->name) { | ||
161 | int width, height; | ||
162 | get_text_size(cr, config->font, &width, &height, "%s", view->name); | ||
163 | cairo_move_to(cr, view->border_thickness, 2); | ||
164 | cairo_set_source_u32(cr, colors->text); | ||
165 | pango_printf(cr, config->font, "%s", view->name); | ||
166 | } | ||
167 | |||
168 | // header bottom line | ||
169 | render_sharp_line(cr, colors->border, | ||
170 | view->actual_geometry.origin.x - b->origin.x, | ||
171 | tb->size.h - 1, | ||
172 | view->actual_geometry.size.w, 1); | ||
173 | } | ||
174 | |||
175 | void map_update_view_border(swayc_t *view, void *data) { | ||
176 | if (view->type == C_VIEW) { | ||
177 | update_view_border(view); | ||
178 | } | ||
179 | } | ||
180 | |||
181 | void update_view_border(swayc_t *view) { | ||
182 | cairo_t *cr = NULL; | ||
183 | cairo_surface_t *surface = NULL; | ||
184 | |||
185 | if (view->border) { | ||
186 | free(view->border); | ||
187 | view->border = NULL; | ||
188 | } | ||
189 | |||
190 | swayc_t *focused = get_focused_view(&root_container); | ||
191 | swayc_t *container = swayc_parent_by_type(view, C_CONTAINER); | ||
192 | swayc_t *focused_inactive = NULL; | ||
193 | if (container) { | ||
194 | focused_inactive = swayc_focus_by_type(container, C_VIEW); | ||
195 | } else { | ||
196 | container = swayc_parent_by_type(view, C_WORKSPACE); | ||
197 | if (container) { | ||
198 | focused_inactive = swayc_focus_by_type(container, C_VIEW); | ||
199 | } | ||
200 | } | ||
201 | |||
202 | switch (view->border_type) { | ||
203 | case B_NONE: | ||
204 | break; | ||
205 | case B_PIXEL: | ||
206 | cr = create_border_buffer(view, view->border_geometry, &surface); | ||
207 | if (!cr) { | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | if (focused == view) { | ||
212 | render_borders(view, cr, &config->border_colors.focused); | ||
213 | } else if (focused_inactive == view) { | ||
214 | render_borders(view, cr, &config->border_colors.focused_inactive); | ||
215 | } else { | ||
216 | render_borders(view, cr, &config->border_colors.unfocused); | ||
217 | } | ||
218 | |||
219 | break; | ||
220 | case B_NORMAL: | ||
221 | cr = create_border_buffer(view, view->border_geometry, &surface); | ||
222 | if (!cr) { | ||
223 | break; | ||
224 | } | ||
225 | |||
226 | if (focused == view) { | ||
227 | render_with_title_bar(view, cr, &config->border_colors.focused); | ||
228 | } else if (focused_inactive == view) { | ||
229 | render_with_title_bar(view, cr, &config->border_colors.focused_inactive); | ||
230 | } else { | ||
231 | render_with_title_bar(view, cr, &config->border_colors.unfocused); | ||
232 | } | ||
233 | |||
234 | break; | ||
235 | } | ||
236 | if (surface) { | ||
237 | cairo_surface_flush(surface); | ||
238 | cairo_surface_destroy(surface); | ||
239 | } | ||
240 | if (cr) { | ||
241 | cairo_destroy(cr); | ||
242 | } | ||
243 | } | ||
244 | |||
245 | void render_view_borders(wlc_handle view) { | ||
246 | swayc_t *c = swayc_by_handle(view); | ||
247 | |||
248 | if (!c || c->border_type == B_NONE) { | ||
249 | return; | ||
250 | } | ||
251 | |||
252 | if (c->border_type == B_NORMAL) { | ||
253 | // update window title | ||
254 | const char *new_name = wlc_view_get_title(view); | ||
255 | |||
256 | if (new_name && strcmp(c->name, new_name) != 0) { | ||
257 | free(c->name); | ||
258 | c->name = strdup(new_name); | ||
259 | update_view_border(c); | ||
260 | } | ||
261 | } | ||
262 | |||
263 | if (c->border) { | ||
264 | wlc_pixels_write(WLC_RGBA8888, &c->border_geometry, c->border); | ||
265 | } | ||
266 | } | ||