diff --git a/apps/multi.c b/apps/multi.c index c33fd30..3b60493 100644 --- a/apps/multi.c +++ b/apps/multi.c @@ -4,9 +4,11 @@ * All rights reserved. */ +#include #include "apps_multi.h" #define D(x) twin_double_to_fixed(x) +#define ASSET_PATH "assets/" static void apps_line_start(twin_screen_t *screen, int x, int y, int w, int h) { @@ -272,6 +274,55 @@ static void apps_flower_start(twin_screen_t *screen, int x, int y, int w, int h) twin_window_show(window); } +static void apps_blur(twin_screen_t *screen, int x, int y, int w, int h) +{ + twin_pixmap_t *raw_background = NULL; +#if defined(CONFIG_LOADER_PNG) + raw_background = twin_pixmap_from_file(ASSET_PATH "tux.png", TWIN_ARGB32); +#endif + if (!raw_background) + return; + twin_window_t *window = twin_window_create( + screen, TWIN_ARGB32, TwinWindowApplication, x, y, w, h); + twin_window_set_name(window, "Blur"); + twin_pixmap_t *scaled_background = twin_pixmap_create( + TWIN_ARGB32, window->pixmap->width, window->pixmap->height); + twin_fixed_t sx, sy; + sx = twin_fixed_div( + twin_int_to_fixed(raw_background->width), + twin_int_to_fixed(window->client.right - window->client.left)); + sy = twin_fixed_div( + twin_int_to_fixed(raw_background->height), + twin_int_to_fixed(window->client.bottom - window->client.top)); + + twin_matrix_scale(&raw_background->transform, sx, sy); + twin_operand_t srcop = { + .source_kind = TWIN_PIXMAP, + .u.pixmap = raw_background, + }; + + twin_composite(scaled_background, 0, 0, &srcop, 0, 0, 0, 0, 0, TWIN_SOURCE, + scaled_background->width, scaled_background->height); + + twin_pointer_t src, dst; + for (int y = window->client.top; y < window->client.bottom; y++) + for (int x = window->client.left; x < window->client.right; x++) { + src = + twin_pixmap_pointer(scaled_background, x - window->client.left, + y - window->client.top); + dst = twin_pixmap_pointer(window->pixmap, x, y); + *dst.argb32 = *src.argb32 | 0xff000000; + } + twin_stack_blur(window->pixmap, 5, window->client.left, + window->client.right, window->client.top, + window->client.bottom); + + twin_pixmap_destroy(scaled_background); + twin_pixmap_destroy(raw_background); + twin_window_show(window); + return; +} + void apps_multi_start(twin_screen_t *screen, const char *name, int x, @@ -286,4 +337,5 @@ void apps_multi_start(twin_screen_t *screen, apps_ascii_start(screen, x += 20, y += 20, w, h); apps_jelly_start(screen, x += 20, y += 20, w / 2, h); apps_flower_start(screen, x += 20, y += 20, w, h); + apps_blur(screen, x += 20, y += 20, w / 2, h / 2); } diff --git a/configs/Kconfig b/configs/Kconfig index 7e281f1..516d650 100644 --- a/configs/Kconfig +++ b/configs/Kconfig @@ -55,6 +55,28 @@ config CURSOR default n depends on !BACKEND_VNC +config DROP_SHADOW + bool "Render drop shadow for active window" + default y + +config HORIZONTAL_OFFSET + int "Horizontal offset" + default 1 + range 1 10 + depends on DROP_SHADOW + +config VERTICAL_OFFSET + int "Vertical offset" + default 1 + range 1 10 + depends on DROP_SHADOW + +config SHADOW_BLUR + int "Shadow blur radius" + default 10 + range 1 10 + depends on DROP_SHADOW + endmenu menu "Image Loaders" diff --git a/include/twin.h b/include/twin.h index 28a1d17..07c2011 100644 --- a/include/twin.h +++ b/include/twin.h @@ -195,6 +195,15 @@ typedef struct _twin_pixmap { * Pixels */ twin_animation_t *animation; + +#if defined(CONFIG_DROP_SHADOW) + /* + * When the pixel map is within the active window, it will have a drop + * shadow to enhance its visual distinction. + */ + bool shadow; +#endif + twin_pointer_t p; /* * When representing a window, this point @@ -423,6 +432,13 @@ typedef void (*twin_destroy_func_t)(twin_window_t *window); struct _twin_window { twin_screen_t *screen; twin_pixmap_t *pixmap; + +#if defined(CONFIG_DROP_SHADOW) + /* Set the shadow range for horizontal and vertical directions. */ + twin_coord_t shadow_x; + twin_coord_t shadow_y; +#endif + twin_window_style_t style; twin_rect_t client; twin_rect_t damage; @@ -652,8 +668,26 @@ void twin_fill(twin_pixmap_t *dst, * draw-common.c */ +/* Blur the specified area in the pixel map. */ +void twin_stack_blur(twin_pixmap_t *px, + int radius, + twin_coord_t left, + twin_coord_t right, + twin_coord_t top, + twin_coord_t bottom); + void twin_premultiply_alpha(twin_pixmap_t *px); +/* + * Overwrite the original pixel values for a specified number of pixels in + * width. + */ +void twin_cover(twin_pixmap_t *dst, + twin_argb32_t color, + twin_coord_t x, + twin_coord_t y, + twin_coord_t width); + /* * event.c */ diff --git a/include/twin_private.h b/include/twin_private.h index e4ee7c2..21bb825 100644 --- a/include/twin_private.h +++ b/include/twin_private.h @@ -181,6 +181,15 @@ typedef int64_t twin_xfixed_t; (((t) = twin_get_8(d, i) + twin_get_8(s, i)), (twin_argb32_t) twin_sat(t) \ << (i)) +#define _twin_add_ARGB(s, d, i, t) (((t) = (s) + twin_get_8(d, i))) +#define _twin_add(s, d, t) (((t) = (s) + (d))) +#define _twin_div(d, den, i, t) \ + (((t) = (d) / (den)), (t) = twin_get_8((t), 0), \ + (twin_argb32_t) twin_sat(t) << (i)) +#define _twin_sub_ARGB(s, d, i, t) (((t) = (s) - twin_get_8(d, i))) +#define _twin_sub(s, d, t) (((t) = (s) - (d))) +#define twin_put_8(d, i, t) (((t) = (d) << (i))) + #define twin_argb32_to_rgb16(s) \ ((((s) >> 3) & 0x001f) | (((s) >> 5) & 0x07e0) | (((s) >> 8) & 0xf800)) #define twin_rgb16_to_argb32(s) \ @@ -188,6 +197,35 @@ typedef int64_t twin_xfixed_t; ((((s) << 5) & 0xfc00) | (((s) >> 1) & 0x300)) | \ ((((s) << 8) & 0xf80000) | (((s) << 3) & 0x70000)) | 0xff000000) +#ifndef min +#if defined(__GNUC__) || defined(__clang__) +#define min(x, y) \ + ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; \ + }) +#else +/* Generic implementation: potential side effects */ +#define min(x, y) ((x) < (y) ? (x) : (y)) +#endif +#endif +#ifndef max +#if defined(__GNUC__) || defined(__clang__) +#define max(x, y) \ + ({ \ + typeof(x) _x = (x); \ + typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x > _y ? _x : _y; \ + }) +#else +/* Generic implementation: potential side effects */ +#define max(x, y) ((x) > (y) ? (x) : (y)) +#endif +#endif + typedef union { twin_pointer_t p; twin_argb32_t c; @@ -468,7 +506,7 @@ void _twin_path_sfinish(twin_path_t *path); #define twin_glyph_snap_y(g) (twin_glyph_snap_x(g) + twin_glyph_n_snap_x(g)) /* - * dispatch stuff + * Dispatch stuff */ typedef struct _twin_queue { struct _twin_queue *next; @@ -593,6 +631,21 @@ void _twin_button_init(twin_button_t *button, twin_style_t font_style, twin_dispatch_proc_t dispatch); +/* + * Visual effect stuff + */ + +#if defined(CONFIG_DROP_SHADOW) +/* + * Add a shadow with the specified color, horizontal offset, and vertical + * offset. + */ +void twin_shadow_border(twin_pixmap_t *shadow, + twin_argb32_t color, + twin_coord_t shift_x, + twin_coord_t shift_y); +#endif + /* utility */ #ifdef _MSC_VER diff --git a/src/draw-common.c b/src/draw-common.c index 005bd1f..4c84e1d 100644 --- a/src/draw-common.c +++ b/src/draw-common.c @@ -7,6 +7,209 @@ #include "twin_private.h" +#define TWIN_TITLE_HEIGHT 20 + +static void _twin_apply_stack_blur(twin_pixmap_t *trg_px, + twin_pixmap_t *src_px, + int radius, + int first_str, + int first_end, + int second_str, + int second_end, + bool horiz_scan) +{ + int den = (radius + 1) * (radius + 1); + twin_pointer_t src_ptr, trg_ptr, old_ptr, new_ptr; + twin_argb32_t sumInR, sumOutR, sumR, sumInG, sumOutG, sumG, sumInB, sumOutB, + sumB, sumInA, sumOutA, sumA, _cur, _old, _new, _src; + uint16_t tmpR, tmpG, tmpB, tmpA; + for (int first = first_str; first < first_end; first++) { + sumInR = sumOutR = sumR = sumInG = sumOutG = sumG = sumInB = sumOutB = + sumB = sumInA = sumOutA = sumA = 0x00000000; + + /* Initialize sumOut by padding. */ + if (horiz_scan) + src_ptr = twin_pixmap_pointer(src_px, second_str, first); + else + src_ptr = twin_pixmap_pointer(src_px, first, second_str); + _src = *src_ptr.argb32; + + for (int i = second_str; i < second_str + radius; i++) { + sumOutR = _twin_add_ARGB(sumOutR, _src, 0, tmpR); + sumOutG = _twin_add_ARGB(sumOutG, _src, 8, tmpG); + sumOutB = _twin_add_ARGB(sumOutB, _src, 16, tmpB); + sumOutA = _twin_add_ARGB(sumOutA, _src, 24, tmpA); + for (int j = 0; j < (i - second_str) + 1; j++) { + sumR = _twin_add_ARGB(sumR, _src, 0, tmpR); + sumG = _twin_add_ARGB(sumG, _src, 8, tmpG); + sumB = _twin_add_ARGB(sumB, _src, 16, tmpB); + sumA = _twin_add_ARGB(sumA, _src, 24, tmpA); + } + } + + /* Initialize sumIn. */ + for (int i = second_str; i < second_str + radius; i++) { + if (horiz_scan) + src_ptr = twin_pixmap_pointer(src_px, i, first); + else + src_ptr = twin_pixmap_pointer(src_px, first, i); + _src = *src_ptr.argb32; + sumInR = _twin_add_ARGB(sumInR, _src, 0, tmpR); + sumInG = _twin_add_ARGB(sumInG, _src, 8, tmpG); + sumInB = _twin_add_ARGB(sumInB, _src, 16, tmpB); + sumInA = _twin_add_ARGB(sumInA, _src, 24, tmpA); + for (int j = 0; j < radius - (i - second_str); j++) { + sumR = _twin_add_ARGB(sumR, _src, 0, tmpR); + sumG = _twin_add_ARGB(sumG, _src, 8, tmpG); + sumB = _twin_add_ARGB(sumB, _src, 16, tmpB); + sumA = _twin_add_ARGB(sumA, _src, 24, tmpA); + } + } + + for (int cur = second_str; cur < second_end; cur++) { + if (horiz_scan) { + src_ptr = twin_pixmap_pointer(src_px, cur, first); + trg_ptr = twin_pixmap_pointer(trg_px, cur, first); + old_ptr = twin_pixmap_pointer( + src_px, max(cur - radius, second_str), first); + new_ptr = twin_pixmap_pointer( + src_px, min(cur + radius, second_end - 1), first); + } else { + src_ptr = twin_pixmap_pointer(src_px, first, cur); + trg_ptr = twin_pixmap_pointer(trg_px, first, cur); + old_ptr = twin_pixmap_pointer(src_px, first, + max(cur - radius, second_str)); + new_ptr = twin_pixmap_pointer( + src_px, first, min(cur + radius, second_end - 1)); + } + _cur = *src_ptr.argb32; + _old = *old_ptr.argb32; + _new = *new_ptr.argb32; + /* STEP 1 : sumOut + current */ + sumOutR = _twin_add_ARGB(sumOutR, _cur, 0, tmpR); + sumOutG = _twin_add_ARGB(sumOutG, _cur, 8, tmpG); + sumOutB = _twin_add_ARGB(sumOutB, _cur, 16, tmpB); + sumOutA = _twin_add_ARGB(sumOutA, _cur, 24, tmpA); + /* STEP 2 : sumIn + new */ + sumInR = _twin_add_ARGB(sumInR, _new, 0, tmpR); + sumInG = _twin_add_ARGB(sumInG, _new, 8, tmpG); + sumInB = _twin_add_ARGB(sumInB, _new, 16, tmpB); + sumInA = _twin_add_ARGB(sumInA, _new, 24, tmpA); + /* STEP 3 : sum + sumIn */ + sumR = _twin_add(sumR, sumInR, tmpR); + sumG = _twin_add(sumG, sumInG, tmpG); + sumB = _twin_add(sumB, sumInB, tmpB); + sumA = _twin_add(sumA, sumInA, tmpA); + /* STEP 4 : sum / denominator */ + *trg_ptr.argb32 = + (_twin_div(sumR, den, 0, tmpR) | _twin_div(sumG, den, 8, tmpG) | + _twin_div(sumB, den, 16, tmpB) | + _twin_div(sumA, den, 24, tmpA)); + /* STEP 5 : sum - sumOut */ + sumR = _twin_sub(sumR, sumOutR, tmpR); + sumG = _twin_sub(sumG, sumOutG, tmpG); + sumB = _twin_sub(sumB, sumOutB, tmpB); + sumA = _twin_sub(sumA, sumOutA, tmpA); + /* STEP 6 : sumOut - old */ + sumOutR = _twin_sub_ARGB(sumOutR, _old, 0, tmpR); + sumOutG = _twin_sub_ARGB(sumOutG, _old, 8, tmpG); + sumOutB = _twin_sub_ARGB(sumOutB, _old, 16, tmpB); + sumOutA = _twin_sub_ARGB(sumOutA, _old, 24, tmpA); + /* STEP 7 : sumIn - current */ + sumInR = _twin_sub_ARGB(sumInR, _cur, 0, tmpR); + sumInG = _twin_sub_ARGB(sumInG, _cur, 8, tmpG); + sumInB = _twin_sub_ARGB(sumInB, _cur, 16, tmpB); + sumInA = _twin_sub_ARGB(sumInA, _cur, 24, tmpA); + } + } +} + +void twin_stack_blur(twin_pixmap_t *px, + int radius, + twin_coord_t left, + twin_coord_t right, + twin_coord_t top, + twin_coord_t bottom) +{ + if (px->format != TWIN_ARGB32) + return; + twin_pixmap_t *tmp_px = + twin_pixmap_create(px->format, px->width, px->height); + memcpy(tmp_px->p.v, px->p.v, + px->width * px->height * twin_bytes_per_pixel(px->format)); + /* + * Originally, performing a 2D convolution on each pixel takes O(width * + * height * k²). However, by first scanning horizontally and then vertically + * across the pixel map, and applying a 1D convolution to each pixel, the + * complexity is reduced to O(2 * width * height * k). + */ + /* Horizontally scan. */ + _twin_apply_stack_blur(tmp_px, px, radius, top, bottom, left, right, true); + /* Vertically scan. */ + _twin_apply_stack_blur(px, tmp_px, radius, left, right, top, bottom, false); + twin_pixmap_destroy(tmp_px); + return; +} + +#if defined(CONFIG_DROP_SHADOW) +void twin_shadow_border(twin_pixmap_t *shadow, + twin_argb32_t color, + twin_coord_t offset_x, + twin_coord_t offset_y) +{ + twin_coord_t right_edge = (*shadow).width - (*shadow).window->shadow_x, + bottom_edge = (*shadow).height - (*shadow).window->shadow_y, + right_span = right_edge, clip, corner_offset, y_start, + offset = min(offset_x, offset_y); + + switch (shadow->window->style) { + case TwinWindowApplication: + y_start = TWIN_TITLE_HEIGHT; + break; + case TwinWindowPlain: + default: + y_start = 0; + break; + } + + for (twin_coord_t y = y_start; y < (*shadow).height; y++) { + /* Render the right edge of the shadow border. */ + if (y < bottom_edge) { + /* + * Create a shadow with a diagonal shape, extending from the + * top-left to the bottom-right. + */ + clip = min((y - y_start), offset_x); + twin_cover(shadow, color, right_edge, y, clip); + } else { + /* Calculate the range of the corner. */ + right_span++; + } + /* Render the bottom edge of the shadow border. */ + if (y >= bottom_edge && y < bottom_edge + offset_y) { + /* + * Create a shadow with a diagonal shape, extending from the + * top-left to the bottom-right. + */ + clip = max(0, y - bottom_edge); + twin_cover(shadow, color, clip, y, right_edge - clip); + /* Render the bottom-right corner of the shadow border. */ + /* + * Handle the case where the vertical shadow offset is larger than + * the horizontal shadow offset. + */ + corner_offset = min(right_span - right_edge, offset); + for (twin_coord_t i = 0; i < corner_offset; i++) { + /* The corner's pixels are symmetrical to the diagonal. */ + twin_cover(shadow, color, right_edge + i, y, 1); + twin_cover(shadow, color, right_edge + (y - bottom_edge), + bottom_edge + i, 1); + } + } + } +} +#endif + static twin_argb32_t _twin_apply_alpha(twin_argb32_t v) { uint16_t t1, t2, t3; @@ -46,3 +249,18 @@ void twin_premultiply_alpha(twin_pixmap_t *px) p.argb32[x] = _twin_apply_alpha(p.argb32[x]); } } + +void twin_cover(twin_pixmap_t *dst, + twin_argb32_t color, + twin_coord_t x, + twin_coord_t y, + twin_coord_t width) +{ + if (x < 0 || y < 0 || width < 0 || x + width > dst->width || + y >= dst->height) + return; + for (twin_coord_t i = 0; i < width; i++) { + twin_pointer_t pt = twin_pixmap_pointer(dst, x + i, y); + *pt.argb32 = color; + } +} diff --git a/src/image.c b/src/image.c index 21829e0..017be4e 100644 --- a/src/image.c +++ b/src/image.c @@ -39,7 +39,7 @@ IIF(LOADER_HAS(GIF))( \ _(gif) \ ) \ - IIF(LOADER_HAS(TVG))( \ + IIF(LOADER_HAS(TVG))( \ _(tvg) \ ) /* clang-format on */ diff --git a/src/pixmap.c b/src/pixmap.c index 1dca92a..a1b0d45 100644 --- a/src/pixmap.c +++ b/src/pixmap.c @@ -45,6 +45,9 @@ twin_pixmap_t *twin_pixmap_create(twin_format_t format, pixmap->stride = stride; pixmap->disable = 0; pixmap->animation = NULL; +#if defined(CONFIG_DROP_SHADOW) + pixmap->shadow = false; +#endif pixmap->p.v = pixmap + 1; memset(pixmap->p.v, '\0', space); return pixmap; diff --git a/src/screen.c b/src/screen.c index dab844b..f8a816e 100644 --- a/src/screen.c +++ b/src/screen.c @@ -135,7 +135,7 @@ static void twin_screen_span_pixmap(twin_screen_t maybe_unused *screen, return; if (p->y + p->height <= y) return; - /* bounds check in x*/ + /* bounds check in x */ p_left = left; if (p_left < p->x) p_left = p->x; diff --git a/src/window.c b/src/window.c index 794107a..3222975 100644 --- a/src/window.c +++ b/src/window.c @@ -18,6 +18,7 @@ #define TWIN_TITLE_HEIGHT 20 #define TWIN_RESIZE_SIZE ((TWIN_TITLE_HEIGHT + 4) / 5) #define TWIN_TITLE_BW ((TWIN_TITLE_HEIGHT + 11) / 12) +#define SHADOW_COLOR 0xff000000 twin_window_t *twin_window_create(twin_screen_t *screen, twin_format_t format, @@ -39,8 +40,8 @@ twin_window_t *twin_window_create(twin_screen_t *screen, case TwinWindowApplication: left = TWIN_BW; top = TWIN_BW + TWIN_TITLE_HEIGHT + TWIN_BW; - right = TWIN_BW + TWIN_RESIZE_SIZE; - bottom = TWIN_BW + TWIN_RESIZE_SIZE; + right = TWIN_BW; + bottom = TWIN_BW; break; case TwinWindowPlain: default: @@ -56,7 +57,22 @@ twin_window_t *twin_window_create(twin_screen_t *screen, window->client.top = top; window->client.right = width - right; window->client.bottom = height - bottom; +#if defined(CONFIG_DROP_SHADOW) + /* Handle drop shadow. */ + /* + * Add a shadowed area to the pixel map of the window to create a drop + * shadow effect. This calculation method is based on the range of + * CONFIG_HORIZONTAL_OFFSET, CONFIG_VERTICAL_OFFSET, and CONFIG_SHADOW_BLUR. + */ + window->shadow_x = 2 * CONFIG_HORIZONTAL_OFFSET + CONFIG_SHADOW_BLUR; + window->shadow_y = 2 * CONFIG_VERTICAL_OFFSET + CONFIG_SHADOW_BLUR; + window->pixmap = twin_pixmap_create(format, width + window->shadow_x, + height + window->shadow_y); +#else window->pixmap = twin_pixmap_create(format, width, height); +#endif + if (!window->pixmap) + return NULL; twin_pixmap_clip(window->pixmap, window->client.left, window->client.top, window->client.right, window->client.bottom); twin_pixmap_origin_to_clip(window->pixmap); @@ -137,20 +153,30 @@ bool twin_window_valid_range(twin_window_t *window, twin_coord_t x, twin_coord_t y) { + twin_coord_t offset_x = 0, offset_y = 0; +#if defined(CONFIG_DROP_SHADOW) + /* Handle drop shadow. */ + /* + * When the click coordinates fall within the drop shadow area, it will not + * be in the valid range of the window. + */ + offset_x = window->shadow_x, offset_y = window->shadow_y; +#endif + switch (window->style) { case TwinWindowPlain: default: if (window->pixmap->x <= x && - x < window->pixmap->x + window->pixmap->width && + x < window->pixmap->x + window->pixmap->width - offset_x && window->pixmap->y <= y && - y < window->pixmap->y + window->pixmap->height) + y < window->pixmap->y + window->pixmap->height - offset_y) return true; return false; case TwinWindowApplication: if (window->pixmap->x <= x && - x < window->pixmap->x + window->pixmap->width && + x < window->pixmap->x + window->pixmap->width - offset_x && window->pixmap->y <= y && - y < window->pixmap->y + window->pixmap->height) { + y < window->pixmap->y + window->pixmap->height - offset_y) { if (y < window->pixmap->y + (window->client.top)) return !twin_pixmap_transparent(window->pixmap, x, y); return true; @@ -316,6 +342,62 @@ static void twin_window_frame(twin_window_t *window) twin_path_destroy(path); } +#if defined(CONFIG_DROP_SHADOW) +static void twin_window_drop_shadow(twin_window_t *window) +{ + twin_pixmap_t *prev_active_pix = window->screen->top, + *active_pix = window->pixmap; + twin_source_u src; + twin_coord_t y, ori_wid, ori_hei; + + /* Remove the drop shadow from the previously active pixel map. */ + if (prev_active_pix) { + src.c = 0x00000000; + for (y = 0; y < prev_active_pix->height; y++) { + if (y < prev_active_pix->height - prev_active_pix->window->shadow_y) + twin_cover( + prev_active_pix, src.c, + prev_active_pix->width - prev_active_pix->window->shadow_x, + y, prev_active_pix->window->shadow_x); + else + twin_cover(prev_active_pix, src.c, 0, y, + prev_active_pix->width); + } + prev_active_pix->shadow = false; + } + + /* Mark the previously active pixel map as damaged to update its changes. */ + if (prev_active_pix && active_pix != prev_active_pix) + twin_pixmap_damage(prev_active_pix, 0, 0, prev_active_pix->width, + prev_active_pix->height); + + /* + * The shadow effect of the window only becomes visible when the window is + * active. + */ + active_pix->shadow = true; + ori_wid = active_pix->width - active_pix->window->shadow_x; + ori_hei = active_pix->height - active_pix->window->shadow_y; + /* + * Create a darker border of the active window that gives a more + * dimensional appearance. + */ + /* The shift offset and color of the shadow can be selected by the user. */ + twin_shadow_border(active_pix, SHADOW_COLOR, CONFIG_VERTICAL_OFFSET, + CONFIG_HORIZONTAL_OFFSET); + + /* Add a blur effect to the shadow of the active window. */ + /* Right side of the active window */ + twin_stack_blur(active_pix, CONFIG_SHADOW_BLUR, ori_wid, + ori_wid + active_pix->window->shadow_x, 0, + ori_hei + active_pix->window->shadow_y); + /* Bottom side of the active window */ + twin_stack_blur(active_pix, CONFIG_SHADOW_BLUR, 0, + ori_wid + active_pix->window->shadow_x, ori_hei, + ori_hei + active_pix->window->shadow_y); +} +#endif + void twin_window_draw(twin_window_t *window) { twin_pixmap_t *pixmap = window->pixmap; @@ -428,6 +510,10 @@ bool twin_window_dispatch(twin_window_t *window, twin_event_t *event) twin_window_frame(window->screen->top->window); } } +#if defined(CONFIG_DROP_SHADOW) + /* Handle drop shadow. */ + twin_window_drop_shadow(window); +#endif if (window->client.left <= ev.u.pointer.x && ev.u.pointer.x < window->client.right && window->client.top <= ev.u.pointer.y &&