Skip to content
Merged
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
10 changes: 6 additions & 4 deletions .github/workflows/headless-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
BACKEND_HEADLESS=y \
TOOLS=y \
TOOL_HEADLESS_CTL=y \
DEMO_MULTI=y
APP_MULTI=y

- name: Build with headless backend
run: |
Expand Down Expand Up @@ -80,6 +80,8 @@ jobs:
echo "Screenshot verification passed"

- name: Test deterministic rendering
env:
MADO_FROZEN_TIME: "1"
run: |
# Run twice and compare outputs for determinism
echo "First run..."
Expand All @@ -106,7 +108,7 @@ jobs:
fi
done

# Compare images (allowing minor differences)
# Compare images exactly
# compare writes the metric to stderr, stdout gets the diff image
DIFF=$(compare -metric AE run1.png run2.png null: 2>&1) || true
# Extract just the numeric value (AE metric is an integer)
Expand All @@ -117,8 +119,8 @@ jobs:
fi
echo "Pixel difference: $DIFF"

if [ "$DIFF" -gt "100" ]; then
echo "ERROR: Renderings differ by more than 100 pixels"
if [ "$DIFF" -ne "0" ]; then
echo "ERROR: Renderings differ"
exit 1
fi
echo "Deterministic rendering test passed"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ jobs:
- name: no widget toolkit
options: WM_MOBILE=y WIDGETS=n
- name: RGB565 phone surfaces
options: WM_MOBILE=y DEMO_CELLPHONE_RGB16=y
options: WM_MOBILE=y APP_CELLPHONE_RGB16=y
- name: TLSF heap
options: WM_MOBILE=y MEM_TLSF=y MEM_TLSF_POOL_SIZE=4194304
- name: Pixman renderer
Expand Down
57 changes: 29 additions & 28 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ target.o-y :=
TARGET_LIBS :=

target.a-y :=
target.a-$(CONFIG_DEMO_APPLICATIONS) += libapps.a
target.a-$(CONFIG_APPS) += libapps.a
target.a-y += libtwin.a

# All translation units must share the arithmetic policy and target ABI.
Expand Down Expand Up @@ -166,35 +166,36 @@ endif
# Applications

libapps.a_files-y := apps/dummy.c
libapps.a_files-$(CONFIG_DEMO_APPLICATIONS) += apps/apps.c
libapps.a_files-$(CONFIG_DEMO_MULTI) += apps/multi.c
libapps.a_files-$(CONFIG_DEMO_CLOCK) += apps/clock.c
libapps.a_files-$(CONFIG_DEMO_CALCULATOR) += apps/calc.c
libapps.a_files-$(CONFIG_DEMO_SPLINE) += apps/spline.c
libapps.a_files-$(CONFIG_DEMO_ANIMATION) += apps/animation.c
libapps.a_files-$(CONFIG_DEMO_IMAGE) += apps/image.c

libapps.a_files-$(CONFIG_DEMO_CELLPHONE) += \
libapps.a_files-$(CONFIG_APPS) += apps/apps.c
libapps.a_files-$(CONFIG_APP_MULTI) += apps/multi.c
libapps.a_files-$(CONFIG_APP_CLOCK) += apps/clock.c
libapps.a_files-$(CONFIG_APP_CALCULATOR) += apps/calc.c
libapps.a_files-$(CONFIG_APP_SPLINE) += apps/spline.c
libapps.a_files-$(CONFIG_APP_ANIMATION) += apps/animation.c
libapps.a_files-$(CONFIG_APP_IMAGE) += apps/image.c
libapps.a_files-$(CONFIG_APP_SNAKE) += apps/snake.c
libapps.a_files-$(CONFIG_APP_PONG) += apps/pong.c
libapps.a_files-$(CONFIG_APP_TETRIS) += apps/tetris.c
libapps.a_files-$(CONFIG_APP_GAMES) += apps/games.c

libapps.a_files-$(CONFIG_APP_UIKIT) += \
apps/cellphone/ui.c \
apps/cellphone/ui-paint.c \
apps/cellphone/ui-input.c \
apps/cellphone/ui-input.c

libapps.a_files-$(CONFIG_APP_CELLPHONE) += \
apps/cellphone/shell.c \
apps/cellphone/home.c \
apps/cellphone/lock.c \
apps/cellphone/data.c \
apps/cellphone/calc.c \
apps/cellphone/dialer.c \
apps/cellphone/contacts.c \
apps/cellphone/sms.c \
apps/cellphone/music.c \
apps/cellphone/photos.c \
apps/cellphone/camera.c \
apps/cellphone/settings.c \
apps/cellphone/calllog.c \
apps/cellphone/games.c \
apps/cellphone/snake.c \
apps/cellphone/pong.c \
apps/cellphone/tetris.c
apps/cellphone/settings.c
libapps.a_files-$(CONFIG_APP_PHONE_DIALER) += apps/cellphone/dialer.c
libapps.a_files-$(CONFIG_APP_PHONE_CALLLOG) += apps/cellphone/calllog.c
libapps.a_files-$(CONFIG_APP_PHONE_MESSAGES) += apps/cellphone/sms.c
libapps.a_files-$(CONFIG_APP_PHONE_CONTACTS) += apps/cellphone/contacts.c
libapps.a_files-$(CONFIG_APP_PHONE_MUSIC) += apps/cellphone/music.c
libapps.a_files-$(CONFIG_APP_PHONE_PHOTOS) += apps/cellphone/photos.c
libapps.a_files-$(CONFIG_APP_PHONE_CAMERA) += apps/cellphone/camera.c

libapps.a_includes-y := include apps apps/cellphone
# Emscripten size optimization
Expand Down Expand Up @@ -259,7 +260,7 @@ endif

# Standalone application

ifeq ($(CONFIG_DEMO_APPLICATIONS), y)
ifeq ($(CONFIG_APPS), y)
target-y += demo-$(BACKEND)
demo-$(BACKEND)_depends-y += $(target.a-y)
demo-$(BACKEND)_files-y = apps/main.c
Expand Down Expand Up @@ -300,7 +301,7 @@ demo-$(BACKEND)_ldflags-y += \
-flto
endif
endif
endif # CONFIG_DEMO_APPLICATIONS
endif # CONFIG_APPS

# Font editor tool
# Tools should not be built for WebAssembly
Expand Down Expand Up @@ -332,10 +333,10 @@ endif
# Headless tests: built only for 'make check' / 'make check-update'
# (and listed for 'make clean' so their binaries are removed)
TESTS := test-core test-wm test-lifetime
ifeq ($(CONFIG_DEMO_APPLICATIONS), y)
ifeq ($(CONFIG_APPS), y)
TESTS += test-demos
endif
ifeq ($(CONFIG_DEMO_CELLPHONE), y)
ifeq ($(CONFIG_APP_CELLPHONE), y)
TESTS += test-cellphone
endif

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ $ ./demo-sdl
```

The default build uses the desktop window manager: overlapping, decorated
windows running the demo apps. The window manager is a build-time
windows running the applications. The window manager is a build-time
choice, and the desktop and mobile policies are mutually exclusive. Build
the phone instead, a lock screen (drag the knob to unlock), a home screen
and a dozen apps with vector-drawn interfaces (Photos shows JPEG
Expand All @@ -142,7 +142,7 @@ $ ./demo-sdl # phone; drag from the left edge to go back

`WM_NONE=y` builds with no window manager at all, showing a single app.
The window manager is fixed for the life of the build: nothing switches
or checks it at run time. The demo apps in `apps/apps.c` run under all
or checks it at run time. The applications in `apps/apps.c` run under all
three; the phone shell depends on `WM_MOBILE` and is built only there. See [docs/phone.md](docs/phone.md) for the design of
the window manager, compositor and caches.

Expand Down
24 changes: 13 additions & 11 deletions apps/animation.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,13 @@ static void _apps_animation_paint(twin_custom_widget_t *custom)
{
apps_animation_data_t *anim =
(apps_animation_data_t *) twin_custom_widget_data(custom);
twin_pixmap_t *current_frame = NULL;

/* Paint the current frame; only the timer advances it, so repainting
* (exposure, a restore after eviction) does not skip frames */
twin_animation_t *a = twin_pixmap_animation(anim->pix);
current_frame = a ? twin_animation_get_current_frame(a) : anim->pix;
/* Paint the current frame: an animation redraws its pixmap in place,
* and only the timer advances it, so repainting (exposure, a restore
* after eviction) does not skip frames */
twin_pixmap_t *current_frame = anim->pix;

/* A policy may give the window more room than the image (a phone's
* work area, the whole screen without a window manager): center it on
* the widget's black background */
* work area, the whole screen without a window manager): center it */
twin_pixmap_t *dst = _apps_animation_pixmap(custom);
twin_coord_t w = twin_custom_widget_width(custom);
twin_coord_t h = twin_custom_widget_height(custom);
Expand All @@ -47,7 +44,9 @@ static void _apps_animation_paint(twin_custom_widget_t *custom)
.source_kind = TWIN_PIXMAP,
.u.pixmap = current_frame,
};
twin_composite(dst, x, y, &srcop, 0, 0, NULL, 0, 0, TWIN_SOURCE, fw, fh);
/* Over the cleared widget: a GIF's transparent pixels stay clear, and
* whatever is behind the window shows through them */
twin_composite(dst, x, y, &srcop, 0, 0, NULL, 0, 0, TWIN_OVER, fw, fh);
}

static twin_time_t _apps_animation_timeout(twin_time_t now, void *closure)
Expand All @@ -71,7 +70,7 @@ static twin_dispatch_result_t _apps_animation_dispatch(
_apps_animation_paint(custom);
break;
case TwinEventDestroy: {
/* The pixmap owns the animation, so this frees every frame too */
/* The pixmap owns the animation, so this frees the decoder too */
apps_animation_data_t *anim =
(apps_animation_data_t *) twin_custom_widget_data(custom);
twin_clear_timeout(anim->timeout);
Expand All @@ -90,7 +89,7 @@ static twin_custom_widget_t *_apps_animation_init(twin_box_t *parent,
twin_pixmap_t *pix)
{
twin_custom_widget_t *custom = twin_custom_widget_create(
parent, 0xff000000, 0, 0, _apps_animation_dispatch,
parent, 0x00000000, 0, 0, _apps_animation_dispatch,
sizeof(apps_animation_data_t));
if (!custom)
return NULL;
Expand Down Expand Up @@ -135,6 +134,9 @@ bool apps_animation_start(twin_screen_t *screen,
twin_pixmap_destroy(pix);
return false;
}
/* See-through: a clear box makes the window translucent, so the GIF's
* transparency reaches the screen instead of a solid fill */
twin_widget_set(twin_box_widget(twin_toplevel_box(toplevel)), 0x00000000);
/* The widget owns the image from here on */
if (!apps_animation_create(twin_toplevel_box(toplevel), pix)) {
twin_toplevel_destroy(toplevel);
Expand Down
30 changes: 20 additions & 10 deletions apps/apps.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,48 @@

#include "apps.h"

#if defined(CONFIG_DEMO_CELLPHONE)
#if defined(CONFIG_APP_CELLPHONE)
#include "phone.h"
#endif

/* Every app below the phone runs under all three window managers */
static const apps_entry_t apps[] = {
#if defined(CONFIG_DEMO_CELLPHONE) && defined(CONFIG_WM_MOBILE)
#if defined(CONFIG_APP_CELLPHONE) && defined(CONFIG_WM_MOBILE)
/* Mobile only: the phone shell lists the other apps on its home
* screen */
{"Phone", TWIN_SYM_MOBILE, 0, 0, PHONE_W, PHONE_H, NULL, phone_boot,
phone_shutdown},
#endif
#if defined(CONFIG_DEMO_MULTI)
#if defined(CONFIG_APP_MULTI)
{"Demo", TWIN_SYM_LIST, 100, 100, 400, 400, apps_multi_start, NULL, NULL},
#endif
#if defined(CONFIG_DEMO_CLOCK)
#if defined(CONFIG_APP_CLOCK)
{"Clock", TWIN_SYM_CLOCK, 10, 10, 200, 200, apps_clock_start, NULL, NULL},
#endif
#if defined(CONFIG_DEMO_CALCULATOR)
{"Calc", TWIN_SYM_CALCULATOR, 100, 100, 200, 200, apps_calc_start, NULL,
NULL},
#if defined(CONFIG_APP_CALCULATOR)
{"Calculator", TWIN_SYM_CALCULATOR, 100, 100, 220, 300, apps_calc_start,
NULL, NULL},
#endif
#if defined(CONFIG_DEMO_SPLINE)
#if defined(CONFIG_APP_SPLINE)
{"Spline", TWIN_SYM_SHUFFLE, 20, 20, 400, 400, apps_spline_start, NULL,
NULL},
#endif
#if defined(CONFIG_DEMO_ANIMATION)
#if defined(CONFIG_APP_ANIMATION)
{"Animation", TWIN_SYM_PLAY, 20, 20, 0, 0, apps_animation_start, NULL,
NULL},
#endif
#if defined(CONFIG_DEMO_IMAGE)
#if defined(CONFIG_APP_IMAGE)
{"Vectors", TWIN_SYM_GLOBE, 20, 20, 0, 0, apps_image_start, NULL, NULL},
#endif
/* Kit views in plain windows; the phone lists its own, themed rows */
#if defined(CONFIG_APP_SNAKE)
{"Snake", TWIN_SYM_PLAY, 60, 40, 0, 0, apps_snake_start, NULL, NULL},
#endif
#if defined(CONFIG_APP_PONG)
{"Pong", TWIN_SYM_PLAY, 90, 60, 0, 0, apps_pong_start, NULL, NULL},
#endif
#if defined(CONFIG_APP_TETRIS)
{"Tetris", TWIN_SYM_PLAY, 120, 80, 0, 0, apps_tetris_start, NULL, NULL},
#endif
{0}, /* keeps the table valid when nothing is built */
};
Expand Down
11 changes: 6 additions & 5 deletions apps/apps.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
*/

/*
* Registry of the demo applications that are built. The demo launcher
* (main.c), the phone's home screen and the tests all read it, so a new app
* is listed everywhere by adding one entry.
* Registry of the applications that are built. The launcher (main.c), the
* phone's home screen and the tests all read it, so a new app is listed
* everywhere by adding one entry.
*
* Which window managers an app runs under is settled at build time, like
* the window manager itself: an app runs under all of them unless its
* Kconfig option depends on one (DEMO_CELLPHONE depends on WM_MOBILE), and
* Kconfig option depends on one (APP_CELLPHONE depends on WM_MOBILE), and
* its entry is compiled in only when it can run.
*/

Expand Down Expand Up @@ -48,6 +48,7 @@ const apps_entry_t *apps_at(int idx);
#define ASSET_PATH "assets/"

apps_start_fn apps_animation_start, apps_calc_start, apps_clock_start,
apps_image_start, apps_multi_start, apps_spline_start;
apps_image_start, apps_multi_start, apps_spline_start, apps_snake_start,
apps_pong_start, apps_tetris_start;

#endif /* _APPS_H_ */
Loading
Loading