From fdfb12b2ce2de8e271ca884bbe0c8c154f1e5578 Mon Sep 17 00:00:00 2001 From: Johan Malm Date: Sat, 27 Mar 2021 21:04:12 +0000 Subject: [PATCH] Added support for rounded corners --- include/ssd.h | 12 +++- src/output.c | 56 ++++++++++----- src/ssd.c | 184 +++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 215 insertions(+), 37 deletions(-) diff --git a/include/ssd.h b/include/ssd.h index 734f2901..8a18977d 100644 --- a/include/ssd.h +++ b/include/ssd.h @@ -11,14 +11,22 @@ enum ssd_part_type { LAB_SSD_PART_RIGHT, LAB_SSD_PART_BOTTOM, LAB_SSD_PART_LEFT, + LAB_SSD_PART_CORNER_TOP_RIGHT, + LAB_SSD_PART_CORNER_TOP_LEFT, LAB_SSD_END_MARKER }; struct ssd_part { struct wlr_box box; enum ssd_part_type type; - struct wlr_texture *texture; - float *color; + struct { + struct wlr_texture *active; + struct wlr_texture *inactive; + } texture; + struct { + float *active; + float *inactive; + } color; struct wl_list link; }; diff --git a/src/output.c b/src/output.c index 595027bd..00e49af7 100644 --- a/src/output.c +++ b/src/output.c @@ -434,6 +434,30 @@ render_icon(struct output *output, pixman_region32_t *output_damage, matrix); } +void +render_texture_helper(struct output *output, pixman_region32_t *output_damage, + struct wlr_box *_box, struct wlr_texture *texture) +{ + if (!texture) { + return; + } + struct wlr_box box; + memcpy(&box, _box, sizeof(struct wlr_box)); + + double ox = 0, oy = 0; + wlr_output_layout_output_coords(output->server->output_layout, + output->wlr_output, &ox, &oy); + box.x += ox; + box.y += oy; + scale_box(&box, output->wlr_output->scale); + + float matrix[9]; + wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, 0, + output->wlr_output->transform_matrix); + render_texture(output->wlr_output, output_damage, texture, &box, + matrix); +} + static bool isbutton(enum ssd_part_type type) { @@ -450,39 +474,35 @@ render_deco(struct view *view, struct output *output, return; } + struct wlr_seat *seat = view->server->seat.seat; + bool focused = view->surface == seat->keyboard_state.focused_surface; + struct ssd_part *part; wl_list_for_each_reverse(part, &view->ssd.parts, link) { - if (part->texture) { - ; // render_texture() + if (part->texture.active) { + struct wlr_texture *texture = focused ? + part->texture.active : part->texture.inactive; + render_texture_helper(output, output_damage, &part->box, + texture); } else { - render_rect(output, output_damage, &part->box, - part->color); + float *color = focused ? + part->color.active : part->color.inactive; + render_rect(output, output_damage, &part->box, color); } } - /* render title */ - struct wlr_seat *seat = view->server->seat.seat; - float *color; - struct theme *theme = view->server->theme; - if (view->surface == seat->keyboard_state.focused_surface) { - color = theme->window_active_title_bg_color; - } else { - color = theme->window_inactive_title_bg_color; - } - struct wlr_box box = ssd_box(view, LAB_SSD_PART_TITLE); - render_rect(output, output_damage, &box, color); - /* button background */ struct wlr_cursor *cur = view->server->seat.cursor; enum ssd_part_type type = ssd_at(view, cur->x, cur->y); - box = ssd_box(view, type); + struct wlr_box box = ssd_box(view, type); if (isbutton(type) && wlr_box_contains_point(&box, cur->x, cur->y)) { - color = (float[4]){ 0.5, 0.5, 0.5, 0.5 }; + float *color = (float[4]){ 0.5, 0.5, 0.5, 0.5 }; render_rect(output, output_damage, &box, color); } /* buttons */ + struct theme *theme = view->server->theme; if (view->surface == seat->keyboard_state.focused_surface) { box = ssd_box(view, LAB_SSD_BUTTON_CLOSE); render_icon(output, output_damage, &box, diff --git a/src/ssd.c b/src/ssd.c index c7748a20..afeacd35 100644 --- a/src/ssd.c +++ b/src/ssd.c @@ -5,6 +5,9 @@ */ #include +#include +#include +#include #include "config/rcxml.h" #include "labwc.h" #include "theme.h" @@ -41,43 +44,44 @@ struct wlr_box ssd_box(struct view *view, enum ssd_part_type type) { struct wlr_box box = { 0 }; + int corner_square = rc.title_height + BORDER_WIDTH; assert(view); switch (type) { case LAB_SSD_BUTTON_CLOSE: - box.width = rc.title_height; - box.height = rc.title_height; box.x = view->x + view->w - rc.title_height; box.y = view->y - rc.title_height; + box.width = rc.title_height; + box.height = rc.title_height; break; case LAB_SSD_BUTTON_MAXIMIZE: - box.width = rc.title_height; - box.height = rc.title_height; box.x = view->x + view->w - rc.title_height * 2; box.y = view->y - rc.title_height; - break; - case LAB_SSD_BUTTON_ICONIFY: box.width = rc.title_height; box.height = rc.title_height; + break; + case LAB_SSD_BUTTON_ICONIFY: box.x = view->x + view->w - rc.title_height * 3; box.y = view->y - rc.title_height; + box.width = rc.title_height; + box.height = rc.title_height; break; case LAB_SSD_PART_TITLE: - box.x = view->x; + box.x = view->x + rc.title_height; box.y = view->y - rc.title_height; - box.width = view->w; + box.width = view->w - 2 * rc.title_height; box.height = rc.title_height; break; case LAB_SSD_PART_TOP: - box.x = view->x - BORDER_WIDTH; - box.y = view->y - rc.title_height - BORDER_WIDTH; - box.width = view->w + 2 * BORDER_WIDTH; + box.x = view->x + rc.title_height; + box.y = view->y - corner_square; + box.width = view->w - 2 * rc.title_height; box.height = BORDER_WIDTH; break; case LAB_SSD_PART_RIGHT: box.x = view->x + view->w; - box.y = view->y - rc.title_height; + box.y = view->y; box.width = BORDER_WIDTH; - box.height = view->h + rc.title_height; + box.height = view->h; break; case LAB_SSD_PART_BOTTOM: box.x = view->x - BORDER_WIDTH; @@ -87,9 +91,21 @@ ssd_box(struct view *view, enum ssd_part_type type) break; case LAB_SSD_PART_LEFT: box.x = view->x - BORDER_WIDTH; - box.y = view->y - rc.title_height; + box.y = view->y; box.width = BORDER_WIDTH; - box.height = view->h + rc.title_height; + box.height = view->h; + break; + case LAB_SSD_PART_CORNER_TOP_LEFT: + box.x = view->x - BORDER_WIDTH; + box.y = view->y - corner_square; + box.width = corner_square; + box.height = corner_square; + break; + case LAB_SSD_PART_CORNER_TOP_RIGHT: + box.x = view->x + view->w - rc.title_height; + box.y = view->y - corner_square; + box.width = corner_square; + box.height = corner_square; break; default: break; @@ -119,6 +135,106 @@ add_part(struct view *view, enum ssd_part_type type) return part; } +struct rounded_corner_ctx { + struct wlr_box *box; + double radius; + double line_width; + float *fill_color; + float *border_color; + enum { + LAB_CORNER_UNKNOWN = 0, + LAB_CORNER_TOP_LEFT, + LAB_CORNER_TOP_RIGHT, + } corner; +}; + +static void set_source(cairo_t *cairo, float *c) +{ + cairo_set_source_rgba(cairo, c[0], c[1], c[2], c[3]); +} + +static struct wlr_texture * +rounded_rect(struct wlr_renderer *renderer, struct rounded_corner_ctx *ctx) +{ + /* 1 degree in radians (=2π/360) */ + double deg = 0.017453292519943295; + + if (ctx->corner == LAB_CORNER_UNKNOWN) { + return NULL; + } + + double w = ctx->box->width; + double h = ctx->box->height; + double r = ctx->radius; + + cairo_surface_t *surf = + cairo_image_surface_create(CAIRO_FORMAT_ARGB32, w, h); + cairo_t *cairo = cairo_create(surf); + + /* set transparent background */ + cairo_set_operator(cairo, CAIRO_OPERATOR_CLEAR); + cairo_paint(cairo); + + /* fill */ + cairo_set_line_width(cairo, 0.0); + cairo_new_sub_path(cairo); + switch (ctx->corner) { + case LAB_CORNER_TOP_LEFT: + cairo_arc(cairo, r, r, r, 180 * deg, 270 * deg); + cairo_line_to(cairo, w, 0); + cairo_line_to(cairo, w, h); + cairo_line_to(cairo, 0, h); + break; + case LAB_CORNER_TOP_RIGHT: + cairo_arc(cairo, w - r, r, r, -90 * deg, 0 * deg); + cairo_line_to(cairo, w, h); + cairo_line_to(cairo, 0, h); + cairo_line_to(cairo, 0, 0); + break; + default: + warn("unknown corner type"); + } + cairo_close_path(cairo); + cairo_set_operator(cairo, CAIRO_OPERATOR_SOURCE); + set_source(cairo, ctx->fill_color); + cairo_fill_preserve(cairo); + cairo_stroke(cairo); + + /* border */ + cairo_set_line_cap(cairo, CAIRO_LINE_CAP_ROUND); + set_source(cairo, ctx->border_color); + cairo_set_line_width(cairo, BORDER_WIDTH); + double half_line_width = BORDER_WIDTH / 2.0; + switch (ctx->corner) { + case LAB_CORNER_TOP_LEFT: + cairo_move_to(cairo, half_line_width, h); + cairo_line_to(cairo, half_line_width, r + BORDER_WIDTH); + cairo_arc(cairo, r, r, r - half_line_width, 180 * deg, 270 * deg); + cairo_line_to(cairo, w, half_line_width); + break; + case LAB_CORNER_TOP_RIGHT: + cairo_move_to(cairo, 0, half_line_width); + cairo_line_to(cairo, w - r, half_line_width); + cairo_arc(cairo, w - r, r, r - half_line_width, -90 * deg, 0 * deg); + cairo_line_to(cairo, w - half_line_width, h); + break; + default: + warn("unknown corner type"); + } + cairo_stroke(cairo); + + /* convert to wlr_texture */ + cairo_surface_flush(surf); + unsigned char *data = cairo_image_surface_get_data(surf); + struct wlr_texture *texture = wlr_texture_from_pixels(renderer, + DRM_FORMAT_ARGB8888, cairo_image_surface_get_stride(surf), + w, h, data); + + cairo_destroy(cairo); + cairo_surface_destroy(surf); + return texture; +} + void ssd_create(struct view *view) { @@ -131,7 +247,6 @@ ssd_create(struct view *view) view->ssd.box.height = view->h; /* border */ - float *color = theme->window_active_handle_bg_color; enum ssd_part_type border[4] = { LAB_SSD_PART_TOP, LAB_SSD_PART_RIGHT, @@ -141,8 +256,43 @@ ssd_create(struct view *view) for (int i = 0; i < 4; i++) { part = add_part(view, border[i]); part->box = ssd_box(view, border[i]); - part->color = color; + part->color.active = theme->window_active_handle_bg_color; + part->color.inactive = theme->window_active_handle_bg_color; } + + /* titlebar */ + part = add_part(view, LAB_SSD_PART_TITLE); + part->box = ssd_box(view, LAB_SSD_PART_TITLE); + part->color.active = theme->window_active_title_bg_color; + part->color.inactive = theme->window_inactive_title_bg_color; + + /* titlebar top left corner */ + struct wlr_renderer *renderer = view->server->renderer; + part = add_part(view, LAB_SSD_PART_CORNER_TOP_LEFT); + part->box = ssd_box(view, part->type); + struct rounded_corner_ctx ctx = { + .box = &part->box, + .radius = 7.0, /* TODO: get from config */ + .line_width = 1.0, + .fill_color = theme->window_active_title_bg_color, + .border_color = theme->window_active_handle_bg_color, + .corner = LAB_CORNER_TOP_LEFT, + }; + part->texture.active = rounded_rect(renderer, &ctx); + + ctx.fill_color = theme->window_inactive_title_bg_color, + part->texture.inactive = rounded_rect(renderer, &ctx); + + /* titlebar top right corner */ + part = add_part(view, LAB_SSD_PART_CORNER_TOP_RIGHT); + part->box = ssd_box(view, part->type); + ctx.box = &part->box; + ctx.corner = LAB_CORNER_TOP_RIGHT; + ctx.fill_color = theme->window_active_title_bg_color, + part->texture.active = rounded_rect(renderer, &ctx); + + ctx.fill_color = theme->window_inactive_title_bg_color, + part->texture.inactive = rounded_rect(renderer, &ctx); } void