Skip to content

Render drop shadow for active window #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 4, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions apps/multi.c
Original file line number Diff line number Diff line change
@@ -4,9 +4,11 @@
* All rights reserved.
*/

#include <stddef.h>
#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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it is worthy to have an application for stack blur. Any idea to keep it?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The apps_blur() function currently only resizes tux.png. This function isn't worthwhile to show by creating a standalone window feature. If it is still not worthy to have an application by adding another function, "blur," the apps_blur() function could be removed entirely.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The drop shadow feature's availability depends on configuration settings, so it should not expose any public functions.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The drop shadow functionality relies on the blur effect, but the blur effect is designed to be used independently as well. This is why in draw.c, the function twin_shadow_border() is conditionally built, while the function twin_stack_blur() is not.

}
22 changes: 22 additions & 0 deletions configs/Kconfig
Original file line number Diff line number Diff line change
@@ -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"
34 changes: 34 additions & 0 deletions include/twin.h
Original file line number Diff line number Diff line change
@@ -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
*/
55 changes: 54 additions & 1 deletion include/twin_private.h
Original file line number Diff line number Diff line change
@@ -181,13 +181,51 @@ 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) \
(((((s) << 3) & 0xf8) | (((s) >> 2) & 0x7)) | \
((((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
218 changes: 218 additions & 0 deletions src/draw-common.c
Original file line number Diff line number Diff line change
@@ -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;
}
}
2 changes: 1 addition & 1 deletion src/image.c
Original file line number Diff line number Diff line change
@@ -39,7 +39,7 @@
IIF(LOADER_HAS(GIF))( \
_(gif) \
) \
IIF(LOADER_HAS(TVG))( \
IIF(LOADER_HAS(TVG))( \
_(tvg) \
)
/* clang-format on */
3 changes: 3 additions & 0 deletions src/pixmap.c
Original file line number Diff line number Diff line change
@@ -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;
2 changes: 1 addition & 1 deletion src/screen.c
Original file line number Diff line number Diff line change
@@ -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;
98 changes: 92 additions & 6 deletions src/window.c
Original file line number Diff line number Diff line change
@@ -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 &&