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:
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 (;;)