diff options
author | David Robillard <d@drobilla.net> | 2020-10-29 13:45:46 +0100 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2020-10-30 10:58:27 +0100 |
commit | 750eb8027b1f164c276ac7b1b14f04485cf4429d (patch) | |
tree | 1906196c3088d3f7c90255c1d653764a16f3c5c2 /bindings | |
parent | 21d1e350a3b22ba690553f8371714fd9e896c7c8 (diff) | |
download | pugl-750eb8027b1f164c276ac7b1b14f04485cf4429d.tar.gz pugl-750eb8027b1f164c276ac7b1b14f04485cf4429d.tar.bz2 pugl-750eb8027b1f164c276ac7b1b14f04485cf4429d.zip |
Move C++ bindings to a separate directory
This seemed messy and potentially misleading for what is fundamentally a C++
library. It also makes it possible to set separate clang-tidy and clang-format
settings for each to avoid "tainting" the C settings, though currently the
headers use the same checks.
Diffstat (limited to 'bindings')
-rw-r--r-- | bindings/cxx/include/.clang-tidy | 5 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/pugl.hpp | 742 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/pugl.ipp | 166 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/pugl_cairo.hpp | 50 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/pugl_gl.hpp | 60 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/pugl_stub.hpp | 50 | ||||
-rw-r--r-- | bindings/cxx/include/pugl/pugl_vulkan.hpp | 170 |
7 files changed, 1243 insertions, 0 deletions
diff --git a/bindings/cxx/include/.clang-tidy b/bindings/cxx/include/.clang-tidy new file mode 100644 index 0000000..6453ca0 --- /dev/null +++ b/bindings/cxx/include/.clang-tidy @@ -0,0 +1,5 @@ +Checks: > + *, + -*-uppercase-literal-suffix, +FormatStyle: file +HeaderFilterRegex: 'pugl/.*' diff --git a/bindings/cxx/include/pugl/pugl.hpp b/bindings/cxx/include/pugl/pugl.hpp new file mode 100644 index 0000000..65793b2 --- /dev/null +++ b/bindings/cxx/include/pugl/pugl.hpp @@ -0,0 +1,742 @@ +/* + Copyright 2012-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl.hpp + @brief Pugl C++ API wrapper. +*/ + +#ifndef PUGL_PUGL_HPP +#define PUGL_PUGL_HPP + +#include "pugl/pugl.h" + +#include <cassert> +#include <chrono> +#include <cstdint> +#include <functional> +#include <memory> +#include <stdexcept> +#include <type_traits> + +/** + Pugl C++ API namespace. +*/ +namespace pugl { + +/** + @defgroup pugl_cxx C++ API + C++ API wrapper. + + @ingroup pugl + @{ +*/ + +namespace detail { + +/// Free function for a C object +template<typename T> +using FreeFunc = void (*)(T*); + +/// Simple overhead-free deleter for a C object +template<typename T, FreeFunc<T> Free> +struct Deleter { + void operator()(T* ptr) { Free(ptr); } +}; + +/// Generic C++ wrapper for a C object +template<class T, FreeFunc<T> Free> +class Wrapper +{ +public: + Wrapper(const Wrapper&) = delete; + Wrapper& operator=(const Wrapper&) = delete; + + Wrapper(Wrapper&&) noexcept = default; + Wrapper& operator=(Wrapper&&) noexcept = default; + + T* cobj() noexcept { return _ptr.get(); } + const T* cobj() const noexcept { return _ptr.get(); } + +protected: + explicit Wrapper(T* ptr) noexcept + : _ptr(ptr, Deleter<T, Free>{}) + {} + +private: + std::unique_ptr<T, Deleter<T, Free>> _ptr; +}; + +} // namespace detail + +using Rect = PuglRect; ///< @copydoc PuglRect + +/** + @name Events + @{ +*/ + +/** + A strongly-typed analogue of PuglEvent. + + This is bit-for-bit identical to the corresponding PuglEvent, so events are + simply cast to this type to avoid any copying overhead. + + @tparam t The `type` field of the corresponding PuglEvent. + + @tparam Base The specific struct type of the corresponding PuglEvent. +*/ +template<PuglEventType t, class Base> +struct Event final : Base { + using BaseEvent = Base; + + static constexpr const PuglEventType type = t; +}; + +using Mod = PuglMod; ///< @copydoc PuglMod +using Mods = PuglMods; ///< @copydoc PuglMods +using Key = PuglKey; ///< @copydoc PuglKey +using EventType = PuglEventType; ///< @copydoc PuglEventType +using EventFlag = PuglEventFlag; ///< @copydoc PuglEventFlag +using EventFlags = PuglEventFlags; ///< @copydoc PuglEventFlags +using CrossingMode = PuglCrossingMode; ///< @copydoc PuglCrossingMode + +/// @copydoc PuglEventCreate +using CreateEvent = Event<PUGL_CREATE, PuglEventCreate>; + +/// @copydoc PuglEventDestroy +using DestroyEvent = Event<PUGL_DESTROY, PuglEventDestroy>; + +/// @copydoc PuglEventConfigure +using ConfigureEvent = Event<PUGL_CONFIGURE, PuglEventConfigure>; + +/// @copydoc PuglEventMap +using MapEvent = Event<PUGL_MAP, PuglEventMap>; + +/// @copydoc PuglEventUnmap +using UnmapEvent = Event<PUGL_UNMAP, PuglEventUnmap>; + +/// @copydoc PuglEventUpdate +using UpdateEvent = Event<PUGL_UPDATE, PuglEventUpdate>; + +/// @copydoc PuglEventExpose +using ExposeEvent = Event<PUGL_EXPOSE, PuglEventExpose>; + +/// @copydoc PuglEventClose +using CloseEvent = Event<PUGL_CLOSE, PuglEventClose>; + +/// @copydoc PuglEventFocus +using FocusInEvent = Event<PUGL_FOCUS_IN, PuglEventFocus>; + +/// @copydoc PuglEventFocus +using FocusOutEvent = Event<PUGL_FOCUS_OUT, PuglEventFocus>; + +/// @copydoc PuglEventKey +using KeyPressEvent = Event<PUGL_KEY_PRESS, PuglEventKey>; + +/// @copydoc PuglEventKey +using KeyReleaseEvent = Event<PUGL_KEY_RELEASE, PuglEventKey>; + +/// @copydoc PuglEventText +using TextEvent = Event<PUGL_TEXT, PuglEventText>; + +/// @copydoc PuglEventCrossing +using PointerInEvent = Event<PUGL_POINTER_IN, PuglEventCrossing>; + +/// @copydoc PuglEventCrossing +using PointerOutEvent = Event<PUGL_POINTER_OUT, PuglEventCrossing>; + +/// @copydoc PuglEventButton +using ButtonPressEvent = Event<PUGL_BUTTON_PRESS, PuglEventButton>; + +/// @copydoc PuglEventButton +using ButtonReleaseEvent = Event<PUGL_BUTTON_RELEASE, PuglEventButton>; + +/// @copydoc PuglEventMotion +using MotionEvent = Event<PUGL_MOTION, PuglEventMotion>; + +/// @copydoc PuglEventScroll +using ScrollEvent = Event<PUGL_SCROLL, PuglEventScroll>; + +/// @copydoc PuglEventClient +using ClientEvent = Event<PUGL_CLIENT, PuglEventClient>; + +/// @copydoc PuglEventTimer +using TimerEvent = Event<PUGL_TIMER, PuglEventTimer>; + +/// @copydoc PuglEventLoopEnter +using LoopEnterEvent = Event<PUGL_LOOP_ENTER, PuglEventLoopEnter>; + +/// @copydoc PuglEventLoopLeave +using LoopLeaveEvent = Event<PUGL_LOOP_LEAVE, PuglEventLoopLeave>; + +/** + @} + @name Status + @{ +*/ + +/// @copydoc PuglStatus +enum class Status { + success, ///< @copydoc PUGL_SUCCESS + failure, ///< @copydoc PUGL_FAILURE + unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR + badBackend, ///< @copydoc PUGL_BAD_BACKEND + badConfiguration, ///< @copydoc PUGL_BAD_CONFIGURATION + badParameter, ///< @copydoc PUGL_BAD_PARAMETER + backendFailed, ///< @copydoc PUGL_BACKEND_FAILED + registrationFailed, ///< @copydoc PUGL_REGISTRATION_FAILED + realizeFailed, ///< @copydoc PUGL_REALIZE_FAILED + setFormatFailed, ///< @copydoc PUGL_SET_FORMAT_FAILED + createContextFailed, ///< @copydoc PUGL_CREATE_CONTEXT_FAILED + unsupportedType, ///< @copydoc PUGL_UNSUPPORTED_TYPE +}; + +static_assert(Status(PUGL_UNSUPPORTED_TYPE) == Status::unsupportedType, ""); + +/// @copydoc puglStrerror +static inline const char* +strerror(const pugl::Status status) noexcept +{ + return puglStrerror(static_cast<PuglStatus>(status)); +} + +/** + @} + @name World + @{ +*/ + +class World; + +/// @copydoc PuglWorldType +enum class WorldType { + program, ///< @copydoc PUGL_PROGRAM + module, ///< @copydoc PUGL_MODULE +}; + +static_assert(WorldType(PUGL_MODULE) == WorldType::module, ""); + +/// @copydoc PuglWorldFlag +enum class WorldFlag { + threads = PUGL_WORLD_THREADS, ///< @copydoc PUGL_WORLD_THREADS +}; + +static_assert(WorldFlag(PUGL_WORLD_THREADS) == WorldFlag::threads, ""); + +using WorldFlags = PuglWorldFlags; ///< @copydoc PuglWorldFlags + +/// @copydoc PuglLogLevel +enum class LogLevel { + err = PUGL_LOG_LEVEL_ERR, ///< @copydoc PUGL_LOG_LEVEL_ERR + warning = PUGL_LOG_LEVEL_WARNING, ///< @copydoc PUGL_LOG_LEVEL_WARNING + info = PUGL_LOG_LEVEL_INFO, ///< @copydoc PUGL_LOG_LEVEL_INFO + debug = PUGL_LOG_LEVEL_DEBUG, ///< @copydoc PUGL_LOG_LEVEL_DEBUG +}; + +static_assert(LogLevel(PUGL_LOG_LEVEL_DEBUG) == LogLevel::debug, ""); + +/// @copydoc PuglLogFunc +using LogFunc = + std::function<void(World& world, LogLevel level, const char* msg)>; + +/** + A `std::chrono` compatible clock that uses Pugl time. +*/ +class Clock +{ +public: + using rep = double; ///< Time representation + using duration = std::chrono::duration<double>; ///< Duration in seconds + using time_point = std::chrono::time_point<Clock>; ///< A Pugl time point + + static constexpr bool is_steady = true; ///< Steady clock flag, always true + + /// Construct a clock that uses time from puglGetTime() + explicit Clock(World& world) + : _world{world} + {} + + Clock(const Clock&) = delete; + Clock& operator=(const Clock&) = delete; + + Clock(Clock&&) = delete; + Clock& operator=(Clock&&) = delete; + + /// Return the current time + time_point now() const; + +private: + const pugl::World& _world; +}; + +/// @copydoc PuglWorld +class World : public detail::Wrapper<PuglWorld, puglFreeWorld> +{ +public: + World(const World&) = delete; + World& operator=(const World&) = delete; + + World(World&&) = delete; + World& operator=(World&&) = delete; + + explicit World(WorldType type, WorldFlags flags) + : Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), flags)} + , _clock(*this) + { + if (!cobj()) { + throw std::runtime_error("Failed to create pugl::World"); + } + } + + explicit World(WorldType type) + : World{type, {}} + { + if (!cobj()) { + throw std::runtime_error("Failed to create pugl::World"); + } + } + + /// @copydoc puglGetNativeWorld + void* nativeWorld() { return puglGetNativeWorld(cobj()); } + + // TODO: setLogFunc + + Status setLogLevel(const LogLevel level) + { + return static_cast<Status>( + puglSetLogLevel(cobj(), static_cast<PuglLogLevel>(level))); + } + + /// @copydoc puglSetClassName + Status setClassName(const char* const name) + { + return static_cast<Status>(puglSetClassName(cobj(), name)); + } + + /// @copydoc puglGetTime + double time() const { return puglGetTime(cobj()); } + + /// @copydoc puglUpdate + Status update(const double timeout) + { + return static_cast<Status>(puglUpdate(cobj(), timeout)); + } + + /// Return a clock that uses Pugl time + const Clock& clock() { return _clock; } + +private: + Clock _clock; +}; + +inline Clock::time_point +Clock::now() const +{ + return time_point{duration{_world.time()}}; +} + +/** + @} + @name View + @{ +*/ + +using Backend = PuglBackend; ///< @copydoc PuglBackend +using NativeView = PuglNativeView; ///< @copydoc PuglNativeView + +/// @copydoc PuglViewHint +enum class ViewHint { + useCompatProfile, ///< @copydoc PUGL_USE_COMPAT_PROFILE + useDebugContext, ///< @copydoc PUGL_USE_DEBUG_CONTEXT + contextVersionMajor, ///< @copydoc PUGL_CONTEXT_VERSION_MAJOR + contextVersionMinor, ///< @copydoc PUGL_CONTEXT_VERSION_MINOR + redBits, ///< @copydoc PUGL_RED_BITS + greenBits, ///< @copydoc PUGL_GREEN_BITS + blueBits, ///< @copydoc PUGL_BLUE_BITS + alphaBits, ///< @copydoc PUGL_ALPHA_BITS + depthBits, ///< @copydoc PUGL_DEPTH_BITS + stencilBits, ///< @copydoc PUGL_STENCIL_BITS + samples, ///< @copydoc PUGL_SAMPLES + doubleBuffer, ///< @copydoc PUGL_DOUBLE_BUFFER + swapInterval, ///< @copydoc PUGL_SWAP_INTERVAL + resizable, ///< @copydoc PUGL_RESIZABLE + ignoreKeyRepeat, ///< @copydoc PUGL_IGNORE_KEY_REPEAT + refreshRate, ///< @copydoc PUGL_REFRESH_RATE +}; + +static_assert(ViewHint(PUGL_REFRESH_RATE) == ViewHint::refreshRate, ""); + +using ViewHintValue = PuglViewHintValue; ///< @copydoc PuglViewHintValue + +/// @copydoc PuglCursor +enum class Cursor { + arrow, ///< @copydoc PUGL_CURSOR_ARROW + caret, ///< @copydoc PUGL_CURSOR_CARET + crosshair, ///< @copydoc PUGL_CURSOR_CROSSHAIR + hand, ///< @copydoc PUGL_CURSOR_HAND + no, ///< @copydoc PUGL_CURSOR_NO + leftRight, ///< @copydoc PUGL_CURSOR_LEFT_RIGHT + upDown, ///< @copydoc PUGL_CURSOR_UP_DOWN +}; + +static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, ""); + +/// @copydoc PuglView +class View : protected detail::Wrapper<PuglView, puglFreeView> +{ +public: + /** + @name Setup + Methods for creating and destroying a view. + @{ + */ + + explicit View(World& world) + : Wrapper{puglNewView(world.cobj())} + , _world(world) + { + if (!cobj()) { + throw std::runtime_error("Failed to create pugl::View"); + } + + puglSetHandle(cobj(), this); + puglSetEventFunc(cobj(), dispatchEvent); + } + + virtual ~View() = default; + + View(const View&) = delete; + View& operator=(const View&) = delete; + + View(View&&) = delete; + View&& operator=(View&&) = delete; + + const pugl::World& world() const { return _world; } + pugl::World& world() { return _world; } + + /// @copydoc puglSetViewHint + Status setHint(ViewHint hint, int value) + { + return static_cast<Status>( + puglSetViewHint(cobj(), static_cast<PuglViewHint>(hint), value)); + } + + /// @copydoc puglGetViewHint + int getHint(ViewHint hint) + { + return puglGetViewHint(cobj(), static_cast<PuglViewHint>(hint)); + } + + /** + @} + @name Frame + Methods for working with the position and size of a view. + @{ + */ + + /// @copydoc puglGetFrame + Rect frame() const { return puglGetFrame(cobj()); } + + /// @copydoc puglSetFrame + Status setFrame(Rect frame) + { + return static_cast<Status>(puglSetFrame(cobj(), frame)); + } + + /// @copydoc puglSetDefaultSize + Status setDefaultSize(int width, int height) + { + return static_cast<Status>(puglSetDefaultSize(cobj(), width, height)); + } + + /// @copydoc puglSetMinSize + Status setMinSize(int width, int height) + { + return static_cast<Status>(puglSetMinSize(cobj(), width, height)); + } + + /// @copydoc puglSetMaxSize + Status setMaxSize(int width, int height) + { + return static_cast<Status>(puglSetMaxSize(cobj(), width, height)); + } + + /// @copydoc puglSetAspectRatio + Status setAspectRatio(int minX, int minY, int maxX, int maxY) + { + return static_cast<Status>( + puglSetAspectRatio(cobj(), minX, minY, maxX, maxY)); + } + + /** + @} + @name Windows + Methods for working with top-level windows. + @{ + */ + + /// @copydoc puglSetWindowTitle + Status setWindowTitle(const char* title) + { + return static_cast<Status>(puglSetWindowTitle(cobj(), title)); + } + + /// @copydoc puglSetParentWindow + Status setParentWindow(NativeView parent) + { + return static_cast<Status>(puglSetParentWindow(cobj(), parent)); + } + + /// @copydoc puglSetTransientFor + Status setTransientFor(NativeView parent) + { + return static_cast<Status>(puglSetTransientFor(cobj(), parent)); + } + + /// @copydoc puglRealize + Status realize() { return static_cast<Status>(puglRealize(cobj())); } + + /// @copydoc puglShowWindow + Status showWindow() { return static_cast<Status>(puglShowWindow(cobj())); } + + /// @copydoc puglHideWindow + Status hideWindow() { return static_cast<Status>(puglHideWindow(cobj())); } + + /// @copydoc puglGetVisible + bool visible() const { return puglGetVisible(cobj()); } + + /// @copydoc puglGetNativeWindow + NativeView nativeWindow() { return puglGetNativeWindow(cobj()); } + + /** + @} + @name Graphics + Methods for working with the graphics context and scheduling + redisplays. + @{ + */ + + /// @copydoc puglGetContext + void* context() { return puglGetContext(cobj()); } + + /// @copydoc puglPostRedisplay + Status postRedisplay() + { + return static_cast<Status>(puglPostRedisplay(cobj())); + } + + /// @copydoc puglPostRedisplayRect + Status postRedisplayRect(const Rect rect) + { + return static_cast<Status>(puglPostRedisplayRect(cobj(), rect)); + } + + /** + @} + @name Interaction + Methods for interacting with the user and window system. + @{ + */ + + /// @copydoc puglGrabFocus + Status grabFocus() { return static_cast<Status>(puglGrabFocus(cobj())); } + + /// @copydoc puglHasFocus + bool hasFocus() const { return puglHasFocus(cobj()); } + + /// @copydoc puglSetBackend + Status setBackend(const PuglBackend* backend) + { + return static_cast<Status>(puglSetBackend(cobj(), backend)); + } + + /// @copydoc puglSetCursor + Status setCursor(const Cursor cursor) + { + return static_cast<Status>( + puglSetCursor(cobj(), static_cast<PuglCursor>(cursor))); + } + + /// @copydoc puglRequestAttention + Status requestAttention() + { + return static_cast<Status>(puglRequestAttention(cobj())); + } + + /// @copydoc puglStartTimer + Status startTimer(const uintptr_t id, const double timeout) + { + return static_cast<Status>(puglStartTimer(cobj(), id, timeout)); + } + + /// @copydoc puglStopTimer + Status stopTimer(const uintptr_t id) + { + return static_cast<Status>(puglStopTimer(cobj(), id)); + } + + /** + @} + @name Event Handlers + Methods called when events are dispatched to the view. + @{ + */ + + virtual Status onCreate(const CreateEvent&) PUGL_CONST_FUNC; + virtual Status onDestroy(const DestroyEvent&) PUGL_CONST_FUNC; + virtual Status onConfigure(const ConfigureEvent&) PUGL_CONST_FUNC; + virtual Status onMap(const MapEvent&) PUGL_CONST_FUNC; + virtual Status onUnmap(const UnmapEvent&) PUGL_CONST_FUNC; + virtual Status onUpdate(const UpdateEvent&) PUGL_CONST_FUNC; + virtual Status onExpose(const ExposeEvent&) PUGL_CONST_FUNC; + virtual Status onClose(const CloseEvent&) PUGL_CONST_FUNC; + virtual Status onFocusIn(const FocusInEvent&) PUGL_CONST_FUNC; + virtual Status onFocusOut(const FocusOutEvent&) PUGL_CONST_FUNC; + virtual Status onKeyPress(const KeyPressEvent&) PUGL_CONST_FUNC; + virtual Status onKeyRelease(const KeyReleaseEvent&) PUGL_CONST_FUNC; + virtual Status onText(const TextEvent&) PUGL_CONST_FUNC; + virtual Status onPointerIn(const PointerInEvent&) PUGL_CONST_FUNC; + virtual Status onPointerOut(const PointerOutEvent&) PUGL_CONST_FUNC; + virtual Status onButtonPress(const ButtonPressEvent&) PUGL_CONST_FUNC; + virtual Status onButtonRelease(const ButtonReleaseEvent&) PUGL_CONST_FUNC; + virtual Status onMotion(const MotionEvent&) PUGL_CONST_FUNC; + virtual Status onScroll(const ScrollEvent&) PUGL_CONST_FUNC; + virtual Status onClient(const ClientEvent&) PUGL_CONST_FUNC; + virtual Status onTimer(const TimerEvent&) PUGL_CONST_FUNC; + virtual Status onLoopEnter(const LoopEnterEvent&) PUGL_CONST_FUNC; + virtual Status onLoopLeave(const LoopLeaveEvent&) PUGL_CONST_FUNC; + + /** + @} + */ + + PuglView* cobj() { return Wrapper::cobj(); } + const PuglView* cobj() const { return Wrapper::cobj(); } + +private: + template<class Typed, class Base> + static const Typed& typedEventRef(const Base& base) + { + const auto& event = static_cast<const Typed&>(base); + static_assert(sizeof(event) == sizeof(typename Typed::BaseEvent), ""); + static_assert(std::is_standard_layout<Typed>::value, ""); + assert(event.type == Typed::type); + return event; + } + + static PuglStatus + dispatchEvent(PuglView* view, const PuglEvent* event) noexcept + { + try { + View* self = static_cast<View*>(puglGetHandle(view)); + + return self->dispatch(event); + } catch (...) { + return PUGL_UNKNOWN_ERROR; + } + } + + PuglStatus dispatch(const PuglEvent* event) + { + switch (event->type) { + case PUGL_NOTHING: + return PUGL_SUCCESS; + case PUGL_CREATE: + return static_cast<PuglStatus>( + onCreate(typedEventRef<CreateEvent>(event->any))); + case PUGL_DESTROY: + return static_cast<PuglStatus>( + onDestroy(typedEventRef<DestroyEvent>(event->any))); + case PUGL_CONFIGURE: + return static_cast<PuglStatus>( + onConfigure(typedEventRef<ConfigureEvent>(event->configure))); + case PUGL_MAP: + return static_cast<PuglStatus>( + onMap(typedEventRef<MapEvent>(event->any))); + case PUGL_UNMAP: + return static_cast<PuglStatus>( + onUnmap(typedEventRef<UnmapEvent>(event->any))); + case PUGL_UPDATE: + return static_cast<PuglStatus>( + onUpdate(typedEventRef<UpdateEvent>(event->any))); + case PUGL_EXPOSE: + return static_cast<PuglStatus>( + onExpose(typedEventRef<ExposeEvent>(event->expose))); + case PUGL_CLOSE: + return static_cast<PuglStatus>( + onClose(typedEventRef<CloseEvent>(event->any))); + case PUGL_FOCUS_IN: + return static_cast<PuglStatus>( + onFocusIn(typedEventRef<FocusInEvent>(event->focus))); + case PUGL_FOCUS_OUT: + return static_cast<PuglStatus>( + onFocusOut(typedEventRef<FocusOutEvent>(event->focus))); + case PUGL_KEY_PRESS: + return static_cast<PuglStatus>( + onKeyPress(typedEventRef<KeyPressEvent>(event->key))); + case PUGL_KEY_RELEASE: + return static_cast<PuglStatus>( + onKeyRelease(typedEventRef<KeyReleaseEvent>(event->key))); + case PUGL_TEXT: + return static_cast<PuglStatus>( + onText(typedEventRef<TextEvent>(event->text))); + case PUGL_POINTER_IN: + return static_cast<PuglStatus>( + onPointerIn(typedEventRef<PointerInEvent>(event->crossing))); + case PUGL_POINTER_OUT: + return static_cast<PuglStatus>( + onPointerOut(typedEventRef<PointerOutEvent>(event->crossing))); + case PUGL_BUTTON_PRESS: + return static_cast<PuglStatus>( + onButtonPress(typedEventRef<ButtonPressEvent>(event->button))); + case PUGL_BUTTON_RELEASE: + return static_cast<PuglStatus>(onButtonRelease( + typedEventRef<ButtonReleaseEvent>(event->button))); + case PUGL_MOTION: + return static_cast<PuglStatus>( + onMotion(typedEventRef<MotionEvent>(event->motion))); + case PUGL_SCROLL: + return static_cast<PuglStatus>( + onScroll(typedEventRef<ScrollEvent>(event->scroll))); + case PUGL_CLIENT: + return static_cast<PuglStatus>( + onClient(typedEventRef<ClientEvent>(event->client))); + case PUGL_TIMER: + return static_cast<PuglStatus>( + onTimer(typedEventRef<TimerEvent>(event->timer))); + case PUGL_LOOP_ENTER: + return static_cast<PuglStatus>( + onLoopEnter(typedEventRef<LoopEnterEvent>(event->any))); + case PUGL_LOOP_LEAVE: + return static_cast<PuglStatus>( + onLoopLeave(typedEventRef<LoopLeaveEvent>(event->any))); + } + + return PUGL_FAILURE; + } + + World& _world; +}; + +/** + @} + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_HPP diff --git a/bindings/cxx/include/pugl/pugl.ipp b/bindings/cxx/include/pugl/pugl.ipp new file mode 100644 index 0000000..aa6e6f1 --- /dev/null +++ b/bindings/cxx/include/pugl/pugl.ipp @@ -0,0 +1,166 @@ +/* + Copyright 2012-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl.ipp + @brief Pugl C++ API wrapper implementation. + + This file must be included exactly once in the application. +*/ + +#include "pugl/pugl.hpp" + +namespace pugl { + +Status +View::onCreate(const CreateEvent&) +{ + return pugl::Status::success; +} + +Status +View::onDestroy(const DestroyEvent&) +{ + return pugl::Status::success; +} + +Status +View::onConfigure(const ConfigureEvent&) +{ + return pugl::Status::success; +} + +Status +View::onMap(const MapEvent&) +{ + return pugl::Status::success; +} + +Status +View::onUnmap(const UnmapEvent&) +{ + return pugl::Status::success; +} + +Status +View::onUpdate(const UpdateEvent&) +{ + return pugl::Status::success; +} + +Status +View::onExpose(const ExposeEvent&) +{ + return pugl::Status::success; +} + +Status +View::onClose(const CloseEvent&) +{ + return pugl::Status::success; +} + +Status +View::onFocusIn(const FocusInEvent&) +{ + return pugl::Status::success; +} + +Status +View::onFocusOut(const FocusOutEvent&) +{ + return pugl::Status::success; +} + +Status +View::onKeyPress(const KeyPressEvent&) +{ + return pugl::Status::success; +} + +Status +View::onKeyRelease(const KeyReleaseEvent&) +{ + return pugl::Status::success; +} + +Status +View::onText(const TextEvent&) +{ + return pugl::Status::success; +} + +Status +View::onPointerIn(const PointerInEvent&) +{ + return pugl::Status::success; +} + +Status +View::onPointerOut(const PointerOutEvent&) +{ + return pugl::Status::success; +} + +Status +View::onButtonPress(const ButtonPressEvent&) +{ + return pugl::Status::success; +} + +Status +View::onButtonRelease(const ButtonReleaseEvent&) +{ + return pugl::Status::success; +} + +Status +View::onMotion(const MotionEvent&) +{ + return pugl::Status::success; +} + +Status +View::onScroll(const ScrollEvent&) +{ + return pugl::Status::success; +} + +Status +View::onClient(const ClientEvent&) +{ + return pugl::Status::success; +} + +Status +View::onTimer(const TimerEvent&) +{ + return pugl::Status::success; +} + +Status +View::onLoopEnter(const LoopEnterEvent&) +{ + return pugl::Status::success; +} + +Status +View::onLoopLeave(const LoopLeaveEvent&) +{ + return pugl::Status::success; +} + +} // namespace pugl diff --git a/bindings/cxx/include/pugl/pugl_cairo.hpp b/bindings/cxx/include/pugl/pugl_cairo.hpp new file mode 100644 index 0000000..213c977 --- /dev/null +++ b/bindings/cxx/include/pugl/pugl_cairo.hpp @@ -0,0 +1,50 @@ +/* + Copyright 2012-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_cairo.hpp + @brief Declaration of Cairo backend accessor for C++. +*/ + +#ifndef PUGL_PUGL_CAIRO_HPP +#define PUGL_PUGL_CAIRO_HPP + +#include "pugl/pugl.h" +#include "pugl/pugl_cairo.h" + +namespace pugl { + +/** + @defgroup cairoxx Cairo + Cairo graphics support. + @ingroup pugl_cxx + @{ +*/ + +/// @copydoc puglCairoBackend +static inline const PuglBackend* +cairoBackend() noexcept +{ + return puglCairoBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_CAIRO_HPP diff --git a/bindings/cxx/include/pugl/pugl_gl.hpp b/bindings/cxx/include/pugl/pugl_gl.hpp new file mode 100644 index 0000000..b7c581e --- /dev/null +++ b/bindings/cxx/include/pugl/pugl_gl.hpp @@ -0,0 +1,60 @@ +/* + Copyright 2012-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_gl.hpp + @brief OpenGL-specific C++ API. +*/ + +#ifndef PUGL_PUGL_GL_HPP +#define PUGL_PUGL_GL_HPP + +#include "pugl/pugl.h" +#include "pugl/pugl_gl.h" + +namespace pugl { + +/** + @defgroup glxx OpenGL + OpenGL graphics support. + @ingroup pugl_cxx + @{ +*/ + +/// @copydoc PuglGlFunc +using GlFunc = PuglGlFunc; + +/// @copydoc puglGetProcAddress +static inline GlFunc +getProcAddress(const char* name) noexcept +{ + return puglGetProcAddress(name); +} + +/// @copydoc puglGlBackend +static inline const PuglBackend* +glBackend() noexcept +{ + return puglGlBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_GL_HPP diff --git a/bindings/cxx/include/pugl/pugl_stub.hpp b/bindings/cxx/include/pugl/pugl_stub.hpp new file mode 100644 index 0000000..362682b --- /dev/null +++ b/bindings/cxx/include/pugl/pugl_stub.hpp @@ -0,0 +1,50 @@ +/* + Copyright 2012-2020 David Robillard <d@drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_stub.hpp + @brief Declaration of Stub backend accessor for C++. +*/ + +#ifndef PUGL_PUGL_STUB_HPP +#define PUGL_PUGL_STUB_HPP + +#include "pugl/pugl.h" +#include "pugl/pugl_stub.h" + +namespace pugl { + +/** + @defgroup stubxx Stub + Stub graphics support. + @ingroup pugl_cxx + @{ +*/ + +/// @copydoc puglStubBackend +static inline const PuglBackend* +stubBackend() noexcept +{ + return puglStubBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_STUB_HPP diff --git a/bindings/cxx/include/pugl/pugl_vulkan.hpp b/bindings/cxx/include/pugl/pugl_vulkan.hpp new file mode 100644 index 0000000..9241aba --- /dev/null +++ b/bindings/cxx/include/pugl/pugl_vulkan.hpp @@ -0,0 +1,170 @@ +/* + Copyright 2012-2020 David Robillard <http://drobilla.net> + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +*/ + +/** + @file pugl_vulkan.hpp Vulkan-specific C++ API. + + Note that this header includes Vulkan headers, so if you are writing a + program or plugin that dynamically loads vulkan, you should first define + `VK_NO_PROTOTYPES` before including it. +*/ + +#ifndef PUGL_PUGL_VULKAN_HPP +#define PUGL_PUGL_VULKAN_HPP + +#include "pugl/pugl.h" +#include "pugl/pugl.hpp" +#include "pugl/pugl_vulkan.h" + +#include <vulkan/vulkan_core.h> + +#include <cstdint> + +namespace pugl { + +/** + @defgroup vulkanxx Vulkan + Vulkan graphics support. + + Note that the Pugl C++ wrapper does not use vulkan.hpp because it is a + heavyweight dependency which not everyone uses, and its design is not very + friendly to dynamic loading in plugins anyway. However, if you do use + vulkan.hpp smart handles, it is relatively straightforward to wrap the + result of createSurface() manually. + + @ingroup pugl_cxx + @{ +*/ + +/// @copydoc PuglVulkanLoader +class PUGL_API VulkanLoader final + : public detail::Wrapper<PuglVulkanLoader, puglFreeVulkanLoader> +{ +public: + /** + Create a new dynamic loader for Vulkan functions. + + This dynamically loads the Vulkan library and gets the load functions + from it. + + Note that this constructor does not throw exceptions, though failure is + possible. To check if the Vulkan library failed to load, test this + loader, which is explicitly convertible to `bool`. It is safe to use a + failed loader, but the accessors will always return null. + */ + explicit VulkanLoader(World& world) noexcept + : Wrapper{puglNewVulkanLoader(world.cobj())} + {} + + /** + Return the `vkGetInstanceProcAddr` function. + + @return Null if the Vulkan library failed to load, or does not contain + this function (which is unlikely and indicates a broken system). + */ + PFN_vkGetInstanceProcAddr getInstanceProcAddrFunc() const noexcept + { + return cobj() ? puglGetInstanceProcAddrFunc(cobj()) : nullptr; + } + + /** + Return the `vkGetDeviceProcAddr` function. + + @return Null if the Vulkan library failed to load, or does not contain + this function (which is unlikely and indicates a broken system). + */ + PFN_vkGetDeviceProcAddr getDeviceProcAddrFunc() const noexcept + { + return cobj() ? puglGetDeviceProcAddrFunc(cobj()) : nullptr; + } + + /// Return true if this loader is valid to use + explicit operator bool() const noexcept { return cobj(); } +}; + +/** + A simple wrapper for an array of static C strings. + + This provides a minimal API that supports iteration, like `std::vector`, but + avoids allocation, exceptions, and a dependency on the C++ standard library. +*/ +class PUGL_API StaticStringArray final +{ +public: + using value_type = const char*; + using const_iterator = const char* const*; + using size_type = uint32_t; + + StaticStringArray(const char* const* strings, const uint32_t size) noexcept + : _strings{strings} + , _size{size} + {} + + const char* const* begin() const noexcept { return _strings; } + const char* const* end() const noexcept { return _strings + _size; } + const char* const* data() const noexcept { return _strings; } + uint32_t size() const noexcept { return _size; } + +private: + const char* const* _strings; + uint32_t _size; +}; + +/** + Return the Vulkan instance extensions required to draw to a PuglView. + + If successful, the returned array always contains "VK_KHR_surface", along + with whatever other platform-specific extensions are required. + + @return An array of extension name strings. +*/ +inline StaticStringArray +getInstanceExtensions() noexcept +{ + uint32_t count = 0; + const char* const* const extensions = puglGetInstanceExtensions(&count); + + return StaticStringArray{extensions, count}; +} + +/// @copydoc puglCreateSurface +inline VkResult +createSurface(const VulkanLoader& loader, + View& view, + VkInstance instance, + const VkAllocationCallbacks* const allocator, + VkSurfaceKHR* const surface) noexcept +{ + const VkResult r = puglCreateSurface( + loader.cobj(), view.cobj(), instance, allocator, surface); + + return (!r && !surface) ? VK_ERROR_INITIALIZATION_FAILED : r; +} + +/// @copydoc puglVulkanBackend +static inline const PuglBackend* +vulkanBackend() noexcept +{ + return puglVulkanBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_VULKAN_HPP |