examples

Toy examples in single C files.
git clone git://henryandlizzy.uk/examples
Log | Files | Refs

commit cf735b6b145557a74db354879fc3c94593106b3d
parent 3a4d0747c840b3bf4c45c04d8d37b9475d00571e
Author: Henry Wilson <henry@henryandlizzy.uk>
Date:   Sat, 22 Jun 2024 01:18:53 +0100

sdl-gl: Use coroutine for click-drag state machine

Diffstat:
MTupfile | 2+-
Msrc/coroutine_owner.hpp | 28++++++++++++++--------------
Msrc/sdl-gl.cpp | 125+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
3 files changed, 119 insertions(+), 36 deletions(-)

diff --git a/Tupfile b/Tupfile @@ -1,7 +1,7 @@ WARNINGS = -Wall -Wextra -Werror COMMON_FLAGS = $(WARNINGS) -fdiagnostics-color=always -fsanitize=undefined,address CFLAGS = $(COMMON_FLAGS) -CXXFLAGS = -std=c++20 $(COMMON_FLAGS) +CXXFLAGS = -std=c++23 $(COMMON_FLAGS) LDLIBS_aio = -lrt LDLIBS_alsa-simple = -lasound diff --git a/src/coroutine_owner.hpp b/src/coroutine_owner.hpp @@ -4,7 +4,7 @@ #include <utility> // exchange template <typename T = void> -struct coroutine_owner +struct [[nodiscard]] coroutine_owner { coroutine_owner() = default; @@ -21,9 +21,7 @@ struct coroutine_owner coroutine_owner& operator =(coroutine_owner&& old) { - if (h) - h.destroy(); - h = std::exchange(old.h, {}); + reset(old.release()); return *this; } @@ -33,29 +31,31 @@ struct coroutine_owner h.destroy(); } - std::coroutine_handle<T> release() + explicit operator bool() const { - return std::exchange(h, {}); + return !!h; } - std::coroutine_handle<T> const* operator ->() const + std::coroutine_handle<T> release() { - return &h; + return std::exchange(h, {}); } - std::coroutine_handle<T>* operator ->() + void reset(std::coroutine_handle<T> v = {}) { - return &h; + coroutine_owner{std::exchange(h, v)}; } - std::coroutine_handle<T> const& operator *() const + template <typename Self> + auto* operator ->(this Self&& self) { - return h; + return &self.h; } - std::coroutine_handle<T>& operator *() + template <typename Self> + auto& operator *(this Self&& self) { - return h; + return self.h; } private: diff --git a/src/sdl-gl.cpp b/src/sdl-gl.cpp @@ -5,13 +5,71 @@ #include <SDL2/SDL_mouse.h> #include <SDL2/SDL_video.h> #include <err.h> + +#include "coroutine_owner.hpp" + +#include <cassert> +#include <exception> #include <numbers> +#include <print> #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a))) #define ARRAY_SSIZE(a) ((ssize_t)ARRAY_SIZE(a)) extern "C" char const* __asan_default_options() { return "detect_leaks=0"; } +struct coroutine_task +{ + struct promise_type + { + coroutine_task get_return_object(void) noexcept { return {}; } + std::suspend_never initial_suspend(void) const noexcept { return {}; } + std::suspend_never final_suspend() const noexcept { return {}; } + void unhandled_exception() const noexcept { std::terminate(); } + void return_void(void) const noexcept {} + }; +}; + +template <typename T> +struct awaitable_value +{ + bool ready(T& e) + { + assert(not event); + if (not waiting) + return false; + + event = &e; + waiting.release().resume(); + return true; + } + bool operator()(T& e) + { + return ready(e); + } + + bool await_ready() const + { + return event; + } + + void await_suspend(std::coroutine_handle<> h) + { + assert(not waiting); + waiting.reset(h); + }; + + T& await_resume() + { + assert(event); + return *std::exchange(event, nullptr); + } + +private: + T* event = nullptr; + coroutine_owner<> waiting = {}; +}; + int screen_width = 800; int screen_height = 450; @@ -146,10 +204,21 @@ void presentScene(void) SDL_GL_SwapWindow(window); } -void key(SDL_KeyboardEvent* e) +void key(SDL_KeyboardEvent& e) { - switch (e->keysym.sym) + static uint8_t ctrls; + switch (e.keysym.sym) { + case SDLK_LCTRL: + case SDLK_RCTRL: + ctrls += e.state ? 1 : -1; + break; + + case SDLK_c: + if (e.state && ctrls) + exit(0); + break; + case SDLK_ESCAPE: exit(0); } @@ -162,35 +231,45 @@ point mouse_to_screen(int x, int y) return {screen_x, screen_y}; } -bool drag = false; -point drag_pos; +point mouse_to_local(int x, int y) +{ + return mouse_to_screen(x, y) * (1/scale); +} void mouse_motion(SDL_MouseMotionEvent& e) { - auto world = mouse_to_screen(e.x, e.y) * (1/scale); - if (drag) - { - pan = drag_pos + world; - } - mouse_pos = world - pan; + mouse_pos = mouse_to_local(e.x, e.y) - pan; } -void mouse_button(SDL_MouseButtonEvent& e) +awaitable_value<SDL_MouseButtonEvent const> click_event; +awaitable_value<SDL_Event const> motion_or_release_event; + +coroutine_task mouse_task() { - if (e.button != SDL_BUTTON_LEFT) - return; - drag = e.state == SDL_PRESSED; - auto world = mouse_to_screen(e.x, e.y) * (1/scale); - if (drag) { - drag_pos = pan - world; + for (;;) + { + SDL_MouseButtonEvent const& e = co_await click_event; + if (e.button != SDL_BUTTON_RIGHT) + continue; + + point drag_pos = pan - mouse_to_local(e.x, e.y); + + for (;;) + { + SDL_Event const& e2 = co_await motion_or_release_event; + if (e2.type == SDL_MOUSEMOTION) + pan = drag_pos + mouse_to_local(e2.motion.x, e2.motion.y); + else if (auto& b = e2.button; b.button == SDL_BUTTON_RIGHT) + break; + } + } } void wheel(SDL_MouseWheelEvent* e) { float factor = exp(0.1f * e->y); - auto world = mouse_to_screen(e->mouseX, e->mouseY) * (1/scale); - pan += world * (1/factor-1); + pan += mouse_to_local(e->mouseX, e->mouseY) * (1/factor-1); scale *= factor; } @@ -217,14 +296,17 @@ void doInput() exit(0); case SDL_KEYDOWN: case SDL_KEYUP: - key(&event.key); + key(event.key); break; case SDL_MOUSEMOTION: mouse_motion(event.motion); + motion_or_release_event(event); break; case SDL_MOUSEBUTTONDOWN: + click_event.ready(event.button); + break; case SDL_MOUSEBUTTONUP: - mouse_button(event.button); + motion_or_release_event(event); break; case SDL_MOUSEWHEEL: wheel(&event.wheel); @@ -242,6 +324,7 @@ void doInput() int main() { + mouse_task(); initSDL(); atexit(SDL_Quit); for (;;)