aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2020-04-03 19:23:19 +0200
committerDavid Robillard <d@drobilla.net>2020-04-03 19:23:19 +0200
commit841b9705f10c9dc9a5b3c60da4a590863e18e5b4 (patch)
treeedf7975b85a3e8fa17e363d30dba92b10d94585d
parent8f28d8c97a8dedc240336da1ece4f4aa4207876b (diff)
downloadpugl-841b9705f10c9dc9a5b3c60da4a590863e18e5b4.tar.gz
pugl-841b9705f10c9dc9a5b3c60da4a590863e18e5b4.tar.bz2
pugl-841b9705f10c9dc9a5b3c60da4a590863e18e5b4.zip
Rewrite C++ bindings
-rw-r--r--.clang-format4
-rw-r--r--.clang-tidy11
-rw-r--r--doc/layout.xml1
-rw-r--r--doc/mainpage.md4
-rw-r--r--doc/reference.doxygen.in20
-rw-r--r--doc/style.css73
-rw-r--r--examples/pugl_cxx_demo.cpp141
-rw-r--r--pugl/pugl.h52
-rw-r--r--pugl/pugl.hpp621
-rw-r--r--pugl/pugl.ipp153
-rw-r--r--pugl/pugl_cairo.h2
-rw-r--r--pugl/pugl_cairo.hpp49
-rw-r--r--pugl/pugl_gl.h2
-rw-r--r--pugl/pugl_gl.hpp59
-rw-r--r--pugl/pugl_stub.h2
-rw-r--r--pugl/pugl_stub.hpp49
-rw-r--r--wscript50
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
diff --git a/wscript b/wscript
index 2fc48de..18728df 100644
--- a/wscript
+++ b/wscript
@@ -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'])