From 264d11a618249fe822d510c9d962d75b85c27416 Mon Sep 17 00:00:00 2001 From: "K. Lange" Date: Fri, 22 Jul 2022 12:19:09 +0900 Subject: [PATCH] compositor: blur-behind windows --- apps/compositor.c | 69 ++++++++++++++++++++++++++++++++- apps/panel.c | 6 ++- apps/terminal.c | 11 +++++- base/usr/include/toaru/yutani.h | 1 + lib/graphics.c | 66 +++++++++++++++++++++++++++++++ 5 files changed, 148 insertions(+), 5 deletions(-) diff --git a/apps/compositor.c b/apps/compositor.c index 481f3420..4ed2d5be 100644 --- a/apps/compositor.c +++ b/apps/compositor.c @@ -61,6 +61,15 @@ static void mouse_stop_drag(yutani_globals_t * yg); static void window_move(yutani_globals_t * yg, yutani_server_window_t * window, int x, int y); static yutani_server_window_t * top_at(yutani_globals_t * yg, uint16_t x, uint16_t y); +#define ENABLE_BLUR_BEHIND +#ifdef ENABLE_BLUR_BEHIND +#define BLUR_CLIP_MAX 20 +#define BLUR_KERNEL 10 +static sprite_t * blur_sprite = NULL; +static gfx_context_t * blur_ctx = NULL; +static gfx_context_t * clip_ctx = NULL; +#endif + /** * Print usage information. */ @@ -730,7 +739,7 @@ static int yutani_blit_window(yutani_globals_t * yg, yutani_server_window_t * wi double opacity = (double)(window->opacity) / 255.0; - if (window->rotation || window == yg->resizing_window || window->anim_mode) { + if (window->rotation || window == yg->resizing_window || window->anim_mode || (window->server_flags & YUTANI_WINDOW_FLAG_BLUR_BEHIND)) { double m[2][3]; gfx_matrix_identity(m); @@ -803,6 +812,12 @@ static int yutani_blit_window(yutani_globals_t * yg, yutani_server_window_t * wi } } } +#ifdef ENABLE_BLUR_BEHIND + if (window->server_flags & YUTANI_WINDOW_FLAG_BLUR_BEHIND) { + extern void draw_sprite_transform_blur(gfx_context_t * ctx, gfx_context_t * blur_ctx, const sprite_t * sprite, gfx_matrix_t matrix, float alpha, uint8_t threshold); + draw_sprite_transform_blur(yg->backend_ctx, blur_ctx, &_win_sprite, m, opacity, window->alpha_threshold); + } else +#endif if (matrix_is_translation(m)) { draw_sprite_alpha(yg->backend_ctx, &_win_sprite, m[0][2], m[1][2], opacity); } else { @@ -995,6 +1010,16 @@ static void resize_display(yutani_globals_t * yg) { reinit_graphics_yutani(yg->backend_ctx, yg->host_window); yutani_window_resize_done(yg->host_context, yg->host_window); } + +#ifdef ENABLE_BLUR_BEHIND + sprite_free(blur_sprite); + free(blur_ctx); + blur_sprite = create_sprite(yg->backend_ctx->width, yg->backend_ctx->height, ALPHA_OPAQUE); + blur_ctx = init_graphics_sprite(blur_sprite); + clip_ctx->width = yg->backend_ctx->width; + clip_ctx->height = yg->backend_ctx->height; +#endif + TRACE("graphics context resized..."); yg->width = yg->backend_ctx->width; yg->height = yg->backend_ctx->height; @@ -1041,12 +1066,19 @@ static void redraw_windows(yutani_globals_t * yg) { } gfx_clear_clip(yg->backend_ctx); +#ifdef ENABLE_BLUR_BEHIND + gfx_clear_clip(clip_ctx); +#endif /* If the mouse has moved, that counts as two damage regions */ if ((yg->last_mouse_x != tmp_mouse_x) || (yg->last_mouse_y != tmp_mouse_y)) { has_updates = 2; gfx_add_clip(yg->backend_ctx, yg->last_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, yg->last_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, MOUSE_WIDTH, MOUSE_HEIGHT); gfx_add_clip(yg->backend_ctx, tmp_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X, tmp_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y, MOUSE_WIDTH, MOUSE_HEIGHT); +#ifdef ENABLE_BLUR_BEHIND + gfx_add_clip(clip_ctx, yg->last_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X - BLUR_CLIP_MAX, yg->last_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y - BLUR_CLIP_MAX, MOUSE_WIDTH + BLUR_CLIP_MAX * 2, MOUSE_HEIGHT + BLUR_CLIP_MAX * 2); + gfx_add_clip(clip_ctx, tmp_mouse_x / MOUSE_SCALE - MOUSE_OFFSET_X - BLUR_CLIP_MAX, tmp_mouse_y / MOUSE_SCALE - MOUSE_OFFSET_Y - BLUR_CLIP_MAX, MOUSE_WIDTH + BLUR_CLIP_MAX * 2, MOUSE_HEIGHT + BLUR_CLIP_MAX * 2); +#endif } yg->last_mouse_x = tmp_mouse_x; @@ -1075,12 +1107,22 @@ static void redraw_windows(yutani_globals_t * yg) { /* We add a clip region for each window in the update queue */ has_updates = 1; gfx_add_clip(yg->backend_ctx, rect->x, rect->y, rect->width, rect->height); +#ifdef ENABLE_BLUR_BEHIND + gfx_add_clip(clip_ctx, rect->x - BLUR_CLIP_MAX, rect->y - BLUR_CLIP_MAX, rect->width + BLUR_CLIP_MAX * 2, rect->height + BLUR_CLIP_MAX * 2); +#endif free(rect); free(win); } /* Render */ if (has_updates) { + +#ifdef ENABLE_BLUR_BEHIND + /* Extend clips */ + char * oclip = yg->backend_ctx->clips; + yg->backend_ctx->clips = clip_ctx->clips; +#endif + /* * In theory, we should restrict this to windows within the clip region, * but calculating that may be more trouble than it's worth; @@ -1088,6 +1130,11 @@ static void redraw_windows(yutani_globals_t * yg) { */ yutani_blit_windows(yg); +#ifdef ENABLE_BLUR_BEHIND + /* Restore clip context */ + yg->backend_ctx->clips = oclip; +#endif + /* Send VirtualBox rects */ yutani_post_vbox_rects(yg); @@ -1555,6 +1602,18 @@ static void handle_key_event(yutani_globals_t * yg, struct yutani_msg_key_event yg->debug_bounds = (1-yg->debug_bounds); return; } +#endif +#ifdef ENABLE_BLUR_BEHIND + if ((ke->event.action == KEY_ACTION_DOWN) && + (ke->event.modifiers & KEY_MOD_LEFT_SUPER) && + (ke->event.modifiers & KEY_MOD_LEFT_SHIFT) && + (ke->event.keycode == 'v')) { + if (focused->z != YUTANI_ZORDER_BOTTOM && focused->z != YUTANI_ZORDER_TOP) { + focused->server_flags ^= YUTANI_WINDOW_FLAG_BLUR_BEHIND; + mark_window(yg, focused); + } + return; + } #endif /* Screenshot key */ if ((ke->event.action == KEY_ACTION_DOWN) && @@ -2180,6 +2239,14 @@ int main(int argc, char * argv[]) { yg->backend_ctx = init_graphics_fullscreen_double_buffer(); } +#ifdef ENABLE_BLUR_BEHIND + blur_sprite = create_sprite(yg->backend_ctx->width, yg->backend_ctx->height, ALPHA_OPAQUE); + blur_ctx = init_graphics_sprite(blur_sprite); + clip_ctx = calloc(1, sizeof(gfx_context_t)); + clip_ctx->width = yg->backend_ctx->width; + clip_ctx->height = yg->backend_ctx->height; +#endif + if (!yg->backend_ctx) { free(yg); TRACE("Failed to open framebuffer, bailing."); diff --git a/apps/panel.c b/apps/panel.c index 2e9d7d60..963d64a5 100644 --- a/apps/panel.c +++ b/apps/panel.c @@ -441,7 +441,8 @@ static void handle_key_event(struct yutani_msg_key_event * ke) { (ke->event.action == KEY_ACTION_DOWN)) { /* show menu */ if (!alt_f2) { - alt_f2 = yutani_window_create(yctx, ALTF2_WIDTH, ALTF2_HEIGHT); + alt_f2 = yutani_window_create_flags(yctx, ALTF2_WIDTH, ALTF2_HEIGHT, YUTANI_WINDOW_FLAG_BLUR_BEHIND); + yutani_window_update_shape(yctx, alt_f2, 5); yutani_window_move(yctx, alt_f2, center_x(ALTF2_WIDTH), center_y(ALTF2_HEIGHT)); a2ctx = init_graphics_yutani_double_buffer(alt_f2); redraw_altf2(); @@ -495,7 +496,8 @@ static void handle_key_event(struct yutani_msg_key_event * ke) { new_focused = active_window + direction; /* Create tab window */ alttab = yutani_window_create_flags(yctx, ALTTAB_WIDTH, ALTTAB_HEIGHT, - YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS | YUTANI_WINDOW_FLAG_NO_ANIMATION); + YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS | YUTANI_WINDOW_FLAG_NO_ANIMATION | YUTANI_WINDOW_FLAG_BLUR_BEHIND); + yutani_window_update_shape(yctx, alttab, 5); yutani_set_stack(yctx, alttab, YUTANI_ZORDER_OVERLAY); diff --git a/apps/terminal.c b/apps/terminal.c index 0e39a5eb..c317f801 100644 --- a/apps/terminal.c +++ b/apps/terminal.c @@ -65,6 +65,7 @@ static void usage(char * argv[]) { " -x --grid \033[3mMake resizes round to nearest match for character cell size.\033[0m\n" " -n --no-frame \033[3mDisable decorations.\033[0m\n" " -g --geometry \033[3mSet requested terminal size WIDTHxHEIGHT\033[0m\n" + " -B --blurred \033[3mBlur background behind terminal.\033[0m\n" "\n" " This terminal emulator provides basic support for VT220 escapes and\n" " XTerm extensions, including 256 color support and font effects.\n", @@ -2289,6 +2290,7 @@ static void parse_geometry(char ** argv, char * str) { int main(int argc, char ** argv) { + int _flags = 0; window_width = char_width * 80; window_height = char_height * 24; @@ -2300,12 +2302,13 @@ int main(int argc, char ** argv) { {"grid", no_argument, 0, 'x'}, {"no-frame", no_argument, 0, 'n'}, {"geometry", required_argument, 0, 'g'}, + {"blurred", no_argument, 0, 'B'}, {0,0,0,0} }; /* Read some arguments */ int index, c; - while ((c = getopt_long(argc, argv, "bhxnFls:g:", long_opts, &index)) != -1) { + while ((c = getopt_long(argc, argv, "bhxnFls:g:B", long_opts, &index)) != -1) { if (!c) { if (long_opts[index].flag == 0) { c = long_opts[index].val; @@ -2336,6 +2339,9 @@ int main(int argc, char ** argv) { case 'g': parse_geometry(argv,optarg); break; + case 'B': + _flags = YUTANI_WINDOW_FLAG_BLUR_BEHIND; + break; case '?': break; default: @@ -2370,7 +2376,8 @@ int main(int argc, char ** argv) { init_decorations(); struct decor_bounds bounds; decor_get_bounds(NULL, &bounds); - window = yutani_window_create(yctx, window_width + bounds.width, window_height + bounds.height + menu_bar_height); + window = yutani_window_create_flags(yctx, window_width + bounds.width, window_height + bounds.height + menu_bar_height, _flags); + yutani_window_update_shape(yctx, window, 20); } if (_fullscreen) { diff --git a/base/usr/include/toaru/yutani.h b/base/usr/include/toaru/yutani.h index 1d2bcdda..039ab6d3 100644 --- a/base/usr/include/toaru/yutani.h +++ b/base/usr/include/toaru/yutani.h @@ -480,6 +480,7 @@ struct yutani_msg_clipboard { #define YUTANI_WINDOW_FLAG_ALT_ANIMATION (1 << 3) #define YUTANI_WINDOW_FLAG_DIALOG_ANIMATION (1 << 4) #define YUTANI_WINDOW_FLAG_NO_ANIMATION (1 << 5) +#define YUTANI_WINDOW_FLAG_BLUR_BEHIND (1 << 8) /* YUTANI_SPECIAL_REQUEST * diff --git a/lib/graphics.c b/lib/graphics.c index e7a5f25c..753dc31e 100644 --- a/lib/graphics.c +++ b/lib/graphics.c @@ -405,6 +405,12 @@ void blur_context_box(gfx_context_t * _src, int radius) { _box_blur_vertical(_src,radius); } +void blur_from_into(gfx_context_t * _src, gfx_context_t * _dest, int radius) { + + draw_fill(_dest, rgb(255,0,0)); + +} + static int (*load_sprite_jpg)(sprite_t *, const char *) = NULL; static int (*load_sprite_png)(sprite_t *, const char *) = NULL; @@ -979,6 +985,66 @@ void draw_sprite_transform(gfx_context_t * ctx, const sprite_t * sprite, gfx_mat sprite_free(scanline); } +void draw_sprite_transform_blur(gfx_context_t * ctx, gfx_context_t * blur_ctx, const sprite_t * sprite, gfx_matrix_t matrix, float alpha, uint8_t threshold) { + double inverse[2][3]; + + /* Calculate the inverse matrix for use in calculating sprite + * coordinate from screen coordinate. */ + gfx_matrix_invert(matrix, inverse); + + /* Use primary matrix to obtain corners of the transformed + * sprite in screen coordinates. */ + double ul_x, ul_y; + double ll_x, ll_y; + double ur_x, ur_y; + double lr_x, lr_y; + + apply_matrix(0, 0, matrix, &ul_x, &ul_y); + apply_matrix(0, sprite->height, matrix, &ll_x, &ll_y); + apply_matrix(sprite->width, 0, matrix, &ur_x, &ur_y); + apply_matrix(sprite->width, sprite->height, matrix, &lr_x, &lr_y); + + /* Use the corners to calculate bounds within the target context. */ + int32_t _left = clamp(fmin(fmin(ul_x, ll_x), fmin(ur_x, lr_x)), 0, ctx->width); + int32_t _top = clamp(fmin(fmin(ul_y, ll_y), fmin(ur_y, lr_y)), 0, ctx->height); + int32_t _right = clamp(fmax(fmax(ul_x+1, ll_x+1), fmax(ur_x+1, lr_x+1)), 0, ctx->width); + int32_t _bottom = clamp(fmax(fmax(ul_y+1, ll_y+1), fmax(ur_y+1, lr_y+1)), 0, ctx->height); + + blur_ctx->clips_size = ctx->clips_size; + blur_ctx->clips = ctx->clips; + blur_ctx->backbuffer = ctx->backbuffer; + gfx_context_t * f = init_graphics_subregion(blur_ctx, _left, _top, _right - _left, _bottom - _top); + flip(f); + f->backbuffer = f->buffer; + blur_context_box(f, 10); + free(f); + blur_ctx->backbuffer = blur_ctx->buffer; + blur_ctx->clips_size = 0; + blur_ctx->clips = NULL; + + sprite_t * scanline = create_sprite(_right - _left, 1, ALPHA_EMBEDDED); + sprite_t * blurline = create_sprite(_right - _left, 1, ALPHA_EMBEDDED); + uint8_t alp = alpha * 255; + + for (int32_t _y = _top; _y < _bottom; ++_y) { + if (!_is_in_clip(ctx, _y)) continue; + for (int32_t _x = _left; _x < _right; ++_x) { + double u, v; + apply_matrix(_x, _y, inverse, &u, &v); + SPRITE(scanline,_x - _left,0) = gfx_bilinear_interpolation(sprite, u, v); + SPRITE(blurline,_x - _left,0) = (_ALP(SPRITE(scanline,_x - _left,0)) > threshold) ? GFX(blur_ctx,_x,_y) : 0; + } + apply_alpha_vector(blurline->bitmap, blurline->width, alp); + apply_alpha_vector(scanline->bitmap, scanline->width, alp); + draw_sprite(ctx,blurline,_left,_y); + draw_sprite(ctx,scanline,_left,_y); + } + + sprite_free(scanline); + sprite_free(blurline); + +} + void draw_sprite_rotate(gfx_context_t * ctx, const sprite_t * sprite, int32_t x, int32_t y, float rotation, float alpha) { gfx_matrix_t m; gfx_matrix_identity(m);