Skip to content
Open
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions examples/SDL2_Life/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
life
18 changes: 18 additions & 0 deletions examples/SDL2_Life/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
CC ?= gcc
CFLAGS ?= -std=c11 -Wall -Wextra -O2
PKGS := sdl2
PKG_CFLAGS := $(shell pkg-config --cflags $(PKGS))
PKG_LIBS := $(shell pkg-config --libs $(PKGS))

TARGET := life
SRC := main.c

all: $(TARGET)

$(TARGET): $(SRC)
$(CC) $(CFLAGS) $(PKG_CFLAGS) $(SRC) -o $(TARGET) $(PKG_LIBS)

clean:
rm -f $(TARGET)

.PHONY: all clean
139 changes: 139 additions & 0 deletions examples/SDL2_Life/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Conway's Game of Life for CardputerZero (320x170, SDL2).
*
* Ported from the Phoebe LVGL app (app_life). A toroidal cellular automaton
* that auto-reseeds when the population stalls, goes extinct, or ages out.
* No network or sensors. Cells are drawn as filled rects scaled to the panel.
*
* Keys: SPACE reseed, P pause, ESC / Q quit.
*/
#include <SDL2/SDL.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

#define SCREEN_W 320
#define SCREEN_H 170

/* Grid: cells are CELL x CELL px. 80x42 grid at CELL=4 fills 320x168. */
#define CELL 4
#define GW (SCREEN_W / CELL) /* 80 */
#define GH (SCREEN_H / CELL) /* 42 */

#define STEP_MS 90 /* generation interval */

static uint8_t cur[GW * GH];
static uint8_t nxt[GW * GH];

static uint32_t rng_state;
static uint32_t xrand(void) {
uint32_t x = rng_state;
x ^= x << 13; x ^= x >> 17; x ^= x << 5;
rng_state = x;
return x;
}

static int gen, stall, prev_pop;

static void seed(void) {
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
rng_state ^= ((uint32_t)ts.tv_nsec * 2654435761u) | 1u;
for (int i = 0; i < GW * GH; i++)
cur[i] = (xrand() % 100) < 28 ? 1 : 0;
gen = 0;
stall = 0;
prev_pop = -1;
}

static void step(void) {
int pop = 0;
for (int y = 0; y < GH; y++) {
const int yu = (y + GH - 1) % GH;
const int yd = (y + 1) % GH;
for (int x = 0; x < GW; x++) {
const int xl = (x + GW - 1) % GW;
const int xr = (x + 1) % GW;
const int n = cur[yu * GW + xl] + cur[yu * GW + x] + cur[yu * GW + xr]
+ cur[y * GW + xl] + cur[y * GW + xr]
+ cur[yd * GW + xl] + cur[yd * GW + x] + cur[yd * GW + xr];
const uint8_t alive = cur[y * GW + x];
const uint8_t next = (n == 3 || (alive && n == 2)) ? 1 : 0;
nxt[y * GW + x] = next;
pop += next;
}
}
memcpy(cur, nxt, sizeof(cur));

if (pop == prev_pop) stall++;
else stall = 0;
prev_pop = pop;
if (pop == 0 || stall > 14 || ++gen > 600) seed();
}

static void draw(SDL_Renderer *ren) {
SDL_SetRenderDrawColor(ren, 0, 0, 0, 255); /* COLOR_BG */
SDL_RenderClear(ren);
SDL_SetRenderDrawColor(ren, 0x99, 0xFF, 0x00, 255); /* COLOR_ACCENT */
for (int y = 0; y < GH; y++) {
for (int x = 0; x < GW; x++) {
if (!cur[y * GW + x]) continue;
SDL_Rect r = { x * CELL, y * CELL, CELL - 1, CELL - 1 };
SDL_RenderFillRect(ren, &r);
}
}
SDL_RenderPresent(ren);
}

int main(int argc, char **argv) {
(void)argc; (void)argv;

if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
fprintf(stderr, "SDL_Init: %s\n", SDL_GetError());
return 1;
}

SDL_Window *win = SDL_CreateWindow("Life",
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
SCREEN_W, SCREEN_H, SDL_WINDOW_BORDERLESS);
if (!win) { fprintf(stderr, "SDL_CreateWindow: %s\n", SDL_GetError());
SDL_Quit(); return 1; }

SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_SOFTWARE);
if (!ren) { fprintf(stderr, "SDL_CreateRenderer: %s\n", SDL_GetError());
SDL_DestroyWindow(win); SDL_Quit(); return 1; }

rng_state = 0x2545f491u;
seed();

int running = 1, paused = 0;
Uint32 last_step = SDL_GetTicks();
while (running) {
Uint32 now = SDL_GetTicks();
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
if (ev.type == SDL_QUIT) running = 0;
else if (ev.type == SDL_KEYDOWN) {
SDL_Keycode k = ev.key.keysym.sym;
if (k == SDLK_ESCAPE || k == SDLK_q) running = 0;
else if (k == SDLK_SPACE) seed();
else if (k == SDLK_p) paused = !paused;
}
}

if (!paused && now - last_step >= STEP_MS) {
step();
last_step = now;
}
draw(ren);

Uint32 elapsed = SDL_GetTicks() - now;
if (elapsed < 16) SDL_Delay(16 - elapsed);
}

SDL_DestroyRenderer(ren);
SDL_DestroyWindow(win);
SDL_Quit();
return 0;
}
3 changes: 3 additions & 0 deletions examples/SDL2_Life/packaging/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
make
3 changes: 3 additions & 0 deletions examples/SDL2_Life/packaging/ci-deps.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/env bash
set -euo pipefail
apt-get install -y libsdl2-dev
Binary file added examples/SDL2_Life/packaging/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions examples/SDL2_Life/packaging/meta.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
PKG_NAME=sdl2-life
PKG_VERSION=0.1
PKG_REVISION=m5stack1
PKG_DESC="Conway's Game of Life for CardputerZero (SDL2)"
PKG_DEPENDS="libsdl2-2.0-0, libwayland-client0"
APP_NAME="Game of Life"
APP_EXEC=/usr/share/APPLaunch/bin/sdl2-life
APP_TERMINAL=false
APP_ICON=share/images/sdl2-life.png
51 changes: 51 additions & 0 deletions examples/SDL2_Life/packaging/stage.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/usr/bin/env bash
set -euo pipefail

install -D -m 0755 life "$STAGE$APP_INSTALL_DIR/life"

# Wayland/KMSDRM launcher wrapper (see SDL2_HelloWorld for rationale).
cat >"$STAGE$INSTALL_PREFIX/bin/$PKG_NAME" <<EOF
#!/bin/sh
LOG=/tmp/$PKG_NAME.log
: >"\$LOG" 2>/dev/null || LOG=/dev/null

if [ -z "\${XDG_RUNTIME_DIR:-}" ]; then
_uid=\$(id -u 2>/dev/null || echo 1000)
if [ -d "/run/user/\$_uid" ]; then
XDG_RUNTIME_DIR="/run/user/\$_uid"
elif [ -d "/run/user/1000" ]; then
XDG_RUNTIME_DIR="/run/user/1000"
fi
[ -n "\$XDG_RUNTIME_DIR" ] && export XDG_RUNTIME_DIR
fi

_wl_ok=0
if [ -n "\${WAYLAND_DISPLAY:-}" ] && [ -n "\${XDG_RUNTIME_DIR:-}" ] && \\
[ -S "\$XDG_RUNTIME_DIR/\$WAYLAND_DISPLAY" ]; then
_wl_ok=1
elif [ -n "\${XDG_RUNTIME_DIR:-}" ]; then
for _c in wayland-0 wayland-1; do
if [ -S "\$XDG_RUNTIME_DIR/\$_c" ]; then
WAYLAND_DISPLAY=\$_c
export WAYLAND_DISPLAY
_wl_ok=1
break
fi
done
fi

if [ -z "\${SDL_VIDEODRIVER:-}" ]; then
if [ "\$_wl_ok" = 1 ]; then
SDL_VIDEODRIVER=wayland
elif [ -e /dev/dri/card0 ]; then
SDL_VIDEODRIVER=kmsdrm
else
SDL_VIDEODRIVER=offscreen
fi
export SDL_VIDEODRIVER
fi

echo "[$PKG_NAME] driver=\$SDL_VIDEODRIVER WAYLAND_DISPLAY=\${WAYLAND_DISPLAY:-} XDG_RUNTIME_DIR=\${XDG_RUNTIME_DIR:-} uid=\$(id -u)" >>"\$LOG" 2>&1
exec $APP_INSTALL_DIR/life "\$@" >>"\$LOG" 2>&1
EOF
chmod 0755 "$STAGE$INSTALL_PREFIX/bin/$PKG_NAME"