Allow configuration of colors with alpha channel
Patch status: needinfo
Patch by Klemens Baum
Long description:
This commit comprises the following changes: Properly validate color strings with optional alpha channel For example #DDE83030 and #E83030 are both allowed Use a 32-bit visual for i3bar if available Note: We cannot just use the root visual because some graphics drivers (e.g. fglrx) do not support setting a 32-bit root visual. Pass the current visual to draw_text (needed for Pango)
To apply this patch, use:
curl http://cr.i3wm.org/patch/63/raw.patch | git am
b/i3-config-wizard/main.c
30 |
@@ -72,6 +72,7 @@ static uint32_t xcb_numlock_mask; |
31 |
xcb_connection_t *conn; |
32 |
static xcb_key_symbols_t *keysyms; |
33 |
xcb_screen_t *root_screen; |
34 |
+static xcb_visualtype_t* visual_type; |
35 |
static xcb_get_modifier_mapping_reply_t *modmap_reply; |
36 |
static i3Font font; |
37 |
static i3Font bold_font; |
38 |
@@ -484,7 +485,7 @@ static int handle_expose() { |
39 |
set_font(&font); |
40 |
|
41 |
#define txt(x, row, text) \ |
42 |
- draw_text_ascii(text, pixmap, pixmap_gc,\ |
43 |
+ draw_text_ascii(text, pixmap, visual_type, pixmap_gc,\ |
44 |
x, (row - 1) * font.height + 4, 300 - x * 2) |
45 |
|
46 |
if (current_step == STEP_WELCOME) { |
47 |
@@ -814,6 +815,7 @@ int main(int argc, char *argv[]) { |
48 |
#undef xmacro |
49 |
|
50 |
root_screen = xcb_aux_get_screen(conn, screens); |
51 |
+ visual_type = get_visualtype(root_screen); |
52 |
root = root_screen->root; |
53 |
|
54 |
if (!(modmap_reply = xcb_get_modifier_mapping_reply(conn, modmap_cookie, NULL))) |
b/i3-input/main.c
59 |
@@ -43,6 +43,7 @@ static xcb_key_symbols_t *symbols; |
60 |
static bool modeswitch_active = false; |
61 |
static xcb_window_t win; |
62 |
static xcb_pixmap_t pixmap; |
63 |
+static xcb_visualtype_t* visual_type; |
64 |
static xcb_gcontext_t pixmap_gc; |
65 |
static xcb_char2b_t glyphs_ucs[512]; |
66 |
static char *glyphs_utf8[512]; |
67 |
@@ -137,13 +138,13 @@ static int handle_expose(void *data, xcb_connection_t *conn, xcb_expose_event_t |
68 |
|
69 |
/* draw the prompt … */ |
70 |
if (prompt != NULL) { |
71 |
- draw_text(prompt, pixmap, pixmap_gc, 4, 4, 492); |
72 |
+ draw_text(prompt, pixmap, visual_type, pixmap_gc, 4, 4, 492); |
73 |
} |
74 |
/* … and the text */ |
75 |
if (input_position > 0) |
76 |
{ |
77 |
i3String *input = i3string_from_ucs2(glyphs_ucs, input_position); |
78 |
- draw_text(input, pixmap, pixmap_gc, prompt_offset + 4, 4, 492); |
79 |
+ draw_text(input, pixmap, visual_type, pixmap_gc, prompt_offset + 4, 4, 492); |
80 |
i3string_free(input); |
81 |
} |
82 |
|
83 |
@@ -385,6 +386,7 @@ int main(int argc, char *argv[]) { |
84 |
focus_cookie = xcb_get_input_focus(conn); |
85 |
|
86 |
root_screen = xcb_aux_get_screen(conn, screens); |
87 |
+ visual_type = get_visualtype(root_screen); |
88 |
root = root_screen->root; |
89 |
|
90 |
symbols = xcb_key_symbols_alloc(conn); |
b/i3-nagbar/main.c
95 |
@@ -41,6 +41,7 @@ typedef struct { |
96 |
|
97 |
static xcb_window_t win; |
98 |
static xcb_pixmap_t pixmap; |
99 |
+static xcb_visualtype_t* visual_type; |
100 |
static xcb_gcontext_t pixmap_gc; |
101 |
static xcb_rectangle_t rect = { 0, 0, 600, 20 }; |
102 |
static i3Font font; |
103 |
@@ -191,7 +192,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { |
104 |
|
105 |
/* restore font color */ |
106 |
set_font_colors(pixmap_gc, color_text, color_background); |
107 |
- draw_text(prompt, pixmap, pixmap_gc, |
108 |
+ draw_text(prompt, pixmap, visual_type, pixmap_gc, |
109 |
4 + 4, 4 + 4, rect.width - 4 - 4); |
110 |
|
111 |
/* render close button */ |
112 |
@@ -218,7 +219,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { |
113 |
|
114 |
values[0] = 1; |
115 |
set_font_colors(pixmap_gc, color_text, color_button_background); |
116 |
- draw_text_ascii("X", pixmap, pixmap_gc, y - w - line_width + w / 2 - 4, |
117 |
+ draw_text_ascii("X", pixmap, visual_type, pixmap_gc, y - w - line_width + w / 2 - 4, |
118 |
4 + 4 - 1, rect.width - y + w + line_width - w / 2 + 4); |
119 |
y -= w; |
120 |
|
121 |
@@ -249,7 +250,7 @@ static int handle_expose(xcb_connection_t *conn, xcb_expose_event_t *event) { |
122 |
values[0] = color_text; |
123 |
values[1] = color_button_background; |
124 |
set_font_colors(pixmap_gc, color_text, color_button_background); |
125 |
- draw_text(buttons[c].label, pixmap, pixmap_gc, |
126 |
+ draw_text(buttons[c].label, pixmap, visual_type, pixmap_gc, |
127 |
y - w - line_width + 6, 4 + 3, rect.width - y + w + line_width - 6); |
128 |
|
129 |
y -= w; |
130 |
@@ -363,6 +364,7 @@ int main(int argc, char *argv[]) { |
131 |
#undef xmacro |
132 |
|
133 |
root_screen = xcb_aux_get_screen(conn, screens); |
134 |
+ visual_type = get_visualtype(root_screen); |
135 |
root = root_screen->root; |
136 |
|
137 |
if (bar_type == TYPE_ERROR) { |
b/i3bar/src/xcb.c
142 |
@@ -48,6 +48,9 @@ xcb_connection_t *xcb_connection; |
143 |
int screen; |
144 |
xcb_screen_t *root_screen; |
145 |
xcb_window_t xcb_root; |
146 |
+xcb_visualtype_t *visual_type; |
147 |
+uint8_t depth; |
148 |
+xcb_colormap_t colormap; |
149 |
|
150 |
/* selection window for tray support */ |
151 |
static xcb_window_t selwin = XCB_NONE; |
152 |
@@ -173,7 +176,8 @@ void refresh_statusline(void) { |
153 |
|
154 |
uint32_t colorpixel = (block->color ? get_colorpixel(block->color) : colors.bar_fg); |
155 |
set_font_colors(statusline_ctx, colorpixel, colors.bar_bg); |
156 |
- draw_text(block->full_text, statusline_pm, statusline_ctx, x + block->x_offset, 1, block->width); |
157 |
+ draw_text(block->full_text, statusline_pm, visual_type, |
158 |
+ statusline_ctx, x + block->x_offset, 1, block->width); |
159 |
x += block->width + block->x_offset + block->x_append; |
160 |
|
161 |
if (TAILQ_NEXT(block, blocks) != NULL && !block->no_separator && block->sep_block_width > 0) { |
162 |
@@ -261,7 +265,16 @@ void unhide_bars(void) { |
163 |
void init_colors(const struct xcb_color_strings_t *new_colors) { |
164 |
#define PARSE_COLOR(name, def) \ |
165 |
do { \ |
166 |
- colors.name = get_colorpixel(new_colors->name ? new_colors->name : def); \ |
167 |
+ const char* color = def; \ |
168 |
+ const char* user_color = new_colors->name; \ |
169 |
+ if (user_color) { \ |
170 |
+ if (valid_colorpixel(user_color)) { \ |
171 |
+ color = user_color; \ |
172 |
+ } else { \ |
173 |
+ ELOG("Invalid color specified for %s: %s", #name, user_color); \ |
174 |
+ } \ |
175 |
+ } \ |
176 |
+ colors.name = get_colorpixel(color); \ |
177 |
} while (0) |
178 |
PARSE_COLOR(bar_fg, "#FFFFFF"); |
179 |
PARSE_COLOR(bar_bg, "#000000"); |
180 |
@@ -872,34 +885,55 @@ char *init_xcb_early() { |
181 |
root_screen = xcb_aux_get_screen(xcb_connection, screen); |
182 |
xcb_root = root_screen->root; |
183 |
|
184 |
+ depth = root_screen->root_depth; |
185 |
+ colormap = root_screen->default_colormap; |
186 |
+ |
187 |
+ visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, 32); |
188 |
+ if (visual_type) { |
189 |
+ depth = xcb_aux_get_depth_of_visual(root_screen, visual_type->visual_id); |
190 |
+ |
191 |
+ colormap = xcb_generate_id(xcb_connection); |
192 |
+ xcb_void_cookie_t cm_cookie = xcb_create_colormap_checked(xcb_connection, |
193 |
+ XCB_COLORMAP_ALLOC_NONE, |
194 |
+ colormap, |
195 |
+ xcb_root, |
196 |
+ visual_type->visual_id); |
197 |
+ if (xcb_request_failed(cm_cookie, "Could not allocate colormap")) { |
198 |
+ exit(EXIT_FAILURE); |
199 |
+ } |
200 |
+ } else { |
201 |
+ visual_type = get_visualtype(root_screen); |
202 |
+ } |
203 |
+ |
204 |
/* We draw the statusline to a seperate pixmap, because it looks the same on all bars and |
205 |
* this way, we can choose to crop it */ |
206 |
uint32_t mask = XCB_GC_FOREGROUND; |
207 |
uint32_t vals[] = { colors.bar_bg, colors.bar_bg }; |
208 |
|
209 |
+ statusline_pm = xcb_generate_id(xcb_connection); |
210 |
+ xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, |
211 |
+ depth, |
212 |
+ statusline_pm, |
213 |
+ xcb_root, |
214 |
+ root_screen->width_in_pixels, |
215 |
+ root_screen->height_in_pixels); |
216 |
+ |
217 |
statusline_clear = xcb_generate_id(xcb_connection); |
218 |
xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection, |
219 |
statusline_clear, |
220 |
- xcb_root, |
221 |
+ statusline_pm, |
222 |
mask, |
223 |
vals); |
224 |
|
225 |
+ mask |= XCB_GC_BACKGROUND; |
226 |
+ vals[0] = colors.bar_fg; |
227 |
statusline_ctx = xcb_generate_id(xcb_connection); |
228 |
xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection, |
229 |
statusline_ctx, |
230 |
- xcb_root, |
231 |
+ statusline_pm, |
232 |
0, |
233 |
NULL); |
234 |
|
235 |
- statusline_pm = xcb_generate_id(xcb_connection); |
236 |
- xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, |
237 |
- root_screen->root_depth, |
238 |
- statusline_pm, |
239 |
- xcb_root, |
240 |
- root_screen->width_in_pixels, |
241 |
- root_screen->height_in_pixels); |
242 |
- |
243 |
- |
244 |
/* The various Watchers to communicate with xcb */ |
245 |
xcb_io = smalloc(sizeof(ev_io)); |
246 |
xcb_prep = smalloc(sizeof(ev_prepare)); |
247 |
@@ -1267,7 +1301,7 @@ void realloc_sl_buffer(void) { |
248 |
xcb_free_pixmap(xcb_connection, statusline_pm); |
249 |
statusline_pm = xcb_generate_id(xcb_connection); |
250 |
xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, |
251 |
- root_screen->root_depth, |
252 |
+ depth, |
253 |
statusline_pm, |
254 |
xcb_root, |
255 |
MAX(root_screen->width_in_pixels, statusline_width), |
256 |
@@ -1279,7 +1313,7 @@ void realloc_sl_buffer(void) { |
257 |
statusline_clear = xcb_generate_id(xcb_connection); |
258 |
xcb_void_cookie_t clear_ctx_cookie = xcb_create_gc_checked(xcb_connection, |
259 |
statusline_clear, |
260 |
- xcb_root, |
261 |
+ statusline_pm, |
262 |
mask, |
263 |
vals); |
264 |
|
265 |
@@ -1289,7 +1323,7 @@ void realloc_sl_buffer(void) { |
266 |
xcb_free_gc(xcb_connection, statusline_ctx); |
267 |
xcb_void_cookie_t sl_ctx_cookie = xcb_create_gc_checked(xcb_connection, |
268 |
statusline_ctx, |
269 |
- xcb_root, |
270 |
+ statusline_pm, |
271 |
mask, |
272 |
vals); |
273 |
|
274 |
@@ -1324,37 +1358,42 @@ void reconfig_windows(void) { |
275 |
|
276 |
walk->bar = xcb_generate_id(xcb_connection); |
277 |
walk->buffer = xcb_generate_id(xcb_connection); |
278 |
- mask = XCB_CW_BACK_PIXEL | XCB_CW_OVERRIDE_REDIRECT | XCB_CW_EVENT_MASK; |
279 |
+ mask = XCB_CW_BACK_PIXEL | XCB_CW_BORDER_PIXEL | XCB_CW_OVERRIDE_REDIRECT |
280 |
+ | XCB_CW_EVENT_MASK | XCB_CW_COLORMAP; |
281 |
/* Black background */ |
282 |
values[0] = colors.bar_bg; |
283 |
+ /* Border */ |
284 |
+ values[1] = root_screen->black_pixel; |
285 |
/* If hide_on_modifier is set, i3 is not supposed to manage our bar-windows */ |
286 |
- values[1] = config.hide_on_modifier; |
287 |
+ values[2] = config.hide_on_modifier; |
288 |
/* We enable the following EventMask fields: |
289 |
* EXPOSURE, to get expose events (we have to re-draw then) |
290 |
* SUBSTRUCTURE_REDIRECT, to get ConfigureRequests when the tray |
291 |
* child windows use ConfigureWindow |
292 |
* BUTTON_PRESS, to handle clicks on the workspace buttons |
293 |
* */ |
294 |
- values[2] = XCB_EVENT_MASK_EXPOSURE | |
295 |
+ values[3] = XCB_EVENT_MASK_EXPOSURE | |
296 |
XCB_EVENT_MASK_SUBSTRUCTURE_REDIRECT; |
297 |
if (!config.disable_ws) { |
298 |
- values[2] |= XCB_EVENT_MASK_BUTTON_PRESS; |
299 |
+ values[3] |= XCB_EVENT_MASK_BUTTON_PRESS; |
300 |
} |
301 |
+ /* Colormap */ |
302 |
+ values[4] = colormap; |
303 |
xcb_void_cookie_t win_cookie = xcb_create_window_checked(xcb_connection, |
304 |
- root_screen->root_depth, |
305 |
+ depth, |
306 |
walk->bar, |
307 |
xcb_root, |
308 |
walk->rect.x, walk->rect.y + walk->rect.h - font.height - 6, |
309 |
walk->rect.w, font.height + 6, |
310 |
0, |
311 |
XCB_WINDOW_CLASS_INPUT_OUTPUT, |
312 |
- root_screen->root_visual, |
313 |
+ visual_type->visual_id, |
314 |
mask, |
315 |
values); |
316 |
|
317 |
/* The double-buffer we use to render stuff off-screen */ |
318 |
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, |
319 |
- root_screen->root_depth, |
320 |
+ depth, |
321 |
walk->buffer, |
322 |
walk->bar, |
323 |
walk->rect.w, |
324 |
@@ -1494,7 +1533,7 @@ void reconfig_windows(void) { |
325 |
|
326 |
DLOG("Recreating buffer for output %s\n", walk->name); |
327 |
xcb_void_cookie_t pm_cookie = xcb_create_pixmap_checked(xcb_connection, |
328 |
- root_screen->root_depth, |
329 |
+ depth, |
330 |
walk->buffer, |
331 |
walk->bar, |
332 |
walk->rect.w, |
333 |
@@ -1633,7 +1672,7 @@ void draw_bars(bool unhide) { |
334 |
1, |
335 |
&rect); |
336 |
set_font_colors(outputs_walk->bargc, fg_color, bg_color); |
337 |
- draw_text(ws_walk->name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, ws_walk->name_width); |
338 |
+ draw_text(ws_walk->name, outputs_walk->buffer, visual_type, outputs_walk->bargc, i + 5, 3, ws_walk->name_width); |
339 |
i += 10 + ws_walk->name_width + 1; |
340 |
|
341 |
} |
342 |
@@ -1669,7 +1708,7 @@ void draw_bars(bool unhide) { |
343 |
&rect); |
344 |
|
345 |
set_font_colors(outputs_walk->bargc, fg_color, bg_color); |
346 |
- draw_text(binding.name, outputs_walk->buffer, outputs_walk->bargc, i + 5, 3, binding.width); |
347 |
+ draw_text(binding.name, outputs_walk->buffer, visual_type, outputs_walk->bargc, i + 5, 3, binding.width); |
348 |
} |
349 |
|
350 |
i = 0; |
b/include/libi3.h
355 |
@@ -231,14 +231,25 @@ int ipc_recv_message(int sockfd, uint32_t *message_type, |
356 |
void fake_configure_notify(xcb_connection_t *conn, xcb_rectangle_t r, xcb_window_t window, int border_width); |
357 |
|
358 |
/** |
359 |
+ * Checks whether the given string denotes a valid color. |
360 |
+ * |
361 |
+ * Color strings start with # followed by two hexadecimal digits for each |
362 |
+ * component. |
363 |
+ * |
364 |
+ * RGB components must be specified while alpha is optional, |
365 |
+ * i.e. all valid colors are of the form #RRGGBB or #AARRGGBB. |
366 |
+ */ |
367 |
+bool valid_colorpixel(const char* hex); |
368 |
+ |
369 |
+/** |
370 |
* Returns the colorpixel to use for the given hex color (think of HTML). Only |
371 |
* works for true-color (vast majority of cases) at the moment, avoiding a |
372 |
* roundtrip to X11. |
373 |
* |
374 |
- * The hex_color has to start with #, for example #FF00FF. |
375 |
+ * See valid_colorpixel() for a description of the color format. |
376 |
* |
377 |
* NOTE that get_colorpixel() does _NOT_ check the given color code for validity. |
378 |
- * This has to be done by the caller. |
379 |
+ * The caller should use valid_colorpixel() to sanitize user input. |
380 |
* |
381 |
* NOTE that this function may in the future rely on a global xcb_connection_t |
382 |
* variable called 'conn' to be present. |
383 |
@@ -328,14 +339,14 @@ void set_font_colors(xcb_gcontext_t gc, uint32_t foreground, uint32_t background |
384 |
* Text must be specified as an i3String. |
385 |
* |
386 |
*/ |
387 |
-void draw_text(i3String *text, xcb_drawable_t drawable, |
388 |
+void draw_text(i3String *text, xcb_drawable_t drawable, xcb_visualtype_t* visual, |
389 |
xcb_gcontext_t gc, int x, int y, int max_width); |
390 |
|
391 |
/** |
392 |
* ASCII version of draw_text to print static strings. |
393 |
* |
394 |
*/ |
395 |
-void draw_text_ascii(const char *text, xcb_drawable_t drawable, |
396 |
+void draw_text_ascii(const char *text, xcb_drawable_t drawable, xcb_visualtype_t* visual, |
397 |
xcb_gcontext_t gc, int x, int y, int max_width); |
398 |
|
399 |
/** |
b/libi3/font.c
404 |
@@ -79,11 +79,11 @@ static bool load_pango_font(i3Font *font, const char *desc) { |
405 |
* |
406 |
*/ |
407 |
static void draw_text_pango(const char *text, size_t text_len, |
408 |
- xcb_drawable_t drawable, int x, int y, int max_width) { |
409 |
+ xcb_drawable_t drawable, xcb_visualtype_t* visual, int x, int y, int max_width) { |
410 |
/* Create the Pango layout */ |
411 |
/* root_visual_type is cached in load_pango_font */ |
412 |
cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, |
413 |
- root_visual_type, x + max_width, y + savedFont->height); |
414 |
+ visual, x + max_width, y + savedFont->height); |
415 |
cairo_t *cr = cairo_create(surface); |
416 |
PangoLayout *layout = pango_cairo_create_layout(cr); |
417 |
pango_layout_set_font_description(layout, savedFont->specific.pango_desc); |
418 |
@@ -322,7 +322,7 @@ static void draw_text_xcb(const xcb_char2b_t *text, size_t text_len, xcb_drawabl |
419 |
* Text must be specified as an i3String. |
420 |
* |
421 |
*/ |
422 |
-void draw_text(i3String *text, xcb_drawable_t drawable, |
423 |
+void draw_text(i3String *text, xcb_drawable_t drawable, xcb_visualtype_t* visual, |
424 |
xcb_gcontext_t gc, int x, int y, int max_width) { |
425 |
assert(savedFont != NULL); |
426 |
|
427 |
@@ -338,7 +338,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable, |
428 |
case FONT_TYPE_PANGO: |
429 |
/* Render the text using Pango */ |
430 |
draw_text_pango(i3string_as_utf8(text), i3string_get_num_bytes(text), |
431 |
- drawable, x, y, max_width); |
432 |
+ drawable, visual, x, y, max_width); |
433 |
return; |
434 |
#endif |
435 |
default: |
436 |
@@ -350,7 +350,7 @@ void draw_text(i3String *text, xcb_drawable_t drawable, |
437 |
* ASCII version of draw_text to print static strings. |
438 |
* |
439 |
*/ |
440 |
-void draw_text_ascii(const char *text, xcb_drawable_t drawable, |
441 |
+void draw_text_ascii(const char *text, xcb_drawable_t drawable, xcb_visualtype_t* visual, |
442 |
xcb_gcontext_t gc, int x, int y, int max_width) { |
443 |
assert(savedFont != NULL); |
444 |
|
445 |
@@ -364,7 +364,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, |
446 |
if (text_len > 255) { |
447 |
/* The text is too long to draw it directly to X */ |
448 |
i3String *str = i3string_from_utf8(text); |
449 |
- draw_text(str, drawable, gc, x, y, max_width); |
450 |
+ draw_text(str, drawable, visual, gc, x, y, max_width); |
451 |
i3string_free(str); |
452 |
} else { |
453 |
/* X11 coordinates for fonts start at the baseline */ |
454 |
@@ -378,7 +378,7 @@ void draw_text_ascii(const char *text, xcb_drawable_t drawable, |
455 |
case FONT_TYPE_PANGO: |
456 |
/* Render the text using Pango */ |
457 |
draw_text_pango(text, strlen(text), |
458 |
- drawable, x, y, max_width); |
459 |
+ drawable, visual, x, y, max_width); |
460 |
return; |
461 |
#endif |
462 |
default: |
b/libi3/get_colorpixel.c
467 |
@@ -7,9 +7,54 @@ |
468 |
*/ |
469 |
#include <stdlib.h> |
470 |
#include <stdint.h> |
471 |
+#include <string.h> |
472 |
+#include <assert.h> |
473 |
|
474 |
#include "libi3.h" |
475 |
|
476 |
+bool has_alpha(const char* hex) { |
477 |
+ assert(strlen(hex) >= 7); |
478 |
+ |
479 |
+ return hex[7] != '\0'; |
480 |
+} |
481 |
+ |
482 |
+bool in_range(char val, char lower, char upper) { |
483 |
+ return lower <= val && val <= upper; |
484 |
+} |
485 |
+ |
486 |
+bool is_hex(char c) { |
487 |
+ return in_range(c, '0', '9') |
488 |
+ || in_range(c, 'A', 'F') |
489 |
+ || in_range(c, 'a', 'f'); |
490 |
+} |
491 |
+ |
492 |
+bool valid_colorpixel(const char* hex) { |
493 |
+ if (hex[0] != '#') { |
494 |
+ return false; |
495 |
+ } |
496 |
+ |
497 |
+ size_t len = strlen(hex); |
498 |
+ if (len < 7) { |
499 |
+ return false; |
500 |
+ } |
501 |
+ |
502 |
+ if (has_alpha(hex)) { |
503 |
+ if (len != 9) { |
504 |
+ return false; |
505 |
+ } |
506 |
+ } else if (len != 7) { |
507 |
+ return false; |
508 |
+ } |
509 |
+ |
510 |
+ while (*++hex) { |
511 |
+ if (!is_hex(*hex)) { |
512 |
+ return false; |
513 |
+ } |
514 |
+ } |
515 |
+ |
516 |
+ return true; |
517 |
+} |
518 |
+ |
519 |
/* |
520 |
* Returns the colorpixel to use for the given hex color (think of HTML). Only |
521 |
* works for true-color (vast majority of cases) at the moment, avoiding a |
522 |
@@ -25,14 +70,23 @@ |
523 |
* |
524 |
*/ |
525 |
uint32_t get_colorpixel(const char *hex) { |
526 |
- char strgroups[3][3] = {{hex[1], hex[2], '\0'}, |
527 |
- {hex[3], hex[4], '\0'}, |
528 |
- {hex[5], hex[6], '\0'}}; |
529 |
- uint8_t r = strtol(strgroups[0], NULL, 16); |
530 |
- uint8_t g = strtol(strgroups[1], NULL, 16); |
531 |
- uint8_t b = strtol(strgroups[2], NULL, 16); |
532 |
- |
533 |
- /* We set the first 8 bits high to have 100% opacity in case of a 32 bit |
534 |
+ assert(valid_colorpixel(hex)); |
535 |
+ |
536 |
+ /* If specified without alpha, assume 100% opacity in case of a 32 bit |
537 |
* color depth visual. */ |
538 |
- return (0xFF << 24) | (r << 16 | g << 8 | b); |
539 |
+ char strgroups[4][3] = {{'F', 'F', '\0'}}; |
540 |
+ |
541 |
+ size_t first_component = has_alpha(hex) ? 0 : 1; |
542 |
+ for (size_t i = first_component; i < 4; ++i) { |
543 |
+ strgroups[i][0] = *++hex; |
544 |
+ strgroups[i][1] = *++hex; |
545 |
+ strgroups[i][2] = '\0'; |
546 |
+ } |
547 |
+ |
548 |
+ uint8_t a = strtol(strgroups[0], NULL, 16); |
549 |
+ uint8_t r = strtol(strgroups[1], NULL, 16); |
550 |
+ uint8_t g = strtol(strgroups[2], NULL, 16); |
551 |
+ uint8_t b = strtol(strgroups[3], NULL, 16); |
552 |
+ |
553 |
+ return (a << 24 | r << 16 | g << 8 | b); |
554 |
} |
b/src/config_directives.c
559 |
@@ -24,6 +24,18 @@ |
560 |
y(map_close); \ |
561 |
} while (0) |
562 |
|
563 |
+// Macro to sanitize user-provided color strings |
564 |
+#define APPLY_COLOR(classname, value) \ |
565 |
+ do { \ |
566 |
+ if (value) { \ |
567 |
+ if (valid_colorpixel(value)) { \ |
568 |
+ config.client.classname = get_colorpixel(value); \ |
569 |
+ } else { \ |
570 |
+ ELOG("Invalid color specified for %s: %s", #classname, value); \ |
571 |
+ } \ |
572 |
+ } \ |
573 |
+ } while (0) |
574 |
+ |
575 |
/******************************************************************************* |
576 |
* Criteria functions. |
577 |
******************************************************************************/ |
578 |
@@ -403,19 +415,19 @@ CFGFUN(popup_during_fullscreen, const char *value) { |
579 |
|
580 |
CFGFUN(color_single, const char *colorclass, const char *color) { |
581 |
/* used for client.background only currently */ |
582 |
- config.client.background = get_colorpixel(color); |
583 |
+ if (strcmp(colorclass, "client.background") == 0) { |
584 |
+ APPLY_COLOR(background, color); |
585 |
+ } |
586 |
} |
587 |
|
588 |
CFGFUN(color, const char *colorclass, const char *border, const char *background, const char *text, const char *indicator) { |
589 |
#define APPLY_COLORS(classname) \ |
590 |
do { \ |
591 |
if (strcmp(colorclass, "client." #classname) == 0) { \ |
592 |
- config.client.classname.border = get_colorpixel(border); \ |
593 |
- config.client.classname.background = get_colorpixel(background); \ |
594 |
- config.client.classname.text = get_colorpixel(text); \ |
595 |
- if (indicator != NULL) { \ |
596 |
- config.client. classname .indicator = get_colorpixel(indicator); \ |
597 |
- } \ |
598 |
+ APPLY_COLOR(classname.border, border); \ |
599 |
+ APPLY_COLOR(classname.background, background); \ |
600 |
+ APPLY_COLOR(classname.text, text); \ |
601 |
+ APPLY_COLOR(classname.indicator, indicator); \ |
602 |
} \ |
603 |
} while (0) |
604 |
|
b/src/sighandler.c
609 |
@@ -24,6 +24,8 @@ |
610 |
|
611 |
static void open_popups(void); |
612 |
|
613 |
+extern xcb_visualtype_t* visual_type; |
614 |
+ |
615 |
static xcb_gcontext_t pixmap_gc; |
616 |
static xcb_pixmap_t pixmap; |
617 |
static int raised_signal; |
618 |
@@ -150,7 +152,7 @@ static int sig_draw_window(xcb_window_t win, int width, int height, int font_hei |
619 |
if (i == backtrace_string_index) |
620 |
set_font_colors(pixmap_gc, get_colorpixel(bt_colour), get_colorpixel("#000000")); |
621 |
|
622 |
- draw_text(crash_text_i3strings[i], pixmap, pixmap_gc, |
623 |
+ draw_text(crash_text_i3strings[i], pixmap, visual_type, pixmap_gc, |
624 |
8, 5 + i * font_height, width - 16); |
625 |
|
626 |
/* and reset the colour again for other lines */ |
b/src/x.c
631 |
@@ -15,6 +15,8 @@ |
632 |
/* Stores the X11 window ID of the currently focused window */ |
633 |
xcb_window_t focused_id = XCB_NONE; |
634 |
|
635 |
+xcb_visualtype_t* visual_type; |
636 |
+ |
637 |
/* The bottom-to-top window stack of all windows which are managed by i3. |
638 |
* Used for x_get_window_stack(). */ |
639 |
static xcb_window_t *btt_stack; |
640 |
@@ -99,7 +101,10 @@ void x_con_init(Con *con, uint16_t depth) { |
641 |
if (depth != root_depth && depth != XCB_COPY_FROM_PARENT) { |
642 |
/* For custom visuals, we need to create a colormap before creating |
643 |
* this window. It will be freed directly after creating the window. */ |
644 |
- visual = get_visualid_by_depth(depth); |
645 |
+ visual_type = xcb_aux_find_visual_by_attrs(root_screen, -1, depth); |
646 |
+ if (visual_type) { |
647 |
+ visual = visual_type->visual_id; |
648 |
+ } |
649 |
win_colormap = xcb_generate_id(conn); |
650 |
xcb_create_colormap_checked(conn, XCB_COLORMAP_ALLOC_NONE, win_colormap, root, visual); |
651 |
|
652 |
@@ -136,6 +141,10 @@ void x_con_init(Con *con, uint16_t depth) { |
653 |
values[2] = colormap; |
654 |
} |
655 |
|
656 |
+ if (!visual_type) { |
657 |
+ visual_type = get_visualtype(root_screen); |
658 |
+ } |
659 |
+ |
660 |
Rect dims = { -15, -15, 10, 10 }; |
661 |
con->frame = create_window(conn, dims, depth, visual, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCURSOR_CURSOR_POINTER, false, mask, values); |
662 |
|
663 |
@@ -500,7 +509,7 @@ void x_draw_decoration(Con *con) { |
664 |
free(tree); |
665 |
|
666 |
draw_text_ascii(title, |
667 |
- parent->pixmap, parent->pm_gc, |
668 |
+ parent->pixmap, visual_type, parent->pm_gc, |
669 |
con->deco_rect.x + 2, con->deco_rect.y + text_offset_y, |
670 |
con->deco_rect.width - 2); |
671 |
free(title); |
672 |
@@ -529,7 +538,7 @@ void x_draw_decoration(Con *con) { |
673 |
int indent_px = (indent_level * 5) * indent_mult; |
674 |
|
675 |
draw_text(win->name, |
676 |
- parent->pixmap, parent->pm_gc, |
677 |
+ parent->pixmap, visual_type, parent->pm_gc, |
678 |
con->deco_rect.x + 2 + indent_px, con->deco_rect.y + text_offset_y, |
679 |
con->deco_rect.width - 2 - indent_px); |
680 |
|