compositor: blur-behind windows

This commit is contained in:
K. Lange 2022-07-22 12:19:09 +09:00
parent 54a1bcc5ff
commit 264d11a618
5 changed files with 148 additions and 5 deletions

View File

@ -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.");

View File

@ -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);

View File

@ -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) {

View File

@ -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
*

View File

@ -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);