diff --git a/include/border.h b/include/border.h new file mode 100644 index 000000000..85c656e08 --- /dev/null +++ b/include/border.h @@ -0,0 +1,11 @@ +#ifndef _SWAY_BORDER_H +#define _SWAY_BORDER_H +#include +#include "container.h" + +void render_view_borders(wlc_handle view); +void update_view_border(swayc_t *view); +void map_update_view_border(swayc_t *view, void *data); +int get_font_text_height(const char *font); + +#endif diff --git a/include/client/pango.h b/include/client/pango.h index e25a2211d..97c31e381 100644 --- a/include/client/pango.h +++ b/include/client/pango.h @@ -1,12 +1,12 @@ #ifndef _SWAY_CLIENT_PANGO_H #define _SWAY_CLIENT_PANGO_H -#include "client/window.h" -#include "client/buffer.h" +#include +#include #include -PangoLayout *get_pango_layout(struct window *window, struct buffer *buffer, const char *text); -void get_text_size(struct window *window, int *width, int *height, const char *fmt, ...); -void pango_printf(struct window *window, const char *fmt, ...); +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text); +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, const char *fmt, ...); +void pango_printf(cairo_t *cairo, const char *font, const char *fmt, ...); #endif diff --git a/include/config.h b/include/config.h index 4bcf55e8d..fb84423c8 100644 --- a/include/config.h +++ b/include/config.h @@ -148,6 +148,21 @@ struct bar_config { } colors; }; +struct border_colors { + uint32_t border; + uint32_t background; + uint32_t text; + uint32_t indicator; + uint32_t child_border; +}; + +enum edge_border_types { + E_NONE, /**< Don't hide edge borders */ + E_VERTICAL, /**< hide vertical edge borders */ + E_HORIZONTAL, /**< hide horizontal edge borders */ + E_BOTH /**< hide vertical and horizontal edge borders */ +}; + /** * The configuration struct. The result of loading a config file. */ @@ -169,6 +184,7 @@ struct sway_config { enum swayc_layouts default_orientation; enum swayc_layouts default_layout; char *font; + int font_height; // Flags bool focus_follows_mouse; @@ -187,6 +203,20 @@ struct sway_config { list_t *config_chain; const char *current_config; + + enum swayc_border_types border; + int border_thickness; + enum edge_border_types hide_edge_borders; + + // border colors + struct { + struct border_colors focused; + struct border_colors focused_inactive; + struct border_colors unfocused; + struct border_colors urgent; + struct border_colors placeholder; + uint32_t background; + } border_colors; }; /** diff --git a/include/container.h b/include/container.h index a96beab91..26da851e0 100644 --- a/include/container.h +++ b/include/container.h @@ -8,7 +8,7 @@ typedef struct sway_container swayc_t; /** * Different kinds of containers. - * + * * This enum is in order. A container will never be inside of a container below * it on this list. */ @@ -36,6 +36,12 @@ enum swayc_layouts { L_LAYOUTS, }; +enum swayc_border_types { + B_NONE, /**< No border */ + B_PIXEL, /**< 1px border */ + B_NORMAL /**< Normal border with title bar */ +}; + /** * Stores information about a container. * @@ -109,6 +115,16 @@ struct sway_container { * If this container's children include a fullscreen view, this is that view. */ struct sway_container *fullscreen; + /** + * If this container is a view, this may be set to the window's decoration + * buffer (or NULL). + */ + unsigned char *border; + enum swayc_border_types border_type; + struct wlc_geometry border_geometry; + struct wlc_geometry title_bar_geometry; + struct wlc_geometry actual_geometry; + int border_thickness; }; enum visibility_mask { diff --git a/sway/CMakeLists.txt b/sway/CMakeLists.txt index 5b6104f3a..51f27a057 100644 --- a/sway/CMakeLists.txt +++ b/sway/CMakeLists.txt @@ -5,6 +5,8 @@ include_directories( ${JSONC_INCLUDE_DIRS} ${XKBCOMMON_INCLUDE_DIRS} ${LIBINPUT_INCLUDE_DIRS} + ${CAIRO_INCLUDE_DIRS} + ${PANGO_INCLUDE_DIRS} ) add_executable(sway @@ -24,6 +26,7 @@ add_executable(sway output.c resize.c workspace.c + border.c ) add_definitions( @@ -33,12 +36,15 @@ add_definitions( target_link_libraries(sway sway-common sway-protocols + sway-wayland ${WLC_LIBRARIES} ${XKBCOMMON_LIBRARIES} ${PCRE_LIBRARIES} ${JSONC_LIBRARIES} ${WAYLAND_SERVER_LIBRARIES} ${LIBINPUT_LIBRARIES} + ${PANGO_LIBRARIES} + ${JSONC_LIBRARIES} m ) diff --git a/sway/border.c b/sway/border.c new file mode 100644 index 000000000..ab4b70f68 --- /dev/null +++ b/sway/border.c @@ -0,0 +1,266 @@ +#include "border.h" +#include +#include +#include +#include +#include +#include "container.h" +#include "config.h" +#include "client/pango.h" + +void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { + int endian = 1; + if (*(char *)&endian == 1) { // little endian + cairo_set_source_rgba(cairo, + (color >> (1*8) & 0xFF) / 255.0, + (color >> (2*8) & 0xFF) / 255.0, + (color >> (3*8) & 0xFF) / 255.0, + (color >> (0*8) & 0xFF) / 255.0); + } else { // big endian + cairo_set_source_rgba(cairo, + (color >> (0*8) & 0xFF) / 255.0, + (color >> (3*8) & 0xFF) / 255.0, + (color >> (2*8) & 0xFF) / 255.0, + (color >> (1*8) & 0xFF) / 255.0); + } +} + +static cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry geo, cairo_surface_t **surface) { + cairo_t *cr; + view->border_geometry = geo; + int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, geo.size.w); + view->border = calloc(stride * geo.size.h, sizeof(unsigned char)); + if (!view->border) { + sway_log(L_DEBUG, "Unable to allocate buffer"); + return NULL; + } + *surface = cairo_image_surface_create_for_data(view->border, + CAIRO_FORMAT_ARGB32, geo.size.w, geo.size.h, stride); + if (cairo_surface_status(*surface) != CAIRO_STATUS_SUCCESS) { + free(view->border); + view->border = NULL; + sway_log(L_DEBUG, "Unable to allocate surface"); + return NULL; + } + cr = cairo_create(*surface); + if (cairo_status(cr) != CAIRO_STATUS_SUCCESS) { + cairo_surface_destroy(*surface); + free(view->border); + view->border = NULL; + sway_log(L_DEBUG, "Unable to create cairo context"); + return NULL; + } + return cr; +} + +// TODO: move to client/cairo.h when local set_source_u32 is fixed. +/** + * Renders a sharp line of any width and height. + * + * The line is drawn from (x,y) to (x+width,y+height) where width/height is 0 + * if the line has a width/height of one pixel, respectively. + */ +static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y, double width, double height) { + cairo_set_source_u32(cairo, color); + + if (width > 1 && height > 1) { + cairo_rectangle(cairo, x, y, width, height); + cairo_fill(cairo); + } else { + if (width == 1) { + x += 0.5; + height += y; + width = x; + } + + if (height == 1) { + y += 0.5; + width += x; + height = y; + } + + cairo_move_to(cairo, x, y); + cairo_set_line_width(cairo, 1.0); + cairo_line_to(cairo, width, height); + cairo_stroke(cairo); + } +} + +int get_font_text_height(const char *font) { + cairo_surface_t *surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32, 200, 200); + cairo_t *cr = cairo_create(surface); + int width, height; + get_text_size(cr, font, &width, &height, "Gg"); + return height; +} + +static void render_borders(swayc_t *view, cairo_t *cr, struct border_colors *colors) { + struct wlc_geometry *b = &view->border_geometry; + struct wlc_geometry *v = &view->actual_geometry; + + // left border + int left_border = v->origin.x - b->origin.x; + if (left_border > 0) { + render_sharp_line(cr, + colors->child_border, + 0, 0, + left_border, + b->size.h); + } + + // right border + int right_border = b->size.w - v->size.w - left_border; + if (right_border > 0) { + render_sharp_line(cr, + colors->child_border, + b->size.w - right_border, + 0, + right_border, + b->size.h); + } + + // top border + int top_border = v->origin.y - b->origin.y; + if (top_border > 0) { + render_sharp_line(cr, + colors->child_border, + 0, 0, + b->size.w, + top_border); + } + + // bottom border + int bottom_border = b->size.h - (top_border + v->size.h); + if (bottom_border > 0) { + render_sharp_line(cr, + colors->child_border, + 0, + b->size.h - bottom_border, + b->size.w, + bottom_border); + } +} + +static void render_with_title_bar(swayc_t *view, cairo_t *cr, struct border_colors *colors) { + struct wlc_geometry *tb = &view->title_bar_geometry; + struct wlc_geometry *b = &view->border_geometry; + + // borders + render_borders(view, cr, colors); + + // title bar background + cairo_set_source_u32(cr, colors->child_border); + cairo_rectangle(cr, 0, 0, tb->size.w, tb->size.h); + cairo_fill(cr); + + // header top line + render_sharp_line(cr, colors->border, 0, 0, tb->size.w, 1); + + // text + if (view->name) { + int width, height; + get_text_size(cr, config->font, &width, &height, "%s", view->name); + cairo_move_to(cr, view->border_thickness, 2); + cairo_set_source_u32(cr, colors->text); + pango_printf(cr, config->font, "%s", view->name); + } + + // header bottom line + render_sharp_line(cr, colors->border, + view->actual_geometry.origin.x - b->origin.x, + tb->size.h - 1, + view->actual_geometry.size.w, 1); +} + +void map_update_view_border(swayc_t *view, void *data) { + if (view->type == C_VIEW) { + update_view_border(view); + } +} + +void update_view_border(swayc_t *view) { + cairo_t *cr = NULL; + cairo_surface_t *surface = NULL; + + if (view->border) { + free(view->border); + view->border = NULL; + } + + swayc_t *focused = get_focused_view(&root_container); + swayc_t *container = swayc_parent_by_type(view, C_CONTAINER); + swayc_t *focused_inactive = NULL; + if (container) { + focused_inactive = swayc_focus_by_type(container, C_VIEW); + } else { + container = swayc_parent_by_type(view, C_WORKSPACE); + if (container) { + focused_inactive = swayc_focus_by_type(container, C_VIEW); + } + } + + switch (view->border_type) { + case B_NONE: + break; + case B_PIXEL: + cr = create_border_buffer(view, view->border_geometry, &surface); + if (!cr) { + break; + } + + if (focused == view) { + render_borders(view, cr, &config->border_colors.focused); + } else if (focused_inactive == view) { + render_borders(view, cr, &config->border_colors.focused_inactive); + } else { + render_borders(view, cr, &config->border_colors.unfocused); + } + + break; + case B_NORMAL: + cr = create_border_buffer(view, view->border_geometry, &surface); + if (!cr) { + break; + } + + if (focused == view) { + render_with_title_bar(view, cr, &config->border_colors.focused); + } else if (focused_inactive == view) { + render_with_title_bar(view, cr, &config->border_colors.focused_inactive); + } else { + render_with_title_bar(view, cr, &config->border_colors.unfocused); + } + + break; + } + if (surface) { + cairo_surface_flush(surface); + cairo_surface_destroy(surface); + } + if (cr) { + cairo_destroy(cr); + } +} + +void render_view_borders(wlc_handle view) { + swayc_t *c = swayc_by_handle(view); + + if (!c || c->border_type == B_NONE) { + return; + } + + if (c->border_type == B_NORMAL) { + // update window title + const char *new_name = wlc_view_get_title(view); + + if (new_name && strcmp(c->name, new_name) != 0) { + free(c->name); + c->name = strdup(new_name); + update_view_border(c); + } + } + + if (c->border) { + wlc_pixels_write(WLC_RGBA8888, &c->border_geometry, c->border); + } +} diff --git a/sway/commands.c b/sway/commands.c index d2be2f9bd..c621fa92a 100644 --- a/sway/commands.c +++ b/sway/commands.c @@ -31,6 +31,7 @@ #include "ipc-server.h" #include "list.h" #include "input.h" +#include "border.h" typedef struct cmd_results *sway_cmd(int argc, char **argv); @@ -43,6 +44,7 @@ static sway_cmd cmd_assign; static sway_cmd cmd_bar; static sway_cmd cmd_bindcode; static sway_cmd cmd_bindsym; +static sway_cmd cmd_border; static sway_cmd cmd_debuglog; static sway_cmd cmd_exec; static sway_cmd cmd_exec_always; @@ -55,6 +57,7 @@ static sway_cmd cmd_font; static sway_cmd cmd_for_window; static sway_cmd cmd_fullscreen; static sway_cmd cmd_gaps; +static sway_cmd cmd_hide_edge_borders; static sway_cmd cmd_include; static sway_cmd cmd_input; static sway_cmd cmd_kill; @@ -345,6 +348,71 @@ static struct cmd_results *cmd_bindcode(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static struct cmd_results *cmd_border(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "border", EXPECTED_AT_LEAST, 1))) { + return error; + } + + if (argc > 2) { + return cmd_results_new(CMD_FAILURE, "border", + "Expected 'border []"); + } + + enum swayc_border_types border = config->border; + int thickness = config->border_thickness; + + swayc_t *view = NULL; + if (config->active) { + view = get_focused_view(&root_container); + border = view->border_type; + thickness = view->border_thickness; + } + + if (strcasecmp(argv[0], "none") == 0) { + border = B_NONE; + } else if (strcasecmp(argv[0], "normal") == 0) { + border = B_NORMAL; + } else if (strcasecmp(argv[0], "pixel") == 0) { + border = B_PIXEL; + } else if (strcasecmp(argv[0], "toggle") == 0) { + switch (border) { + case B_NONE: + border = B_PIXEL; + break; + case B_NORMAL: + border = B_NONE; + break; + case B_PIXEL: + border = B_NORMAL; + break; + } + } else { + return cmd_results_new(CMD_FAILURE, "border", + "Expected 'border "); + } + + + if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) { + thickness = (int)strtol(argv[1], NULL, 10); + if (errno == ERANGE || thickness < 0) { + errno = 0; + return cmd_results_new(CMD_INVALID, "border", "Number is out out of range."); + } + } + + if (config->active && view) { + view->border_type = border; + view->border_thickness = thickness; + update_geometry(view); + } else { + config->border = border; + config->border_thickness = thickness; + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + static struct cmd_results *cmd_exec_always(int argc, char **argv) { struct cmd_results *error = NULL; if (!config->active) return cmd_results_new(CMD_DEFER, NULL, NULL); @@ -847,9 +915,9 @@ static struct cmd_results *cmd_move(int argc, char **argv) { } else if (strcasecmp(argv[0], "position") == 0 && strcasecmp(argv[1], "mouse") == 0) { if (view->is_floating) { swayc_t *output = swayc_parent_by_type(view, C_OUTPUT); - const struct wlc_geometry *geometry = wlc_view_get_geometry(view->handle); + struct wlc_geometry g; + wlc_view_get_visible_geometry(view->handle, &g); const struct wlc_size *size = wlc_output_get_resolution(output->handle); - struct wlc_geometry g = *geometry; struct wlc_point origin; wlc_pointer_get_position(&origin); @@ -1500,6 +1568,29 @@ static struct cmd_results *cmd_smart_gaps(int argc, char **argv) { return cmd_results_new(CMD_SUCCESS, NULL, NULL); } +static struct cmd_results *cmd_hide_edge_borders(int argc, char **argv) { + struct cmd_results *error = NULL; + if ((error = checkarg(argc, "hide_edge_borders", EXPECTED_EQUAL_TO, 1))) { + return error; + } + + if (strcasecmp(argv[0], "none") == 0) { + config->hide_edge_borders = E_NONE; + } else if (strcasecmp(argv[0], "vertical") == 0) { + config->hide_edge_borders = E_VERTICAL; + } else if (strcasecmp(argv[0], "horizontal") == 0) { + config->hide_edge_borders = E_HORIZONTAL; + } else if (strcasecmp(argv[0], "both") == 0) { + config->hide_edge_borders = E_BOTH; + } else { + return cmd_results_new(CMD_INVALID, "hide_edge_borders", + "Expected 'hide_edge_borders '"); + } + + return cmd_results_new(CMD_SUCCESS, NULL, NULL); +} + + static struct cmd_results *cmd_kill(int argc, char **argv) { if (config->reading) return cmd_results_new(CMD_FAILURE, "kill", "Can't be used in config file."); if (!config->active) return cmd_results_new(CMD_FAILURE, "kill", "Can only be used when sway is running."); @@ -1866,16 +1957,18 @@ static struct cmd_results *cmd_font(int argc, char **argv) { } char *font = join_args(argv, argc); + free(config->font); if (strlen(font) > 6 && strncmp("pango:", font, 6) == 0) { - free(config->font); - config->font = font; - sway_log(L_DEBUG, "Settings font %s", config->font); - return cmd_results_new(CMD_SUCCESS, NULL, NULL); - } else { + config->font = strdup(font + 6); free(font); - return cmd_results_new(CMD_FAILURE, "font", "non-pango font detected"); + } else { + config->font = font; } + config->font_height = get_font_text_height(config->font); + + sway_log(L_DEBUG, "Settings font %s", config->font); + return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -2044,6 +2137,7 @@ static struct cmd_handler handlers[] = { { "bar", cmd_bar }, { "bindcode", cmd_bindcode }, { "bindsym", cmd_bindsym }, + { "border", cmd_border }, { "debuglog", cmd_debuglog }, { "default_orientation", cmd_orientation }, { "exec", cmd_exec }, @@ -2057,6 +2151,7 @@ static struct cmd_handler handlers[] = { { "for_window", cmd_for_window }, { "fullscreen", cmd_fullscreen }, { "gaps", cmd_gaps }, + { "hide_edge_borders", cmd_hide_edge_borders }, { "include", cmd_include }, { "input", cmd_input }, { "kill", cmd_kill }, diff --git a/sway/config.c b/sway/config.c index a877261cb..5501ab314 100644 --- a/sway/config.c +++ b/sway/config.c @@ -22,6 +22,7 @@ #include "input_state.h" #include "criteria.h" #include "input.h" +#include "border.h" struct sway_config *config = NULL; @@ -160,7 +161,8 @@ static void config_defaults(struct sway_config *config) { config->resizing_key = M_RIGHT_CLICK; config->default_layout = L_NONE; config->default_orientation = L_NONE; - config->font = strdup("pango:monospace 10"); + config->font = strdup("monospace 10"); + config->font_height = get_font_text_height(config->font); // Flags config->focus_follows_mouse = true; @@ -181,6 +183,44 @@ static void config_defaults(struct sway_config *config) { config->config_chain = create_list(); config->current_config = NULL; + + // borders + config->border = B_NORMAL; + config->border_thickness = 2; + config->hide_edge_borders = E_NONE; + + // border colors + config->border_colors.focused.border = 0x4C7899FF; + config->border_colors.focused.background = 0x285577FF; + config->border_colors.focused.text = 0xFFFFFFFF; + config->border_colors.focused.indicator = 0x2E9EF4FF; + config->border_colors.focused.child_border = 0x285577FF; + + config->border_colors.focused_inactive.border = 0x333333FF; + config->border_colors.focused_inactive.background = 0x5F676AFF; + config->border_colors.focused_inactive.text = 0xFFFFFFFF; + config->border_colors.focused_inactive.indicator = 0x484E50FF; + config->border_colors.focused_inactive.child_border = 0x5F676AFF; + + config->border_colors.unfocused.border = 0x333333FF; + config->border_colors.unfocused.background = 0x222222FF; + config->border_colors.unfocused.text = 0x888888FF; + config->border_colors.unfocused.indicator = 0x292D2EFF; + config->border_colors.unfocused.child_border = 0x222222FF; + + config->border_colors.urgent.border = 0x2F343AFF; + config->border_colors.urgent.background = 0x900000FF; + config->border_colors.urgent.text = 0xFFFFFFFF; + config->border_colors.urgent.indicator = 0x900000FF; + config->border_colors.urgent.child_border = 0x900000FF; + + config->border_colors.placeholder.border = 0x000000FF; + config->border_colors.placeholder.background = 0x0C0C0CFF; + config->border_colors.placeholder.text = 0xFFFFFFFF; + config->border_colors.placeholder.indicator = 0x000000FF; + config->border_colors.placeholder.child_border = 0x0C0C0CFF; + + config->border_colors.background = 0xFFFFFFFF; } static int compare_modifiers(const void *left, const void *right) { diff --git a/sway/container.c b/sway/container.c index 9330a3deb..21a929b90 100644 --- a/sway/container.c +++ b/sway/container.c @@ -22,8 +22,11 @@ static swayc_t *new_swayc(enum swayc_types type) { c->gaps = -1; c->layout = L_NONE; c->type = type; + c->border_type = config->border; + c->border_thickness = config->border_thickness; if (type != C_VIEW) { c->children = create_list(); + c->border_type = B_NONE; } return c; } @@ -266,11 +269,12 @@ swayc_t *new_view(swayc_t *sibling, wlc_handle handle) { view->is_focused = true; view->sticky = false; // Setup geometry - const struct wlc_geometry* geometry = wlc_view_get_geometry(handle); + struct wlc_geometry geometry; + wlc_view_get_visible_geometry(handle, &geometry); view->width = 0; view->height = 0; - view->desired_width = geometry->size.w; - view->desired_height = geometry->size.h; + view->desired_width = geometry.size.w; + view->desired_height = geometry.size.h; view->is_floating = false; @@ -303,13 +307,14 @@ swayc_t *new_floating_view(wlc_handle handle) { view->sticky = false; // Set the geometry of the floating view - const struct wlc_geometry* geometry = wlc_view_get_geometry(handle); + struct wlc_geometry geometry; + wlc_view_get_visible_geometry(handle, &geometry); // give it requested geometry, but place in center - view->x = (swayc_active_workspace()->width - geometry->size.w) / 2; - view->y = (swayc_active_workspace()->height- geometry->size.h) / 2; - view->width = geometry->size.w; - view->height = geometry->size.h; + view->x = (swayc_active_workspace()->width - geometry.size.w) / 2; + view->y = (swayc_active_workspace()->height- geometry.size.h) / 2; + view->width = geometry.size.w; + view->height = geometry.size.h; view->desired_width = view->width; view->desired_height = view->height; diff --git a/sway/focus.c b/sway/focus.c index 7f96eda77..0c9719b09 100644 --- a/sway/focus.c +++ b/sway/focus.c @@ -7,6 +7,7 @@ #include "config.h" #include "input_state.h" #include "ipc-server.h" +#include "border.h" bool locked_container_focus = false; bool locked_view_focus = false; @@ -28,6 +29,8 @@ static void update_focus(swayc_t *c) { // Case where output changes case C_OUTPUT: + // update borders for views in prev + container_map(prev, map_update_view_border, NULL); wlc_output_focus(c->handle); break; @@ -130,6 +133,7 @@ bool set_focused_container(swayc_t *c) { // unactivate previous focus if (focused->type == C_VIEW) { wlc_view_set_state(focused->handle, WLC_BIT_ACTIVATED, false); + update_view_border(focused); } // activate current focus if (p->type == C_VIEW) { @@ -137,6 +141,7 @@ bool set_focused_container(swayc_t *c) { // set focus if view_focus is unlocked if (!locked_view_focus) { wlc_view_focus(p->handle); + update_view_border(p); } } } else if (p->type == C_WORKSPACE) { diff --git a/sway/handlers.c b/sway/handlers.c index 7d4ea263f..54326dd07 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -9,6 +9,7 @@ #include #include "handlers.h" +#include "border.h" #include "log.h" #include "layout.h" #include "config.h" @@ -150,6 +151,10 @@ static void handle_output_post_render(wlc_handle output) { ipc_get_pixels(output); } +static void handle_view_pre_render(wlc_handle view) { + render_view_borders(view); +} + static void handle_output_resolution_change(wlc_handle output, const struct wlc_size *from, const struct wlc_size *to) { sway_log(L_DEBUG, "Output %u resolution changed to %d x %d", (unsigned int)output, to->w, to->h); swayc_t *c = swayc_by_handle(output); @@ -716,6 +721,7 @@ void register_wlc_handlers() { wlc_set_view_created_cb(handle_view_created); wlc_set_view_destroyed_cb(handle_view_destroyed); wlc_set_view_focus_cb(handle_view_focus); + wlc_set_view_render_pre_cb(handle_view_pre_render); wlc_set_view_request_geometry_cb(handle_view_geometry_request); wlc_set_view_request_state_cb(handle_view_state_request); wlc_set_keyboard_key_cb(handle_key); diff --git a/sway/layout.c b/sway/layout.c index d9c4598f0..3f271cafd 100644 --- a/sway/layout.c +++ b/sway/layout.c @@ -12,6 +12,7 @@ #include "focus.h" #include "output.h" #include "ipc-server.h" +#include "border.h" swayc_t root_container; list_t *scratchpad; @@ -373,6 +374,46 @@ void move_workspace_to(swayc_t* workspace, swayc_t* destination) { update_visibility(src_op); } +static void update_border_geometry_floating(swayc_t *c, struct wlc_geometry *geometry) { + struct wlc_geometry g = *geometry; + c->actual_geometry = g; + + switch (c->border_type) { + case B_NONE: + break; + case B_PIXEL: + g.origin.x -= c->border_thickness; + g.origin.y -= c->border_thickness; + g.size.w += (c->border_thickness * 2); + g.size.h += (c->border_thickness * 2); + break; + case B_NORMAL: + g.origin.x -= c->border_thickness; + uint32_t title_bar_height = config->font_height + 4; // borders + padding + g.origin.y -= title_bar_height; + g.size.w += (c->border_thickness * 2); + g.size.h += (c->border_thickness + title_bar_height); + + struct wlc_geometry title_bar = { + .origin = { + .x = g.origin.x, + .y = g.origin.y + }, + .size = { + .w = g.size.w, + .h = title_bar_height + } + }; + c->title_bar_geometry = title_bar; + break; + } + + c->border_geometry = g; + *geometry = c->actual_geometry; + + update_view_border(c); +} + void update_geometry(swayc_t *container) { if (container->type != C_VIEW) { return; @@ -426,6 +467,81 @@ void update_geometry(swayc_t *container) { geometry.size.h = ws->y + ws->height - geometry.origin.y; } } + + if (swayc_is_fullscreen(container)) { + container->border_geometry = (const struct wlc_geometry){0}; + container->title_bar_geometry = (const struct wlc_geometry){0}; + } else if (container->is_floating) { // allocate border for floating window + update_border_geometry_floating(container, &geometry); + } else if (!container->is_floating) { // allocate border for titled window + container->border_geometry = geometry; + + int border_top = container->border_thickness; + int border_bottom = container->border_thickness; + int border_left = container->border_thickness; + int border_right = container->border_thickness; + + // handle hide_edge_borders + if (config->hide_edge_borders != E_NONE && gap <= 0) { + swayc_t *output = swayc_parent_by_type(container, C_OUTPUT); + const struct wlc_size *size = wlc_output_get_resolution(output->handle); + + if (config->hide_edge_borders == E_HORIZONTAL || config->hide_edge_borders == E_BOTH) { + if (geometry.origin.x == 0) { + border_left = 0; + } + + if (geometry.origin.x + geometry.size.w == size->w) { + border_right = 0; + } + } + + if (config->hide_edge_borders == E_VERTICAL || config->hide_edge_borders == E_BOTH) { + if (geometry.origin.y == 0) { + border_top = 0; + } + + if (geometry.origin.y + geometry.size.h == size->h) { + border_bottom = 0; + } + } + } + + switch (container->border_type) { + case B_NONE: + break; + case B_PIXEL: + geometry.origin.x += border_left; + geometry.origin.y += border_top; + geometry.size.w -= (border_left + border_right); + geometry.size.h -= (border_top + border_bottom); + break; + case B_NORMAL: + { + struct wlc_geometry title_bar = { + .origin = { + .x = container->border_geometry.origin.x, + .y = container->border_geometry.origin.y + }, + .size = { + .w = container->border_geometry.size.w, + .h = config->font_height + 4 // borders + padding + } + }; + geometry.origin.x += border_left; + geometry.origin.y += title_bar.size.h; + geometry.size.w -= (border_left + border_right); + geometry.size.h -= (border_bottom + title_bar.size.h); + container->title_bar_geometry = title_bar; + break; + } + } + + container->actual_geometry = geometry; + + update_view_border(container); + } + wlc_view_set_geometry(container->handle, 0, &geometry); } diff --git a/sway/sway.5.txt b/sway/sway.5.txt index 1bb5cd3ba..2eb0276cb 100644 --- a/sway/sway.5.txt +++ b/sway/sway.5.txt @@ -43,6 +43,15 @@ The following commands may only be used in the configuration file. The following commands cannot be used directly in the configuration file. They are expected to be used with **bindsym** or at runtime through **swaymsg**(1). +**border** []:: + Set border style for windows. _normal_ includes a border of thickness _n_ and + a title bar. _pixel_ is just the border without title bar. Default is _normal_ + with border thickness 2. + +**border** :: + Set border style to _none_ or _toggle_ between the available border styles: + _normal_, _pixel_, _none_. + **exit**:: Exit sway and end your Wayland session. @@ -67,6 +76,9 @@ They are expected to be used with **bindsym** or at runtime through **swaymsg**( **fullscreen**:: Toggles fullscreen status for the focused view. +**hide_edge_borders** :: + Hide window borders adjacent to the screen edges. Default is _none_. + **layout** :: Sets the layout mode of the focused container. _mode_ can be one of _splith_, _splitv_, or _toggle split_. diff --git a/swaybar/config.c b/swaybar/config.c index fddea791f..6b9ff86be 100644 --- a/swaybar/config.c +++ b/swaybar/config.c @@ -35,9 +35,11 @@ uint32_t parse_position(const char *position) { char *parse_font(const char *font) { char *new_font = NULL; if (strncmp("pango:", font, 6) == 0) { - new_font = strdup(font + 6); + font += 6; } + new_font = strdup(font); + return new_font; } diff --git a/swaybar/render.c b/swaybar/render.c index 1573a3734..fff47ab04 100644 --- a/swaybar/render.c +++ b/swaybar/render.c @@ -50,7 +50,7 @@ static void render_sharp_line(cairo_t *cairo, uint32_t color, double x, double y static void render_block(struct window *window, struct config *config, struct status_block *block, double *x, bool edge) { int width, height, sep_width; - get_text_size(window, &width, &height, "%s", block->full_text); + get_text_size(window->cairo, window->font, &width, &height, "%s", block->full_text); int textwidth = width; double block_width = width; @@ -74,7 +74,7 @@ static void render_block(struct window *window, struct config *config, struct st // Add separator if (!edge) { if (config->sep_symbol) { - get_text_size(window, &sep_width, &height, "%s", config->sep_symbol); + get_text_size(window->cairo, window->font, &sep_width, &height, "%s", config->sep_symbol); if (sep_width > block->separator_block_width) { block->separator_block_width = sep_width + margin * 2; } @@ -136,7 +136,7 @@ static void render_block(struct window *window, struct config *config, struct st cairo_move_to(window->cairo, offset, margin); cairo_set_source_u32(window->cairo, block->color); - pango_printf(window, "%s", block->full_text); + pango_printf(window->cairo, window->font, "%s", block->full_text); pos += width; @@ -159,7 +159,7 @@ static void render_block(struct window *window, struct config *config, struct st if (config->sep_symbol) { offset = pos + (block->separator_block_width - sep_width) / 2; cairo_move_to(window->cairo, offset, margin); - pango_printf(window, "%s", config->sep_symbol); + pango_printf(window->cairo, window->font, "%s", config->sep_symbol); } else { cairo_set_line_width(window->cairo, 1); cairo_move_to(window->cairo, pos + block->separator_block_width/2, @@ -201,7 +201,7 @@ static void render_workspace_button(struct window *window, struct config *config char *name = handle_workspace_number(config->strip_workspace_numbers, ws->name); int width, height; - get_text_size(window, &width, &height, "%s", name); + get_text_size(window->cairo, window->font, &width, &height, "%s", name); struct box_colors box_colors; if (ws->urgent) { box_colors = config->colors.urgent_workspace; @@ -228,7 +228,7 @@ static void render_workspace_button(struct window *window, struct config *config // text cairo_set_source_u32(window->cairo, box_colors.text); cairo_move_to(window->cairo, (int)*x + ws_horizontal_padding, margin); - pango_printf(window, "%s", name); + pango_printf(window->cairo, window->font, "%s", name); *x += width + ws_horizontal_padding * 2 + ws_spacing; @@ -237,7 +237,7 @@ static void render_workspace_button(struct window *window, struct config *config static void render_binding_mode_indicator(struct window *window, struct config *config, double pos) { int width, height; - get_text_size(window, &width, &height, "%s", config->mode); + get_text_size(window->cairo, window->font, &width, &height, "%s", config->mode); // background cairo_set_source_u32(window->cairo, config->colors.binding_mode.background); @@ -254,7 +254,7 @@ static void render_binding_mode_indicator(struct window *window, struct config * // text cairo_set_source_u32(window->cairo, config->colors.binding_mode.text); cairo_move_to(window->cairo, (int)pos + ws_horizontal_padding, margin); - pango_printf(window, "%s", config->mode); + pango_printf(window->cairo, window->font, "%s", config->mode); } void render(struct output *output, struct config *config, struct status_line *line) { @@ -278,9 +278,9 @@ void render(struct output *output, struct config *config, struct status_line *li int width, height; if (line->protocol == TEXT) { - get_text_size(window, &width, &height, "%s", line->text_line); + get_text_size(window->cairo, window->font, &width, &height, "%s", line->text_line); cairo_move_to(cairo, window->width - margin - width, margin); - pango_printf(window, "%s", line->text_line); + pango_printf(window->cairo, window->font, "%s", line->text_line); } else if (line->protocol == I3BAR && line->block_line) { double pos = window->width - 0.5; bool edge = true; @@ -312,7 +312,7 @@ void render(struct output *output, struct config *config, struct status_line *li void set_window_height(struct window *window, int height) { int text_width, text_height; - get_text_size(window, &text_width, &text_height, "Test string for measuring purposes"); + get_text_size(window->cairo, window->font, &text_width, &text_height, "Test string for measuring purposes"); if (height > 0) { margin = (height - text_height) / 2; ws_vertical_padding = margin - 1.5; diff --git a/wayland/pango.c b/wayland/pango.c index 9766be6a2..d79d89b3e 100644 --- a/wayland/pango.c +++ b/wayland/pango.c @@ -4,21 +4,19 @@ #include #include #include -#include "client/window.h" -#include "client/buffer.h" #include "log.h" -PangoLayout *get_pango_layout(struct window *window, const char *text) { - PangoLayout *layout = pango_cairo_create_layout(window->cairo); +PangoLayout *get_pango_layout(cairo_t *cairo, const char *font, const char *text) { + PangoLayout *layout = pango_cairo_create_layout(cairo); pango_layout_set_text(layout, text, -1); - PangoFontDescription *desc = pango_font_description_from_string(window->font); + PangoFontDescription *desc = pango_font_description_from_string(font); pango_layout_set_font_description(layout, desc); pango_layout_set_single_paragraph_mode(layout, 1); pango_font_description_free(desc); return layout; } -void get_text_size(struct window *window, int *width, int *height, const char *fmt, ...) { +void get_text_size(cairo_t *cairo, const char *font, int *width, int *height, const char *fmt, ...) { char *buf = malloc(2048); va_list args; @@ -28,8 +26,8 @@ void get_text_size(struct window *window, int *width, int *height, const char *f } va_end(args); - PangoLayout *layout = get_pango_layout(window, buf); - pango_cairo_update_layout(window->cairo, layout); + PangoLayout *layout = get_pango_layout(cairo, font, buf); + pango_cairo_update_layout(cairo, layout); pango_layout_get_pixel_size(layout, width, height); @@ -38,7 +36,7 @@ void get_text_size(struct window *window, int *width, int *height, const char *f free(buf); } -void pango_printf(struct window *window, const char *fmt, ...) { +void pango_printf(cairo_t *cairo, const char *font, const char *fmt, ...) { char *buf = malloc(2048); va_list args; @@ -48,10 +46,10 @@ void pango_printf(struct window *window, const char *fmt, ...) { } va_end(args); - PangoLayout *layout = get_pango_layout(window, buf); - pango_cairo_update_layout(window->cairo, layout); + PangoLayout *layout = get_pango_layout(cairo, font, buf); + pango_cairo_update_layout(cairo, layout); - pango_cairo_show_layout(window->cairo, layout); + pango_cairo_show_layout(cairo, layout); g_object_unref(layout);