From: Michael Stapelberg Date: Sun, 18 Oct 2015 10:26:37 +0000 (+0200) Subject: Merge pull request #2004 from Airblader/bug-2003 X-Git-Tag: 4.12~131 X-Git-Url: https://git.sur5r.net/?a=commitdiff_plain;h=fccf83294a0e449c39ee3a5fcb8c124e2eecc649;hp=a172168e61e37a1c4415f4abfd5ece6c5293dadf;p=i3%2Fi3 Merge pull request #2004 from Airblader/bug-2003 Fix moving windows to a marked workspace by mark. --- diff --git a/docs/ipc b/docs/ipc index 5113d79b..1813e53a 100644 --- a/docs/ipc +++ b/docs/ipc @@ -717,11 +717,15 @@ This event consists of a single serialized map containing a property This event consists of a single serialized map containing a property +change (string)+ which holds the name of current mode in use. The name is the same as specified in config when creating a mode. The default -mode is simply named default. +mode is simply named default. It contains a second property, +pango_markup+, which +defines whether pango markup shall be used for displaying this mode. *Example:* --------------------------- -{ "change": "default" } +{ + "change": "default", + "pango_markup": true +} --------------------------- === window event diff --git a/docs/userguide b/docs/userguide index c4c93796..c98c25f9 100644 --- a/docs/userguide +++ b/docs/userguide @@ -610,6 +610,10 @@ Note that this does not apply to all cases, e.g., when feeding data into a runni causing it to request being focused. To configure the behavior in such cases, refer to <>. ++no_focus+ will also be ignored for the first window on a workspace as there shouldn't be +a reason to not focus the window in this case. This allows for better usability in +combination with +workspace_layout+. + *Syntax*: ------------------- no_focus @@ -1845,6 +1849,10 @@ container to workspace next+, +move container to workspace prev+ to move a container to the next/previous workspace and +move container to workspace current+ (the last one makes sense only when used with criteria). ++workspace next+ cycles through either numbered or named workspaces. But when it +reaches the last numbered/named workspace, it looks for named workspaces after +exhausting numbered ones and looks for numbered ones after exhausting named ones. + See <> for how to move a container/workspace to a different RandR output. diff --git a/i3bar/include/cairo_util.h b/i3bar/include/cairo_util.h deleted file mode 100644 index 37eaa6e2..00000000 --- a/i3bar/include/cairo_util.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * © 2015 Ingo Bürk and contributors (see also: LICENSE) - * - * cairo_util.h: Utility for operations using cairo. - * - */ -#pragma once - -#include - -/* Represents a color split by color channel. */ -typedef struct color_t { - double red; - double green; - double blue; - - /* For compatibility, we also store the colorpixel for now. */ - uint32_t colorpixel; -} color_t; - -/* A wrapper grouping an XCB drawable and both a graphics context - * and the corresponding cairo objects representing it. */ -typedef struct surface_t { - /* The drawable which is being represented. */ - xcb_drawable_t id; - - // TODO remove this once i3 uses solely cairo for drawing operations - /* A classic XCB graphics context. This should not be used for - * drawing operations. */ - xcb_gcontext_t gc; - - /* A cairo surface representing the drawable. */ - cairo_surface_t *surface; - - /* The cairo object representing the drawale. In general, - * this is what one should use for any drawing operation. */ - cairo_t *cr; -} surface_t; - -/** - * Initialize the cairo surface to represent the given drawable. - * - */ -void cairo_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height); - -/** - * Destroys the surface. - * - */ -void cairo_surface_free(surface_t *surface); - -/** - * Parses the given color in hex format to an internal color representation. - * Note that the input must begin with a hash sign, e.g., "#3fbc59". - * - */ -color_t cairo_hex_to_color(const char *color); - -/** - * Set the given color as the source color on the surface. - * - */ -void cairo_set_source_color(surface_t *surface, color_t color); - -/** - * Draw the given text using libi3. - * This function also marks the surface dirty which is needed if other means of - * drawing are used. This will be the case when using XCB to draw text. - * - */ -void cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); - -/** - * Draws a filled rectangle. - * This function is a convenience wrapper and takes care of flushing the - * surface as well as restoring the cairo state. - * Note that the drawing is done using CAIRO_OPERATOR_SOURCE. - * - */ -void cairo_draw_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h); - -/** - * Copies a surface onto another surface. - * Note that the drawing is done using CAIRO_OPERATOR_SOURCE. - * - */ -void cairo_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, - double dest_x, double dest_y, double dest_w, double dest_h); diff --git a/i3bar/include/common.h b/i3bar/include/common.h index 50d1c7b9..4d2dbd35 100644 --- a/i3bar/include/common.h +++ b/i3bar/include/common.h @@ -49,7 +49,7 @@ struct status_block { bool urgent; bool no_separator; - bool is_markup; + bool pango_markup; /* The amount of pixels necessary to render a separater after the block. */ uint32_t sep_block_width; @@ -80,4 +80,4 @@ TAILQ_HEAD(statusline_head, status_block) statusline_head; #include "config.h" #include "libi3.h" #include "parse_json_header.h" -#include "cairo_util.h" +#include "draw_util.h" diff --git a/i3bar/include/draw_util.h b/i3bar/include/draw_util.h new file mode 100644 index 00000000..9bac72ca --- /dev/null +++ b/i3bar/include/draw_util.h @@ -0,0 +1,103 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * © 2015 Ingo Bürk and contributors (see also: LICENSE) + * + * draw.h: Utility for drawing. + * + */ +#pragma once + +#ifdef I3BAR_CAIRO +#include +#endif + +#ifdef I3BAR_CAIRO +/* We need to flush cairo surfaces twice to avoid an assertion bug. See #1989 + * and https://bugs.freedesktop.org/show_bug.cgi?id=92455. */ +#define CAIRO_SURFACE_FLUSH(surface) \ + do { \ + cairo_surface_flush(surface); \ + cairo_surface_flush(surface); \ + } while (0) +#endif + +/* Represents a color split by color channel. */ +typedef struct color_t { + double red; + double green; + double blue; + + /* For compatibility, we also store the colorpixel for now. */ + uint32_t colorpixel; +} color_t; + +/* A wrapper grouping an XCB drawable and both a graphics context + * and the corresponding cairo objects representing it. */ +typedef struct surface_t { + /* The drawable which is being represented. */ + xcb_drawable_t id; + + /* A classic XCB graphics context. */ + xcb_gcontext_t gc; + + int width; + int height; + +#ifdef I3BAR_CAIRO + /* A cairo surface representing the drawable. */ + cairo_surface_t *surface; + + /* The cairo object representing the drawale. In general, + * this is what one should use for any drawing operation. */ + cairo_t *cr; +#endif +} surface_t; + +/** + * Initialize the surface to represent the given drawable. + * + */ +void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height); + +/** + * Destroys the surface. + * + */ +void draw_util_surface_free(surface_t *surface); + +/** + * Parses the given color in hex format to an internal color representation. + * Note that the input must begin with a hash sign, e.g., "#3fbc59". + * + */ +color_t draw_util_hex_to_color(const char *color); + +/** + * Draw the given text using libi3. + * This function also marks the surface dirty which is needed if other means of + * drawing are used. This will be the case when using XCB to draw text. + * + */ +void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width); + +/** + * Draws a filled rectangle. + * This function is a convenience wrapper and takes care of flushing the + * surface as well as restoring the cairo state. + * + */ +void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h); + +/** + * Clears a surface with the given color. + * + */ +void draw_util_clear_surface(surface_t *surface, color_t color); + +/** + * Copies a surface onto another surface. + * + */ +void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, + double dest_x, double dest_y, double width, double height); diff --git a/i3bar/include/outputs.h b/i3bar/include/outputs.h index 7ad3ed50..87cc023e 100644 --- a/i3bar/include/outputs.h +++ b/i3bar/include/outputs.h @@ -13,7 +13,7 @@ #include #include "common.h" -#include "cairo_util.h" +#include "draw_util.h" typedef struct i3_output i3_output; diff --git a/i3bar/src/cairo_util.c b/i3bar/src/cairo_util.c deleted file mode 100644 index 52181aee..00000000 --- a/i3bar/src/cairo_util.c +++ /dev/null @@ -1,135 +0,0 @@ -/* - * vim:ts=4:sw=4:expandtab - * - * © 2015 Ingo Bürk and contributors (see also: LICENSE) - * - * cairo_util.c: Utility for operations using cairo. - * - */ -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "libi3.h" - -xcb_connection_t *xcb_connection; -xcb_visualtype_t *visual_type; - -/* - * Initialize the cairo surface to represent the given drawable. - * - */ -void cairo_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height) { - surface->id = drawable; - - surface->gc = xcb_generate_id(xcb_connection); - xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection, surface->gc, surface->id, 0, NULL); - if (xcb_request_failed(gc_cookie, "Could not create graphical context")) - exit(EXIT_FAILURE); - - surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height); - surface->cr = cairo_create(surface->surface); -} - -/* - * Destroys the surface. - * - */ -void cairo_surface_free(surface_t *surface) { - xcb_free_gc(xcb_connection, surface->gc); - cairo_surface_destroy(surface->surface); - cairo_destroy(surface->cr); -} - -/* - * Parses the given color in hex format to an internal color representation. - * Note that the input must begin with a hash sign, e.g., "#3fbc59". - * - */ -color_t cairo_hex_to_color(const char *color) { - char groups[3][3] = { - {color[1], color[2], '\0'}, - {color[3], color[4], '\0'}, - {color[5], color[6], '\0'}}; - - return (color_t){ - .red = strtol(groups[0], NULL, 16) / 255.0, - .green = strtol(groups[1], NULL, 16) / 255.0, - .blue = strtol(groups[2], NULL, 16) / 255.0, - .colorpixel = get_colorpixel(color)}; -} - -/* - * Set the given color as the source color on the surface. - * - */ -void cairo_set_source_color(surface_t *surface, color_t color) { - cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue); -} - -/** - * Draw the given text using libi3. - * This function also marks the surface dirty which is needed if other means of - * drawing are used. This will be the case when using XCB to draw text. - * - */ -void cairo_draw_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { - set_font_colors(surface->gc, fg_color.colorpixel, bg_color.colorpixel); - draw_text(text, surface->id, surface->gc, visual_type, x, y, max_width); - - cairo_surface_mark_dirty(surface->surface); -} - -/** - * Draws a filled rectangle. - * This function is a convenience wrapper and takes care of flushing the - * surface as well as restoring the cairo state. - * Note that the drawing is done using CAIRO_OPERATOR_SOURCE. - * - */ -void cairo_draw_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) { - cairo_save(surface->cr); - - /* Using the SOURCE operator will copy both color and alpha information directly - * onto the surface rather than blending it. This is a bit more efficient and - * allows better color control for the user when using opacity. */ - cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_color(surface, color); - - cairo_rectangle(surface->cr, x, y, w, h); - cairo_fill(surface->cr); - - /* Make sure we flush the surface for any text drawing operations that could follow. - * Since we support drawing text via XCB, we need this. */ - cairo_surface_flush(surface->surface); - - cairo_restore(surface->cr); -} - -/** - * Copies a surface onto another surface. - * Note that the drawing is done using CAIRO_OPERATOR_SOURCE. - * - */ -void cairo_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, - double dest_x, double dest_y, double dest_w, double dest_h) { - cairo_save(dest->cr); - - /* Using the SOURCE operator will copy both color and alpha information directly - * onto the surface rather than blending it. This is a bit more efficient and - * allows better color control for the user when using opacity. */ - cairo_set_operator(dest->cr, CAIRO_OPERATOR_SOURCE); - cairo_set_source_surface(dest->cr, src->surface, src_x, src_y); - - cairo_rectangle(dest->cr, dest_x, dest_y, dest_w, dest_h); - cairo_fill(dest->cr); - - /* Make sure we flush the surface for any text drawing operations that could follow. - * Since we support drawing text via XCB, we need this. */ - cairo_surface_flush(dest->surface); - cairo_restore(dest->cr); -} diff --git a/i3bar/src/child.c b/i3bar/src/child.c index cfc96d5f..2b10be49 100644 --- a/i3bar/src/child.c +++ b/i3bar/src/child.c @@ -206,7 +206,7 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) { return 1; } if (strcasecmp(ctx->last_map_key, "markup") == 0) { - ctx->block.is_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango"))); + ctx->block.pango_markup = (len == strlen("pango") && !strncasecmp((const char *)val, "pango", strlen("pango"))); return 1; } if (strcasecmp(ctx->last_map_key, "align") == 0) { @@ -220,24 +220,15 @@ static int stdin_string(void *context, const unsigned char *val, size_t len) { return 1; } if (strcasecmp(ctx->last_map_key, "min_width") == 0) { - char *copy = (char *)smalloc(len + 1); - strncpy(copy, (const char *)val, len); - copy[len] = 0; - ctx->block.min_width_str = copy; + sasprintf(&(ctx->block.min_width_str), "%.*s", len, val); return 1; } if (strcasecmp(ctx->last_map_key, "name") == 0) { - char *copy = (char *)smalloc(len + 1); - strncpy(copy, (const char *)val, len); - copy[len] = 0; - ctx->block.name = copy; + sasprintf(&(ctx->block.name), "%.*s", len, val); return 1; } if (strcasecmp(ctx->last_map_key, "instance") == 0) { - char *copy = (char *)smalloc(len + 1); - strncpy(copy, (const char *)val, len); - copy[len] = 0; - ctx->block.instance = copy; + sasprintf(&(ctx->block.instance), "%.*s", len, val); return 1; } @@ -275,15 +266,15 @@ static int stdin_end_map(void *context) { if (new_block->min_width_str) { i3String *text = i3string_from_utf8(new_block->min_width_str); - i3string_set_markup(text, new_block->is_markup); + i3string_set_markup(text, new_block->pango_markup); new_block->min_width = (uint32_t)predict_text_width(text); i3string_free(text); } - i3string_set_markup(new_block->full_text, new_block->is_markup); + i3string_set_markup(new_block->full_text, new_block->pango_markup); if (new_block->short_text != NULL) - i3string_set_markup(new_block->short_text, new_block->is_markup); + i3string_set_markup(new_block->short_text, new_block->pango_markup); TAILQ_INSERT_TAIL(&statusline_buffer, new_block, blocks); return 1; diff --git a/i3bar/src/config.c b/i3bar/src/config.c index 0e2dd05a..f3412719 100644 --- a/i3bar/src/config.c +++ b/i3bar/src/config.c @@ -30,10 +30,7 @@ static bool parsing_bindings; */ static int config_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { FREE(cur_key); - - cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); - strncpy(cur_key, (const char *)keyVal, keyLen); - cur_key[keyLen] = '\0'; + sasprintf(&(cur_key), "%.*s", keyLen, keyVal); if (strcmp(cur_key, "bindings") == 0) parsing_bindings = true; diff --git a/i3bar/src/draw_util.c b/i3bar/src/draw_util.c new file mode 100644 index 00000000..e1facf39 --- /dev/null +++ b/i3bar/src/draw_util.c @@ -0,0 +1,201 @@ +/* + * vim:ts=4:sw=4:expandtab + * + * © 2015 Ingo Bürk and contributors (see also: LICENSE) + * + * draw.c: Utility for drawing. + * + */ +#include +#include +#include +#include +#include +#ifdef I3BAR_CAIRO +#include +#endif + +#include "common.h" +#include "libi3.h" + +xcb_connection_t *xcb_connection; +xcb_visualtype_t *visual_type; + +/* Forward declarations */ +static void draw_util_set_source_color(surface_t *surface, color_t color); + +/* + * Initialize the surface to represent the given drawable. + * + */ +void draw_util_surface_init(surface_t *surface, xcb_drawable_t drawable, int width, int height) { + surface->id = drawable; + surface->width = width; + surface->height = height; + + surface->gc = xcb_generate_id(xcb_connection); + xcb_void_cookie_t gc_cookie = xcb_create_gc_checked(xcb_connection, surface->gc, surface->id, 0, NULL); + if (xcb_request_failed(gc_cookie, "Could not create graphical context")) + exit(EXIT_FAILURE); + +#ifdef I3BAR_CAIRO + surface->surface = cairo_xcb_surface_create(xcb_connection, surface->id, visual_type, width, height); + surface->cr = cairo_create(surface->surface); +#endif +} + +/* + * Destroys the surface. + * + */ +void draw_util_surface_free(surface_t *surface) { + xcb_free_gc(xcb_connection, surface->gc); +#ifdef I3BAR_CAIRO + cairo_surface_destroy(surface->surface); + cairo_destroy(surface->cr); +#endif +} + +/* + * Parses the given color in hex format to an internal color representation. + * Note that the input must begin with a hash sign, e.g., "#3fbc59". + * + */ +color_t draw_util_hex_to_color(const char *color) { + char groups[3][3] = { + {color[1], color[2], '\0'}, + {color[3], color[4], '\0'}, + {color[5], color[6], '\0'}}; + + return (color_t){ + .red = strtol(groups[0], NULL, 16) / 255.0, + .green = strtol(groups[1], NULL, 16) / 255.0, + .blue = strtol(groups[2], NULL, 16) / 255.0, + .colorpixel = get_colorpixel(color)}; +} + +/* + * Set the given color as the source color on the surface. + * + */ +static void draw_util_set_source_color(surface_t *surface, color_t color) { +#ifdef I3BAR_CAIRO + cairo_set_source_rgb(surface->cr, color.red, color.green, color.blue); +#else + uint32_t colorpixel = color.colorpixel; + xcb_change_gc(xcb_connection, surface->gc, XCB_GC_FOREGROUND | XCB_GC_BACKGROUND, + (uint32_t[]){colorpixel, colorpixel}); +#endif +} + +/** + * Draw the given text using libi3. + * This function also marks the surface dirty which is needed if other means of + * drawing are used. This will be the case when using XCB to draw text. + * + */ +void draw_util_text(i3String *text, surface_t *surface, color_t fg_color, color_t bg_color, int x, int y, int max_width) { +#ifdef I3BAR_CAIRO + /* Flush any changes before we draw the text as this might use XCB directly. */ + CAIRO_SURFACE_FLUSH(surface->surface); +#endif + + set_font_colors(surface->gc, fg_color.colorpixel, bg_color.colorpixel); + draw_text(text, surface->id, surface->gc, visual_type, x, y, max_width); + +#ifdef I3BAR_CAIRO + /* Notify cairo that we (possibly) used another way to draw on the surface. */ + cairo_surface_mark_dirty(surface->surface); +#endif +} + +/** + * Draws a filled rectangle. + * This function is a convenience wrapper and takes care of flushing the + * surface as well as restoring the cairo state. + * + */ +void draw_util_rectangle(surface_t *surface, color_t color, double x, double y, double w, double h) { +#ifdef I3BAR_CAIRO + cairo_save(surface->cr); + + /* Using the SOURCE operator will copy both color and alpha information directly + * onto the surface rather than blending it. This is a bit more efficient and + * allows better color control for the user when using opacity. */ + cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); + draw_util_set_source_color(surface, color); + + cairo_rectangle(surface->cr, x, y, w, h); + cairo_fill(surface->cr); + + /* Make sure we flush the surface for any text drawing operations that could follow. + * Since we support drawing text via XCB, we need this. */ + CAIRO_SURFACE_FLUSH(surface->surface); + + cairo_restore(surface->cr); +#else + draw_util_set_source_color(surface, color); + + xcb_rectangle_t rect = {x, y, w, h}; + xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); +#endif +} + +/** + * Clears a surface with the given color. + * + */ +void draw_util_clear_surface(surface_t *surface, color_t color) { +#ifdef I3BAR_CAIRO + cairo_save(surface->cr); + + /* Using the SOURCE operator will copy both color and alpha information directly + * onto the surface rather than blending it. This is a bit more efficient and + * allows better color control for the user when using opacity. */ + cairo_set_operator(surface->cr, CAIRO_OPERATOR_SOURCE); + draw_util_set_source_color(surface, color); + + cairo_paint(surface->cr); + + /* Make sure we flush the surface for any text drawing operations that could follow. + * Since we support drawing text via XCB, we need this. */ + CAIRO_SURFACE_FLUSH(surface->surface); + + cairo_restore(surface->cr); +#else + draw_util_set_source_color(surface, color); + + xcb_rectangle_t rect = {0, 0, surface->width, surface->height}; + xcb_poly_fill_rectangle(xcb_connection, surface->id, surface->gc, 1, &rect); +#endif +} + +/** + * Copies a surface onto another surface. + * + */ +void draw_util_copy_surface(surface_t *src, surface_t *dest, double src_x, double src_y, + double dest_x, double dest_y, double width, double height) { +#ifdef I3BAR_CAIRO + cairo_save(dest->cr); + + /* Using the SOURCE operator will copy both color and alpha information directly + * onto the surface rather than blending it. This is a bit more efficient and + * allows better color control for the user when using opacity. */ + cairo_set_operator(dest->cr, CAIRO_OPERATOR_SOURCE); + cairo_set_source_surface(dest->cr, src->surface, dest_x - src_x, src_y); + + cairo_rectangle(dest->cr, dest_x, dest_y, width, height); + cairo_fill(dest->cr); + + /* Make sure we flush the surface for any text drawing operations that could follow. + * Since we support drawing text via XCB, we need this. */ + CAIRO_SURFACE_FLUSH(src->surface); + CAIRO_SURFACE_FLUSH(dest->surface); + + cairo_restore(dest->cr); +#else + xcb_copy_area(xcb_connection, src->id, dest->id, dest->gc, (int16_t)src_x, (int16_t)src_y, + (int16_t)dest_x, (int16_t)dest_y, (uint16_t)width, (uint16_t)height); +#endif +} diff --git a/i3bar/src/mode.c b/i3bar/src/mode.c index 7f7537af..d6767786 100644 --- a/i3bar/src/mode.c +++ b/i3bar/src/mode.c @@ -20,6 +20,8 @@ struct mode_json_params { char *json; char *cur_key; + char *name; + bool pango_markup; mode *mode; }; @@ -31,17 +33,31 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) { struct mode_json_params *params = (struct mode_json_params *)params_; if (!strcmp(params->cur_key, "change")) { - /* Save the name */ - params->mode->name = i3string_from_markup_with_length((const char *)val, len); - /* Save its rendered width */ - params->mode->width = predict_text_width(params->mode->name); - - DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name)); + sasprintf(&(params->name), "%.*s", len, val); FREE(params->cur_key); + return 1; + } + FREE(params->cur_key); + return 0; +} + +/* + * Parse a boolean. + * + */ +static int mode_boolean_cb(void *params_, int val) { + struct mode_json_params *params = (struct mode_json_params *)params_; + + if (strcmp(params->cur_key, "pango_markup") == 0) { + DLOG("Setting pango_markup to %d.\n", val); + params->pango_markup = val; + + FREE(params->cur_key); return 1; } + FREE(params->cur_key); return 0; } @@ -54,10 +70,21 @@ static int mode_string_cb(void *params_, const unsigned char *val, size_t len) { static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { struct mode_json_params *params = (struct mode_json_params *)params_; FREE(params->cur_key); + sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal); + return 1; +} + +static int mode_end_map_cb(void *params_) { + struct mode_json_params *params = (struct mode_json_params *)params_; - params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); - strncpy(params->cur_key, (const char *)keyVal, keyLen); - params->cur_key[keyLen] = '\0'; + /* Save the name */ + params->mode->name = i3string_from_utf8(params->name); + i3string_set_markup(params->mode->name, params->pango_markup); + /* Save its rendered width */ + params->mode->width = predict_text_width(params->mode->name); + + DLOG("Got mode change: %s\n", i3string_as_utf8(params->mode->name)); + FREE(params->cur_key); return 1; } @@ -65,7 +92,9 @@ static int mode_map_key_cb(void *params_, const unsigned char *keyVal, size_t ke /* A datastructure to pass all these callbacks to yajl */ static yajl_callbacks mode_callbacks = { .yajl_string = mode_string_cb, + .yajl_boolean = mode_boolean_cb, .yajl_map_key = mode_map_key_cb, + .yajl_end_map = mode_end_map_cb, }; /* diff --git a/i3bar/src/outputs.c b/i3bar/src/outputs.c index fd2b1363..d0d175ca 100644 --- a/i3bar/src/outputs.c +++ b/i3bar/src/outputs.c @@ -108,9 +108,8 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len struct outputs_json_params *params = (struct outputs_json_params *)params_; if (!strcmp(params->cur_key, "current_workspace")) { - char *copy = smalloc(sizeof(const unsigned char) * (len + 1)); - strncpy(copy, (const char *)val, len); - copy[len] = '\0'; + char *copy = NULL; + sasprintf(©, "%.*s", len, val); char *end; errno = 0; @@ -118,7 +117,8 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len if (errno == 0 && (end && *end == '\0')) params->outputs_walk->ws = parsed_num; - free(copy); + + FREE(copy); FREE(params->cur_key); return 1; } @@ -127,14 +127,9 @@ static int outputs_string_cb(void *params_, const unsigned char *val, size_t len return 0; } - char *name = smalloc(sizeof(const unsigned char) * (len + 1)); - strncpy(name, (const char *)val, len); - name[len] = '\0'; - - params->outputs_walk->name = name; + sasprintf(&(params->outputs_walk->name), "%.*s", len, val); FREE(params->cur_key); - return 1; } @@ -228,11 +223,7 @@ static int outputs_end_map_cb(void *params_) { static int outputs_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { struct outputs_json_params *params = (struct outputs_json_params *)params_; FREE(params->cur_key); - - params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); - strncpy(params->cur_key, (const char *)keyVal, keyLen); - params->cur_key[keyLen] = '\0'; - + sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal); return 1; } diff --git a/i3bar/src/workspaces.c b/i3bar/src/workspaces.c index 961a41f5..77b351e8 100644 --- a/i3bar/src/workspaces.c +++ b/i3bar/src/workspaces.c @@ -102,8 +102,6 @@ static int workspaces_integer_cb(void *params_, long long val) { static int workspaces_string_cb(void *params_, const unsigned char *val, size_t len) { struct workspaces_json_params *params = (struct workspaces_json_params *)params_; - char *output_name; - if (!strcmp(params->cur_key, "name")) { const char *ws_name = (const char *)val; params->workspaces_walk->canonical_name = sstrndup(ws_name, len); @@ -147,11 +145,11 @@ static int workspaces_string_cb(void *params_, const unsigned char *val, size_t if (!strcmp(params->cur_key, "output")) { /* We add the ws to the TAILQ of the output, it belongs to */ - output_name = smalloc(sizeof(const unsigned char) * (len + 1)); - strncpy(output_name, (const char *)val, len); - output_name[len] = '\0'; + char *output_name = NULL; + sasprintf(&output_name, "%.*s", len, val); + i3_output *target = get_output_by_name(output_name); - if (target) { + if (target != NULL) { params->workspaces_walk->output = target; TAILQ_INSERT_TAIL(params->workspaces_walk->output->workspaces, @@ -201,11 +199,7 @@ static int workspaces_start_map_cb(void *params_) { static int workspaces_map_key_cb(void *params_, const unsigned char *keyVal, size_t keyLen) { struct workspaces_json_params *params = (struct workspaces_json_params *)params_; FREE(params->cur_key); - - params->cur_key = smalloc(sizeof(unsigned char) * (keyLen + 1)); - strncpy(params->cur_key, (const char *)keyVal, keyLen); - params->cur_key[keyLen] = '\0'; - + sasprintf(&(params->cur_key), "%.*s", keyLen, keyVal); return 1; } diff --git a/i3bar/src/xcb.c b/i3bar/src/xcb.c index ac9b50f9..63b9863a 100644 --- a/i3bar/src/xcb.c +++ b/i3bar/src/xcb.c @@ -30,7 +30,6 @@ #include #include #include -#include #include "common.h" #include "libi3.h" @@ -174,16 +173,16 @@ static void draw_separator(uint32_t x, struct status_block *block) { uint32_t center_x = x - sep_offset; if (config.separator_symbol == NULL) { /* Draw a classic one pixel, vertical separator. */ - cairo_draw_rectangle(&statusline_surface, colors.sep_fg, - center_x, - logical_px(sep_voff_px), - logical_px(1), - bar_height - 2 * logical_px(sep_voff_px)); + draw_util_rectangle(&statusline_surface, colors.sep_fg, + center_x, + logical_px(sep_voff_px), + logical_px(1), + bar_height - 2 * logical_px(sep_voff_px)); } else { /* Draw a custom separator. */ uint32_t separator_x = MAX(x - block->sep_block_width, center_x - separator_symbol_width / 2); - cairo_draw_text(config.separator_symbol, &statusline_surface, colors.sep_fg, colors.bar_bg, - separator_x, logical_px(ws_voff_px), x - separator_x); + draw_util_text(config.separator_symbol, &statusline_surface, colors.sep_fg, colors.bar_bg, + separator_x, logical_px(ws_voff_px), x - separator_x); } } @@ -244,12 +243,7 @@ void refresh_statusline(bool use_short_text) { realloc_sl_buffer(); /* Clear the statusline pixmap. */ - cairo_save(statusline_surface.cr); - cairo_set_source_color(&statusline_surface, colors.bar_bg); - cairo_set_operator(statusline_surface.cr, CAIRO_OPERATOR_SOURCE); - cairo_paint(statusline_surface.cr); - cairo_surface_flush(statusline_surface.surface); - cairo_restore(statusline_surface.cr); + draw_util_clear_surface(&statusline_surface, colors.bar_bg); /* Draw the text of each block. */ uint32_t x = 0; @@ -263,17 +257,17 @@ void refresh_statusline(bool use_short_text) { fg_color = colors.urgent_ws_fg; /* Draw the background */ - cairo_draw_rectangle(&statusline_surface, colors.urgent_ws_bg, - x - logical_px(2), - logical_px(1), - block->width + logical_px(4), - bar_height - logical_px(2)); + draw_util_rectangle(&statusline_surface, colors.urgent_ws_bg, + x - logical_px(2), + logical_px(1), + block->width + logical_px(4), + bar_height - logical_px(2)); } else { - fg_color = (block->color ? cairo_hex_to_color(block->color) : colors.bar_fg); + fg_color = (block->color ? draw_util_hex_to_color(block->color) : colors.bar_fg); } - cairo_draw_text(block->full_text, &statusline_surface, fg_color, colors.bar_bg, - x + block->x_offset, logical_px(ws_voff_px), block->width); + draw_util_text(block->full_text, &statusline_surface, fg_color, colors.bar_bg, + x + block->x_offset, logical_px(ws_voff_px), block->width); x += block->width + block->sep_block_width + block->x_offset + block->x_append; /* If this is not the last block, draw a separator. */ @@ -351,9 +345,9 @@ void unhide_bars(void) { * */ void init_colors(const struct xcb_color_strings_t *new_colors) { -#define PARSE_COLOR(name, def) \ - do { \ - colors.name = cairo_hex_to_color(new_colors->name ? new_colors->name : def); \ +#define PARSE_COLOR(name, def) \ + do { \ + colors.name = draw_util_hex_to_color(new_colors->name ? new_colors->name : def); \ } while (0) PARSE_COLOR(bar_fg, "#FFFFFF"); PARSE_COLOR(bar_bg, "#000000"); @@ -372,9 +366,9 @@ void init_colors(const struct xcb_color_strings_t *new_colors) { PARSE_COLOR(focus_ws_border, "#4c7899"); #undef PARSE_COLOR -#define PARSE_COLOR_FALLBACK(name, fallback) \ - do { \ - colors.name = new_colors->name ? cairo_hex_to_color(new_colors->name) : colors.fallback; \ +#define PARSE_COLOR_FALLBACK(name, fallback) \ + do { \ + colors.name = new_colors->name ? draw_util_hex_to_color(new_colors->name) : colors.fallback; \ } while (0) /* For the binding mode indicator colors, we don't hardcode a default. @@ -993,6 +987,13 @@ void xcb_chk_cb(struct ev_loop *loop, ev_check *watcher, int revents) { } while ((event = xcb_poll_for_event(xcb_connection)) != NULL) { + if (event->response_type == 0) { + xcb_generic_error_t *error = (xcb_generic_error_t *)event; + DLOG("Received X11 error, sequence 0x%x, error_code = %d\n", error->sequence, error->error_code); + free(event); + continue; + } + int type = (event->response_type & ~0x80); if (type == xkb_base && xkb_base > -1) { @@ -1133,7 +1134,7 @@ char *init_xcb_early() { xcb_root, root_screen->width_in_pixels, root_screen->height_in_pixels); - cairo_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels); + draw_util_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels); /* The various watchers to communicate with xcb */ xcb_io = smalloc(sizeof(ev_io)); @@ -1495,7 +1496,7 @@ void realloc_sl_buffer(void) { DLOG("Re-allocating statusline buffer, statusline_width = %d, root_screen->width_in_pixels = %d\n", statusline_width, root_screen->width_in_pixels); xcb_free_pixmap(xcb_connection, statusline_surface.id); - cairo_surface_free(&statusline_surface); + draw_util_surface_free(&statusline_surface); xcb_pixmap_t statusline_id = xcb_generate_id(xcb_connection); xcb_void_cookie_t sl_pm_cookie = xcb_create_pixmap_checked(xcb_connection, @@ -1504,7 +1505,7 @@ void realloc_sl_buffer(void) { xcb_root, MAX(root_screen->width_in_pixels, statusline_width), bar_height); - cairo_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels); + draw_util_surface_init(&statusline_surface, statusline_id, root_screen->width_in_pixels, root_screen->height_in_pixels); if (xcb_request_failed(sl_pm_cookie, "Could not allocate statusline buffer")) exit(EXIT_FAILURE); @@ -1655,8 +1656,8 @@ void reconfig_windows(bool redraw_bars) { 1, (unsigned char *)&atoms[_NET_WM_WINDOW_TYPE_DOCK]); - cairo_surface_init(&walk->bar, bar_id, walk->rect.w, bar_height); - cairo_surface_init(&walk->buffer, buffer_id, walk->rect.w, bar_height); + draw_util_surface_init(&walk->bar, bar_id, walk->rect.w, bar_height); + draw_util_surface_init(&walk->buffer, buffer_id, walk->rect.w, bar_height); xcb_void_cookie_t strut_cookie = config_strut_partial(walk); @@ -1736,10 +1737,10 @@ void reconfig_windows(bool redraw_bars) { walk->rect.w, bar_height); - cairo_surface_free(&(walk->bar)); - cairo_surface_free(&(walk->buffer)); - cairo_surface_init(&(walk->bar), walk->bar.id, walk->rect.w, bar_height); - cairo_surface_init(&(walk->buffer), walk->buffer.id, walk->rect.w, bar_height); + draw_util_surface_free(&(walk->bar)); + draw_util_surface_free(&(walk->buffer)); + draw_util_surface_init(&(walk->bar), walk->bar.id, walk->rect.w, bar_height); + draw_util_surface_init(&(walk->buffer), walk->buffer.id, walk->rect.w, bar_height); xcb_void_cookie_t map_cookie, umap_cookie; if (redraw_bars) { @@ -1797,12 +1798,7 @@ void draw_bars(bool unhide) { } /* First things first: clear the backbuffer */ - cairo_save(outputs_walk->buffer.cr); - cairo_set_source_color(&(outputs_walk->buffer), colors.bar_bg); - cairo_set_operator(outputs_walk->buffer.cr, CAIRO_OPERATOR_SOURCE); - cairo_paint(outputs_walk->buffer.cr); - cairo_surface_flush(outputs_walk->buffer.surface); - cairo_restore(outputs_walk->buffer.cr); + draw_util_clear_surface(&(outputs_walk->buffer), colors.bar_bg); if (!config.disable_ws) { i3_ws *ws_walk; @@ -1832,23 +1828,23 @@ void draw_bars(bool unhide) { } /* Draw the border of the button. */ - cairo_draw_rectangle(&(outputs_walk->buffer), border_color, - workspace_width, - logical_px(1), - ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), - font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)); + draw_util_rectangle(&(outputs_walk->buffer), border_color, + workspace_width, + logical_px(1), + ws_walk->name_width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), + font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)); /* Draw the inside of the button. */ - cairo_draw_rectangle(&(outputs_walk->buffer), bg_color, - workspace_width + logical_px(1), - 2 * logical_px(1), - ws_walk->name_width + 2 * logical_px(ws_hoff_px), - font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); + draw_util_rectangle(&(outputs_walk->buffer), bg_color, + workspace_width + logical_px(1), + 2 * logical_px(1), + ws_walk->name_width + 2 * logical_px(ws_hoff_px), + font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); - cairo_draw_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - logical_px(ws_voff_px), - ws_walk->name_width); + draw_util_text(ws_walk->name, &(outputs_walk->buffer), fg_color, bg_color, + workspace_width + logical_px(ws_hoff_px) + logical_px(1), + logical_px(ws_voff_px), + ws_walk->name_width); workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + ws_walk->name_width; if (TAILQ_NEXT(ws_walk, tailq) != NULL) @@ -1862,22 +1858,22 @@ void draw_bars(bool unhide) { color_t fg_color = colors.binding_mode_fg; color_t bg_color = colors.binding_mode_bg; - cairo_draw_rectangle(&(outputs_walk->buffer), colors.binding_mode_border, - workspace_width, - logical_px(1), - binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), - font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)); + draw_util_rectangle(&(outputs_walk->buffer), colors.binding_mode_border, + workspace_width, + logical_px(1), + binding.width + 2 * logical_px(ws_hoff_px) + 2 * logical_px(1), + font.height + 2 * logical_px(ws_voff_px) - 2 * logical_px(1)); - cairo_draw_rectangle(&(outputs_walk->buffer), bg_color, - workspace_width + logical_px(1), - 2 * logical_px(1), - binding.width + 2 * logical_px(ws_hoff_px), - font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); + draw_util_rectangle(&(outputs_walk->buffer), bg_color, + workspace_width + logical_px(1), + 2 * logical_px(1), + binding.width + 2 * logical_px(ws_hoff_px), + font.height + 2 * logical_px(ws_voff_px) - 4 * logical_px(1)); - cairo_draw_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color, - workspace_width + logical_px(ws_hoff_px) + logical_px(1), - logical_px(ws_voff_px), - binding.width); + draw_util_text(binding.name, &(outputs_walk->buffer), fg_color, bg_color, + workspace_width + logical_px(ws_hoff_px) + logical_px(1), + logical_px(ws_voff_px), + binding.width); unhide = true; workspace_width += 2 * logical_px(ws_hoff_px) + 2 * logical_px(1) + binding.width; @@ -1907,8 +1903,8 @@ void draw_bars(bool unhide) { int x_src = (int16_t)(statusline_width - visible_statusline_width); int x_dest = (int16_t)(outputs_walk->rect.w - tray_width - logical_px(sb_hoff_px) - visible_statusline_width); - cairo_copy_surface(&statusline_surface, &(outputs_walk->buffer), x_dest - x_src, 0, - x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); + draw_util_copy_surface(&statusline_surface, &(outputs_walk->buffer), x_src, 0, + x_dest, 0, (int16_t)visible_statusline_width, (int16_t)bar_height); } workspace_width = 0; @@ -1937,8 +1933,8 @@ void redraw_bars(void) { continue; } - cairo_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0, - 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); + draw_util_copy_surface(&(outputs_walk->buffer), &(outputs_walk->bar), 0, 0, + 0, 0, outputs_walk->rect.w, outputs_walk->rect.h); xcb_flush(xcb_connection); } } diff --git a/include/bindings.h b/include/bindings.h index 88b4a6cc..e9e5dac9 100644 --- a/include/bindings.h +++ b/include/bindings.h @@ -25,7 +25,7 @@ const char *DEFAULT_BINDING_MODE; */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *border, const char *whole_window, - const char *command, const char *mode); + const char *command, const char *mode, bool pango_markup); /** * Grab the bound keys (tell X to send us keypress events for those keycodes) diff --git a/include/config.h b/include/config.h index fd6fe6c0..6312d3d2 100644 --- a/include/config.h +++ b/include/config.h @@ -77,6 +77,7 @@ struct Variable { */ struct Mode { char *name; + bool pango_markup; struct bindings_head *bindings; SLIST_ENTRY(Mode) modes; diff --git a/include/config_directives.h b/include/config_directives.h index c0c70bb4..97b8b424 100644 --- a/include/config_directives.h +++ b/include/config_directives.h @@ -66,7 +66,7 @@ CFGFUN(new_window, const char *windowtype, const char *border, const long width) CFGFUN(workspace, const char *workspace, const char *output); CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command); -CFGFUN(enter_mode, const char *mode); +CFGFUN(enter_mode, const char *pango_markup, const char *mode); CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command); CFGFUN(bar_font, const char *font); diff --git a/include/libi3.h b/include/libi3.h index c1e109ef..75d3639b 100644 --- a/include/libi3.h +++ b/include/libi3.h @@ -243,7 +243,7 @@ bool i3string_is_markup(i3String *str); /** * Set whether the i3String should use Pango markup. */ -void i3string_set_markup(i3String *str, bool is_markup); +void i3string_set_markup(i3String *str, bool pango_markup); /** * Escape pango markup characters in the given string. diff --git a/libi3/font.c b/libi3/font.c index 9e808a89..c90f8be0 100644 --- a/libi3/font.c +++ b/libi3/font.c @@ -103,7 +103,7 @@ static bool load_pango_font(i3Font *font, const char *desc) { */ static void draw_text_pango(const char *text, size_t text_len, xcb_drawable_t drawable, xcb_visualtype_t *visual, int x, int y, - int max_width, bool is_markup) { + int max_width, bool pango_markup) { /* Create the Pango layout */ /* root_visual_type is cached in load_pango_font */ cairo_surface_t *surface = cairo_xcb_surface_create(conn, drawable, @@ -117,7 +117,7 @@ static void draw_text_pango(const char *text, size_t text_len, pango_layout_set_wrap(layout, PANGO_WRAP_CHAR); pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); - if (is_markup) + if (pango_markup) pango_layout_set_markup(layout, text, text_len); else pango_layout_set_text(layout, text, text_len); @@ -143,7 +143,7 @@ static void draw_text_pango(const char *text, size_t text_len, * Calculate the text width using Pango rendering. * */ -static int predict_text_width_pango(const char *text, size_t text_len, bool is_markup) { +static int predict_text_width_pango(const char *text, size_t text_len, bool pango_markup) { /* Create a dummy Pango layout */ /* root_visual_type is cached in load_pango_font */ cairo_surface_t *surface = cairo_xcb_surface_create(conn, root_screen->root, root_visual_type, 1, 1); @@ -154,7 +154,7 @@ static int predict_text_width_pango(const char *text, size_t text_len, bool is_m gint width; pango_layout_set_font_description(layout, savedFont->specific.pango_desc); - if (is_markup) + if (pango_markup) pango_layout_set_markup(layout, text, text_len); else pango_layout_set_text(layout, text, text_len); diff --git a/libi3/string.c b/libi3/string.c index 7741fde0..328b41c0 100644 --- a/libi3/string.c +++ b/libi3/string.c @@ -24,7 +24,7 @@ struct _i3String { xcb_char2b_t *ucs2; size_t num_glyphs; size_t num_bytes; - bool is_markup; + bool pango_markup; }; /* @@ -52,7 +52,7 @@ i3String *i3string_from_markup(const char *from_markup) { i3String *str = i3string_from_utf8(from_markup); /* Set the markup flag */ - str->is_markup = true; + str->pango_markup = true; return str; } @@ -86,7 +86,7 @@ i3String *i3string_from_markup_with_length(const char *from_markup, size_t num_b i3String *str = i3string_from_utf8_with_length(from_markup, num_bytes); /* set the markup flag */ - str->is_markup = true; + str->pango_markup = true; return str; } @@ -118,7 +118,7 @@ i3String *i3string_from_ucs2(const xcb_char2b_t *from_ucs2, size_t num_glyphs) { */ i3String *i3string_copy(i3String *str) { i3String *copy = i3string_from_utf8(i3string_as_utf8(str)); - copy->is_markup = str->is_markup; + copy->pango_markup = str->pango_markup; return copy; } @@ -178,14 +178,14 @@ size_t i3string_get_num_bytes(i3String *str) { * Whether the given i3String is in Pango markup. */ bool i3string_is_markup(i3String *str) { - return str->is_markup; + return str->pango_markup; } /* * Set whether the i3String should use Pango markup. */ -void i3string_set_markup(i3String *str, bool is_markup) { - str->is_markup = is_markup; +void i3string_set_markup(i3String *str, bool pango_markup) { + str->pango_markup = pango_markup; } /* diff --git a/man/i3.man b/man/i3.man index 203b42ee..16302e08 100644 --- a/man/i3.man +++ b/man/i3.man @@ -77,6 +77,12 @@ i3 keeps your layout in a tree data structure. Window:: An X11 window, like the Firefox browser window or a terminal emulator. +Floating Window:: +A window which "floats" on top of other windows. This style is used by i3 to +display X11 windows with type "dialog", such as the "Print" or "Open File" +dialog boxes in many GUI applications. Use of floating windows can be +fine-tuned with the for_window command (see HTML userguide). + Split container:: A split container contains multiple other split containers or windows. + diff --git a/parser-specs/config.spec b/parser-specs/config.spec index b9542c8c..2170ace3 100644 --- a/parser-specs/config.spec +++ b/parser-specs/config.spec @@ -326,8 +326,10 @@ state BINDCOMMAND: ################################################################################ state MODENAME: + pango_markup = '--pango_markup' + -> modename = word - -> call cfg_enter_mode($modename); MODEBRACE + -> call cfg_enter_mode($pango_markup, $modename); MODEBRACE state MODEBRACE: end diff --git a/src/bindings.c b/src/bindings.c index c0e01030..b5f31901 100644 --- a/src/bindings.c +++ b/src/bindings.c @@ -27,7 +27,7 @@ const char *DEFAULT_BINDING_MODE = "default"; * the list of modes. * */ -static struct Mode *mode_from_name(const char *name) { +static struct Mode *mode_from_name(const char *name, bool pango_markup) { struct Mode *mode; /* Try to find the mode in the list of modes and return it */ @@ -39,6 +39,7 @@ static struct Mode *mode_from_name(const char *name) { /* If the mode was not found, create a new one */ mode = scalloc(1, sizeof(struct Mode)); mode->name = sstrdup(name); + mode->pango_markup = pango_markup; mode->bindings = scalloc(1, sizeof(struct bindings_head)); TAILQ_INIT(mode->bindings); SLIST_INSERT_HEAD(&modes, mode, modes); @@ -54,7 +55,7 @@ static struct Mode *mode_from_name(const char *name) { */ Binding *configure_binding(const char *bindtype, const char *modifiers, const char *input_code, const char *release, const char *border, const char *whole_window, - const char *command, const char *modename) { + const char *command, const char *modename, bool pango_markup) { Binding *new_binding = scalloc(1, sizeof(Binding)); DLOG("bindtype %s, modifiers %s, input code %s, release %s\n", bindtype, modifiers, input_code, release); new_binding->release = (release != NULL ? B_UPON_KEYRELEASE : B_UPON_KEYPRESS); @@ -91,7 +92,7 @@ Binding *configure_binding(const char *bindtype, const char *modifiers, const ch if (group_bits_set > 1) ELOG("Keybinding has more than one Group specified, but your X server is always in precisely one group. The keybinding can never trigger.\n"); - struct Mode *mode = mode_from_name(modename); + struct Mode *mode = mode_from_name(modename, pango_markup); TAILQ_INSERT_TAIL(mode->bindings, new_binding, bindings); return new_binding; @@ -437,7 +438,8 @@ void switch_mode(const char *new_mode) { grab_all_keys(conn); char *event_msg; - sasprintf(&event_msg, "{\"change\":\"%s\"}", mode->name); + sasprintf(&event_msg, "{\"change\":\"%s\", \"pango_markup\":%s}", + mode->name, (mode->pango_markup ? "true" : "false")); ipc_send_event("mode", I3_IPC_EVENT_MODE, event_msg); FREE(event_msg); diff --git a/src/config_directives.c b/src/config_directives.c index cd0432fe..99b70db8 100644 --- a/src/config_directives.c +++ b/src/config_directives.c @@ -108,7 +108,7 @@ CFGFUN(font, const char *font) { } CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) { - configure_binding(bindtype, modifiers, key, release, border, whole_window, command, DEFAULT_BINDING_MODE); + configure_binding(bindtype, modifiers, key, release, border, whole_window, command, DEFAULT_BINDING_MODE, false); } /******************************************************************************* @@ -116,12 +116,13 @@ CFGFUN(binding, const char *bindtype, const char *modifiers, const char *key, co ******************************************************************************/ static char *current_mode; +static bool current_mode_pango_markup; CFGFUN(mode_binding, const char *bindtype, const char *modifiers, const char *key, const char *release, const char *border, const char *whole_window, const char *command) { - configure_binding(bindtype, modifiers, key, release, border, whole_window, command, current_mode); + configure_binding(bindtype, modifiers, key, release, border, whole_window, command, current_mode, current_mode_pango_markup); } -CFGFUN(enter_mode, const char *modename) { +CFGFUN(enter_mode, const char *pango_markup, const char *modename) { if (strcasecmp(modename, DEFAULT_BINDING_MODE) == 0) { ELOG("You cannot use the name %s for your mode\n", DEFAULT_BINDING_MODE); exit(1); @@ -129,6 +130,7 @@ CFGFUN(enter_mode, const char *modename) { DLOG("\t now in mode %s\n", modename); FREE(current_mode); current_mode = sstrdup(modename); + current_mode_pango_markup = (pango_markup != NULL); } CFGFUN(exec, const char *exectype, const char *no_startup_id, const char *command) { diff --git a/src/manage.c b/src/manage.c index e3769670..5cfe490e 100644 --- a/src/manage.c +++ b/src/manage.c @@ -524,13 +524,23 @@ void manage_window(xcb_window_t window, xcb_get_window_attributes_cookie_t cooki /* Send an event about window creation */ ipc_send_window_event("new", nc); + if (set_focus && assignment_for(cwindow, A_NO_FOCUS) != NULL) { + /* The first window on a workspace should always be focused. We have to + * compare with == 1 because the container has already been inserted at + * this point. */ + if (con_num_children(ws) == 1) { + DLOG("This is the first window on this workspace, ignoring no_focus.\n"); + } else { + DLOG("no_focus was set for con = %p, not setting focus.\n", nc); + set_focus = false; + } + } + /* Defer setting focus after the 'new' event has been sent to ensure the * proper window event sequence. */ if (set_focus && !nc->window->doesnt_accept_focus && nc->mapped) { - if (assignment_for(cwindow, A_NO_FOCUS) == NULL) { - DLOG("Now setting focus.\n"); - con_focus(nc); - } + DLOG("Now setting focus.\n"); + con_focus(nc); } tree_render(); diff --git a/src/window.c b/src/window.c index 5898333f..eba15632 100644 --- a/src/window.c +++ b/src/window.c @@ -342,7 +342,7 @@ void window_update_motif_hints(i3Window *win, xcb_get_property_reply_t *prop, bo i3String *window_parse_title_format(i3Window *win) { /* We need to ensure that we only escape the window title if pango * is used by the current font. */ - const bool is_markup = font_is_pango(); + const bool pango_markup = font_is_pango(); char *format = win->title_format; if (format == NULL) @@ -359,19 +359,19 @@ i3String *window_parse_title_format(i3Window *win) { for (char *walk = format; *walk != '\0'; walk++) { if (STARTS_WITH(walk, "%title")) { if (escaped_title == NULL) - escaped_title = win->name == NULL ? "" : i3string_as_utf8(is_markup ? i3string_escape_markup(win->name) : win->name); + escaped_title = win->name == NULL ? "" : i3string_as_utf8(pango_markup ? i3string_escape_markup(win->name) : win->name); buffer_len = buffer_len - strlen("%title") + strlen(escaped_title); walk += strlen("%title") - 1; } else if (STARTS_WITH(walk, "%class")) { if (escaped_class == NULL) - escaped_class = is_markup ? g_markup_escape_text(win->class_class, -1) : win->class_class; + escaped_class = pango_markup ? g_markup_escape_text(win->class_class, -1) : win->class_class; buffer_len = buffer_len - strlen("%class") + strlen(escaped_class); walk += strlen("%class") - 1; } else if (STARTS_WITH(walk, "%instance")) { if (escaped_instance == NULL) - escaped_instance = is_markup ? g_markup_escape_text(win->class_instance, -1) : win->class_instance; + escaped_instance = pango_markup ? g_markup_escape_text(win->class_instance, -1) : win->class_instance; buffer_len = buffer_len - strlen("%instance") + strlen(escaped_instance); walk += strlen("%instance") - 1; @@ -403,6 +403,6 @@ i3String *window_parse_title_format(i3Window *win) { *outwalk = '\0'; i3String *formatted = i3string_from_utf8(buffer); - i3string_set_markup(formatted, is_markup); + i3string_set_markup(formatted, pango_markup); return formatted; } diff --git a/src/workspace.c b/src/workspace.c index e7a09c70..dc0a596d 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -506,34 +506,13 @@ void workspace_show_by_name(const char *num) { */ Con *workspace_next(void) { Con *current = con_get_workspace(focused); - Con *next = NULL; + Con *next = NULL, *first = NULL, *first_opposite = NULL; Con *output; if (current->num == -1) { /* If currently a named workspace, find next named workspace. */ - next = TAILQ_NEXT(current, nodes); - } else { - /* If currently a numbered workspace, find next numbered workspace. */ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - /* Skip outputs starting with __, they are internal. */ - if (con_is_internal(output)) - continue; - NODES_FOREACH(output_get_content(output)) { - if (child->type != CT_WORKSPACE) - continue; - if (child->num == -1) - break; - /* Need to check child against current and next because we are - * traversing multiple lists and thus are not guaranteed the - * relative order between the list of workspaces. */ - if (current->num < child->num && (!next || child->num < next->num)) - next = child; - } - } - } - - /* Find next named workspace. */ - if (!next) { + if ((next = TAILQ_NEXT(current, nodes)) != NULL) + return next; bool found_current = false; TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ @@ -542,18 +521,20 @@ Con *workspace_next(void) { NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; + if (!first) + first = child; + if (!first_opposite && child->num != -1) + first_opposite = child; if (child == current) { - found_current = 1; - } else if (child->num == -1 && (current->num != -1 || found_current)) { + found_current = true; + } else if (child->num == -1 && found_current) { next = child; - goto workspace_next_end; + return next; } } } - } - - /* Find first workspace. */ - if (!next) { + } else { + /* If currently a numbered workspace, find next numbered workspace. */ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) @@ -561,12 +542,24 @@ Con *workspace_next(void) { NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; - if (!next || (child->num != -1 && child->num < next->num)) + if (!first) + first = child; + if (!first_opposite && child->num == -1) + first_opposite = child; + if (child->num == -1) + break; + /* Need to check child against current and next because we are + * traversing multiple lists and thus are not guaranteed the + * relative order between the list of workspaces. */ + if (current->num < child->num && (!next || child->num < next->num)) next = child; } } } -workspace_next_end: + + if (!next) + next = first_opposite ? first_opposite : first; + return next; } @@ -576,7 +569,7 @@ workspace_next_end: */ Con *workspace_prev(void) { Con *current = con_get_workspace(focused); - Con *prev = NULL; + Con *prev = NULL, *first_opposite = NULL, *last = NULL; Con *output; if (current->num == -1) { @@ -584,6 +577,28 @@ Con *workspace_prev(void) { prev = TAILQ_PREV(current, nodes_head, nodes); if (prev && prev->num != -1) prev = NULL; + if (!prev) { + bool found_current = false; + TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { + /* Skip outputs starting with __, they are internal. */ + if (con_is_internal(output)) + continue; + NODES_FOREACH_REVERSE(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (!last) + last = child; + if (!first_opposite && child->num != -1) + first_opposite = child; + if (child == current) { + found_current = true; + } else if (child->num == -1 && found_current) { + prev = child; + goto workspace_prev_end; + } + } + } + } } else { /* If numbered workspace, find previous numbered workspace. */ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { @@ -591,7 +606,13 @@ Con *workspace_prev(void) { if (con_is_internal(output)) continue; NODES_FOREACH_REVERSE(output_get_content(output)) { - if (child->type != CT_WORKSPACE || child->num == -1) + if (child->type != CT_WORKSPACE) + continue; + if (!last) + last = child; + if (!first_opposite && child->num == -1) + first_opposite = child; + if (child->num == -1) continue; /* Need to check child against current and previous because we * are traversing multiple lists and thus are not guaranteed @@ -602,40 +623,8 @@ Con *workspace_prev(void) { } } - /* Find previous named workspace. */ - if (!prev) { - bool found_current = false; - TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { - /* Skip outputs starting with __, they are internal. */ - if (con_is_internal(output)) - continue; - NODES_FOREACH_REVERSE(output_get_content(output)) { - if (child->type != CT_WORKSPACE) - continue; - if (child == current) { - found_current = true; - } else if (child->num == -1 && (current->num != -1 || found_current)) { - prev = child; - goto workspace_prev_end; - } - } - } - } - - /* Find last workspace. */ - if (!prev) { - TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { - /* Skip outputs starting with __, they are internal. */ - if (con_is_internal(output)) - continue; - NODES_FOREACH_REVERSE(output_get_content(output)) { - if (child->type != CT_WORKSPACE) - continue; - if (!prev || child->num > prev->num) - prev = child; - } - } - } + if (!prev) + prev = first_opposite ? first_opposite : last; workspace_prev_end: return prev; @@ -675,7 +664,7 @@ Con *workspace_next_on_output(void) { if (child->type != CT_WORKSPACE) continue; if (child == current) { - found_current = 1; + found_current = true; } else if (child->num == -1 && (current->num != -1 || found_current)) { next = child; goto workspace_next_on_output_end; diff --git a/testcases/t/201-config-parser.t b/testcases/t/201-config-parser.t index a2b0a3a9..7b3a181e 100644 --- a/testcases/t/201-config-parser.t +++ b/testcases/t/201-config-parser.t @@ -53,7 +53,7 @@ mode "meh" { EOT my $expected = <<'EOT'; -cfg_enter_mode(meh) +cfg_enter_mode((null), meh) cfg_mode_binding(bindsym, Mod1,Shift, x, (null), (null), (null), resize grow) cfg_mode_binding(bindcode, Mod1, 44, (null), (null), (null), resize shrink) cfg_mode_binding(bindsym, Mod1, x, --release, (null), (null), exec foo) @@ -627,7 +627,7 @@ mode "yo" { EOT $expected = <<'EOT'; -cfg_enter_mode(yo) +cfg_enter_mode((null), yo) cfg_mode_binding(bindsym, (null), x, (null), (null), (null), resize shrink left) ERROR: CONFIG: Expected one of these tokens: , '#', 'set', 'bindsym', 'bindcode', 'bind', '}' ERROR: CONFIG: (in file ) diff --git a/testcases/t/242-no-focus.t b/testcases/t/242-no-focus.t index 143ae5cf..0a7f5c93 100644 --- a/testcases/t/242-no-focus.t +++ b/testcases/t/242-no-focus.t @@ -30,13 +30,15 @@ font -misc-fixed-medium-r-normal--13-120-75-75-C-70-iso10646-1 EOT $pid = launch_with_config($config); - + $ws = fresh_workspace; $first = open_window; $focused = get_focused($ws); $second = open_window; +sync_with_i3; isnt(get_focused($ws), $focused, 'focus has changed'); +is($x->input_focus, $second->id, 'input focus has changed'); exit_gracefully($pid); @@ -53,13 +55,37 @@ no_focus [instance=notme] EOT $pid = launch_with_config($config); - + $ws = fresh_workspace; $first = open_window; $focused = get_focused($ws); $second = open_window(wm_class => 'notme'); +sync_with_i3; is(get_focused($ws), $focused, 'focus has not changed'); +is($x->input_focus, $first->id, 'input focus has not changed'); + +exit_gracefully($pid); + +##################################################################### +## 3: no_focus doesn't affect the first window opened on a workspace +##################################################################### + +$config = < 'focusme'); + +sync_with_i3; +is($x->input_focus, $first->id, 'input focus has changed'); exit_gracefully($pid); diff --git a/testcases/t/528-workspace-next-prev.t b/testcases/t/528-workspace-next-prev.t new file mode 100644 index 00000000..79e83a07 --- /dev/null +++ b/testcases/t/528-workspace-next-prev.t @@ -0,0 +1,127 @@ +#!perl +# vim:ts=4:sw=4:expandtab +# +# Please read the following documents before working on tests: +# • http://build.i3wm.org/docs/testsuite.html +# (or docs/testsuite) +# +# • http://build.i3wm.org/docs/lib-i3test.html +# (alternatively: perldoc ./testcases/lib/i3test.pm) +# +# • http://build.i3wm.org/docs/ipc.html +# (or docs/ipc) +# +# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf +# (unless you are already familiar with Perl) +# +# Tests whether 'workspace next' works correctly. +# +use List::Util qw(first); +use i3test i3_autostart => 0; + +sub assert_next { + my ($expected) = @_; + + cmd 'workspace next'; + # We need to sync after changing focus to a different output to wait for the + # EnterNotify to be processed, otherwise it will be processed at some point + # later in time and mess up our subsequent tests. + sync_with_i3; + + is(focused_ws, $expected, "workspace $expected focused"); +} + + +my $config = <root->warp_pointer(0, 0); +sync_with_i3; + +cmd 'workspace A'; +# ensure workspace A stays open +open_window; + +cmd 'workspace B'; +# ensure workspace B stays open +open_window; + +cmd 'workspace D'; +# ensure workspace D stays open +open_window; + +cmd 'workspace E'; +# ensure workspace E stays open +open_window; + +cmd 'focus output right'; + +cmd 'workspace 1'; +# ensure workspace 1 stays open +open_window; + +cmd 'workspace 2'; +# ensure workspace 2 stays open +open_window; + +cmd 'workspace 3'; +# ensure workspace 3 stays open +open_window; + +cmd 'workspace 4'; +# ensure workspace 4 stays open +open_window; + +cmd 'workspace 5'; +# ensure workspace 5 stays open +open_window; + +cmd 'workspace C'; +# ensure workspace C stays open +open_window; + +cmd 'workspace F'; +# ensure workspace F stays open +open_window; + +cmd 'focus output right'; + +################################################################################ +# Use workspace next and verify the correct order. +################################################################################ + +# The current order should be: +# output 1: A, B, D, E +# output 2: 1, 2, 3, 4, 5, C, F + +cmd 'workspace A'; +is(focused_ws, 'A', 'back on workspace A'); + +assert_next('B'); +assert_next('D'); +assert_next('E'); +assert_next('C'); +assert_next('F'); +assert_next('1'); +assert_next('2'); +assert_next('3'); +assert_next('4'); +assert_next('5'); +assert_next('A'); +assert_next('B'); + +exit_gracefully($pid); + +done_testing;