diff --git a/overlay/rootfs/usr/bin/snake b/overlay/rootfs/usr/bin/snake index c8bf935..5eda79d 100755 Binary files a/overlay/rootfs/usr/bin/snake and b/overlay/rootfs/usr/bin/snake differ diff --git a/overlay/rootfs/usr/bin/tetris b/overlay/rootfs/usr/bin/tetris index ead3fdd..a1a0738 100755 Binary files a/overlay/rootfs/usr/bin/tetris and b/overlay/rootfs/usr/bin/tetris differ diff --git a/prebuilt/games/usr/bin/snake b/prebuilt/games/usr/bin/snake index c8bf935..5eda79d 100755 Binary files a/prebuilt/games/usr/bin/snake and b/prebuilt/games/usr/bin/snake differ diff --git a/prebuilt/games/usr/bin/tetris b/prebuilt/games/usr/bin/tetris index ead3fdd..a1a0738 100755 Binary files a/prebuilt/games/usr/bin/tetris and b/prebuilt/games/usr/bin/tetris differ diff --git a/src/games/snake.cpp b/src/games/snake.cpp index 5744a0c..e2fe959 100644 --- a/src/games/snake.cpp +++ b/src/games/snake.cpp @@ -376,45 +376,42 @@ int main() { int xfd = ConnectionNumber(dpy); const unsigned long FRAME_MS = 1000 / 30; // 30 fps rendering - unsigned long lastFrame = now_ms(); + unsigned long frameStart = now_ms(); while (running) { - unsigned long now = now_ms(); - long until = (long)nextTick - (long)now; - if (until < 0) until = 0; - // Never block select longer than one frame so the screen stays smooth - long wait = (until < (long)FRAME_MS) ? until : (long)FRAME_MS; - - struct timeval tv; - tv.tv_sec = wait / 1000; - tv.tv_usec = (wait % 1000) * 1000; - fd_set fds; - FD_ZERO(&fds); - FD_SET(xfd, &fds); - int r = select(xfd + 1, &fds, nullptr, nullptr, &tv); - bool dirty = false; - if (r > 0) { + // Process all pending input immediately (non-blocking) before each frame + while (XPending(dpy)) { XEvent e; - while (XPending(dpy)) { - XNextEvent(dpy, &e); - handle_event(e); - if (!running) break; - dirty = true; - } + XNextEvent(dpy, &e); + handle_event(e); if (!running) break; } - now = now_ms(); - if (running && now >= nextTick) { + if (!running) break; + + unsigned long now = now_ms(); + + // Game logic step at its own rate + if (now >= nextTick) { if (state == PLAYING) step(); - if (state == PLAYING) nextTick = now + speed; - else nextTick = now + 80; // keep loop responsive while paused/over - dirty = true; + nextTick = now + (state == PLAYING ? speed : 80); } - // Render at 60 fps or whenever state changed - if (dirty || now - lastFrame >= FRAME_MS) { - render(); - lastFrame = now; + + // Render every frame + render(); + + // Frame pacing: sleep until next frame, but wake up immediately on input + now = now_ms(); + long sleepMs = (long)FRAME_MS - (long)(now - frameStart); + if (sleepMs > 0) { + struct timeval tv; + tv.tv_sec = sleepMs / 1000; + tv.tv_usec = (sleepMs % 1000) * 1000; + fd_set fds; + FD_ZERO(&fds); + FD_SET(xfd, &fds); + select(xfd + 1, &fds, nullptr, nullptr, &tv); } + frameStart = now_ms(); } XFreeGC(dpy, gc); diff --git a/src/games/tetris.cpp b/src/games/tetris.cpp index d19bfb4..e517099 100644 --- a/src/games/tetris.cpp +++ b/src/games/tetris.cpp @@ -500,45 +500,42 @@ int main() { int xfd = ConnectionNumber(dpy); const unsigned long FRAME_MS = 1000 / 30; // 30 fps rendering - unsigned long lastFrame = now_ms(); + unsigned long frameStart = now_ms(); while (running) { - unsigned long now = now_ms(); - long until = (long)nextTick - (long)now; - if (until < 0) until = 0; - // Never block select longer than one frame so the screen stays smooth - long wait = (until < (long)FRAME_MS) ? until : (long)FRAME_MS; - - struct timeval tv; - tv.tv_sec = wait / 1000; - tv.tv_usec = (wait % 1000) * 1000; - fd_set fds; - FD_ZERO(&fds); - FD_SET(xfd, &fds); - int r = select(xfd + 1, &fds, nullptr, nullptr, &tv); - bool dirty = false; - if (r > 0) { + // Process all pending input immediately (non-blocking) before each frame + while (XPending(dpy)) { XEvent e; - while (XPending(dpy)) { - XNextEvent(dpy, &e); - handle_event(e); - if (!running) break; - dirty = true; - } + XNextEvent(dpy, &e); + handle_event(e); if (!running) break; } - now = now_ms(); - if (running && now >= nextTick) { + if (!running) break; + + unsigned long now = now_ms(); + + // Game logic step at its own rate + if (now >= nextTick) { if (state == PLAYING) step(); - if (state == PLAYING) nextTick = now + speed; - else nextTick = now + 80; - dirty = true; + nextTick = now + (state == PLAYING ? speed : 80); } - // Render at 60 fps or whenever state changed - if (dirty || now - lastFrame >= FRAME_MS) { - render(); - lastFrame = now; + + // Render every frame + render(); + + // Frame pacing: sleep until next frame, but wake up immediately on input + now = now_ms(); + long sleepMs = (long)FRAME_MS - (long)(now - frameStart); + if (sleepMs > 0) { + struct timeval tv; + tv.tv_sec = sleepMs / 1000; + tv.tv_usec = (sleepMs % 1000) * 1000; + fd_set fds; + FD_ZERO(&fds); + FD_SET(xfd, &fds); + select(xfd + 1, &fds, nullptr, nullptr, &tv); } + frameStart = now_ms(); } XFreeGC(dpy, gc);