ui/sdl2: add Pebble watch-frame window decorations - #1
Merged
Conversation
gmarull
force-pushed
the
qemu-deco
branch
2 times, most recently
from
May 8, 2026 13:52
0cfb24b to
656aae4
Compare
Wrap the SDL2 display in a borderless, shaped window that renders a
watch-frame PNG around the guest framebuffer. The frame area drags the
window via SDL_SetWindowHitTest; configured button hot-zones synthesize
back/up/select/down key events; a small close icon at the top-right
shuts the VM down.
Selection is via -display sdl,decoration=<name>. Built-in presets ship
under pc-bios/pebble-decorations/:
- pt2-sb, pt2-br : Pebble Time 2 (200x228 framebuffer)
- pr2-bk20, pr2-gd14 : Pebble Round 2 (260x260 round, inscribed-circle
shape mask so framebuffer corners stay clipped)
- p2d-bk, p2d-wh : Pebble Flint (144x168)
The framebuffer renders at native pixel size; the PNG is scaled per
axis so its inner screen rect lines up with the framebuffer. PNG alpha
is binarized at load time and at shape-mask build time so the watch
silhouette has crisp edges with no halo on a black-cleared canvas.
Two transparency paths, one per host:
- Linux/X11: SDL_CreateShapedWindow + binary alpha mask clip the
silhouette at the WM level, the existing path.
- macOS: libsdl.org SDL 2.32 doesn't honor SDL_WINDOW_TRANSPARENT
(an SDL3-only flag) and its NSWindow setup leaves the contentView
opaque even with NSWindow.opaque=NO. ui/sdl2-cocoa.m replaces
contentView with a custom NSView that paints a per-frame CGImage
via drawRect: through NSGraphicsContext (the only path that
preserves per-pixel alpha on this combination). SDL's original
SDLView is re-hosted as a fullsize subview with alphaValue=0 so
NSResponder hooks (mouse, keyboard, SDL_SetWindowHitTest) keep
firing without painting over our view. The frame is composited in
software by sdl2_decoration_compose() since no SDL renderer exists
on this path.
Also fix an upstream-style bug exposed by the shaped window: PIXMAN
x8r8g8b8 / x8b8g8r8 / r8g8b8x8 surfaces were being mapped to SDL ARGB/
ABGR/RGBA8888, so the don't-care byte was interpreted as alpha. With
sdl2-compat treating rendered alpha as window transparency, every black
framebuffer pixel became invisible. Map the X-byte formats to the
matching SDL XRGB/XBGR/RGBX8888 instead.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Wrap the SDL2 display in a borderless, shaped window that renders a watch-frame PNG around the guest framebuffer. The frame area drags the window via SDL_SetWindowHitTest; configured button hot-zones synthesize back/up/select/down key events; a small close icon at the top-right shuts the VM down.
Selection is via -display sdl,decoration=. Built-in presets ship under pc-bios/pebble-decorations/:
The framebuffer renders at native pixel size; the PNG is scaled per axis so its inner screen rect lines up with the framebuffer. PNG alpha is binarized at load time and at shape-mask build time so the watch silhouette has crisp edges with no halo on a black-cleared canvas.
Also fix an upstream-style bug exposed by the shaped window: PIXMAN x8r8g8b8 / x8b8g8r8 / r8g8b8x8 surfaces were being mapped to SDL ARGB/ ABGR/RGBA8888, so the don't-care byte was interpreted as alpha. With sdl2-compat treating rendered alpha as window transparency, every black framebuffer pixel became invisible. Map the X-byte formats to the matching SDL XRGB/XBGR/RGBX8888 instead.