Last active
March 3, 2026 06:28
-
-
Save YclepticStudios/bf8fc41c4074217fe741286a2359e82b to your computer and use it in GitHub Desktop.
Helper script for recording Pebble watch emulator GIFs on Linux (tested on Ubuntu 24)
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
| #!/bin/bash | |
| # ============================================================================= | |
| # record_pebble.sh | |
| # | |
| # DESCRIPTION: | |
| # Records the selected Pebble emulator window and converts the captured | |
| # output to a minimal, pixel perfect animated GIF. | |
| # | |
| # USAGE: | |
| # ./record_pebble.sh [output.gif] | |
| # | |
| # DEPENDENCIES: | |
| # sudo apt install xdotool ffmpeg xwininfo | |
| # | |
| # TIPS: | |
| # - Call 'light_enable(true)' in your app's startup handler to keep the | |
| # backlight at full brightness throughout the recording. | |
| # - Disable vibrations, if your app has them, to prevent the emulator screen | |
| # from shaking and interfering with the recording. | |
| # ============================================================================= | |
| set -euo pipefail | |
| # Variables | |
| OUTPUT="${1:-output.gif}" | |
| TMP_DIR=$(mktemp -d) | |
| MP4="$TMP_DIR/recording.mp4" | |
| DECIMATED="$TMP_DIR/decimated.mp4" | |
| PALETTE="$TMP_DIR/palette.png" | |
| FPS=15 | |
| # Error handling | |
| trap 'echo "Error on line $LINENO" >&2; rm -rf "$TMP_DIR"' ERR | |
| trap 'rm -rf "$TMP_DIR"' EXIT | |
| # Get targeted window information | |
| echo "Click the emulator window to record..." | |
| WIN_INFO=$(xwininfo) | |
| WIN_ID=$(echo "$WIN_INFO" | grep "Window id:" | awk '{print $4}') | |
| X=$(echo "$WIN_INFO" | grep "Absolute upper-left X" | awk '{print $NF}') | |
| Y=$(echo "$WIN_INFO" | grep "Absolute upper-left Y" | awk '{print $NF}') | |
| W=$(echo "$WIN_INFO" | grep "Width:" | awk '{print $NF}') | |
| H=$(echo "$WIN_INFO" | grep "Height:" | awk '{print $NF}') | |
| # Remove padding from basalt emulator (better way to do this?) | |
| if [[ "$W" -eq 148 && "$H" -eq 172 ]]; then | |
| X=$((X + 2)) | |
| Y=$((Y + 2)) | |
| W=$((W - 4)) | |
| H=$((H - 4)) | |
| fi | |
| # Transfer focus from the terminal to the selected window | |
| xdotool windowactivate --sync "$WIN_ID" | |
| # Capture the screen as a lossless video to the temp directory | |
| echo "Capturing window. Press Ctrl+C to stop recording..." | |
| ffmpeg -f x11grab -draw_mouse 0 -framerate $FPS -i "$DISPLAY" \ | |
| -vf "crop=${W}:${H}:${X}:${Y}" -c:v libx264rgb -crf 0 "$MP4" -v error || true | |
| # Strip out unused frames, maintaining the lossless quality | |
| echo "Processing recording..." | |
| ffmpeg -i "$MP4" -vf "mpdecimate,fps=$FPS" -c:v libx264rgb -crf 0 \ | |
| "$DECIMATED" -v error | |
| # Generate a minimal color pallet | |
| ffmpeg -i "$DECIMATED" -vf "palettegen=max_colors=64:reserve_transparent=0" \ | |
| "$PALETTE" -v error | |
| # Encode the video as a gif | |
| ffmpeg -i "$DECIMATED" -i "$PALETTE" \ | |
| -filter_complex "[0:v][1:v]paletteuse=dither=none" -y "$OUTPUT" -v error | |
| echo "GIF saved to: $OUTPUT" |
Author
@YclepticStudios very cool! Do you mind if I borrow this method and incorporate into https://github.com/coredevices/pebble-tool?
just merged it - it works on Mac, want to try on Ubuntu 24.04?
Author
@ericmigi Don't mind at all! I had to change this line to get it to work on Linux (do see caveats below though).
--- a/pebble_tool/commands/screenshot.py
+++ b/pebble_tool/commands/screenshot.py
@@ -270,7 +270,7 @@ class ScreenshotCommand(PebbleCommand):
capture_output=True, text=True)
if result.returncode != 0 or not result.stdout.strip():
raise ToolError("Could not find QEMU emulator window. Is the emulator running?")
- win_id = result.stdout.strip().split('\n')[0]
+ win_id = result.stdout.strip().split('\n')[-1]
result = subprocess.run(['xwininfo', '-id', win_id],
capture_output=True, text=True)
Now for the caveats. This method is going to be quite brittle, at least on Linux. The ffmpeg video processing part should be pretty portable, but the window capturing is not very robust.
- Using
x11grabwill probably fail on Wayland based systems depending on how QEMU is running - Using
xdotool searchand grabbing the first (or last) window handle isn't deterministic. Ignoring the potential for multiple windows, each QEMU window has 3 window handles. You want the inner most one to avoid dealing with window title and background shading offsets, but the order they are returned from xdotool isn't deterministic. You would probably have to query the hierarchy to get that method to work reliably. I was able to avoid that issue in my script by grabbing the window the user clicked on, though that's obviously not your situation. - This will likely break on multi-monitor systems since it assumes the first display when capturing (I use a single monitor, so couldn't test).
Cool stuff though and I'd love to see it implemented into pebble-tool! Might want a more portable window IDing / recording method though.
Thanks, I just pushed a fix!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
See the script header for dependencies and instructions. Note that this script will likely only work on similar environments (tested on Ubuntu 24), but could likely be modified to work on more systems relatively easily.
Example usage and output: