diff options
-rw-r--r-- | .clang-format | 4 | ||||
-rw-r--r-- | .clang-tidy | 11 | ||||
-rw-r--r-- | doc/layout.xml | 1 | ||||
-rw-r--r-- | doc/mainpage.md | 4 | ||||
-rw-r--r-- | doc/reference.doxygen.in | 20 | ||||
-rw-r--r-- | doc/style.css | 73 | ||||
-rw-r--r-- | examples/pugl_cxx_demo.cpp | 141 | ||||
-rw-r--r-- | pugl/pugl.h | 52 | ||||
-rw-r--r-- | pugl/pugl.hpp | 621 | ||||
-rw-r--r-- | pugl/pugl.ipp | 153 | ||||
-rw-r--r-- | pugl/pugl_cairo.h | 2 | ||||
-rw-r--r-- | pugl/pugl_cairo.hpp | 49 | ||||
-rw-r--r-- | pugl/pugl_gl.h | 2 | ||||
-rw-r--r-- | pugl/pugl_gl.hpp | 59 | ||||
-rw-r--r-- | pugl/pugl_stub.h | 2 | ||||
-rw-r--r-- | pugl/pugl_stub.hpp | 49 | ||||
-rw-r--r-- | wscript | 50 |
17 files changed, 1188 insertions, 105 deletions
diff --git a/.clang-format b/.clang-format index b788676..043fd1f 100644 --- a/.clang-format +++ b/.clang-format @@ -59,7 +59,7 @@ Cpp11BracedListStyle: true DerivePointerAlignment: false DisableFormat: false ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false +FixNamespaceComments: true ForEachMacros: - foreach - Q_FOREACH @@ -104,7 +104,7 @@ SpaceAfterCStyleCast: false SpaceAfterLogicalNot: false SpaceAfterTemplateKeyword: false SpaceBeforeAssignmentOperators: true -SpaceBeforeCpp11BracedList: true +SpaceBeforeCpp11BracedList: false SpaceBeforeCtorInitializerColon: true SpaceBeforeInheritanceColon: true SpaceBeforeParens: ControlStatements diff --git a/.clang-tidy b/.clang-tidy index 055d63b..7e79698 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,6 @@ Checks: > *, + -*avoid-c-arrays, -*magic-numbers, -*uppercase-literal-suffix, -android-cloexec-fopen, @@ -7,10 +8,16 @@ Checks: > -cert-flp30-c, -clang-analyzer-alpha.*, -clang-analyzer-security.FloatLoopCounter, + -fuchsia-default-arguments-calls, + -fuchsia-default-arguments-declarations, + -google-runtime-references, -hicpp-multiway-paths-covered, -hicpp-signed-bitwise, -llvm-header-guard, - -readability-else-after-return + -modernize-use-trailing-return-type, + -readability-else-after-return, + -readability-implicit-bool-conversion, + -readability-named-parameter, WarningsAsErrors: '' -HeaderFilterRegex: 'pugl/.*|test/.*' +HeaderFilterRegex: 'pugl/.*|test/.*|examples/.*' FormatStyle: file diff --git a/doc/layout.xml b/doc/layout.xml index 1889302..4744245 100644 --- a/doc/layout.xml +++ b/doc/layout.xml @@ -25,6 +25,7 @@ <!-- Layout definition for a class page --> <class> <briefdescription visible="yes"/> + <detaileddescription title=""/> <includes visible="$SHOW_INCLUDE_FILES"/> <inheritancegraph visible="$CLASS_GRAPH"/> <collaborationgraph visible="$COLLABORATION_GRAPH"/> diff --git a/doc/mainpage.md b/doc/mainpage.md index aa6f925..c04bf9e 100644 --- a/doc/mainpage.md +++ b/doc/mainpage.md @@ -1,6 +1,6 @@ This is the API documentation for Pugl. -This documentation is based around the [C API](@ref pugl_api), -there is also a [C++ API](@ref pugl) in the `pugl` namespace. +This page refers to the [C API](@ref pugl_c), +there is also a [C++ API](@ref pugl_cxx) in the `pugl` namespace. The Pugl API revolves around two main objects: the [World](@ref world) and the [View](@ref view). diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index 1357fe4..833085c 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -447,7 +447,7 @@ LOOKUP_CACHE_SIZE = 0 # normally produced when WARNINGS is set to YES. # The default value is: NO. -EXTRACT_ALL = YES +EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. @@ -498,7 +498,7 @@ EXTRACT_ANON_NSPACES = NO # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. -HIDE_UNDOC_MEMBERS = YES +HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set @@ -506,7 +506,7 @@ HIDE_UNDOC_MEMBERS = YES # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. -HIDE_UNDOC_CLASSES = YES +HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be @@ -543,7 +543,7 @@ CASE_SENSE_NAMES = YES # scope will be hidden. # The default value is: NO. -HIDE_SCOPE_NAMES = NO +HIDE_SCOPE_NAMES = YES # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to @@ -752,7 +752,7 @@ WARNINGS = YES # will automatically be disabled. # The default value is: YES. -WARN_IF_UNDOCUMENTED = YES +WARN_IF_UNDOCUMENTED = NO # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters @@ -769,7 +769,7 @@ WARN_IF_DOC_ERROR = YES # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. -WARN_NO_PARAMDOC = YES +WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. @@ -1165,7 +1165,7 @@ HTML_EXTRA_FILES = # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_HUE = 160 +HTML_COLORSTYLE_HUE = 120 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A @@ -1173,7 +1173,7 @@ HTML_COLORSTYLE_HUE = 160 # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. -HTML_COLORSTYLE_SAT = 100 +HTML_COLORSTYLE_SAT = 32 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 @@ -1930,7 +1930,7 @@ MAN_LINKS = NO # captures the structure of the code including all documentation. # The default value is: NO. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of @@ -2090,7 +2090,7 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = PUGL_API PUGL_DISABLE_DEPRECATED +PREDEFINED = PUGL_API PUGL_DISABLE_DEPRECATED PUGL_CONST_FUNC= # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/doc/style.css b/doc/style.css index 28f0519..680fe77 100644 --- a/doc/style.css +++ b/doc/style.css @@ -233,6 +233,14 @@ div.groupHeader { font-weight: 700; } +h2.groupheader { + line-height: 1.18em; + font-size: 1em; + font-weight: 600; + font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif; + margin: 1.25em 0 0.5em 0; +} + a + h2.groupheader { display: none; } @@ -387,6 +395,18 @@ table.memberdecls { line-height: 1.3em; } +table.memberdecls h3 { + line-height: 1.18em; + font-size: 1em; + font-weight: 600; + font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif; + margin: 1.25em 0 0.5em 0; +} + +tr.inherit_header td { + padding: 1em 0 0.5em 0; +} + .mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams { margin: 0; padding: 0; @@ -419,13 +439,28 @@ td.memSeparator { display: none; } +td.mlabels-left { + margin-left: 0; + padding-left: 0; +} + td.mlabels-right { - vertical-align: top; - padding-top: 4px; color: #B4C342; + font-weight: normal; + margin-left: 1em; + vertical-align: bottom; } .memtitle { + border-bottom: 1px solid #EEE; + font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif; + font-size: 1.18em; + font-weight: 600; + line-height: 1.41em; + margin: 1.5em 0 0 0; +} + +.permalink { display: none; } @@ -446,18 +481,20 @@ td.mlabels-right { } .memitem { - padding: 0.5em 0.5em 0.25em 0.5em; - margin: 1em 0 2em 0; + padding: 0; + margin: 0 0 3em 0; } .memproto { border-bottom: 1px solid #EEE; + border-left: 1px solid #EEE; + color: #444; + float: right; font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed; - font-size: 1.09em; - font-weight: 600; - line-height: 1.41em; - margin-bottom: 0.25em; - padding-bottom: 0.125em; + font-size: small; + margin-bottom: 1em; + margin-left: 1em; + padding: 0.25em 0 0.25em 0.25em; } .memproto .paramname { @@ -465,6 +502,11 @@ td.mlabels-right { padding-right: 0.25em; } +.mlabels { + padding-left: 0; + padding-right: 0; +} + .memdoc { padding: 0; } @@ -473,6 +515,19 @@ td.mlabels-right { font-style: italic; color: #444; margin-bottom: 0.75em; + margin-top: 0; + padding-top: 0.25em; + font-weight: normal; +} + +.memdoc > p:first-child, .memdoc .textblock > h3:first-child { + color: #444; + margin-bottom: 0.75em; + margin-top: 0; + padding-top: 0.25em; + font-weight: normal; + color: #444; + font-size: 0.9em; } .paramkey { diff --git a/examples/pugl_cxx_demo.cpp b/examples/pugl_cxx_demo.cpp new file mode 100644 index 0000000..8a2dc31 --- /dev/null +++ b/examples/pugl_cxx_demo.cpp @@ -0,0 +1,141 @@ +/* + 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_cxx_demo.cpp A simple demo of the Pugl C++ API. +*/ + +#include "cube_view.h" +#include "demo_utils.h" +#include "test/test_utils.h" + +#include "pugl/gl.h" +#include "pugl/pugl.h" +#include "pugl/pugl.hpp" +#include "pugl/pugl.ipp" +#include "pugl/pugl_gl.hpp" + +#include <cmath> + +class CubeView : public pugl::View +{ +public: + explicit CubeView(pugl::World& world) + : pugl::View{world} + {} + + pugl::Status onConfigure(const pugl::ConfigureEvent& event) override; + pugl::Status onUpdate(const pugl::UpdateEvent& event) override; + pugl::Status onExpose(const pugl::ExposeEvent& event) override; + pugl::Status onKeyPress(const pugl::KeyPressEvent& event) override; + pugl::Status onClose(const pugl::CloseEvent& event) override; + + bool quit() const { return _quit; } + +private: + double _xAngle{0.0}; + double _yAngle{0.0}; + double _lastDrawTime{0.0}; + bool _quit{false}; +}; + +pugl::Status +CubeView::onConfigure(const pugl::ConfigureEvent& event) +{ + reshapeCube(static_cast<float>(event.width), + static_cast<float>(event.height)); + + return pugl::Status::success; +} + +pugl::Status +CubeView::onUpdate(const pugl::UpdateEvent&) +{ + return postRedisplay(); +} + +pugl::Status +CubeView::onExpose(const pugl::ExposeEvent&) +{ + const double thisTime = world().time(); + const double dTime = thisTime - _lastDrawTime; + const double dAngle = dTime * 100.0; + + _xAngle = fmod(_xAngle + dAngle, 360.0); + _yAngle = fmod(_yAngle + dAngle, 360.0); + displayCube(cobj(), + 8.0f, + static_cast<float>(_xAngle), + static_cast<float>(_yAngle), + false); + + _lastDrawTime = thisTime; + + return pugl::Status::success; +} + +pugl::Status +CubeView::onKeyPress(const pugl::KeyPressEvent& event) +{ + if (event.key == PUGL_KEY_ESCAPE || event.key == 'q') { + _quit = true; + } + + return pugl::Status::success; +} + +pugl::Status +CubeView::onClose(const pugl::CloseEvent&) +{ + _quit = true; + + return pugl::Status::success; +} + +int +main(int argc, char** argv) +{ + const PuglTestOptions opts = puglParseTestOptions(&argc, &argv); + + pugl::World world{pugl::WorldType::program}; + CubeView view{world}; + PuglFpsPrinter fpsPrinter{}; + + world.setClassName("PuglCppTest"); + + view.setWindowTitle("Pugl C++ Test"); + view.setFrame({0, 0, 512, 512}); + view.setMinSize(64, 64); + view.setAspectRatio(1, 1, 16, 9); + view.setBackend(pugl::glBackend()); + view.setHint(pugl::ViewHint::resizable, opts.resizable); + view.setHint(pugl::ViewHint::samples, opts.samples); + view.setHint(pugl::ViewHint::doubleBuffer, opts.doubleBuffer); + view.setHint(pugl::ViewHint::swapInterval, opts.doubleBuffer); + view.setHint(pugl::ViewHint::ignoreKeyRepeat, opts.ignoreKeyRepeat); + view.realize(); + view.showWindow(); + + unsigned framesDrawn = 0; + while (!view.quit()) { + world.update(0.0); + + ++framesDrawn; + puglPrintFps(world.cobj(), &fpsPrinter, &framesDrawn); + } + + return 0; +} diff --git a/pugl/pugl.h b/pugl/pugl.h index 57e23fa..fbdf5db 100644 --- a/pugl/pugl.h +++ b/pugl/pugl.h @@ -52,6 +52,12 @@ # endif #endif +#if defined(__GNUC__) +# define PUGL_CONST_FUNC __attribute__((const)) +#else +# define PUGL_CONST_FUNC +#endif + #ifdef __cplusplus # define PUGL_BEGIN_DECLS extern "C" { # define PUGL_END_DECLS } @@ -63,9 +69,13 @@ PUGL_BEGIN_DECLS /** - @defgroup pugl_api Pugl + @defgroup pugl Pugl A minimal portable API for embeddable GUIs. @{ + + @defgroup pugl_c C API + Public C API. + @{ */ /** @@ -87,7 +97,7 @@ typedef struct { Event definitions. All updates to the view happen via events, which are dispatched to the - view's #PuglEventFunc by Pugl. Most events map directly to one from the + view's event function by Pugl. Most events map directly to one from the underlying window system, but some are constructed by Pugl itself so there is not necessarily a direct correspondence. @@ -355,12 +365,12 @@ typedef struct { as text input. Keys are represented portably as Unicode code points, using the "natural" - code point for the key where possible (see #PuglKey for details). The #key + code point for the key where possible (see #PuglKey for details). The `key` field is the code for the pressed key, without any modifiers applied. For - example, a press or release of the 'A' key will have #key 97 ('a') + example, a press or release of the 'A' key will have `key` 97 ('a') regardless of whether shift or control are being held. - Alternatively, the raw #keycode can be used to work directly with physical + Alternatively, the raw `keycode` can be used to work directly with physical keys, but note that this value is not portable and differs between platforms and hardware. */ @@ -407,7 +417,7 @@ typedef struct { This event is sent when the pointer enters or leaves the view. This can happen for several reasons (not just the user dragging the pointer over the - window edge), as described by the #mode field. + window edge), as described by the `mode` field. */ typedef struct { PuglEventType type; ///< #PUGL_POINTER_IN or #PUGL_POINTER_OUT @@ -456,7 +466,7 @@ typedef struct { Scroll event. The scroll distance is expressed in "lines", an arbitrary unit that - corresponds to a single tick of a detented mouse wheel. For example, #dy = + corresponds to a single tick of a detented mouse wheel. For example, `dy` = 1.0 scrolls 1 line up. Some systems and devices support finer resolution and/or higher values for fast scrolls, so programs should handle any value gracefully. @@ -494,7 +504,7 @@ typedef struct { This event is sent at the regular interval specified in the call to puglStartTimer() that activated it. - The #id is the application-specific ID given to puglStartTimer() which + The `id` is the application-specific ID given to puglStartTimer() which distinguishes this timer from others. It should always be checked in the event handler, even in applications that register only one timer. */ @@ -507,7 +517,7 @@ typedef struct { /** View event. - This is a union of all event types. The #type must be checked to determine + This is a union of all event types. The type must be checked to determine which fields are safe to access. A pointer to PuglEvent can either be cast to the appropriate type, or the union members used. @@ -594,7 +604,7 @@ typedef struct PuglWorldImpl PuglWorld; typedef void* PuglWorldHandle; /** - The type of a PuglWorld. + The type of a World. */ typedef enum { PUGL_PROGRAM, ///< Top-level application @@ -728,19 +738,18 @@ puglGetTime(const PuglWorld* world); This function is a single iteration of the main loop, and should be called repeatedly to update all views. - If a positive timeout is given, then events will be processed for that - amount of time, starting from when this function was called. For purely - event-driven programs, a timeout of -1.0 can be used to block indefinitely - until something happens. For continuously animating programs, a timeout - that is a reasonable fraction of the ideal frame period should be used, to - minimise input latency by ensuring that as many input events are consumed as - possible before drawing. Plugins should always use a timeout of 0.0 to - avoid blocking the host. + If `timeout` is zero, then this function will not block. Plugins should + always use a timeout of zero to avoid blocking the host. - @param world The world to update. + If a positive `timeout` is given, then events will be processed for that + amount of time, starting from when this function was called. - @param timeout Maximum time to wait, in seconds. If zero, the call returns - immediately, if negative, the call blocks indefinitely. + If a negative `timeout` is given, this function will block indefinitely + until an event occurs. + + For continuously animating programs, a timeout that is a reasonable fraction + of the ideal frame period should be used, to minimise input latency by + ensuring that as many input events are consumed as possible before drawing. @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error. */ @@ -1496,6 +1505,7 @@ puglLeaveContext(PuglView* view, bool drawing); /** @} @} + @} */ PUGL_END_DECLS diff --git a/pugl/pugl.hpp b/pugl/pugl.hpp index 73cfe2a..e40424a 100644 --- a/pugl/pugl.hpp +++ b/pugl/pugl.hpp @@ -1,5 +1,5 @@ /* - Copyright 2012-2019 David Robillard <http://drobilla.net> + 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 @@ -23,12 +23,18 @@ #include "pugl/pugl.h" -/** - @defgroup puglxx C++ +#include <cassert> +#include <chrono> +#include <functional> +#include <memory> +#include <stdexcept> +#include <type_traits> +/** + @defgroup pugl_cxx C++ API C++ API wrapper. - @ingroup pugl_api + @ingroup pugl @{ */ @@ -37,84 +43,607 @@ */ namespace 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: + T* cobj() { return _ptr.get(); } + const T* cobj() const { return _ptr.get(); } + +protected: + explicit Wrapper(T* ptr) + : _ptr(ptr, Deleter<T, Free>{}) + {} + +private: + std::unique_ptr<T, Deleter<T, Free>> _ptr; +}; + +} // namespace detail + +using Rect = PuglRect; ///< @copydoc PuglRect + +/** + @defgroup eventsxx Events + @ingroup pugl_cxx + @copydoc 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>; + /** - A drawable region that receives events. + @} + @defgroup statusxx Status + @ingroup pugl_cxx + @copydoc status + @{ +*/ + +/// @copydoc PuglStatus +enum class Status { + success, ///< @copydoc PUGL_SUCCESS + failure, ///< @copydoc PUGL_FAILURE + unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR + badBackend, ///< @copydoc PUGL_BAD_BACKEND + 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, ""); - This is a thin wrapper for a PuglView that contains only a pointer. +/// @copydoc puglStrerror +static inline const char* +strerror(const pugl::Status status) +{ + return puglStrerror(static_cast<PuglStatus>(status)); +} - @ingroup puglxx +/** + @} + @defgroup worldxx World + @ingroup pugl_cxx + @copydoc world + @{ */ -class View { + +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: - View(int* pargc, char** argv) - : _view(puglInit(pargc, argv)) + 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} + {} + + /// Return the current time + time_point now() const; + +private: + const pugl::World& _world; +}; + +/// @copydoc PuglWorld +class World : public detail::Wrapper<PuglWorld, puglFreeWorld> +{ +public: + explicit World(WorldType type, WorldFlags flags = {}) + : Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), flags)} + , _clock(*this) { - puglSetHandle(_view, this); - puglSetEventFunc(_view, _onEvent); + if (!cobj()) { + throw std::runtime_error("Failed to create pugl::World"); + } } - virtual ~View() { puglDestroy(_view); } + /// @copydoc puglGetNativeWorld + void* nativeWorld() { return puglGetNativeWorld(cobj()); } + + // TODO: setLogFunc - virtual void initWindowParent(PuglNativeWindow parent) { - puglInitWindowParent(_view, parent); + Status setLogLevel(const LogLevel level) + { + return static_cast<Status>( + puglSetLogLevel(cobj(), static_cast<PuglLogLevel>(level))); } - virtual void initWindowSize(int width, int height) { - puglInitWindowSize(_view, width, height); + /// @copydoc puglSetClassName + Status setClassName(const char* const name) + { + return static_cast<Status>(puglSetClassName(cobj(), name)); } - virtual void initWindowMinSize(int width, int height) { - puglInitWindowMinSize(_view, width, height); + /// @copydoc puglGetTime + double time() const { return puglGetTime(cobj()); } + + /// @copydoc puglUpdate + Status update(const double timeout) + { + return static_cast<Status>(puglUpdate(cobj(), timeout)); } - 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); + /// 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()}}; +} + +/** + @} + @defgroup viewxx View + @ingroup pugl_cxx + @copydoc 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 +}; + +static_assert(ViewHint(PUGL_IGNORE_KEY_REPEAT) == ViewHint::ignoreKeyRepeat, + ""); + +using ViewHintValue = PuglViewHintValue; ///< @copydoc PuglViewHintValue + +/// @copydoc PuglView +class View : public 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 void initResizable(bool resizable) { - puglInitResizable(_view, resizable); + virtual ~View() = default; + + 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)); } - virtual void initTransientFor(uintptr_t parent) { - puglInitTransientFor(_view, parent); + /** + @} + @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)); } - virtual void initBackend(const PuglBackend* backend) { - puglInitBackend(_view, backend); + /// @copydoc puglSetMinSize + Status setMinSize(int width, int height) + { + return static_cast<Status>(puglSetMinSize(cobj(), width, height)); } - virtual void createWindow(const char* title) { - puglCreateWindow(_view, title); + /// @copydoc puglSetAspectRatio + Status setAspectRatio(int minX, int minY, int maxX, int maxY) + { + return static_cast<Status>( + puglSetAspectRatio(cobj(), minX, minY, maxX, maxY)); } - virtual void showWindow() { puglShowWindow(_view); } - virtual void hideWindow() { puglHideWindow(_view); } - virtual PuglNativeWindow getNativeWindow() { return puglGetNativeWindow(_view); } + /** + @} + @name Windows + Methods for working with top-level windows. + @{ + */ - virtual void onEvent(const PuglEvent* event) = 0; + /// @copydoc puglSetWindowTitle + Status setWindowTitle(const char* title) + { + return static_cast<Status>(puglSetWindowTitle(cobj(), title)); + } - 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); } + /// @copydoc puglSetParentWindow + Status setParentWindow(NativeView parent) + { + return static_cast<Status>(puglSetParentWindow(cobj(), parent)); + } - PuglView* cobj() { return _view; } + /// @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 puglRequestAttention + Status requestAttention() + { + return static_cast<Status>(puglRequestAttention(cobj())); + } + + /** + @} + @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; + + /** + @} + */ private: - static void _onEvent(PuglView* view, const PuglEvent* event) { - ((View*)puglGetHandle(view))->onEvent(event); + template<class Typed, class Base> + static const Typed& typedEventRef(const Base& base) + { + const Typed& 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; } - PuglView* _view; + 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))); + } + + return PUGL_FAILURE; + } + + World& _world; }; -} // namespace pugl +/** + @} +*/ + +} // namespace pugl /** @} */ -#endif /* PUGL_PUGL_HPP */ +#endif /* PUGL_PUGL_HPP */ diff --git a/pugl/pugl.ipp b/pugl/pugl.ipp new file mode 100644 index 0000000..2fdf255 --- /dev/null +++ b/pugl/pugl.ipp @@ -0,0 +1,153 @@ +/* + 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.ipp 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; +} + +} diff --git a/pugl/pugl_cairo.h b/pugl/pugl_cairo.h index e71072e..0321d0c 100644 --- a/pugl/pugl_cairo.h +++ b/pugl/pugl_cairo.h @@ -28,7 +28,7 @@ PUGL_BEGIN_DECLS /** @defgroup cairo Cairo Cairo graphics support. - @ingroup pugl_api + @ingroup pugl_c @{ */ diff --git a/pugl/pugl_cairo.hpp b/pugl/pugl_cairo.hpp new file mode 100644 index 0000000..f31201e --- /dev/null +++ b/pugl/pugl_cairo.hpp @@ -0,0 +1,49 @@ +/* + 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_cairo.hpp 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() +{ + return puglCairoBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_CAIRO_HPP diff --git a/pugl/pugl_gl.h b/pugl/pugl_gl.h index 9c5fa94..3257bdf 100644 --- a/pugl/pugl_gl.h +++ b/pugl/pugl_gl.h @@ -28,7 +28,7 @@ PUGL_BEGIN_DECLS /** @defgroup gl OpenGL OpenGL graphics support. - @ingroup pugl_api + @ingroup pugl_c @{ */ diff --git a/pugl/pugl_gl.hpp b/pugl/pugl_gl.hpp new file mode 100644 index 0000000..ae618ec --- /dev/null +++ b/pugl/pugl_gl.hpp @@ -0,0 +1,59 @@ +/* + 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_gl.hpp 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) +{ + return puglGetProcAddress(name); +} + +/// @copydoc puglGlBackend +static inline const PuglBackend* +glBackend() +{ + return puglGlBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_GL_HPP diff --git a/pugl/pugl_stub.h b/pugl/pugl_stub.h index da918aa..fa89552 100644 --- a/pugl/pugl_stub.h +++ b/pugl/pugl_stub.h @@ -36,7 +36,7 @@ PUGL_BEGIN_DECLS for other backends to reuse since not all need non-trivial implementations of every backend function. - @ingroup pugl_api + @ingroup pugl_c @{ */ diff --git a/pugl/pugl_stub.hpp b/pugl/pugl_stub.hpp new file mode 100644 index 0000000..7e544ee --- /dev/null +++ b/pugl/pugl_stub.hpp @@ -0,0 +1,49 @@ +/* + 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_stub.hpp 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() +{ + return puglStubBackend(); +} + +/** + @} +*/ + +} // namespace pugl + +#endif // PUGL_PUGL_STUB_HPP @@ -22,6 +22,7 @@ out = 'build' # Build directory def options(ctx): ctx.load('compiler_c') + ctx.load('compiler_cxx') opts = ctx.configuration_options() opts.add_option('--target', default=None, dest='target', @@ -41,24 +42,34 @@ def options(ctx): def configure(conf): conf.load('compiler_c', cache=True) + try: + conf.load('compiler_cxx', cache=True) + except Exception: + pass + conf.load('autowaf', cache=True) autowaf.set_c_lang(conf, 'c99') + if 'COMPILER_CXX' in conf.env: + autowaf.set_cxx_lang(conf, 'c++11') conf.env.ALL_HEADERS = Options.options.all_headers conf.env.TARGET_PLATFORM = Options.options.target or sys.platform platform = conf.env.TARGET_PLATFORM + def append_cflags(flags): + conf.env.append_value('CFLAGS', flags) + conf.env.append_value('CXXFLAGS', flags) + if platform == 'darwin': - conf.env.append_unique('CFLAGS', ['-Wno-deprecated-declarations']) + append_cflags(['-Wno-deprecated-declarations']) if conf.env.MSVC_COMPILER: - conf.env.append_unique('CFLAGS', ['/wd4191']) + append_cflags(['/wd4191', '/wd4355']) else: - conf.env.append_value('LINKFLAGS', ['-fvisibility=hidden']) - conf.env.append_value('CFLAGS', ['-fvisibility=hidden']) + conf.env.append_unique('LINKFLAGS', ['-fvisibility=hidden']) + append_cflags(['-fvisibility=hidden']) if Options.options.strict: - conf.env.append_value('CFLAGS', ['-Wunused-parameter', - '-Wno-pedantic']) + append_cflags(['-Wunused-parameter', '-Wno-pedantic']) if conf.env.TARGET_PLATFORM == 'darwin': conf.env.append_unique('CFLAGS', ['-DGL_SILENCE_DEPRECATION']) @@ -78,8 +89,12 @@ def configure(conf): '-Wno-switch-enum', ]) - conf.env.append_value('CXXFLAGS', ['-Wno-c++98-compat', - '-Wno-c++98-compat-pedantic']) + conf.env.append_value('CXXFLAGS', [ + '-Wno-c++98-compat', + '-Wno-c++98-compat-pedantic', + '-Wno-documentation-unknown-command', + '-Wno-old-style-cast', + ]) conf.check_cc(lib='m', uselib_store='M', mandatory=False) conf.check_cc(lib='dl', uselib_store='DL', mandatory=False) @@ -189,6 +204,7 @@ def build(bld): includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION bld.install_files(includedir, bld.path.ant_glob('pugl/*.h')) bld.install_files(includedir, bld.path.ant_glob('pugl/*.hpp')) + bld.install_files(includedir, bld.path.ant_glob('pugl/*.ipp')) if bld.env.ALL_HEADERS: detaildir = os.path.join(includedir, 'detail') bld.install_files(detaildir, bld.path.ant_glob('pugl/detail/*.h')) @@ -293,6 +309,8 @@ def build(bld): source=['pugl/detail/x11_cairo.c']) def build_example(prog, source, platform, backend, **kwargs): + lang = 'cxx' if source[0].endswith('.cpp') else 'c' + use = ['pugl_%s_static' % platform, 'pugl_%s_%s_static' % (platform, backend)] @@ -312,7 +330,7 @@ def build(bld): deps.get(platform, {}).get(k, []) + deps.get(backend_lib, {}).get(k, []))}) - bld(features = 'c cprogram', + bld(features = '%s %sprogram' % (lang, lang), source = source, target = target, use = use, @@ -353,6 +371,12 @@ def build(bld): 'pugl_%s_stub_static' % platform], uselib = deps[platform]['uselib'] + ['CAIRO']) + if bld.env.CXX and bld.env.HAVE_GL: + build_example('pugl_cxx_demo', ['examples/pugl_cxx_demo.cpp'], + platform, 'gl', + defines=['PUGL_DISABLE_DEPRECATED'], + uselib=['GL', 'M']) + if bld.env.DOCS: autowaf.build_dox(bld, 'PUGL', PUGL_VERSION, top, out) @@ -377,7 +401,13 @@ def lint(ctx): files = [c['file'] for c in commands if os.path.basename(c['file']) != 'glad.c'] - subprocess.call(['clang-tidy'] + files, cwd='build') + c_files = [f for f in files if f.endswith('.c')] + cpp_files = [f for f in files if f.endswith('.cpp')] + + subprocess.call(['clang-tidy'] + c_files, cwd='build') + + subprocess.call(['clang-tidy', '--header-filter=".*\\.hpp'] + cpp_files, + cwd='build') try: subprocess.call(['iwyu_tool.py', '-o', 'clang', '-p', 'build']) |