diff --git a/include/render.h b/include/border.h similarity index 60% rename from include/render.h rename to include/border.h index c3d1ca872..63cd63d29 100644 --- a/include/render.h +++ b/include/border.h @@ -1,9 +1,10 @@ -#ifndef _SWAY_RENDER_H -#define _SWAY_RENDER_H +#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); +int get_font_text_height(const char *font); #endif diff --git a/include/config.h b/include/config.h index a35cfd0a8..fb84423c8 100644 --- a/include/config.h +++ b/include/config.h @@ -184,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; diff --git a/include/container.h b/include/container.h index 07514c8a5..26da851e0 100644 --- a/include/container.h +++ b/include/container.h @@ -115,15 +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; + /** + * 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 presumed_geometry; - int border_thickness; + 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 6c62d6763..51f27a057 100644 --- a/sway/CMakeLists.txt +++ b/sway/CMakeLists.txt @@ -26,7 +26,7 @@ add_executable(sway output.c resize.c workspace.c - render.c + border.c ) add_definitions( @@ -36,6 +36,7 @@ add_definitions( target_link_libraries(sway sway-common sway-protocols + sway-wayland ${WLC_LIBRARIES} ${XKBCOMMON_LIBRARIES} ${PCRE_LIBRARIES} diff --git a/sway/border.c b/sway/border.c new file mode 100644 index 000000000..8c9106e46 --- /dev/null +++ b/sway/border.c @@ -0,0 +1,247 @@ +#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) { + 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 { + 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 + 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 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) { + wlc_pixels_write(WLC_RGBA8888, &c->border_geometry, c->border); + } +} diff --git a/sway/commands.c b/sway/commands.c index bc182ceeb..c53b13c3c 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); @@ -359,6 +360,14 @@ static struct cmd_results *cmd_border(int argc, char **argv) { } 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; @@ -367,7 +376,7 @@ static struct cmd_results *cmd_border(int argc, char **argv) { } else if (strcasecmp(argv[0], "pixel") == 0) { border = B_PIXEL; } else if (strcasecmp(argv[0], "toggle") == 0) { - switch (config->border) { + switch (border) { case B_NONE: border = B_PIXEL; break; @@ -383,16 +392,24 @@ static struct cmd_results *cmd_border(int argc, char **argv) { "Expected 'border "); } + if (argc == 2 && (border == B_NORMAL || border == B_PIXEL)) { - int thickness = (int)strtol(argv[1], NULL, 10); + 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; } - config->border = border; return cmd_results_new(CMD_SUCCESS, NULL, NULL); } @@ -904,9 +921,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); @@ -1954,6 +1971,8 @@ static struct cmd_results *cmd_font(int argc, char **argv) { 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); } diff --git a/sway/config.c b/sway/config.c index 193cfad26..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; @@ -161,6 +162,7 @@ static void config_defaults(struct sway_config *config) { config->default_layout = L_NONE; config->default_orientation = L_NONE; config->font = strdup("monospace 10"); + config->font_height = get_font_text_height(config->font); // Flags config->focus_follows_mouse = true; diff --git a/sway/container.c b/sway/container.c index ac0d32317..21a929b90 100644 --- a/sway/container.c +++ b/sway/container.c @@ -22,8 +22,8 @@ static swayc_t *new_swayc(enum swayc_types type) { c->gaps = -1; c->layout = L_NONE; c->type = type; - c->border_type = B_PIXEL; // TODO: Load default from config - c->border_thickness = 2; + c->border_type = config->border; + c->border_thickness = config->border_thickness; if (type != C_VIEW) { c->children = create_list(); c->border_type = B_NONE; @@ -269,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; @@ -306,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..4cae3b472 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; @@ -130,6 +131,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 +139,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 47b649fdd..fc4736403 100644 --- a/sway/handlers.c +++ b/sway/handlers.c @@ -9,7 +9,7 @@ #include #include "handlers.h" -#include "render.h" +#include "border.h" #include "log.h" #include "layout.h" #include "config.h" @@ -349,15 +349,14 @@ static void handle_view_geometry_request(wlc_handle handle, const struct wlc_geo view->desired_width = geometry->size.w; view->desired_height = geometry->size.h; - if (view->is_floating) { - view->width = view->desired_width; - view->height = view->desired_height; - view->x = geometry->origin.x; - view->y = geometry->origin.y; - arrange_windows(view->parent, -1, -1); - } + /* if (view->is_floating) { */ + /* view->width = view->desired_width; */ + /* view->height = view->desired_height; */ + /* view->x = geometry->origin.x; */ + /* view->y = geometry->origin.y; */ + /* /1* arrange_windows(view->parent, -1, -1); *1/ */ + /* } */ } - update_view_border(view); } static void handle_view_state_request(wlc_handle view, enum wlc_view_state_bit state, bool toggle) { diff --git a/sway/layout.c b/sway/layout.c index be898c58c..a282f36e8 100644 --- a/sway/layout.c +++ b/sway/layout.c @@ -12,7 +12,7 @@ #include "focus.h" #include "output.h" #include "ipc-server.h" -#include "render.h" +#include "border.h" swayc_t root_container; list_t *scratchpad; @@ -427,6 +427,80 @@ 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 { + // make room for border + 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/render.c b/sway/render.c deleted file mode 100644 index 9388c1d05..000000000 --- a/sway/render.c +++ /dev/null @@ -1,113 +0,0 @@ -#include "render.h" -#include -#include -#include -#include -#include "container.h" - -void cairo_set_source_u32(cairo_t *cairo, uint32_t color) { - cairo_set_source_rgba(cairo, - (color >> (3*8) & 0xFF) / 255.0, - (color >> (2*8) & 0xFF) / 255.0, - (color >> (1*8) & 0xFF) / 255.0, - (color >> (0*8) & 0xFF) / 255.0); -} - -cairo_t *create_border_buffer(swayc_t *view, struct wlc_geometry geo, - cairo_surface_t **surface) { - const int channels = 4; - cairo_t *cr; - view->border_geometry = geo; - view->border = calloc(channels * geo.size.w * 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, channels * geo.size.w); - 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; -} - -void update_view_border(swayc_t *view) { - struct wlc_geometry geo; - wlc_view_get_visible_geometry(view->handle, &geo); - cairo_t *cr = NULL; - cairo_surface_t *surface = NULL; - - if (view->border) { - free(view->border); - view->border = NULL; - } - - switch (view->border_type) { - case B_NONE: - view->border_geometry = geo; - break; - case B_PIXEL: - geo.origin.x -= view->border_thickness; - geo.origin.y -= view->border_thickness; - geo.size.w += view->border_thickness * 2; - geo.size.h += view->border_thickness * 2; - if (geo.size.w <= 0 || geo.size.h <= 0) { - view->border = NULL; - break; - } - cr = create_border_buffer(view, geo, &surface); - if (!cr) { - break; - } - cairo_set_source_u32(cr, 0x0000FFFF); - cairo_paint(cr); - break; - case B_NORMAL: - // TODO - break; - } - if (surface) { - cairo_surface_flush(surface); - cairo_surface_destroy(surface); - } - if (cr) { - cairo_destroy(cr); - sway_log(L_DEBUG, "Created border for %p (%dx%d+%d,%d)", view, - geo.size.w, geo.size.h, geo.origin.x, geo.origin.y); - } - view->border_geometry = geo; -} - -void render_view_borders(wlc_handle view) { - swayc_t *c = swayc_by_handle(view); - if (!c || c->border_type == B_NONE) { - return; - } - struct wlc_geometry geo; - wlc_view_get_visible_geometry(view, &geo); - if (geo.size.w != c->presumed_geometry.size.w - || geo.size.h != c->presumed_geometry.size.h - || geo.origin.x != c->presumed_geometry.origin.x - || geo.origin.y != c->presumed_geometry.origin.y) { - update_view_border(c); - c->presumed_geometry = geo; - } - if (c->border) { - geo = c->border_geometry; - sway_log(L_DEBUG, "Rendering border for %p (%dx%d+%d,%d)", c, - geo.size.w, geo.size.h, geo.origin.x, geo.origin.y); - wlc_pixels_write(WLC_RGBA8888, &c->border_geometry, c->border); - } -}