aboutsummaryrefslogtreecommitdiffstats
path: root/pugl/pugl.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'pugl/pugl.hpp')
-rw-r--r--pugl/pugl.hpp437
1 files changed, 395 insertions, 42 deletions
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 <chrono>
+#include <functional>
+#include <ratio>
+#include <stdexcept>
+#include <utility>
/**
@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<PuglEventType t, class Base>
+struct TypedEvent : public Base {
+ static constexpr const PuglEventType type = t;
+};
+
+/* Strong types for every event type. */
+
+using ButtonPressEvent = TypedEvent<PUGL_BUTTON_PRESS, PuglEventButton>;
+using ButtonReleaseEvent = TypedEvent<PUGL_BUTTON_RELEASE, PuglEventButton>;
+using CreateEvent = TypedEvent<PUGL_CREATE, PuglEventAny>;
+using DestroyEvent = TypedEvent<PUGL_DESTROY, PuglEventAny>;
+using MapEvent = TypedEvent<PUGL_MAP, PuglEventAny>;
+using UnmapEvent = TypedEvent<PUGL_UNMAP, PuglEventAny>;
+using ConfigureEvent = TypedEvent<PUGL_CONFIGURE, PuglEventConfigure>;
+using ExposeEvent = TypedEvent<PUGL_EXPOSE, PuglEventExpose>;
+using CloseEvent = TypedEvent<PUGL_CLOSE, PuglEventAny>;
+using KeyPressEvent = TypedEvent<PUGL_KEY_PRESS, PuglEventKey>;
+using KeyReleaseEvent = TypedEvent<PUGL_KEY_RELEASE, PuglEventKey>;
+using TextEvent = TypedEvent<PUGL_TEXT, PuglEventText>;
+using EnterEvent = TypedEvent<PUGL_ENTER_NOTIFY, PuglEventCrossing>;
+using LeaveEvent = TypedEvent<PUGL_LEAVE_NOTIFY, PuglEventCrossing>;
+using MotionEvent = TypedEvent<PUGL_MOTION_NOTIFY, PuglEventMotion>;
+using ScrollEvent = TypedEvent<PUGL_SCROLL, PuglEventScroll>;
+using FocusInEvent = TypedEvent<PUGL_FOCUS_IN, PuglEventFocus>;
+using FocusOutEvent = TypedEvent<PUGL_FOCUS_OUT, PuglEventFocus>;
+using ClientEvent = TypedEvent<PUGL_CLIENT, PuglEventClient>;
+
+static inline const char*
+strerror(pugl::Status status)
+{
+ return puglStrerror(static_cast<PuglStatus>(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<double>;
+ using time_point = std::chrono::time_point<Clock>;
+
+ 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<Status>(puglSetClassName(_world, name));
+ }
+
+ double getTime() const { return puglGetTime(_world); }
+
+ Status pollEvents(const double timeout)
+ {
+ return static_cast<Status>(puglPollEvents(_world, timeout));
+ }
+
+ Status dispatchEvents()
+ {
+ return static_cast<Status>(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<Status>(
+ puglSetViewHint(_view, static_cast<PuglViewHint>(hint), value));
+ }
+
+ bool getVisible() const { return puglGetVisible(_view); }
+
+ Status postRedisplay()
+ {
+ return static_cast<Status>(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<Status>(puglSetFrame(_view, frame));
}
- virtual void initWindowSize(int width, int height) {
- puglInitWindowSize(_view, width, height);
+ Status setMinSize(int width, int height)
+ {
+ return static_cast<Status>(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<Status>(
+ 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<Status>(puglSetWindowTitle(_view, title));
}
- virtual void initResizable(bool resizable) {
- puglInitResizable(_view, resizable);
+ Status setParentWindow(NativeWindow parent)
+ {
+ return static_cast<Status>(puglSetParentWindow(_view, parent));
}
- virtual void initTransientFor(uintptr_t parent) {
- puglInitTransientFor(_view, parent);
+ Status setTransientFor(NativeWindow parent)
+ {
+ return static_cast<Status>(puglSetTransientFor(_view, parent));
}
- virtual void initBackend(const PuglBackend* backend) {
- puglInitBackend(_view, backend);
+ Status createWindow(const char* title)
+ {
+ return static_cast<Status>(puglCreateWindow(_view, title));
}
- virtual void createWindow(const char* title) {
- puglCreateWindow(_view, title);
+ Status showWindow() { return static_cast<Status>(puglShowWindow(_view)); }
+
+ Status hideWindow() { return static_cast<Status>(puglHideWindow(_view)); }
+
+ NativeWindow getNativeWindow() { return puglGetNativeWindow(_view); }
+
+ Status setBackend(const PuglBackend* backend)
+ {
+ return static_cast<Status>(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<Status>(puglGrabFocus(_view)); }
+
+ Status requestAttention()
+ {
+ return static_cast<Status>(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<typename Data>
+class View : public ViewBase
+{
+public:
+ template<class E>
+ using TypedEventFunc = std::function<pugl::Status(View&, const E&)>;
+
+ using NothingEvent = TypedEvent<PUGL_NOTHING, PuglEvent>;
+
+ /**
+ A tuple of event handlers, one for each event type.
+
+ Note that the indices here must correspond to PuglEventType.
+ */
+ using EventFuncs = std::tuple<TypedEventFunc<NothingEvent>,
+ TypedEventFunc<ButtonPressEvent>,
+ TypedEventFunc<ButtonReleaseEvent>,
+ TypedEventFunc<CreateEvent>,
+ TypedEventFunc<DestroyEvent>,
+ TypedEventFunc<MapEvent>,
+ TypedEventFunc<UnmapEvent>,
+ TypedEventFunc<ConfigureEvent>,
+ TypedEventFunc<ExposeEvent>,
+ TypedEventFunc<CloseEvent>,
+ TypedEventFunc<KeyPressEvent>,
+ TypedEventFunc<KeyReleaseEvent>,
+ TypedEventFunc<TextEvent>,
+ TypedEventFunc<EnterEvent>,
+ TypedEventFunc<LeaveEvent>,
+ TypedEventFunc<MotionEvent>,
+ TypedEventFunc<ScrollEvent>,
+ TypedEventFunc<FocusInEvent>,
+ TypedEventFunc<FocusOutEvent>,
+ TypedEventFunc<ClientEvent>>;
+
+ using EventFunc = std::function<pugl::Status(View&, const PuglEvent&)>;
+
+ explicit View(World& world)
+ : ViewBase{world}
+ , _data{}
+ {
+ puglSetEventFunc(_view, _onEvent);
+ }
+
+ View(World& world, Data data)
+ : ViewBase{world}
+ , _data{data}
+ {
+ puglSetEventFunc(_view, _onEvent);
+ }
+
+ template<class HandledEvent>
+ Status setEventFunc(
+ std::function<pugl::Status(View&, const HandledEvent&)> handler)
+ {
+ std::get<HandledEvent::type>(_eventFuncs) = handler;
+
+ return Status::success;
+ }
+
+ template<class HandledEvent>
+ Status setEventFunc(pugl::Status (*handler)(View&, const HandledEvent&))
+ {
+ std::get<HandledEvent::type>(_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<View*>(puglGetHandle(view));
+
+ return static_cast<PuglStatus>(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<const ButtonPressEvent&>(event.button));
+ case PUGL_BUTTON_RELEASE:
+ return dispatchTypedEvent(
+ static_cast<const ButtonReleaseEvent&>(event.button));
+ case PUGL_CREATE:
+ return dispatchTypedEvent(
+ static_cast<const CreateEvent&>(event.any));
+ case PUGL_DESTROY:
+ return dispatchTypedEvent(
+ static_cast<const DestroyEvent&>(event.any));
+ case PUGL_MAP:
+ return dispatchTypedEvent(static_cast<const MapEvent&>(event.any));
+ case PUGL_UNMAP:
+ return dispatchTypedEvent(
+ static_cast<const UnmapEvent&>(event.any));
+ case PUGL_CONFIGURE:
+ return dispatchTypedEvent(
+ static_cast<const ConfigureEvent&>(event.configure));
+ case PUGL_EXPOSE:
+ return dispatchTypedEvent(
+ static_cast<const ExposeEvent&>(event.expose));
+ case PUGL_CLOSE:
+ return dispatchTypedEvent(
+ static_cast<const CloseEvent&>(event.any));
+ case PUGL_KEY_PRESS:
+ return dispatchTypedEvent(
+ static_cast<const KeyPressEvent&>(event.key));
+ case PUGL_KEY_RELEASE:
+ return dispatchTypedEvent(
+ static_cast<const KeyReleaseEvent&>(event.key));
+ case PUGL_TEXT:
+ return dispatchTypedEvent(
+ static_cast<const TextEvent&>(event.text));
+ case PUGL_ENTER_NOTIFY:
+ return dispatchTypedEvent(
+ static_cast<const EnterEvent&>(event.crossing));
+ case PUGL_LEAVE_NOTIFY:
+ return dispatchTypedEvent(
+ static_cast<const LeaveEvent&>(event.crossing));
+ case PUGL_MOTION_NOTIFY:
+ return dispatchTypedEvent(
+ static_cast<const MotionEvent&>(event.motion));
+ case PUGL_SCROLL:
+ return dispatchTypedEvent(
+ static_cast<const ScrollEvent&>(event.scroll));
+ case PUGL_FOCUS_IN:
+ return dispatchTypedEvent(
+ static_cast<const FocusInEvent&>(event.focus));
+ case PUGL_FOCUS_OUT:
+ return dispatchTypedEvent(
+ static_cast<const FocusOutEvent&>(event.focus));
+ case PUGL_CLIENT:
+ return dispatchTypedEvent(
+ static_cast<const ClientEvent&>(event.client));
+ }
+
+ return Status::failure;
+ }
+
+ template<class E>
+ Status dispatchTypedEvent(const E& event)
+ {
+ auto& handler = std::get<E::type>(_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 */