From 315b1a784d270c9f3ca0d430c3c1802a3ebfd918 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Sun, 4 Aug 2019 23:37:58 +0200 Subject: WIP: Update C++ bindings --- pugl/pugl.hpp | 437 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 395 insertions(+), 42 deletions(-) (limited to 'pugl/pugl.hpp') diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp index 73cfe2a..18451ee 100644 --- a/pugl/pugl.hpp +++ b/pugl/pugl.hpp @@ -22,6 +22,13 @@ #define PUGL_PUGL_HPP #include "pugl/pugl.h" +#include "pugl/pugl_gl.h" // FIXME + +#include +#include +#include +#include +#include /** @defgroup puglxx C++ @@ -37,84 +44,430 @@ */ namespace pugl { -/** - A drawable region that receives events. +enum class Status { + success = PUGL_SUCCESS, + failure = PUGL_FAILURE, + unknownError = PUGL_UNKNOWN_ERROR, + badBackend = PUGL_BAD_BACKEND, + backendFailed = PUGL_BACKEND_FAILED, + registrationFailed = PUGL_REGISTRATION_FAILED, + createWindowFailed = PUGL_CREATE_WINDOW_FAILED, + setFormatFailed = PUGL_SET_FORMAT_FAILED, + createContextFailed = PUGL_CREATE_CONTEXT_FAILED, + unsupportedType = PUGL_UNSUPPORTED_TYPE, +}; - This is a thin wrapper for a PuglView that contains only a pointer. +enum class ViewHint { + useCompatProfile, ///< Use compatible (not core) OpenGL profile + useDebugContext, ///< True to use a debug OpenGL context + contextVersionMajor, ///< OpenGL context major version + contextVersionMinor, ///< OpenGL context minor version + redBits, ///< Number of bits for red channel + greenBits, ///< Number of bits for green channel + blueBits, ///< Number of bits for blue channel + alphaBits, ///< Number of bits for alpha channel + depthBits, ///< Number of bits for depth buffer + stencilBits, ///< Number of bits for stencil buffer + samples, ///< Number of samples per pixel (AA) + doubleBuffer, ///< True if double buffering should be used + swapInterval, ///< Number of frames between buffer swaps + resizable, ///< True if window should be resizable + ignoreKeyRepeat, ///< True if key repeat events are ignored +}; - @ingroup puglxx -*/ -class View { +using Rect = PuglRect; +using NativeWindow = PuglNativeWindow; +using GlFunc = PuglGlFunc; +using Event = PuglEvent; + +template +struct TypedEvent : public Base { + static constexpr const PuglEventType type = t; +}; + +/* Strong types for every event type. */ + +using ButtonPressEvent = TypedEvent; +using ButtonReleaseEvent = TypedEvent; +using CreateEvent = TypedEvent; +using DestroyEvent = TypedEvent; +using MapEvent = TypedEvent; +using UnmapEvent = TypedEvent; +using ConfigureEvent = TypedEvent; +using ExposeEvent = TypedEvent; +using CloseEvent = TypedEvent; +using KeyPressEvent = TypedEvent; +using KeyReleaseEvent = TypedEvent; +using TextEvent = TypedEvent; +using EnterEvent = TypedEvent; +using LeaveEvent = TypedEvent; +using MotionEvent = TypedEvent; +using ScrollEvent = TypedEvent; +using FocusInEvent = TypedEvent; +using FocusOutEvent = TypedEvent; +using ClientEvent = TypedEvent; + +static inline const char* +strerror(pugl::Status status) +{ + return puglStrerror(static_cast(status)); +} + +static inline GlFunc +getProcAddress(const char* name) +{ + return puglGetProcAddress(name); +} + +class World; + +class Clock +{ +public: + using rep = double; + using period = std::ratio<1>; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + + static constexpr bool is_steady = true; + + explicit Clock(World& world) + : _world{world} + {} + + time_point now() const; + +private: + const pugl::World& _world; +}; + +class World +{ +public: + World() + : _clock(*this) + , _world(puglNewWorld()) + { + if (!_world) { + throw std::runtime_error("Failed to create pugl::World"); + } + } + + ~World() { puglFreeWorld(_world); } + + World(const World&) = delete; + World& operator=(const World&) = delete; + World(World&&) = delete; + World&& operator=(World&&) = delete; + + Status setClassName(const char* const name) + { + return static_cast(puglSetClassName(_world, name)); + } + + double getTime() const { return puglGetTime(_world); } + + Status pollEvents(const double timeout) + { + return static_cast(puglPollEvents(_world, timeout)); + } + + Status dispatchEvents() + { + return static_cast(puglDispatchEvents(_world)); + } + + const PuglWorld* cobj() const { return _world; } + PuglWorld* cobj() { return _world; } + + const Clock& clock() { return _clock; } + +private: + Clock _clock; + PuglWorld* const _world; +}; + +inline Clock::time_point +Clock::now() const +{ + return time_point{duration{_world.getTime()}}; +} + +class ViewBase +{ public: - View(int* pargc, char** argv) - : _view(puglInit(pargc, argv)) + explicit ViewBase(World& world) + : _world(world) + , _view(puglNewView(world.cobj())) { + if (!_view) { + throw std::runtime_error("Failed to create pugl::View"); + } + puglSetHandle(_view, this); - puglSetEventFunc(_view, _onEvent); } - virtual ~View() { puglDestroy(_view); } + ~ViewBase() { puglFreeView(_view); } + + ViewBase(const ViewBase&) = delete; + ViewBase(ViewBase&&) = delete; + ViewBase& operator=(const ViewBase&) = delete; + ViewBase&& operator=(ViewBase&&) = delete; + + Status setHint(ViewHint hint, int value) + { + return static_cast( + puglSetViewHint(_view, static_cast(hint), value)); + } + + bool getVisible() const { return puglGetVisible(_view); } + + Status postRedisplay() + { + return static_cast(puglPostRedisplay(_view)); + } + + const pugl::World& getWorld() const { return _world; } + pugl::World& getWorld() { return _world; } + + Rect getFrame() const { return puglGetFrame(_view); } - virtual void initWindowParent(PuglNativeWindow parent) { - puglInitWindowParent(_view, parent); + Status setFrame(Rect frame) + { + return static_cast(puglSetFrame(_view, frame)); } - virtual void initWindowSize(int width, int height) { - puglInitWindowSize(_view, width, height); + Status setMinSize(int width, int height) + { + return static_cast(puglSetMinSize(_view, width, height)); } - virtual void initWindowMinSize(int width, int height) { - puglInitWindowMinSize(_view, width, height); + Status setAspectRatio(int minX, int minY, int maxX, int maxY) + { + return static_cast( + puglSetAspectRatio(_view, minX, minY, maxX, maxY)); } - virtual void initWindowAspectRatio(int min_x, int min_y, int max_x, int max_y) { - puglInitWindowAspectRatio(_view, min_x, min_y, max_x, max_y); + Status setWindowTitle(const char* title) + { + return static_cast(puglSetWindowTitle(_view, title)); } - virtual void initResizable(bool resizable) { - puglInitResizable(_view, resizable); + Status setParentWindow(NativeWindow parent) + { + return static_cast(puglSetParentWindow(_view, parent)); } - virtual void initTransientFor(uintptr_t parent) { - puglInitTransientFor(_view, parent); + Status setTransientFor(NativeWindow parent) + { + return static_cast(puglSetTransientFor(_view, parent)); } - virtual void initBackend(const PuglBackend* backend) { - puglInitBackend(_view, backend); + Status createWindow(const char* title) + { + return static_cast(puglCreateWindow(_view, title)); } - virtual void createWindow(const char* title) { - puglCreateWindow(_view, title); + Status showWindow() { return static_cast(puglShowWindow(_view)); } + + Status hideWindow() { return static_cast(puglHideWindow(_view)); } + + NativeWindow getNativeWindow() { return puglGetNativeWindow(_view); } + + Status setBackend(const PuglBackend* backend) + { + return static_cast(puglSetBackend(_view, backend)); } - virtual void showWindow() { puglShowWindow(_view); } - virtual void hideWindow() { puglHideWindow(_view); } - virtual PuglNativeWindow getNativeWindow() { return puglGetNativeWindow(_view); } + void* getContext() { return puglGetContext(_view); } - virtual void onEvent(const PuglEvent* event) = 0; + bool hasFocus() const { return puglHasFocus(_view); } - virtual void* getContext() { return puglGetContext(_view); } - virtual void ignoreKeyRepeat(bool ignore) { puglIgnoreKeyRepeat(_view, ignore); } - virtual void grabFocus() { puglGrabFocus(_view); } - virtual void requestAttention() { puglRequestAttention(_view); } - virtual PuglStatus waitForEvent() { return puglWaitForEvent(_view); } - virtual PuglStatus processEvents() { return puglProcessEvents(_view); } - virtual void postRedisplay() { puglPostRedisplay(_view); } + Status grabFocus() { return static_cast(puglGrabFocus(_view)); } + + Status requestAttention() + { + return static_cast(puglRequestAttention(_view)); + } PuglView* cobj() { return _view; } +protected: + World& _world; + PuglView* _view; +}; + +/** + A drawable region that receives events. + + This is a thin wrapper for a PuglView that contains only a pointer. + + @ingroup puglxx +*/ +template +class View : public ViewBase +{ +public: + template + using TypedEventFunc = std::function; + + using NothingEvent = TypedEvent; + + /** + A tuple of event handlers, one for each event type. + + Note that the indices here must correspond to PuglEventType. + */ + using EventFuncs = std::tuple, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc, + TypedEventFunc>; + + using EventFunc = std::function; + + explicit View(World& world) + : ViewBase{world} + , _data{} + { + puglSetEventFunc(_view, _onEvent); + } + + View(World& world, Data data) + : ViewBase{world} + , _data{data} + { + puglSetEventFunc(_view, _onEvent); + } + + template + Status setEventFunc( + std::function handler) + { + std::get(_eventFuncs) = handler; + + return Status::success; + } + + template + Status setEventFunc(pugl::Status (*handler)(View&, const HandledEvent&)) + { + std::get(_eventFuncs) = handler; + + return Status::success; + } + + const Data& getData() const { return _data; } + Data& getData() { return _data; } + private: - static void _onEvent(PuglView* view, const PuglEvent* event) { - ((View*)puglGetHandle(view))->onEvent(event); + static PuglStatus _onEvent(PuglView* view, const PuglEvent* event) noexcept + { + View* self = static_cast(puglGetHandle(view)); + + return static_cast(self->dispatchEvent(*event)); } - PuglView* _view; + Status dispatchEvent(const PuglEvent& event) + { + switch (event.type) { + case PUGL_NOTHING: + return Status::success; + case PUGL_BUTTON_PRESS: + return dispatchTypedEvent( + static_cast(event.button)); + case PUGL_BUTTON_RELEASE: + return dispatchTypedEvent( + static_cast(event.button)); + case PUGL_CREATE: + return dispatchTypedEvent( + static_cast(event.any)); + case PUGL_DESTROY: + return dispatchTypedEvent( + static_cast(event.any)); + case PUGL_MAP: + return dispatchTypedEvent(static_cast(event.any)); + case PUGL_UNMAP: + return dispatchTypedEvent( + static_cast(event.any)); + case PUGL_CONFIGURE: + return dispatchTypedEvent( + static_cast(event.configure)); + case PUGL_EXPOSE: + return dispatchTypedEvent( + static_cast(event.expose)); + case PUGL_CLOSE: + return dispatchTypedEvent( + static_cast(event.any)); + case PUGL_KEY_PRESS: + return dispatchTypedEvent( + static_cast(event.key)); + case PUGL_KEY_RELEASE: + return dispatchTypedEvent( + static_cast(event.key)); + case PUGL_TEXT: + return dispatchTypedEvent( + static_cast(event.text)); + case PUGL_ENTER_NOTIFY: + return dispatchTypedEvent( + static_cast(event.crossing)); + case PUGL_LEAVE_NOTIFY: + return dispatchTypedEvent( + static_cast(event.crossing)); + case PUGL_MOTION_NOTIFY: + return dispatchTypedEvent( + static_cast(event.motion)); + case PUGL_SCROLL: + return dispatchTypedEvent( + static_cast(event.scroll)); + case PUGL_FOCUS_IN: + return dispatchTypedEvent( + static_cast(event.focus)); + case PUGL_FOCUS_OUT: + return dispatchTypedEvent( + static_cast(event.focus)); + case PUGL_CLIENT: + return dispatchTypedEvent( + static_cast(event.client)); + } + + return Status::failure; + } + + template + Status dispatchTypedEvent(const E& event) + { + auto& handler = std::get(_eventFuncs); + if (handler) { + return handler(*this, event); + } + + return Status::success; + } + + Data _data; + EventFuncs _eventFuncs; }; -} // namespace pugl +} // namespace pugl /** @} */ -#endif /* PUGL_PUGL_HPP */ +#endif /* PUGL_PUGL_HPP */ -- cgit v1.2.1