kaboom: match original X11 physics
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.
This commit is contained in:
parent
503cdb5dca
commit
ac74064d1c
|
|
@ -1,6 +1,7 @@
|
|||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <wayland-server-core.h>
|
||||
|
|
@ -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 (<<KAB_PRECISION) */
|
||||
int py; /* y in pixels */
|
||||
int vx; /* x velocity in fixed-point */
|
||||
int vy; /* y velocity in pixels */
|
||||
int src_x, src_y; /* source tile coords */
|
||||
bool alive;
|
||||
};
|
||||
|
||||
struct kaboom_state {
|
||||
|
|
@ -31,11 +34,14 @@ struct kaboom_state {
|
|||
struct wlr_scene_buffer *scene_buf;
|
||||
struct wlr_buffer base;
|
||||
pixman_image_t *image;
|
||||
pixman_image_t *src_image; /* copy of original icon pixels */
|
||||
struct wl_event_source *timer;
|
||||
struct kaboom_piece *pieces;
|
||||
int npieces;
|
||||
int alive_count;
|
||||
int width, height;
|
||||
int frame;
|
||||
int scr_w, scr_h;
|
||||
int origin_x, origin_y;
|
||||
};
|
||||
|
||||
static void kaboom_destroy(struct kaboom_state *state)
|
||||
|
|
@ -46,6 +52,8 @@ static void kaboom_destroy(struct kaboom_state *state)
|
|||
wlr_scene_node_destroy(&state->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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue