Compare commits

...

1 Commits

Author SHA1 Message Date
K. Lange
b4dd0797c8 Demonstration of 'glass window' effect
This branch demonstrates a possible implementation of a
blurred window effect without making major changes to the
underlying rendering mechanism of the compositor.

By exploiting the graphics engine's use of scanline-based
clipping, we can reliably perform horizontal blurs on the
existing backbuffer when rendering each frame, before we
render the "blurry" window.

This approach is missing several things:
 - Ideally, another mask channel is needed to specify which
   parts of the window should be blurred, rather than trying
   to co-opt the window shape derived from the alpha channel.
 - The blur amount should vary based on the alpha transparency
   of the window object, especially during transitions. It is
   unclear how to do this well alongside transformations.
 - The horizontal-only blur is visually lacking, and ideally
   we would want a full box blur for the best effect, but
   no feasible approach to this with our rendering model has
   been deduced to this point.

If more research is to be done in this area, redesigning the
rendering approach may be the next step.

There are a few other approaches that could be used:
 - We could restrict the renderer to one (or two...) blurred
   windows - particularly, the focused window at the top of
   the main display stack. This would allow us to keep a
   secondary backbuffer with only the lower windows at each
   render stage, which could always be sourced for the blur.
   If we do this twice, once for the top main window and once
   for the panel level, we could produce solid effects, but
   it requires shoveling a lot of extra pixel data around.
 - We could try to cheat the clipping and redraw a bit extra
   around blurred windows, but this gets complicated...

Ultimately, a GPU-accelerated pixel shader implementation that
can operate on the full scene with each frame is likely the
only way that an acceptable blur effect can be achieved.
2021-07-28 17:53:31 +09:00
5 changed files with 167 additions and 4 deletions

View File

@ -420,7 +420,7 @@ static yutani_server_window_t * server_window_create(yutani_globals_t * yg, int
win->client_strings = NULL;
win->anim_mode = yutani_pick_animation(flags, 0);
win->anim_start = yutani_current_time(yg);
win->alpha_threshold = 0;
win->alpha_threshold = (flags & (1 << 5)) ? 10 : 0;
win->show_mouse = 1;
win->tiled = 0;
win->untiled_width = 0;
@ -705,6 +705,56 @@ static inline int matrix_is_translation(gfx_matrix_t m) {
return (m[0][0] == 1.0 && m[0][1] == 0.0 && m[1][0] == 0.0 && m[1][1] == 1.0);
}
static inline int clamp(int a, int l, int h) {
return a < l ? l : (a > h ? h : a);
}
static int _is_in_clip(gfx_context_t * ctx, int32_t y) {
if (!ctx->clips) return 1;
if (y < 0 || y >= ctx->clips_size) return 1;
return ctx->clips[y];
}
static void horizontal_selective_blur(yutani_globals_t * yg, yutani_server_window_t * win) {
int radius = 2;
gfx_context_t * _src = yg->backend_ctx;
int w = _src->width;
int h = _src->height;
if (w < radius) return;
uint32_t * out_color = calloc(sizeof(uint32_t), w);
for (int y = 0; y < h; y++) {
if (!_is_in_clip(_src, y)) continue;
unsigned short r = 0;
unsigned short g = 0;
unsigned short b = 0;
for (int i = -radius; i < radius; ++i) {
int p1 = clamp(i, 0, w - 1);
uint32_t col1 = GFX(_src, p1, y);
r += (int)_RED(col1);
g += (int)_GRE(col1);
b += (int)_BLU(col1);
}
for (int x = 0; x < w; x++) {
out_color[x] = rgb(r >> 2, g >> 2, b >> 2);
int p1 = clamp(x - radius, 0, w - 1);
int p2 = clamp(x + radius, 0, w - 1);
uint32_t col1 = GFX(_src, p1, y);
uint32_t col2 = GFX(_src, p2, y);
r += (int)_RED(col2) - (int)_RED(col1);
g += (int)_GRE(col2) - (int)_GRE(col1);
b += (int)_BLU(col2) - (int)_BLU(col1);
}
for (int x = 0; x < w; x++) {
if (check_top_at(yg,win,x,y)) {
GFX(_src,x,y) = out_color[x];
}
}
}
free(out_color);
}
/**
* Blit a window to the framebuffer.
*
@ -727,6 +777,11 @@ static int yutani_blit_window(yutani_globals_t * yg, yutani_server_window_t * wi
double opacity = (double)(window->opacity) / 255.0;
if (window->server_flags & (1 << 5)) {
/* Blur behind, but restricted to the valid region? */
horizontal_selective_blur(yg, window);
}
if (window->rotation || window == yg->resizing_window || window->anim_mode) {
double m[2][3];

View File

@ -1532,7 +1532,7 @@ int main (int argc, char ** argv) {
height = yctx->display_height;
/* Create the panel window */
panel = yutani_window_create_flags(yctx, width, PANEL_HEIGHT, YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS | YUTANI_WINDOW_FLAG_ALT_ANIMATION);
panel = yutani_window_create_flags(yctx, width, PANEL_HEIGHT, YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS | YUTANI_WINDOW_FLAG_ALT_ANIMATION | (1 << 5));
/* And move it to the top layer */
yutani_set_stack(yctx, panel, YUTANI_ZORDER_TOP);

View File

@ -2296,7 +2296,7 @@ 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, (1 << 5));
update_bounds();
}

108
apps/toastd.c Normal file
View File

@ -0,0 +1,108 @@
/**
* @brief Toast notification daemon.
* @file apps/toastd.c
*
* Provides an endpoint for applications to post notifications
* which are displayed in pop-up "toasts" in the upper-right
* corner of the screen without stealing focus.
*
* @copyright 2021 K. Lange
* This file is part of ToaruOS and is released under the terms
* of the NCSA / University of Illinois License - see LICENSE.md
*/
#include <stdio.h>
#include <unistd.h>
#include <sched.h>
#include <sys/fswait.h>
#include <toaru/pex.h>
#include <toaru/yutani.h>
#include <toaru/markup_text.h>
#include <toaru/graphics.h>
static yutani_t * yctx;
static FILE * pex_endpoint = NULL;
static sprite_t background_sprite;
#define PAD_RIGHT 10
#define PAD_TOP 48
int main(int argc, char * argv[]) {
/* Make sure we were actually expecting to be run... */
if (argc < 2 || strcmp(argv[1],"--really")) {
fprintf(stderr,
"%s: Toast notification daemon\n"
"\n"
" Displays popup notifications from other\n"
" applications in the corner of the screen.\n"
" You probably don't want to run this directly - it is\n"
" started automatically by the session manager.\n", argv[0]);
return 1;
}
/* Daemonize... */
if (!fork()) {
/* Connect to display server... */
yctx = yutani_init();
if (!yctx) {
fprintf(stderr, "%s: Failed to connect to compositor.\n", argv[0]);
}
/* Open pex endpoint to receive notifications... */
pex_endpoint = pex_bind("toast");
if (!pex_endpoint) {
fprintf(stderr, "%s: Failed to establish socket.\n", argv[0]);
}
/* Set up our text rendering and sprite contexts... */
markup_text_init();
load_sprite(&background_sprite, "/usr/share/ttk/toast/default.png");
/* Make a test window? */
yutani_window_t * wina = yutani_window_create_flags(yctx, background_sprite.width, background_sprite.height, YUTANI_WINDOW_FLAG_ALT_ANIMATION);
yutani_window_move(yctx, wina, yctx->display_width - background_sprite.width - PAD_RIGHT, PAD_TOP); /* We need to be able to query the panel location... */
gfx_context_t * ctx = init_graphics_yutani_double_buffer(wina);
draw_fill(ctx, rgba(0,0,0,0));
draw_sprite(ctx, &background_sprite, 0, 0);
/* Wait for messages from pex, or from compositor... */
markup_draw_string(ctx,10,26,"<b><h1>Welcome!</h1></b><br>This is a sample <i>toast</i> notification.",rgb(255,255,255));
flip(ctx);
yutani_flip(yctx, wina);
int should_exit = 0;
while (!should_exit) {
int fds[1] = {fileno(yctx->sock)};
int index = fswait2(1,fds,20);
if (index == 0) {
yutani_msg_t * m = yutani_poll(yctx);
while (m) {
switch (m->type) {
case YUTANI_MSG_KEY_EVENT:
{
struct yutani_msg_key_event * ke = (void*)m->data;
if (ke->event.action == KEY_ACTION_DOWN && ke->event.keycode == 'q') {
should_exit = 1;
sched_yield();
}
}
break;
case YUTANI_MSG_WINDOW_MOUSE_EVENT:
{
struct yutani_msg_window_mouse_event * me = (void*)m->data;
if (me->command == YUTANI_MOUSE_EVENT_DOWN && me->buttons & YUTANI_MOUSE_BUTTON_LEFT) {
yutani_window_drag_start(yctx, wina);
}
}
break;
case YUTANI_MSG_WINDOW_CLOSE:
case YUTANI_MSG_SESSION_END:
should_exit = 1;
break;
default:
break;
}
free(m);
m = yutani_poll_async(yctx);
}
}
}
yutani_close(yctx, wina);
}
return 0;
}

View File

@ -320,7 +320,7 @@ int main(int argc, char * argv[]) {
background = yutani_window_create_flags(yctx, yctx->display_width, yctx->display_height,
YUTANI_WINDOW_FLAG_DISALLOW_RESIZE | YUTANI_WINDOW_FLAG_DISALLOW_DRAG |
YUTANI_WINDOW_FLAG_ALT_ANIMATION | YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS);
YUTANI_WINDOW_FLAG_ALT_ANIMATION | YUTANI_WINDOW_FLAG_NO_STEAL_FOCUS | (1 << 5));
yutani_window_move(yctx, background, 0, 0);
yutani_window_update_shape(yctx, background, 2);
background_ctx = init_graphics_yutani_double_buffer(background);