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 |
|