From ac74064d1c6e88699a6a499b6bafd0e5c3723346 Mon Sep 17 00:00:00 2001 From: lkn Date: Tue, 23 Jun 2026 20:30:52 +0200 Subject: [PATCH] kaboom: match original X11 physics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use fixed-point x positions, strong upward initial velocity (-15 to -21), gravity of 1 pixel/frame², only ~50%% of tiles, and run until all pieces leave the screen. Full-screen scene buffer so pieces can fly freely. --- src/backend/wayland/wl_kaboom.c | 134 ++++++++++++++++++++------------ 1 file changed, 83 insertions(+), 51 deletions(-) diff --git a/src/backend/wayland/wl_kaboom.c b/src/backend/wayland/wl_kaboom.c index ba5f0ab..ba2e3ad 100644 --- a/src/backend/wayland/wl_kaboom.c +++ b/src/backend/wayland/wl_kaboom.c @@ -1,6 +1,7 @@ #ifdef HAVE_CONFIG_H #include "config.h" #endif +#include #include #include #include @@ -16,14 +17,16 @@ #ifdef NORMAL_ICON_KABOOM #define PIECE_SIZE ICON_KABOOM_PIECE_SIZE -#define ANIM_FRAMES 12 -#define ANIM_INTERVAL_MS 30 -#define GRAVITY 2 +#define KAB_PRECISION 4 +#define ANIM_INTERVAL_MS 20 struct kaboom_piece { - int x, y; - int vx, vy; - uint32_t pixels[PIECE_SIZE * PIECE_SIZE]; + int px; /* x in fixed-point (<tree->node); if (state->image) pixman_image_unref(state->image); + if (state->src_image) + pixman_image_unref(state->src_image); wlr_buffer_drop(&state->base); free(state->pieces); free(state); @@ -82,19 +90,27 @@ static void kaboom_render(struct kaboom_state *state) { /* Clear to transparent */ uint32_t *dst = pixman_image_get_data(state->image); - int stride = pixman_image_get_stride(state->image) / 4; - memset(dst, 0, stride * 4 * state->height); + int dst_stride = pixman_image_get_stride(state->image) / 4; + int buf_w = state->scr_w; + int buf_h = state->scr_h; + memset(dst, 0, (size_t)dst_stride * 4 * buf_h); + + /* Draw each alive piece at its screen-relative position */ + uint32_t *src = pixman_image_get_data(state->src_image); + int src_stride = pixman_image_get_stride(state->src_image) / 4; - /* Draw each piece at its current position */ for (int i = 0; i < state->npieces; i++) { struct kaboom_piece *p = &state->pieces[i]; - for (int py = 0; py < PIECE_SIZE; py++) { - int dy = p->y + py; - if (dy < 0 || dy >= state->height) continue; - for (int px = 0; px < PIECE_SIZE; px++) { - int dx = p->x + px; - if (dx < 0 || dx >= state->width) continue; - dst[dy * stride + dx] = p->pixels[py * PIECE_SIZE + px]; + if (!p->alive) continue; + int dx = (p->px >> KAB_PRECISION) - state->origin_x; + int dy = p->py - state->origin_y; + for (int row = 0; row < PIECE_SIZE; row++) { + int yd = dy + row; + if (yd < 0 || yd >= buf_h) continue; + for (int col = 0; col < PIECE_SIZE; col++) { + int xd = dx + col; + if (xd < 0 || xd >= buf_w) continue; + dst[yd * dst_stride + xd] = src[(p->src_y + row) * src_stride + p->src_x + col]; } } } @@ -103,19 +119,24 @@ static void kaboom_render(struct kaboom_state *state) static int kaboom_timer_cb(void *data) { struct kaboom_state *state = data; - state->frame++; - if (state->frame >= ANIM_FRAMES) { + if (state->alive_count <= 0) { kaboom_destroy(state); return 0; } - /* Update piece positions */ + /* Update piece positions (matches original X11 physics) */ for (int i = 0; i < state->npieces; i++) { struct kaboom_piece *p = &state->pieces[i]; - p->vy += GRAVITY; - p->x += p->vx; - p->y += p->vy; + if (!p->alive) continue; + p->px += p->vx; + p->py += p->vy; + p->vy++; /* gravity: 1 pixel/frame² */ + int sx = p->px >> KAB_PRECISION; + if (sx < -state->width || sx > state->scr_w || p->py >= state->scr_h) { + p->alive = false; + state->alive_count--; + } } kaboom_render(state); @@ -126,7 +147,6 @@ static int kaboom_timer_cb(void *data) void wl_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y) { - (void)scr; struct wl_frame_buf *fb = frame_buf_find(win); if (!fb || !fb->image || !wl_state.scene) return; @@ -135,58 +155,70 @@ void wl_do_kaboom(WScreen *scr, WNativeWindow win, int x, int y) int h = fb->height; int cols = w / PIECE_SIZE; int rows = h / PIECE_SIZE; - int npieces = cols * rows; - if (npieces <= 0) + if (cols <= 0 || rows <= 0) return; + /* Allocate pieces (only ~50% used, matching original) */ + int max_pieces = cols * rows; struct kaboom_state *state = calloc(1, sizeof(*state)); if (!state) return; - state->pieces = calloc(npieces, sizeof(struct kaboom_piece)); + state->pieces = calloc(max_pieces, sizeof(struct kaboom_piece)); if (!state->pieces) { free(state); return; } - state->npieces = npieces; state->width = w; state->height = h; - state->frame = 0; + state->scr_w = scr->scr_width; + state->scr_h = scr->scr_height; + state->origin_x = 0; + state->origin_y = 0; - /* Copy pixel data from icon into pieces */ + /* Copy source icon pixels */ + state->src_image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); + if (!state->src_image) { free(state->pieces); free(state); return; } uint32_t *src = pixman_image_get_data(fb->image); + uint32_t *sdst = pixman_image_get_data(state->src_image); int src_stride = pixman_image_get_stride(fb->image) / 4; - for (int r = 0; r < rows; r++) { - for (int c = 0; c < cols; c++) { - struct kaboom_piece *p = &state->pieces[r * cols + c]; - p->x = c * PIECE_SIZE; - p->y = r * PIECE_SIZE; - /* Random velocity: horizontal spread, slight upward */ - p->vx = (rand() % 7) - 3; - p->vy = -(rand() % 4) - 1; - for (int py = 0; py < PIECE_SIZE; py++) { - for (int px = 0; px < PIECE_SIZE; px++) { - int sx = c * PIECE_SIZE + px; - int sy = r * PIECE_SIZE + py; - if (sx < w && sy < h) - p->pixels[py * PIECE_SIZE + px] = src[sy * src_stride + sx]; - } + int sdst_stride = pixman_image_get_stride(state->src_image) / 4; + for (int row = 0; row < h; row++) + memcpy(sdst + row * sdst_stride, src + row * src_stride, w * 4); + + /* Initialize pieces with original kaboom physics */ + int k = 0; + for (int i = 0; i < cols && k < max_pieces; i++) { + for (int j = 0; j < rows && k < max_pieces; j++) { + if (rand() % 2) { + struct kaboom_piece *p = &state->pieces[k]; + p->src_x = i * PIECE_SIZE; + p->src_y = j * PIECE_SIZE; + p->px = (x + i * PIECE_SIZE) << KAB_PRECISION; + p->py = y + j * PIECE_SIZE; + p->vx = rand() % (1 << (KAB_PRECISION + 3)) - (1 << (KAB_PRECISION + 3)) / 2; + p->vy = -15 - rand() % 7; + p->alive = true; + k++; } } } + state->npieces = k; + state->alive_count = k; - /* Create scene buffer for animation */ - state->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, w, h, NULL, 0); - if (!state->image) { free(state->pieces); free(state); return; } + /* Scene buffer covers full screen so pieces can fly anywhere */ + state->image = pixman_image_create_bits(PIXMAN_a8r8g8b8, + state->scr_w, state->scr_h, NULL, 0); + if (!state->image) { kaboom_destroy(state); return; } - wlr_buffer_init(&state->base, &kaboom_buf_impl, w, h); + wlr_buffer_init(&state->base, &kaboom_buf_impl, state->scr_w, state->scr_h); struct wlr_scene_tree *parent = wl_state.level_trees[WL_LAYER_OVERLAY] ? wl_state.level_trees[WL_LAYER_OVERLAY] : &wl_state.scene->tree; state->tree = wlr_scene_tree_create(parent); if (!state->tree) { kaboom_destroy(state); return; } - wlr_scene_node_set_position(&state->tree->node, x, y); + wlr_scene_node_set_position(&state->tree->node, 0, 0); state->scene_buf = wlr_scene_buffer_create(state->tree, &state->base); if (!state->scene_buf) { kaboom_destroy(state); return; } - /* Render first frame (original positions) and hide original icon */ + /* Render first frame and hide original icon */ kaboom_render(state); wlr_scene_buffer_set_buffer_with_damage(state->scene_buf, &state->base, NULL); if (fb->tree)