aboutsummaryrefslogtreecommitdiff
path: root/subprojects/d2tk/pugl
diff options
context:
space:
mode:
authorHanspeter Portner <dev@open-music-kontrollers.ch>2020-03-29 20:58:30 +0200
committerHanspeter Portner <dev@open-music-kontrollers.ch>2020-03-29 20:58:30 +0200
commitb57ad6ab9de36037cf6379a0d603ff21d2f8eb3b (patch)
treeb513cd95da9129f82ba781c0646ab099f7c84f70 /subprojects/d2tk/pugl
parentf73a698a92369d1bfaa26ad46764b1b5810d1a2a (diff)
parent8de920f2eddc7eb5648fef7e966bd1b39c21911c (diff)
downloadmephisto.lv2-b57ad6ab9de36037cf6379a0d603ff21d2f8eb3b.tar.xz
Merge commit '8de920f2eddc7eb5648fef7e966bd1b39c21911c'
Diffstat (limited to 'subprojects/d2tk/pugl')
-rw-r--r--subprojects/d2tk/pugl/.clang-format127
-rw-r--r--subprojects/d2tk/pugl/.clang-tidy16
-rw-r--r--subprojects/d2tk/pugl/.gitattributes1
-rw-r--r--subprojects/d2tk/pugl/.gitlab-ci.yml12
-rw-r--r--subprojects/d2tk/pugl/AUTHORS3
-rw-r--r--subprojects/d2tk/pugl/COPYING2
-rw-r--r--subprojects/d2tk/pugl/README.md35
-rw-r--r--subprojects/d2tk/pugl/doc/header.html2
-rw-r--r--subprojects/d2tk/pugl/doc/layout.xml2
-rw-r--r--subprojects/d2tk/pugl/doc/mainpage.md76
-rw-r--r--subprojects/d2tk/pugl/doc/reference.doxygen.in8
-rw-r--r--subprojects/d2tk/pugl/doc/style.css822
-rw-r--r--subprojects/d2tk/pugl/examples/cube_view.h82
-rw-r--r--subprojects/d2tk/pugl/examples/demo_utils.h175
-rw-r--r--subprojects/d2tk/pugl/examples/glad/glad.c (renamed from subprojects/d2tk/pugl/test/glad/glad.c)132
-rw-r--r--subprojects/d2tk/pugl/examples/glad/glad.h (renamed from subprojects/d2tk/pugl/test/glad/glad.h)200
-rw-r--r--subprojects/d2tk/pugl/examples/glad/khrplatform.h (renamed from subprojects/d2tk/pugl/test/glad/khrplatform.h)0
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_cairo_demo.c (renamed from subprojects/d2tk/pugl/test/pugl_cairo_test.c)151
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_embed_demo.c (renamed from subprojects/d2tk/pugl/test/pugl_test.c)188
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_gl3_demo.c429
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_print_events.c78
-rw-r--r--subprojects/d2tk/pugl/examples/pugl_window_demo.c247
-rw-r--r--subprojects/d2tk/pugl/examples/shader_utils.h (renamed from subprojects/d2tk/pugl/test/shader_utils.h)0
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/implementation.c201
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/implementation.h23
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac.h5
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac.m406
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac_cairo.m41
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac_gl.m53
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/mac_stub.m95
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/types.h20
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win.c304
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win.h42
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win_cairo.c132
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/win_gl.c80
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11.c580
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11.h34
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11_cairo.c157
-rw-r--r--subprojects/d2tk/pugl/pugl/detail/x11_gl.c102
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl.h1097
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl.hpp23
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_cairo.h49
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_cairo_backend.h29
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_gl.h60
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_gl_backend.h29
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_stub.h103
-rw-r--r--subprojects/d2tk/pugl/pugl/pugl_stub_backend.h23
-rw-r--r--subprojects/d2tk/pugl/shaders/rect.frag35
-rw-r--r--subprojects/d2tk/pugl/shaders/rect.vert34
-rw-r--r--subprojects/d2tk/pugl/test/pugl_gl3_test.c395
-rw-r--r--subprojects/d2tk/pugl/test/test_redisplay.c132
-rw-r--r--subprojects/d2tk/pugl/test/test_show_hide.c147
-rw-r--r--subprojects/d2tk/pugl/test/test_timer.c160
-rw-r--r--subprojects/d2tk/pugl/test/test_update.c124
-rw-r--r--subprojects/d2tk/pugl/test/test_utils.h317
-rw-r--r--subprojects/d2tk/pugl/wscript243
56 files changed, 5669 insertions, 2394 deletions
diff --git a/subprojects/d2tk/pugl/.clang-format b/subprojects/d2tk/pugl/.clang-format
new file mode 100644
index 0000000..b788676
--- /dev/null
+++ b/subprojects/d2tk/pugl/.clang-format
@@ -0,0 +1,127 @@
+---
+# Language: Cpp
+# BasedOnStyle: Mozilla
+AccessModifierOffset: -4
+AlignAfterOpenBracket: Align
+AlignConsecutiveAssignments: true
+AlignConsecutiveDeclarations: true
+AlignEscapedNewlines: Left
+AlignOperands: true
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AllowShortBlocksOnASingleLine: false
+AllowShortCaseLabelsOnASingleLine: false
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortLambdasOnASingleLine: All
+AllowShortIfStatementsOnASingleLine: Never
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: TopLevel
+AlwaysBreakAfterReturnType: TopLevel
+AlwaysBreakBeforeMultilineStrings: false
+AlwaysBreakTemplateDeclarations: Yes
+BinPackArguments: false
+BinPackParameters: false
+BraceWrapping:
+ AfterCaseLabel: false
+ AfterClass: true
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: true
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: true
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: true
+ SplitEmptyRecord: false
+ SplitEmptyNamespace: true
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+BreakBeforeInheritanceComma: false
+BreakInheritanceList: BeforeComma
+BreakBeforeTernaryOperators: true
+BreakConstructorInitializersBeforeComma: false
+BreakConstructorInitializers: BeforeComma
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: true
+ColumnLimit: 80
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 4
+ContinuationIndentWidth: 4
+Cpp11BracedListStyle: true
+DerivePointerAlignment: false
+DisableFormat: false
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: '^"(llvm|llvm-c|clang|clang-c)/'
+ Priority: 2
+ - Regex: '^(<|"(gtest|gmock|isl|json)/)'
+ Priority: 3
+ - Regex: '.*'
+ Priority: 1
+IncludeIsMainRegex: '(Test)?$'
+IndentCaseLabels: false
+IndentPPDirectives: AfterHash
+IndentWidth: 4
+IndentWrappedFunctionNames: false
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
+KeepEmptyLinesAtTheStartOfBlocks: true
+MacroBlockBegin: ''
+MacroBlockEnd: ''
+MaxEmptyLinesToKeep: 1
+NamespaceIndentation: None
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: true
+ObjCSpaceBeforeProtocolList: false
+PenaltyBreakAssignment: 100
+PenaltyBreakBeforeFirstCallParameter: 100
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyReturnTypeOnItsOwnLine: 0
+PointerAlignment: Left
+ReflowComments: true
+SortIncludes: true
+SortUsingDeclarations: true
+SpaceAfterCStyleCast: false
+SpaceAfterLogicalNot: false
+SpaceAfterTemplateKeyword: false
+SpaceBeforeAssignmentOperators: true
+SpaceBeforeCpp11BracedList: true
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceBeforeParens: ControlStatements
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceInEmptyParentheses: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: false
+SpacesInContainerLiterals: true
+SpacesInCStyleCastParentheses: false
+SpacesInParentheses: false
+SpacesInSquareBrackets: false
+Standard: Cpp11
+StatementMacros:
+ - PUGL_API
+ - PUGL_DEPRECATED_BY
+ - PUGL_UNUSED
+TabWidth: 4
+UseTab: ForIndentation
+...
+
diff --git a/subprojects/d2tk/pugl/.clang-tidy b/subprojects/d2tk/pugl/.clang-tidy
new file mode 100644
index 0000000..055d63b
--- /dev/null
+++ b/subprojects/d2tk/pugl/.clang-tidy
@@ -0,0 +1,16 @@
+Checks: >
+ *,
+ -*magic-numbers,
+ -*uppercase-literal-suffix,
+ -android-cloexec-fopen,
+ -bugprone-suspicious-string-compare,
+ -cert-flp30-c,
+ -clang-analyzer-alpha.*,
+ -clang-analyzer-security.FloatLoopCounter,
+ -hicpp-multiway-paths-covered,
+ -hicpp-signed-bitwise,
+ -llvm-header-guard,
+ -readability-else-after-return
+WarningsAsErrors: ''
+HeaderFilterRegex: 'pugl/.*|test/.*'
+FormatStyle: file
diff --git a/subprojects/d2tk/pugl/.gitattributes b/subprojects/d2tk/pugl/.gitattributes
new file mode 100644
index 0000000..32967c1
--- /dev/null
+++ b/subprojects/d2tk/pugl/.gitattributes
@@ -0,0 +1 @@
+waflib/* linguist-vendored
diff --git a/subprojects/d2tk/pugl/.gitlab-ci.yml b/subprojects/d2tk/pugl/.gitlab-ci.yml
index 0a2215c..d1d166d 100644
--- a/subprojects/d2tk/pugl/.gitlab-ci.yml
+++ b/subprojects/d2tk/pugl/.gitlab-ci.yml
@@ -85,27 +85,23 @@ mingw64_rel:
mac_dbg:
<<: *build_definition
script: python ./waf configure build -dsT --no-coverage
- tags:
- - macos
+ tags: [macos]
mac_rel:
<<: *build_definition
script: python ./waf configure build -sT --no-coverage
- tags:
- - macos
+ tags: [macos]
win_dbg:
<<: *build_definition
script:
- python ./waf configure build -dT --no-coverage
- tags:
- - windows
+ tags: [windows,msvc,python]
win_rel:
<<: *build_definition
script: python ./waf configure build -T --no-coverage
- tags:
- - windows
+ tags: [windows,msvc,python]
pages:
stage: deploy
diff --git a/subprojects/d2tk/pugl/AUTHORS b/subprojects/d2tk/pugl/AUTHORS
index 6c09a5b..1470491 100644
--- a/subprojects/d2tk/pugl/AUTHORS
+++ b/subprojects/d2tk/pugl/AUTHORS
@@ -6,3 +6,6 @@ Robin Gareus <robin@gareus.org>
Erik Åldstedt Sund <erikalds@gmail.com>
Hanspeter Portner <dev@open-music-kontrollers.ch>
Stefan Westerfeld <stefan@space.twc.de>
+Jordan Halase <jordan@halase.me>
+Oliver Schmidt <oliver@luced.de>
+Zoë Sparks <zoe@milky.flowers> \ No newline at end of file
diff --git a/subprojects/d2tk/pugl/COPYING b/subprojects/d2tk/pugl/COPYING
index b16a139..4a287b9 100644
--- a/subprojects/d2tk/pugl/COPYING
+++ b/subprojects/d2tk/pugl/COPYING
@@ -1,4 +1,4 @@
-Copyright 2011-2019 David Robillard <http://drobilla.net>
+Copyright 2011-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
diff --git a/subprojects/d2tk/pugl/README.md b/subprojects/d2tk/pugl/README.md
index 718e690..ae9c420 100644
--- a/subprojects/d2tk/pugl/README.md
+++ b/subprojects/d2tk/pugl/README.md
@@ -67,9 +67,42 @@ don't depend on Cairo and its dependencies, or vice-versa.
Distributions are encouraged to include static libraries if possible so that
developers can build portable plugin binaries.
+Testing
+-------
+
+There are a few unit tests included which can be run with `python waf test
+--gui-tests`, but unfortunately manual testing is still required.
+
+Several example programs are included that serve as both manual tests and
+demonstrations:
+
+ * `pugl_embed_demo` shows a view embedded in another, and also tests
+ requesting attention (which happens after 5 seconds), keyboard focus
+ (switched by pressing tab), view moving (with the arrow keys), and view
+ resizing (with the arrow keys while shift is held). This program uses only
+ very old OpenGL and should work on any system.
+
+ * `pugl_window_demo` demonstrates multiple top-level windows.
+
+ * `pugl_gl3_demo` demonstrates using more modern OpenGL where dynamic loading
+ and shaders are required. It can also be used to test performance by
+ passing the number of rectangles to draw on the command line.
+
+ * `pugl_cairo_demo` demonstrates using Cairo on top of the native windowing
+ system (without OpenGL), and partial redrawing.
+
+ * `pugl_print_events` is a utility that prints all received events to the
+ console in a human readable format.
+
+All example programs support several command line options to control various
+behaviours, see the output of `--help` for details. Please file an issue if
+any of these programs do not work as expected on your system.
+
Documentation
-------------
- * [API reference](https://lv2.gitlab.io/pugl/)
+The [API reference](https://lv2.gitlab.io/pugl/) for the latest master is
+available online, and can also be built from the source code by configuring
+with `--docs`.
-- David Robillard <d@drobilla.net>
diff --git a/subprojects/d2tk/pugl/doc/header.html b/subprojects/d2tk/pugl/doc/header.html
index 07d4076..54c25b0 100644
--- a/subprojects/d2tk/pugl/doc/header.html
+++ b/subprojects/d2tk/pugl/doc/header.html
@@ -41,7 +41,9 @@
<ul class="tablist">
<li><a href="index.html"><span>Main&#160;Page</span></a></li>
<li><a href="modules.html"><span>Modules</span></a></li>
+ <li><a href="namespaces.html"><span>Namespaces</span></a></li>
<li><a href="annotated.html"><span>Data&#160;Structures</span></a></li>
+ <li><a href="functions.html"><span>Symbols</span></a></li>
<li><a href="files.html"><span>Files</span></a></li>
</ul>
</div>
diff --git a/subprojects/d2tk/pugl/doc/layout.xml b/subprojects/d2tk/pugl/doc/layout.xml
index f752de1..1889302 100644
--- a/subprojects/d2tk/pugl/doc/layout.xml
+++ b/subprojects/d2tk/pugl/doc/layout.xml
@@ -83,6 +83,7 @@
<!-- Layout definition for a namespace page -->
<namespace>
<briefdescription visible="yes"/>
+ <detaileddescription title=""/>
<memberdecl>
<nestednamespaces visible="yes" title=""/>
<constantgroups visible="yes" title=""/>
@@ -93,7 +94,6 @@
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
- <detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<typedefs title=""/>
diff --git a/subprojects/d2tk/pugl/doc/mainpage.md b/subprojects/d2tk/pugl/doc/mainpage.md
index b16551a..d7a96fa 100644
--- a/subprojects/d2tk/pugl/doc/mainpage.md
+++ b/subprojects/d2tk/pugl/doc/mainpage.md
@@ -1,43 +1,73 @@
This is the API documentation for Pugl.
-The complete C API is documented in the [Pugl](@ref pugl) group,
-and the C++ wrapper in the [Puglmm](@ref puglmm) group.
+This documentation is based around the [C API](@ref pugl_api),
+there is also a [C++ API](@ref pugl) in the `pugl` namespace.
The Pugl API revolves around two main objects:
the [World](@ref world) and the [View](@ref view).
-An application creates a single world to manage system-level state,
+An application creates a single world to manage top-level state,
then creates one or more views to display.
-## View creation
+## World
+
+The [World](@ref world) contains all top-level state,
+and manages views and the event loop.
+
+A world must be [created](@ref puglNewWorld) before any views,
+and it must outlive all views.
+
+## View
+
+A [View](@ref view) is a drawable region that receives events.
Creating a visible view is a multi-step process.
-A new view allocated with #puglNewView does not yet represent a "real" system window.
-To display, it must first have a [backend set](@ref puglSetBackend),
+When a new view is [created](@ref puglNewView),
+it does not yet represent a real system view or window.
+To display, it must first have a [backend](@ref puglSetBackend)
+and [event handler](@ref puglSetEventFunc) set,
and be configured by [setting hints](@ref puglSetViewHint)
-and [configuring the frame](@ref frame).
+and optionally [adjusting the frame](@ref frame).
+
+The [Backend](@ref PuglBackend) controls drawing for a view.
+Pugl includes [Cairo](@ref cairo) and [OpenGL](@ref gl) backends,
+as well as a [stub](@ref stub) backend that creates a native window with no drawing context.
-Once the view is configured,
-the corresponding window can be [created](@ref puglCreateWindow)
-and [shown](@ref puglShowWindow).
-Note that a view does not necessary correspond to a top-level system window.
+Once the view is configured,
+it can be [created](@ref puglCreateWindow) and [shown](@ref puglShowWindow).
+By default a view will correspond to a top-level system window.
To create a view within another window,
-call #puglSetParentWindow before #puglCreateWindow.
+it must have a [parent window set](@ref puglSetParentWindow) before being created.
+
+
+## Events
-## Interaction
+[Events](@ref events) are sent to a view when it has received user input or must be drawn.
-Interaction with the user and system happens via [events](@ref interaction).
-Before creating a window,
-a view must have an [event handler](@ref PuglEventFunc) set with #puglSetEventFunc.
-This handler is called whenever something happens that the view must respond to.
+Events are handled by the [event handler](@ref PuglEventFunc) set during initialisation.
+This function is called whenever something happens that the view must respond to.
This includes user interaction like mouse and keyboard input,
-and system events like window resizing and exposure (drawing).
+and system events like window resizing and exposure.
## Event Loop
-Two functions are used to drive the event loop:
+The event loop is driven by repeatedly calling #puglUpdate which processes events from the window system,
+and dispatches them to views when necessary.
+
+Typically, a plugin calls #puglUpdate with timeout 0 in some callback driven by the host.
+A program can use whatever timeout is appropriate:
+event-driven applications may wait forever,
+or for continuous animation,
+use a timeout that is a significant fraction of the frame period
+(with enough time left over to render).
+
+Redrawing can be requested by calling #puglPostRedisplay or #puglPostRedisplayRect,
+which post expose events to the queue.
+Note, however, that this will not wake up a blocked #puglUpdate call on MacOS
+(which does not handle drawing via events).
+For continuous redrawing, call #puglPostRedisplay when a #PUGL_UPDATE event is received.
+This event is sent before views are redrawn,
+so can be used as a hook to expand the update region right before the view is exposed.
- * #puglPollEvents waits for events to become available.
- * #puglDispatchEvents processes all pending events.
+## Error Handling
-Redrawing is accomplished by calling #puglPostRedisplay,
-which posts an expose event to the queue.
+Most functions return a [Status](@ref status) which should be checked to detect failure.
diff --git a/subprojects/d2tk/pugl/doc/reference.doxygen.in b/subprojects/d2tk/pugl/doc/reference.doxygen.in
index eb64d58..1357fe4 100644
--- a/subprojects/d2tk/pugl/doc/reference.doxygen.in
+++ b/subprojects/d2tk/pugl/doc/reference.doxygen.in
@@ -379,7 +379,7 @@ DISTRIBUTE_GROUP_DOC = NO
# is disabled and one has to add nested compounds explicitly via \ingroup.
# The default value is: NO.
-GROUP_NESTED_COMPOUNDS = YES
+GROUP_NESTED_COMPOUNDS = NO
# Set the SUBGROUPING tag to YES to allow class member groups of the same type
# (for instance a group of public functions) to be put as a subgroup of that
@@ -692,7 +692,7 @@ SHOW_FILES = YES
# Folder Tree View (if specified).
# The default value is: YES.
-SHOW_NAMESPACES = NO
+SHOW_NAMESPACES = YES
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
# doxygen should invoke to get the current version for each file (typically from
@@ -1451,7 +1451,7 @@ GENERATE_TREEVIEW = NO
# Minimum value: 0, maximum value: 20, default value: 4.
# This tag requires that the tag GENERATE_HTML is set to YES.
-ENUM_VALUES_PER_LINE = 1
+ENUM_VALUES_PER_LINE = 0
# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
# to set the initial width (in pixels) of the frame in which the tree is shown.
@@ -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_DEPRECATED_BY
+PREDEFINED = PUGL_API PUGL_DISABLE_DEPRECATED
# 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/subprojects/d2tk/pugl/doc/style.css b/subprojects/d2tk/pugl/doc/style.css
index fa366af..dba66b3 100644
--- a/subprojects/d2tk/pugl/doc/style.css
+++ b/subprojects/d2tk/pugl/doc/style.css
@@ -1,744 +1,808 @@
body {
- background: #FFF;
- color: #222;
- font-style: normal;
- line-height: 1.6em;
- margin-left: auto;
- margin-right: auto;
- padding: 1em;
- max-width: 60em;
- font-family: "DejaVu Serif",Palatino,serif;
- text-rendering: optimizeLegibility;
+ background: #FFF;
+ color: #222;
+ font-style: normal;
+ line-height: 1.6em;
+ margin-left: auto;
+ margin-right: auto;
+ padding: 1em;
+ max-width: 60em;
+ font-family: "SF Pro Text", Verdana, "DejaVu Sans", sans-serif;
+ text-rendering: optimizeLegibility;
+}
+
+h1 {
+ font-size: 1.68em;
+ font-weight: 500;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ line-height: 2em;
+ margin: 0 0 0.25em 0;
}
-h1, .title, #projectname, h2, h3, h4, h5, h6 {
- line-height: 1.0125em;
- color: #444;
- font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
- margin: 1em 0 0.5em 0;
+h2 {
+ line-height: 1.68em;
+ font-size: 1.41em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
}
-h1, .titlearea .header .titlebox, #projectname {
- font-size: 300%;
- font-weight: 400;
- margin-bottom: 0.25em;
- margin-top: 0;
+h3 {
+ line-height: 1.41em;
+ font-size: 1.18em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
}
-.header .headertitle .title {
- font-size: 180%;
- font-weight: 400;
- margin: 0.75em 0.25em 0.5em 0;
+h4 {
+ 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;
}
-.ingroups {
- display: inline;
+h5, h6 {
+ font-size: 0.7em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
}
-.title .ingroups a {
- font-size: small;
- margin-left: 1em;
+
+a {
+ color: #546E00;
+ text-decoration: none;
}
-#titlebox, #metabox {
- display: inline-block;
+h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
+ color: #444;
}
-#titlebox{
- display: inline-block;
- width: 75%;
- left: 0;
- top: 0;
+
+a:hover {
+ text-decoration: underline;
}
-#title {
- margin-bottom: 0.25em;
+h1 a:link, h2 a:link, h3 a:link, h4 a:link, h5 a:link, h6 a:link {
+ color: #444;
}
-#shortdesc {
- margin: 0;
- color: #666;
- display: inline-block;
- font-style: italic;
- padding: 0;
+h1 a:visited, h2 a:visited, h3 a:visited, h4 a:visited, h5 a:visited, h6 a:visited {
+ color: #444;
}
-#titlearea {
- margin: 0.25em auto 0.25em auto;
- padding: 0;
- position: relative;
- clear: both;
- line-height: 1.0em;
+p {
+ margin: 0.5em 0 0.5em 0;
}
-h2 {
- font-size: 160%;
- font-weight: 400;
+dt {
+ font-weight: 600;
}
-h3 {
- font-size: 140%;
- font-weight: 400;
+dd {
+ margin-left: 2em;
}
-h4 {
- font-size: 120%;
- font-weight: 500;
+caption {
+ font-weight: 700;
}
-h5, h6 {
- font-size: 110%;
- font-weight: 600;
+.title, #projectname {
+ line-height: 1.0125em;
+ margin: 0.75em 0 0 0;
}
-h1 a, h1 a:link, h1 a:visited ,
-h2 a, h2 a:link, h2 a:visited ,
-h3 a, h3 a:link, h3 a:visited ,
-h4 a, h4 a:link, h4 a:visited ,
-h5 a, h5 a:link, h5 a:visited ,
-h6 a, h6 a:link, h6 a:visited {
- color: #444;
+.titlearea .header .titlebox, #projectname {
+ font-size: 1.68em;
+ font-weight: 400;
+ margin-bottom: 0.25em;
+ margin-top: 0;
}
-p {
- margin: 0.5em 0 0.5em 0;
+#header {
+ padding: 0 0 0.5em 0;
+ border-bottom: 1px solid #EEE;
}
-dt {
- font-weight: 700;
+.header .headertitle .title {
+ line-height: 1.68em;
+ font-size: 1.68em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
}
-dd {
- margin-left: 2em;
+.ingroups {
+ display: none;
}
-caption {
- font-weight: 700;
+.title .ingroups a {
+ font-size: small;
+ margin-left: 1em;
}
-span.legend {
- font-size: small;
- text-align: center;
+#titlebox, #metabox {
+ display: inline-block;
}
-h3.version {
- font-size: small;
- text-align: center;
+#titlebox {
+ display: inline-block;
+ width: 75%;
+ left: 0;
+ top: 0;
}
-div.qindex,div.navtab {
- background-color: #EBEFF6;
- border: 1px solid #A3B4D7;
- text-align: center;
- margin: 2px;
- padding: 2px;
+#title {
+ margin-bottom: 0.25em;
+ line-height: 1.25em;
+ font-size: 2.5em;
+ color: #333;
+ font-weight: 600;
}
-div.navtab {
- margin-right: 15px;
+.PageDoc {
+ margin-top: 1.5em;
}
-/* @group Link Styling */
-a {
- color: #546E00;
- text-decoration: none;
+.PageDoc .header .headertitle .title {
+ display: none;
}
-.contents a:visited {
- color: #344E00;
+#shortdesc {
+ margin: 0;
+ color: #666;
+ display: inline-block;
+ font-style: italic;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ padding: 0;
}
-a:hover {
- text-decoration: underline;
+#titlearea {
+ margin: 0.25em auto 0 auto;
+ padding: 0;
+ position: relative;
+ clear: both;
+ line-height: 1em;
+}
+
+.legend {
+ font-size: small;
+ text-align: center;
+}
+
+.version {
+ font-size: small;
+ text-align: center;
+}
+
+div.qindex,div.navtab {
+ background-color: #EBEFF6;
+ border: 1px solid #A3B4D7;
+ text-align: center;
+ margin: 2px;
+ padding: 2px;
+}
+
+div.navtab {
+ margin-right: 15px;
+}
+
+.contents a:visited {
+ color: #344E00;
}
a.qindexHL {
- background-color: #9CAFD4;
- color: #FFF;
- border: 1px double #869DCA;
+ background-color: #9CAFD4;
+ color: #FFF;
+ border: 1px double #869DCA;
}
code {
- color: #444;
+ color: #444;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
}
-/* @end */
dl.el {
- margin-left: -1cm;
+ margin-left: -1cm;
}
.fragment {
- font-family: "DejaVu Sans Mono",monospace,fixed;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
}
pre.fragment {
- border: 1px solid #C4C4C4;
- background-color: #F9F9F9;
- padding: 0.5em;
- overflow: auto;
+ border: 1px solid #C4C4C4;
+ background-color: #F9F9F9;
+ padding: 0.5em;
+ overflow: auto;
}
div.ah {
- background-color: #000;
- font-weight: 700;
- color: #FFF;
- margin-bottom: 3px;
- margin-top: 3px;
- padding: .2em;
- border: thin solid #333;
+ background-color: #000;
+ font-weight: 700;
+ color: #FFF;
+ margin-bottom: 3px;
+ margin-top: 3px;
+ padding: 0.2em;
+ border: thin solid #333;
}
div.groupHeader {
- margin-left: 16px;
- margin-top: 12px;
- margin-bottom: 6px;
- font-weight: 700;
+ margin-left: 16px;
+ margin-top: 12px;
+ margin-bottom: 6px;
+ font-weight: 700;
}
a + h2.groupheader {
- display: none;
+ display: none;
}
div.groupText {
- margin-left: 16px;
- font-style: italic;
+ margin-left: 16px;
+ font-style: italic;
}
div.contents, #content {
- padding: 0 0.25em 0 0.25em;
- max-width: 60em;
- margin-left: auto;
- margin-right: auto;
+ max-width: 60em;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.groupheader + p {
+ font-style: italic;
+ color: #666;
+ margin: 0 0 1em 0;
}
td.indexkey {
- background-color: #EBEFF6;
- font-weight: 700;
- border: 1px solid #C4CFE5;
- margin: 2px 0;
- padding: 2px 10px;
+ background-color: #EBEFF6;
+ font-weight: 700;
+ border: 1px solid #C4CFE5;
+ margin: 2px 0;
+ padding: 2px 10px;
}
td.indexvalue {
- background-color: #EBEFF6;
- border: 1px solid #C4CFE5;
- padding: 2px 10px;
- margin: 2px 0;
+ background-color: #EBEFF6;
+ border: 1px solid #C4CFE5;
+ padding: 2px 10px;
+ margin: 2px 0;
}
table.memname {
- font-family: "DejaVu Sans Mono",monospace,fixed;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ border-spacing: 0;
+}
+
+table.memname tbody tr:last-child {
+ display: none;
+}
+
+table.memname tbody tr:only-child {
+ display: table-cell;
+}
+
+table.memname tbody tr:nth-last-child(2)::after {
+ content: ")";
}
tr.memlist {
- background-color: #EEF1F7;
+ background-color: #EEF1F7;
}
p.formulaDsp {
- text-align: center;
+ text-align: center;
}
img.formulaInl {
- vertical-align: middle;
+ vertical-align: middle;
}
div.center {
- text-align: center;
- margin-top: 0;
- margin-bottom: 0;
- padding: 0;
+ text-align: center;
+ margin-top: 0;
+ margin-bottom: 0;
+ padding: 0;
}
div.center img {
- border: 0;
+ border: 0;
}
address.footer {
- text-align: right;
+ text-align: right;
}
img.footer {
- border: 0;
- vertical-align: middle;
+ border: 0;
+ vertical-align: middle;
}
-/* @group Code Colorization */
span.keyword {
- color: #586E75;
+ color: #586E75;
}
span.keywordtype {
- color: #546E00;
+ color: #546E00;
}
span.keywordflow {
- color: #586E75;
+ color: #586E75;
}
span.comment {
- color: #6C71C4;
+ color: #6C71C4;
}
span.preprocessor {
- color: #D33682;
+ color: #D33682;
}
span.stringliteral {
- color: #CB4B16;
+ color: #CB4B16;
}
span.charliteral {
- color: #CB4B16;
+ color: #CB4B16;
}
-/* @end */
td.tiny {
- font-size: x-small;
+ font-size: x-small;
}
.dirtab {
- padding: 4px;
- border-collapse: collapse;
- border: 1px solid #A3B4D7;
+ padding: 4px;
+ border-collapse: collapse;
+ border: 1px solid #A3B4D7;
}
th.dirtab {
- background: #EBEFF6;
- font-weight: 700;
+ background: #EBEFF6;
+ font-weight: 700;
}
hr {
- height: 0;
- border: none;
- border-top: 1px solid #DDD;
- margin: 2em 0 1em;
+ height: 0;
+ border: none;
+ border-top: 1px solid #DDD;
+ margin: 2em 0;
}
#footer {
- bottom: 0;
- clear: both;
- font-size: x-small;
- margin: 2em 0 0;
- padding: 0 1em 1em 1em;
- vertical-align: top;
- color: #888;
+ bottom: 0;
+ clear: both;
+ font-size: x-small;
+ margin: 2em 0 0;
+ padding: 0 1em 1em 1em;
+ vertical-align: top;
+ color: #888;
+}
+
+td.ititle {
+ padding-bottom: 0.75em;
}
-/* @group Member Descriptions */
table.memberdecls {
- border-spacing: 0.125em;
- line-height: 1.3em;
+ border-spacing: 0.125em;
+ line-height: 1.3em;
}
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams {
- margin: 0;
- padding: 0;
+ margin: 0;
+ padding: 0;
}
.mdescLeft,.mdescRight {
- color: #555;
+ color: #555;
}
.memItemLeft,.memItemRight,.memTemplParams {
- border: 0;
- font-family: "DejaVu Sans Mono",monospace,fixed;
+ border: 0;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
}
.memItemLeft,.memTemplItemLeft {
- white-space: nowrap;
- padding-left: 2em;
- padding-right: 1em;
+ white-space: nowrap;
+ padding-left: 2em;
}
.memItemLeft a.el {
- font-weight: bold;
+ font-weight: bold;
}
.memTemplParams {
- color: #464646;
- white-space: nowrap;
+ color: #464646;
+ white-space: nowrap;
}
td.memSeparator {
- display: none;
+ display: none;
}
td.mlabels-right {
- vertical-align: top;
- padding-top: 4px;
- color: #B4C342;
+ vertical-align: top;
+ padding-top: 4px;
+ color: #B4C342;
}
.memtitle {
- display: none;
+ display: none;
}
-/* @end */
-/* @group Member Details */
-/* Styles for detailed member documentation */
.memtemplate {
- color: #888;
- font-style: italic;
- font-family: "DejaVu Sans Mono",monospace,fixed;
- font-size: small;
+ color: #888;
+ font-style: italic;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ font-size: small;
}
.memnav {
- background-color: #EEE;
- border: 1px solid #B4C342;
- text-align: center;
- margin: 2px;
- margin-right: 15px;
- padding: 2px;
+ background-color: #EEE;
+ border: 1px solid #B4C342;
+ text-align: center;
+ margin: 2px;
+ margin-right: 15px;
+ padding: 2px;
}
.memitem {
- padding: 0.25em 0.5em 0.25em 0.5em;
- margin: 0 0 1em 0;
- border-radius: 6px;
- border: 1px solid #DDD;
+ padding: 0.5em 0.5em 0.25em 0.5em;
+ margin: 1em 0 2em 0;
}
.memproto {
- font-size: 110%;
- font-weight: 400;
- line-height: 1em;
- color: #000;
+ border-bottom: 1px solid #EEE;
+ 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;
}
.memproto .paramname {
- font-style: normal;
+ font-style: normal;
+ padding-right: 0.25em;
}
.memdoc {
- padding: 0 0.25em 0 0.25em;
+ padding: 0;
+}
+
+.memdoc > p:first-child, .memdoc .textblock > p:first-child {
+ font-style: italic;
+ color: #444;
+ margin-bottom: 0.75em;
}
.paramkey {
- text-align: right;
+ text-align: right;
}
.paramtype {
- color: #666;
- padding-right: 0.5em;
- white-space: nowrap;
+ color: #666;
+ padding: 0 0.25em 0 0.25em;
+ white-space: nowrap;
}
-.paramname {
- color: #111;
- white-space: nowrap;
- font-family: "DejaVu Sans Mono",monospace,fixed;
- font-style: italic;
- padding-right: 0.5em;
+.params .paramname {
+ color: #111;
+ white-space: nowrap;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ font-style: italic;
+ padding-right: 0.5em;
+ vertical-align: top;
}
.fieldname {
- color: #000;
+ color: #000;
}
.fieldtable {
- padding-top: 0.25em;
- border-top: 1px dashed #DDD;
+ margin-top: 0.5em;
+ border-collapse: collapse;
}
.fieldtable tbody tr:first-child {
- display: none;
+ display: none;
}
td.fieldname {
- padding: 0 0.5em 0 0.25em;
- vertical-align: top;
- font-family: "DejaVu Sans Mono",monospace,fixed;
+ vertical-align: top;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+}
+
+td.fielddoc {
+ padding: 0.125em 0.5em 0 0.25em;
+ vertical-align: top;
+}
+
+.fieldtable tbody tr td {
+ border-top: 1px dashed #DDD;
+ border-bottom: 1px dashed #DDD;
}
td.fieldtype {
- color: #666;
- padding: 0 0.5em 0 0;
- vertical-align: top;
- font-family: "DejaVu Sans Mono",monospace,fixed;
+ color: #666;
+ padding: 0 0.5em 0 0;
+ vertical-align: top;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
}
td.fielddoc p {
- margin: 0;
- vertical-align: top;
- padding: 0 0.5em 0 0;
+ margin: 0;
+ padding: 0 0.5em 0 0;
}
p.reference {
- font-size: x-small;
- font-style: italic;
+ font-size: x-small;
+ font-style: italic;
}
-/* @end */
-/* @group Directory (tree) */
-/* for the tree view */
.ftvtree {
- font-family: sans-serif;
- margin: 0;
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ margin: 0;
}
-/* these are for tree view when used as main index */
.directory {
- font-size: small;
- margin: 0.5em;
+ margin: 0.5em;
}
.directory h3 {
- margin: 0;
- margin-top: 1em;
- font-size: 11pt;
+ margin: 0;
+ margin-top: 1em;
+ font-size: 11pt;
}
.directory > h3 {
- margin-top: 0;
+ margin-top: 0;
}
.directory p {
- margin: 0;
- white-space: nowrap;
+ margin: 0;
+ white-space: nowrap;
}
.directory div {
- display: none;
- margin: 0;
+ display: none;
+ margin: 0;
}
.directory img {
- vertical-align: -30%;
+ vertical-align: -30%;
}
td.entry {
- font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
- font-weight: 400;
- padding-right: 1em;
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ padding-right: 1em;
}
-td.entry .arrow {
- display: none;
+.arrow {
+ color: #CCC;
+ user-select: none;
+ font-size: 80%;
+ display: inline-block;
+ width: 16px;
+ height: 22px;
+ vertical-align: top;
}
td.entry b {
- font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
- font-weight: 400;
- font-size: 130%;
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ font-weight: 400;
+ font-size: 130%;
}
-/* these are for tree view when not used as main index */
.directory-alt {
- font-size: 100%;
- font-weight: bold;
+ font-size: 100%;
+ font-weight: bold;
}
.directory-alt h3 {
- margin: 0;
- margin-top: 1em;
- font-size: 11pt;
+ margin: 0;
+ margin-top: 1em;
+ font-size: 11pt;
}
.directory-alt > h3 {
- margin-top: 0;
+ margin-top: 0;
}
.directory-alt p {
- margin: 0;
- white-space: nowrap;
+ margin: 0;
+ white-space: nowrap;
}
.directory-alt div {
- display: none;
- margin: 0;
+ display: none;
+ margin: 0;
}
.directory-alt img {
- vertical-align: -30%;
+ vertical-align: -30%;
}
-/* @end */
div.dynheader {
- margin-top: 8px;
+ margin-top: 8px;
}
address {
- font-style: normal;
- color: #444;
+ font-style: normal;
+ color: #444;
}
table.doxtable {
- border-collapse: collapse;
- margin: 0.5em;
+ border-collapse: collapse;
+ margin: 0.5em;
}
table.doxtable td,table.doxtable th {
- border: 1px solid #DDD;
- padding: 3px 7px 2px;
+ border: 1px solid #DDD;
+ padding: 3px 7px 2px;
}
table.doxtable th {
- background-color: #F3F3F3;
- color: #000;
- padding-bottom: 4px;
- padding-top: 5px;
- text-align: left;
- font-weight: bold;
+ background-color: #F3F3F3;
+ color: #000;
+ padding-bottom: 4px;
+ padding-top: 5px;
+ text-align: left;
+ font-weight: bold;
}
.tabsearch {
- top: 0;
- left: 10px;
- height: 36px;
- z-index: 101;
- overflow: hidden;
- font-size: 13px;
+ top: 0;
+ left: 10px;
+ height: 36px;
+ z-index: 101;
+ overflow: hidden;
+ font-size: 13px;
}
div.navpath {
- color: #DDD;
+ color: #DDD;
}
.navpath ul {
- overflow: hidden;
- margin: 0;
- padding: 0;
+ overflow: hidden;
+ margin: 0;
+ padding: 0;
}
.navpath li {
- float: left;
- padding-left: 0;
- margin-left: 0.5em;
- padding-right: 1em;
+ float: left;
+ padding-left: 0;
+ margin-left: 0.5em;
+ padding-right: 1em;
}
.navpath a {
- display: block;
- text-decoration: none;
- outline: none;
+ display: block;
+ text-decoration: none;
+ outline: none;
}
div.summary {
- font-size: small;
- font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
- margin: 0;
- color: #FFF; /* Hide separator bars */
- border-bottom: 1px solid #DDD;
- padding: 0.25em 0;
+ font-size: small;
+ font-family: "DejaVu Sans", Verdana, Helvetica, Arial, sans-serif;
+ margin: 0;
+ padding: 0.25em 0;
+ display: none;
}
div.summary a {
- white-space: nowrap;
+ white-space: nowrap;
}
-/* Metadata box (right aligned next to title) */
-
#metabox {
- display: inline-block;
- font-size: x-small;
- margin: 0 0 0.25em 0;
- position: absolute;
- right: 0;
- top: 0;
- color: #666;
- font-style: italic;
- padding: 0 1em;
+ display: inline-block;
+ font-size: x-small;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ position: absolute;
+ right: 0;
+ bottom: 0.25em;
+ color: #666;
+ font-style: italic;
}
#meta {
- border-style: hidden;
- margin-right: 0.25em;
+ border-style: hidden;
+ margin-right: 0.25em;
}
#meta tr, #meta th, #meta td {
- background-color: transparent;
- border: 0;
- margin: 0;
- font-weight: normal;
+ background-color: transparent;
+ border: 0;
+ margin: 0;
+ font-weight: normal;
}
#meta th {
- text-align: right;
+ text-align: right;
}
-#meta th:after {
- content: ":";
+#meta th::after {
+ content: ":";
}
div.line {
- font-family: "DejaVu Sans Mono",monospace,fixed;
- line-height: 1.4em;
- white-space: pre-wrap;
+ font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
+ line-height: 1.4em;
+ white-space: pre-wrap;
}
.glow {
- background-color: #2AA198;
- box-shadow: 0 0 10px #2AA198;
+ background-color: #2AA198;
+ box-shadow: 0 0 10px #2AA198;
}
span.lineno {
- padding-right: 4px;
- text-align: right;
- border-right: 2px solid #546E00;
- background-color: #E8E8E8;
- white-space: pre;
+ padding-right: 4px;
+ text-align: right;
+ border-right: 2px solid #546E00;
+ background-color: #E8E8E8;
+ white-space: pre;
}
+
span.lineno a {
- background-color: #D8D8D8;
+ background-color: #D8D8D8;
}
span.lineno a:hover {
- background-color: #C8C8C8;
+ background-color: #C8C8C8;
}
.tabs, .tabs2, .navpath {
- padding: 0.25em 0;
- border-bottom: 1px solid #DDD;
- font-size: small;
- font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
- margin: 0;
+ padding: 0.25em 0;
+ font-size: small;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 0;
}
th {
- text-align: left;
- font-size: 110%;
- font-weight: 500;
+ text-align: left;
+ font-size: 110%;
+ font-weight: 500;
}
.mlabel {
- padding: 0.125em;
+ padding: 0.125em;
}
-#navrow1 {
- /* Disable menu from Doxygen 1.8.15, it is faked in the template */
- display: none;
+#navrow1, #navrow2 {
+ /* Disable menu from Doxygen 1.8.15, it is faked in the template */
+ display: none;
}
-/* tabs*/
-
.tablist {
- margin: 0;
- padding: 0;
- display: table;
+ margin: 0;
+ padding: 0;
+ display: table;
}
.tablist li {
- display: table-cell;
- line-height: 2em;
- list-style: none;
- border-bottom: 0;
+ display: table-cell;
+ line-height: 2em;
+ list-style: none;
+ border-bottom: 0;
}
.tablist a {
- display: block;
- padding: 0 1em 0 0;
- font-family: "DejaVu Sans",Helvetica,Arial,sans-serif;
- text-decoration: none;
- outline: none;
+ display: block;
+ padding: 0 1em 0 0;
+ text-decoration: none;
+ outline: none;
}
.tabs3 .tablist a {
- padding: 0 10px;
+ padding: 0 10px;
}
.tablist li.current a {
- color: #222;
+ color: #222;
}
span.icon {
- display: none;
+ display: none;
}
diff --git a/subprojects/d2tk/pugl/examples/cube_view.h b/subprojects/d2tk/pugl/examples/cube_view.h
new file mode 100644
index 0000000..82648ac
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/cube_view.h
@@ -0,0 +1,82 @@
+/*
+ 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.
+*/
+
+#define GL_SILENCE_DEPRECATION 1
+
+#include "demo_utils.h"
+
+#include "pugl/gl.h"
+
+static inline void
+reshapeCube(const int width, const int height)
+{
+ const float aspect = (float)width / (float)height;
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, width, height);
+
+ float projection[16];
+ perspective(projection, 1.8f, aspect, 1.0f, 100.0f);
+ glLoadMatrixf(projection);
+}
+
+static inline void
+displayCube(PuglView* const view,
+ const double distance,
+ const double xAngle,
+ const double yAngle,
+ const bool entered)
+{
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glTranslatef(0.0f, 0.0f, (float)distance * -1.0f);
+ glRotatef((float)xAngle, 0.0f, 1.0f, 0.0f);
+ glRotatef((float)yAngle, 1.0f, 0.0f, 0.0f);
+
+ const float bg = entered ? 0.2f : 0.0f;
+ glClearColor(bg, bg, bg, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ if (puglHasFocus(view)) {
+ // Draw cube surfaces
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glEnableClientState(GL_COLOR_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, cubeStripVertices);
+ glColorPointer(3, GL_FLOAT, 0, cubeStripVertices);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
+ glDisableClientState(GL_COLOR_ARRAY);
+ glDisableClientState(GL_VERTEX_ARRAY);
+
+ glColor3f(0.0f, 0.0f, 0.0f);
+ } else {
+ glColor3f(1.0f, 1.0f, 1.0f);
+ }
+
+ // Draw cube wireframe
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(3, GL_FLOAT, 0, cubeFrontLineLoop);
+ glDrawArrays(GL_LINE_LOOP, 0, 4);
+ glVertexPointer(3, GL_FLOAT, 0, cubeBackLineLoop);
+ glDrawArrays(GL_LINE_LOOP, 0, 4);
+ glVertexPointer(3, GL_FLOAT, 0, cubeSideLines);
+ glDrawArrays(GL_LINES, 0, 8);
+ glDisableClientState(GL_VERTEX_ARRAY);
+}
diff --git a/subprojects/d2tk/pugl/examples/demo_utils.h b/subprojects/d2tk/pugl/examples/demo_utils.h
new file mode 100644
index 0000000..9a1cb7a
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/demo_utils.h
@@ -0,0 +1,175 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#ifndef PUGL_DEMO_UTILS_H
+#define PUGL_DEMO_UTILS_H
+
+#include "pugl/pugl.h"
+
+#include <math.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+typedef struct {
+ double lastReportTime;
+} PuglFpsPrinter;
+
+typedef float vec4[4];
+typedef vec4 mat4[4];
+
+// clang-format off
+
+static const float cubeStripVertices[] = {
+ -1.0f, 1.0f, 1.0f, // Front top left
+ 1.0f, 1.0f, 1.0f, // Front top right
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+ 1.0f, -1.0f, 1.0f, // Front bottom right
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+ 1.0f, 1.0f, 1.0f, // Front top right
+ 1.0f, 1.0f, -1.0f, // Back top right
+ -1.0f, 1.0f, 1.0f, // Front top left
+ -1.0f, 1.0f, -1.0f, // Back top left
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+ -1.0f, -1.0f, -1.0f, // Back bottom left
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+ -1.0f, 1.0f, -1.0f, // Back top left
+ 1.0f, 1.0f, -1.0f // Back top right
+};
+
+static const float cubeFrontLineLoop[] = {
+ -1.0f, 1.0f, 1.0f, // Front top left
+ 1.0f, 1.0f, 1.0f, // Front top right
+ 1.0f, -1.0f, 1.0f, // Front bottom right
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+};
+
+static const float cubeBackLineLoop[] = {
+ -1.0f, 1.0f, -1.0f, // Back top left
+ 1.0f, 1.0f, -1.0f, // Back top right
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+ -1.0f, -1.0f, -1.0f, // Back bottom left
+};
+
+static const float cubeSideLines[] = {
+ -1.0f, 1.0f, 1.0f, // Front top left
+ -1.0f, 1.0f, -1.0f, // Back top left
+
+ -1.0f, -1.0f, 1.0f, // Front bottom left
+ -1.0f, -1.0f, -1.0f, // Back bottom left
+
+ 1.0f, 1.0f, 1.0f, // Front top right
+ 1.0f, 1.0f, -1.0f, // Back top right
+
+ 1.0f, -1.0f, 1.0f, // Front bottom right
+ 1.0f, -1.0f, -1.0f, // Back bottom right
+};
+
+// clang-format on
+
+static inline void
+mat4Identity(mat4 m)
+{
+ for (int c = 0; c < 4; ++c) {
+ for (int r = 0; r < 4; ++r) {
+ m[c][r] = c == r ? 1.0f : 0.0f;
+ }
+ }
+}
+
+static inline void
+mat4Translate(mat4 m, const float x, const float y, const float z)
+{
+ m[3][0] = x;
+ m[3][1] = y;
+ m[3][2] = z;
+}
+
+static inline void
+mat4Mul(mat4 m, mat4 a, mat4 b)
+{
+ for (int c = 0; c < 4; ++c) {
+ for (int r = 0; r < 4; ++r) {
+ m[c][r] = 0.0f;
+ for (int k = 0; k < 4; ++k) {
+ m[c][r] += a[k][r] * b[c][k];
+ }
+ }
+ }
+}
+
+static inline void
+mat4Ortho(mat4 m,
+ const float l,
+ const float r,
+ const float b,
+ const float t,
+ const float n,
+ const float f)
+{
+ m[0][0] = 2.0f / (r - l);
+ m[0][1] = m[0][2] = m[0][3] = 0.0f;
+
+ m[1][1] = 2.0f / (t - b);
+ m[1][0] = m[1][2] = m[1][3] = 0.0f;
+
+ m[2][2] = -2.0f / (f - n);
+ m[2][0] = m[2][1] = m[2][3] = 0.0f;
+
+ m[3][0] = -(r + l) / (r - l);
+ m[3][1] = -(t + b) / (t - b);
+ m[3][2] = -(f + n) / (f - n);
+ m[3][3] = 1.0f;
+}
+
+/** Calculate a projection matrix for a given perspective. */
+static inline void
+perspective(float* m, float fov, float aspect, float zNear, float zFar)
+{
+ const float h = tanf(fov);
+ const float w = h / aspect;
+ const float depth = zNear - zFar;
+ const float q = (zFar + zNear) / depth;
+ const float qn = 2 * zFar * zNear / depth;
+
+ // clang-format off
+ m[0] = w; m[1] = 0; m[2] = 0; m[3] = 0;
+ m[4] = 0; m[5] = h; m[6] = 0; m[7] = 0;
+ m[8] = 0; m[9] = 0; m[10] = q; m[11] = -1;
+ m[12] = 0; m[13] = 0; m[14] = qn; m[15] = 0;
+ // clang-format on
+}
+
+static inline void
+puglPrintFps(const PuglWorld* world,
+ PuglFpsPrinter* printer,
+ unsigned* const framesDrawn)
+{
+ const double thisTime = puglGetTime(world);
+ if (thisTime > printer->lastReportTime + 5) {
+ const double fps = *framesDrawn / (thisTime - printer->lastReportTime);
+ fprintf(stderr,
+ "%u frames in %.0f seconds = %.3f FPS\n",
+ *framesDrawn,
+ thisTime - printer->lastReportTime,
+ fps);
+
+ printer->lastReportTime = thisTime;
+ *framesDrawn = 0;
+ }
+}
+
+#endif // PUGL_DEMO_UTILS_H
diff --git a/subprojects/d2tk/pugl/test/glad/glad.c b/subprojects/d2tk/pugl/examples/glad/glad.c
index 2a0567c..38f442c 100644
--- a/subprojects/d2tk/pugl/test/glad/glad.c
+++ b/subprojects/d2tk/pugl/examples/glad/glad.c
@@ -4,7 +4,7 @@
Language/Generator: C/C++
Specification: gl
- APIs: gl=3.2
+ APIs: gl=3.3
Profile: core
Extensions:
@@ -14,9 +14,9 @@
Reproducible: True
Commandline:
- --profile="core" --api="gl=3.2" --generator="c" --spec="gl" --local-files --extensions=""
+ --profile="core" --api="gl=3.3" --generator="c" --spec="gl" --local-files --extensions=""
Online:
- https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.2
+ https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.3
*/
#include <stdio.h>
@@ -264,6 +264,7 @@ int GLAD_GL_VERSION_2_1 = 0;
int GLAD_GL_VERSION_3_0 = 0;
int GLAD_GL_VERSION_3_1 = 0;
int GLAD_GL_VERSION_3_2 = 0;
+int GLAD_GL_VERSION_3_3 = 0;
PFNGLACTIVETEXTUREPROC glad_glActiveTexture = NULL;
PFNGLATTACHSHADERPROC glad_glAttachShader = NULL;
PFNGLBEGINCONDITIONALRENDERPROC glad_glBeginConditionalRender = NULL;
@@ -274,8 +275,10 @@ PFNGLBINDBUFFERPROC glad_glBindBuffer = NULL;
PFNGLBINDBUFFERBASEPROC glad_glBindBufferBase = NULL;
PFNGLBINDBUFFERRANGEPROC glad_glBindBufferRange = NULL;
PFNGLBINDFRAGDATALOCATIONPROC glad_glBindFragDataLocation = NULL;
+PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed = NULL;
PFNGLBINDFRAMEBUFFERPROC glad_glBindFramebuffer = NULL;
PFNGLBINDRENDERBUFFERPROC glad_glBindRenderbuffer = NULL;
+PFNGLBINDSAMPLERPROC glad_glBindSampler = NULL;
PFNGLBINDTEXTUREPROC glad_glBindTexture = NULL;
PFNGLBINDVERTEXARRAYPROC glad_glBindVertexArray = NULL;
PFNGLBLENDCOLORPROC glad_glBlendColor = NULL;
@@ -299,6 +302,10 @@ PFNGLCLEARSTENCILPROC glad_glClearStencil = NULL;
PFNGLCLIENTWAITSYNCPROC glad_glClientWaitSync = NULL;
PFNGLCOLORMASKPROC glad_glColorMask = NULL;
PFNGLCOLORMASKIPROC glad_glColorMaski = NULL;
+PFNGLCOLORP3UIPROC glad_glColorP3ui = NULL;
+PFNGLCOLORP3UIVPROC glad_glColorP3uiv = NULL;
+PFNGLCOLORP4UIPROC glad_glColorP4ui = NULL;
+PFNGLCOLORP4UIVPROC glad_glColorP4uiv = NULL;
PFNGLCOMPILESHADERPROC glad_glCompileShader = NULL;
PFNGLCOMPRESSEDTEXIMAGE1DPROC glad_glCompressedTexImage1D = NULL;
PFNGLCOMPRESSEDTEXIMAGE2DPROC glad_glCompressedTexImage2D = NULL;
@@ -320,6 +327,7 @@ PFNGLDELETEFRAMEBUFFERSPROC glad_glDeleteFramebuffers = NULL;
PFNGLDELETEPROGRAMPROC glad_glDeleteProgram = NULL;
PFNGLDELETEQUERIESPROC glad_glDeleteQueries = NULL;
PFNGLDELETERENDERBUFFERSPROC glad_glDeleteRenderbuffers = NULL;
+PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers = NULL;
PFNGLDELETESHADERPROC glad_glDeleteShader = NULL;
PFNGLDELETESYNCPROC glad_glDeleteSync = NULL;
PFNGLDELETETEXTURESPROC glad_glDeleteTextures = NULL;
@@ -362,6 +370,7 @@ PFNGLGENBUFFERSPROC glad_glGenBuffers = NULL;
PFNGLGENFRAMEBUFFERSPROC glad_glGenFramebuffers = NULL;
PFNGLGENQUERIESPROC glad_glGenQueries = NULL;
PFNGLGENRENDERBUFFERSPROC glad_glGenRenderbuffers = NULL;
+PFNGLGENSAMPLERSPROC glad_glGenSamplers = NULL;
PFNGLGENTEXTURESPROC glad_glGenTextures = NULL;
PFNGLGENVERTEXARRAYSPROC glad_glGenVertexArrays = NULL;
PFNGLGENERATEMIPMAPPROC glad_glGenerateMipmap = NULL;
@@ -383,6 +392,7 @@ PFNGLGETCOMPRESSEDTEXIMAGEPROC glad_glGetCompressedTexImage = NULL;
PFNGLGETDOUBLEVPROC glad_glGetDoublev = NULL;
PFNGLGETERRORPROC glad_glGetError = NULL;
PFNGLGETFLOATVPROC glad_glGetFloatv = NULL;
+PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex = NULL;
PFNGLGETFRAGDATALOCATIONPROC glad_glGetFragDataLocation = NULL;
PFNGLGETFRAMEBUFFERATTACHMENTPARAMETERIVPROC glad_glGetFramebufferAttachmentParameteriv = NULL;
PFNGLGETINTEGER64I_VPROC glad_glGetInteger64i_v = NULL;
@@ -392,10 +402,16 @@ PFNGLGETINTEGERVPROC glad_glGetIntegerv = NULL;
PFNGLGETMULTISAMPLEFVPROC glad_glGetMultisamplefv = NULL;
PFNGLGETPROGRAMINFOLOGPROC glad_glGetProgramInfoLog = NULL;
PFNGLGETPROGRAMIVPROC glad_glGetProgramiv = NULL;
+PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v = NULL;
PFNGLGETQUERYOBJECTIVPROC glad_glGetQueryObjectiv = NULL;
+PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v = NULL;
PFNGLGETQUERYOBJECTUIVPROC glad_glGetQueryObjectuiv = NULL;
PFNGLGETQUERYIVPROC glad_glGetQueryiv = NULL;
PFNGLGETRENDERBUFFERPARAMETERIVPROC glad_glGetRenderbufferParameteriv = NULL;
+PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv = NULL;
+PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv = NULL;
+PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv = NULL;
+PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv = NULL;
PFNGLGETSHADERINFOLOGPROC glad_glGetShaderInfoLog = NULL;
PFNGLGETSHADERSOURCEPROC glad_glGetShaderSource = NULL;
PFNGLGETSHADERIVPROC glad_glGetShaderiv = NULL;
@@ -430,6 +446,7 @@ PFNGLISFRAMEBUFFERPROC glad_glIsFramebuffer = NULL;
PFNGLISPROGRAMPROC glad_glIsProgram = NULL;
PFNGLISQUERYPROC glad_glIsQuery = NULL;
PFNGLISRENDERBUFFERPROC glad_glIsRenderbuffer = NULL;
+PFNGLISSAMPLERPROC glad_glIsSampler = NULL;
PFNGLISSHADERPROC glad_glIsShader = NULL;
PFNGLISSYNCPROC glad_glIsSync = NULL;
PFNGLISTEXTUREPROC glad_glIsTexture = NULL;
@@ -442,6 +459,16 @@ PFNGLMAPBUFFERRANGEPROC glad_glMapBufferRange = NULL;
PFNGLMULTIDRAWARRAYSPROC glad_glMultiDrawArrays = NULL;
PFNGLMULTIDRAWELEMENTSPROC glad_glMultiDrawElements = NULL;
PFNGLMULTIDRAWELEMENTSBASEVERTEXPROC glad_glMultiDrawElementsBaseVertex = NULL;
+PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui = NULL;
+PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv = NULL;
+PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui = NULL;
+PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv = NULL;
+PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui = NULL;
+PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv = NULL;
+PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui = NULL;
+PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv = NULL;
+PFNGLNORMALP3UIPROC glad_glNormalP3ui = NULL;
+PFNGLNORMALP3UIVPROC glad_glNormalP3uiv = NULL;
PFNGLPIXELSTOREFPROC glad_glPixelStoref = NULL;
PFNGLPIXELSTOREIPROC glad_glPixelStorei = NULL;
PFNGLPOINTPARAMETERFPROC glad_glPointParameterf = NULL;
@@ -453,13 +480,22 @@ PFNGLPOLYGONMODEPROC glad_glPolygonMode = NULL;
PFNGLPOLYGONOFFSETPROC glad_glPolygonOffset = NULL;
PFNGLPRIMITIVERESTARTINDEXPROC glad_glPrimitiveRestartIndex = NULL;
PFNGLPROVOKINGVERTEXPROC glad_glProvokingVertex = NULL;
+PFNGLQUERYCOUNTERPROC glad_glQueryCounter = NULL;
PFNGLREADBUFFERPROC glad_glReadBuffer = NULL;
PFNGLREADPIXELSPROC glad_glReadPixels = NULL;
PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL;
PFNGLSAMPLECOVERAGEPROC glad_glSampleCoverage = NULL;
PFNGLSAMPLEMASKIPROC glad_glSampleMaski = NULL;
+PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv = NULL;
+PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv = NULL;
+PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf = NULL;
+PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv = NULL;
+PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri = NULL;
+PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv = NULL;
PFNGLSCISSORPROC glad_glScissor = NULL;
+PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui = NULL;
+PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv = NULL;
PFNGLSHADERSOURCEPROC glad_glShaderSource = NULL;
PFNGLSTENCILFUNCPROC glad_glStencilFunc = NULL;
PFNGLSTENCILFUNCSEPARATEPROC glad_glStencilFuncSeparate = NULL;
@@ -468,6 +504,14 @@ PFNGLSTENCILMASKSEPARATEPROC glad_glStencilMaskSeparate = NULL;
PFNGLSTENCILOPPROC glad_glStencilOp = NULL;
PFNGLSTENCILOPSEPARATEPROC glad_glStencilOpSeparate = NULL;
PFNGLTEXBUFFERPROC glad_glTexBuffer = NULL;
+PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui = NULL;
+PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv = NULL;
+PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui = NULL;
+PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv = NULL;
+PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui = NULL;
+PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv = NULL;
+PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui = NULL;
+PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv = NULL;
PFNGLTEXIMAGE1DPROC glad_glTexImage1D = NULL;
PFNGLTEXIMAGE2DPROC glad_glTexImage2D = NULL;
PFNGLTEXIMAGE2DMULTISAMPLEPROC glad_glTexImage2DMultisample = NULL;
@@ -556,6 +600,7 @@ PFNGLVERTEXATTRIB4SVPROC glad_glVertexAttrib4sv = NULL;
PFNGLVERTEXATTRIB4UBVPROC glad_glVertexAttrib4ubv = NULL;
PFNGLVERTEXATTRIB4UIVPROC glad_glVertexAttrib4uiv = NULL;
PFNGLVERTEXATTRIB4USVPROC glad_glVertexAttrib4usv = NULL;
+PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor = NULL;
PFNGLVERTEXATTRIBI1IPROC glad_glVertexAttribI1i = NULL;
PFNGLVERTEXATTRIBI1IVPROC glad_glVertexAttribI1iv = NULL;
PFNGLVERTEXATTRIBI1UIPROC glad_glVertexAttribI1ui = NULL;
@@ -577,7 +622,21 @@ PFNGLVERTEXATTRIBI4UIPROC glad_glVertexAttribI4ui = NULL;
PFNGLVERTEXATTRIBI4UIVPROC glad_glVertexAttribI4uiv = NULL;
PFNGLVERTEXATTRIBI4USVPROC glad_glVertexAttribI4usv = NULL;
PFNGLVERTEXATTRIBIPOINTERPROC glad_glVertexAttribIPointer = NULL;
+PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui = NULL;
+PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv = NULL;
+PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui = NULL;
+PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv = NULL;
+PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui = NULL;
+PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv = NULL;
+PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui = NULL;
+PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv = NULL;
PFNGLVERTEXATTRIBPOINTERPROC glad_glVertexAttribPointer = NULL;
+PFNGLVERTEXP2UIPROC glad_glVertexP2ui = NULL;
+PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv = NULL;
+PFNGLVERTEXP3UIPROC glad_glVertexP3ui = NULL;
+PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv = NULL;
+PFNGLVERTEXP4UIPROC glad_glVertexP4ui = NULL;
+PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv = NULL;
PFNGLVIEWPORTPROC glad_glViewport = NULL;
PFNGLWAITSYNCPROC glad_glWaitSync = NULL;
static void load_GL_VERSION_1_0(GLADloadproc load) {
@@ -932,6 +991,67 @@ static void load_GL_VERSION_3_2(GLADloadproc load) {
glad_glGetMultisamplefv = (PFNGLGETMULTISAMPLEFVPROC)load("glGetMultisamplefv");
glad_glSampleMaski = (PFNGLSAMPLEMASKIPROC)load("glSampleMaski");
}
+static void load_GL_VERSION_3_3(GLADloadproc load) {
+ if(!GLAD_GL_VERSION_3_3) return;
+ glad_glBindFragDataLocationIndexed = (PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)load("glBindFragDataLocationIndexed");
+ glad_glGetFragDataIndex = (PFNGLGETFRAGDATAINDEXPROC)load("glGetFragDataIndex");
+ glad_glGenSamplers = (PFNGLGENSAMPLERSPROC)load("glGenSamplers");
+ glad_glDeleteSamplers = (PFNGLDELETESAMPLERSPROC)load("glDeleteSamplers");
+ glad_glIsSampler = (PFNGLISSAMPLERPROC)load("glIsSampler");
+ glad_glBindSampler = (PFNGLBINDSAMPLERPROC)load("glBindSampler");
+ glad_glSamplerParameteri = (PFNGLSAMPLERPARAMETERIPROC)load("glSamplerParameteri");
+ glad_glSamplerParameteriv = (PFNGLSAMPLERPARAMETERIVPROC)load("glSamplerParameteriv");
+ glad_glSamplerParameterf = (PFNGLSAMPLERPARAMETERFPROC)load("glSamplerParameterf");
+ glad_glSamplerParameterfv = (PFNGLSAMPLERPARAMETERFVPROC)load("glSamplerParameterfv");
+ glad_glSamplerParameterIiv = (PFNGLSAMPLERPARAMETERIIVPROC)load("glSamplerParameterIiv");
+ glad_glSamplerParameterIuiv = (PFNGLSAMPLERPARAMETERIUIVPROC)load("glSamplerParameterIuiv");
+ glad_glGetSamplerParameteriv = (PFNGLGETSAMPLERPARAMETERIVPROC)load("glGetSamplerParameteriv");
+ glad_glGetSamplerParameterIiv = (PFNGLGETSAMPLERPARAMETERIIVPROC)load("glGetSamplerParameterIiv");
+ glad_glGetSamplerParameterfv = (PFNGLGETSAMPLERPARAMETERFVPROC)load("glGetSamplerParameterfv");
+ glad_glGetSamplerParameterIuiv = (PFNGLGETSAMPLERPARAMETERIUIVPROC)load("glGetSamplerParameterIuiv");
+ glad_glQueryCounter = (PFNGLQUERYCOUNTERPROC)load("glQueryCounter");
+ glad_glGetQueryObjecti64v = (PFNGLGETQUERYOBJECTI64VPROC)load("glGetQueryObjecti64v");
+ glad_glGetQueryObjectui64v = (PFNGLGETQUERYOBJECTUI64VPROC)load("glGetQueryObjectui64v");
+ glad_glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)load("glVertexAttribDivisor");
+ glad_glVertexAttribP1ui = (PFNGLVERTEXATTRIBP1UIPROC)load("glVertexAttribP1ui");
+ glad_glVertexAttribP1uiv = (PFNGLVERTEXATTRIBP1UIVPROC)load("glVertexAttribP1uiv");
+ glad_glVertexAttribP2ui = (PFNGLVERTEXATTRIBP2UIPROC)load("glVertexAttribP2ui");
+ glad_glVertexAttribP2uiv = (PFNGLVERTEXATTRIBP2UIVPROC)load("glVertexAttribP2uiv");
+ glad_glVertexAttribP3ui = (PFNGLVERTEXATTRIBP3UIPROC)load("glVertexAttribP3ui");
+ glad_glVertexAttribP3uiv = (PFNGLVERTEXATTRIBP3UIVPROC)load("glVertexAttribP3uiv");
+ glad_glVertexAttribP4ui = (PFNGLVERTEXATTRIBP4UIPROC)load("glVertexAttribP4ui");
+ glad_glVertexAttribP4uiv = (PFNGLVERTEXATTRIBP4UIVPROC)load("glVertexAttribP4uiv");
+ glad_glVertexP2ui = (PFNGLVERTEXP2UIPROC)load("glVertexP2ui");
+ glad_glVertexP2uiv = (PFNGLVERTEXP2UIVPROC)load("glVertexP2uiv");
+ glad_glVertexP3ui = (PFNGLVERTEXP3UIPROC)load("glVertexP3ui");
+ glad_glVertexP3uiv = (PFNGLVERTEXP3UIVPROC)load("glVertexP3uiv");
+ glad_glVertexP4ui = (PFNGLVERTEXP4UIPROC)load("glVertexP4ui");
+ glad_glVertexP4uiv = (PFNGLVERTEXP4UIVPROC)load("glVertexP4uiv");
+ glad_glTexCoordP1ui = (PFNGLTEXCOORDP1UIPROC)load("glTexCoordP1ui");
+ glad_glTexCoordP1uiv = (PFNGLTEXCOORDP1UIVPROC)load("glTexCoordP1uiv");
+ glad_glTexCoordP2ui = (PFNGLTEXCOORDP2UIPROC)load("glTexCoordP2ui");
+ glad_glTexCoordP2uiv = (PFNGLTEXCOORDP2UIVPROC)load("glTexCoordP2uiv");
+ glad_glTexCoordP3ui = (PFNGLTEXCOORDP3UIPROC)load("glTexCoordP3ui");
+ glad_glTexCoordP3uiv = (PFNGLTEXCOORDP3UIVPROC)load("glTexCoordP3uiv");
+ glad_glTexCoordP4ui = (PFNGLTEXCOORDP4UIPROC)load("glTexCoordP4ui");
+ glad_glTexCoordP4uiv = (PFNGLTEXCOORDP4UIVPROC)load("glTexCoordP4uiv");
+ glad_glMultiTexCoordP1ui = (PFNGLMULTITEXCOORDP1UIPROC)load("glMultiTexCoordP1ui");
+ glad_glMultiTexCoordP1uiv = (PFNGLMULTITEXCOORDP1UIVPROC)load("glMultiTexCoordP1uiv");
+ glad_glMultiTexCoordP2ui = (PFNGLMULTITEXCOORDP2UIPROC)load("glMultiTexCoordP2ui");
+ glad_glMultiTexCoordP2uiv = (PFNGLMULTITEXCOORDP2UIVPROC)load("glMultiTexCoordP2uiv");
+ glad_glMultiTexCoordP3ui = (PFNGLMULTITEXCOORDP3UIPROC)load("glMultiTexCoordP3ui");
+ glad_glMultiTexCoordP3uiv = (PFNGLMULTITEXCOORDP3UIVPROC)load("glMultiTexCoordP3uiv");
+ glad_glMultiTexCoordP4ui = (PFNGLMULTITEXCOORDP4UIPROC)load("glMultiTexCoordP4ui");
+ glad_glMultiTexCoordP4uiv = (PFNGLMULTITEXCOORDP4UIVPROC)load("glMultiTexCoordP4uiv");
+ glad_glNormalP3ui = (PFNGLNORMALP3UIPROC)load("glNormalP3ui");
+ glad_glNormalP3uiv = (PFNGLNORMALP3UIVPROC)load("glNormalP3uiv");
+ glad_glColorP3ui = (PFNGLCOLORP3UIPROC)load("glColorP3ui");
+ glad_glColorP3uiv = (PFNGLCOLORP3UIVPROC)load("glColorP3uiv");
+ glad_glColorP4ui = (PFNGLCOLORP4UIPROC)load("glColorP4ui");
+ glad_glColorP4uiv = (PFNGLCOLORP4UIVPROC)load("glColorP4uiv");
+ glad_glSecondaryColorP3ui = (PFNGLSECONDARYCOLORP3UIPROC)load("glSecondaryColorP3ui");
+ glad_glSecondaryColorP3uiv = (PFNGLSECONDARYCOLORP3UIVPROC)load("glSecondaryColorP3uiv");
+}
static int find_extensionsGL(void) {
if (!get_exts()) return 0;
(void)&has_ext;
@@ -986,9 +1106,10 @@ static void find_coreGL(void) {
GLAD_GL_VERSION_3_0 = (major == 3 && minor >= 0) || major > 3;
GLAD_GL_VERSION_3_1 = (major == 3 && minor >= 1) || major > 3;
GLAD_GL_VERSION_3_2 = (major == 3 && minor >= 2) || major > 3;
- if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 2)) {
+ GLAD_GL_VERSION_3_3 = (major == 3 && minor >= 3) || major > 3;
+ if (GLVersion.major > 3 || (GLVersion.major >= 3 && GLVersion.minor >= 3)) {
max_loaded_major = 3;
- max_loaded_minor = 2;
+ max_loaded_minor = 3;
}
}
@@ -1009,6 +1130,7 @@ int gladLoadGLLoader(GLADloadproc load) {
load_GL_VERSION_3_0(load);
load_GL_VERSION_3_1(load);
load_GL_VERSION_3_2(load);
+ load_GL_VERSION_3_3(load);
if (!find_extensionsGL()) return 0;
return GLVersion.major != 0 || GLVersion.minor != 0;
diff --git a/subprojects/d2tk/pugl/test/glad/glad.h b/subprojects/d2tk/pugl/examples/glad/glad.h
index b743679..d8068c6 100644
--- a/subprojects/d2tk/pugl/test/glad/glad.h
+++ b/subprojects/d2tk/pugl/examples/glad/glad.h
@@ -4,7 +4,7 @@
Language/Generator: C/C++
Specification: gl
- APIs: gl=3.2
+ APIs: gl=3.3
Profile: core
Extensions:
@@ -14,9 +14,9 @@
Reproducible: True
Commandline:
- --profile="core" --api="gl=3.2" --generator="c" --spec="gl" --local-files --extensions=""
+ --profile="core" --api="gl=3.3" --generator="c" --spec="gl" --local-files --extensions=""
Online:
- https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.2
+ https://glad.dav1d.de/#profile=core&language=c&specification=gl&loader=on&api=gl%3D3.3
*/
@@ -935,6 +935,22 @@ typedef void (APIENTRY *GLVULKANPROCNV)(void);
#define GL_MAX_COLOR_TEXTURE_SAMPLES 0x910E
#define GL_MAX_DEPTH_TEXTURE_SAMPLES 0x910F
#define GL_MAX_INTEGER_SAMPLES 0x9110
+#define GL_VERTEX_ATTRIB_ARRAY_DIVISOR 0x88FE
+#define GL_SRC1_COLOR 0x88F9
+#define GL_ONE_MINUS_SRC1_COLOR 0x88FA
+#define GL_ONE_MINUS_SRC1_ALPHA 0x88FB
+#define GL_MAX_DUAL_SOURCE_DRAW_BUFFERS 0x88FC
+#define GL_ANY_SAMPLES_PASSED 0x8C2F
+#define GL_SAMPLER_BINDING 0x8919
+#define GL_RGB10_A2UI 0x906F
+#define GL_TEXTURE_SWIZZLE_R 0x8E42
+#define GL_TEXTURE_SWIZZLE_G 0x8E43
+#define GL_TEXTURE_SWIZZLE_B 0x8E44
+#define GL_TEXTURE_SWIZZLE_A 0x8E45
+#define GL_TEXTURE_SWIZZLE_RGBA 0x8E46
+#define GL_TIME_ELAPSED 0x88BF
+#define GL_TIMESTAMP 0x8E28
+#define GL_INT_2_10_10_10_REV 0x8D9F
#ifndef GL_VERSION_1_0
#define GL_VERSION_1_0 1
GLAPI int GLAD_GL_VERSION_1_0;
@@ -1927,6 +1943,184 @@ typedef void (APIENTRYP PFNGLSAMPLEMASKIPROC)(GLuint maskNumber, GLbitfield mask
GLAPI PFNGLSAMPLEMASKIPROC glad_glSampleMaski;
#define glSampleMaski glad_glSampleMaski
#endif
+#ifndef GL_VERSION_3_3
+#define GL_VERSION_3_3 1
+GLAPI int GLAD_GL_VERSION_3_3;
+typedef void (APIENTRYP PFNGLBINDFRAGDATALOCATIONINDEXEDPROC)(GLuint program, GLuint colorNumber, GLuint index, const GLchar *name);
+GLAPI PFNGLBINDFRAGDATALOCATIONINDEXEDPROC glad_glBindFragDataLocationIndexed;
+#define glBindFragDataLocationIndexed glad_glBindFragDataLocationIndexed
+typedef GLint (APIENTRYP PFNGLGETFRAGDATAINDEXPROC)(GLuint program, const GLchar *name);
+GLAPI PFNGLGETFRAGDATAINDEXPROC glad_glGetFragDataIndex;
+#define glGetFragDataIndex glad_glGetFragDataIndex
+typedef void (APIENTRYP PFNGLGENSAMPLERSPROC)(GLsizei count, GLuint *samplers);
+GLAPI PFNGLGENSAMPLERSPROC glad_glGenSamplers;
+#define glGenSamplers glad_glGenSamplers
+typedef void (APIENTRYP PFNGLDELETESAMPLERSPROC)(GLsizei count, const GLuint *samplers);
+GLAPI PFNGLDELETESAMPLERSPROC glad_glDeleteSamplers;
+#define glDeleteSamplers glad_glDeleteSamplers
+typedef GLboolean (APIENTRYP PFNGLISSAMPLERPROC)(GLuint sampler);
+GLAPI PFNGLISSAMPLERPROC glad_glIsSampler;
+#define glIsSampler glad_glIsSampler
+typedef void (APIENTRYP PFNGLBINDSAMPLERPROC)(GLuint unit, GLuint sampler);
+GLAPI PFNGLBINDSAMPLERPROC glad_glBindSampler;
+#define glBindSampler glad_glBindSampler
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIPROC)(GLuint sampler, GLenum pname, GLint param);
+GLAPI PFNGLSAMPLERPARAMETERIPROC glad_glSamplerParameteri;
+#define glSamplerParameteri glad_glSamplerParameteri
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, const GLint *param);
+GLAPI PFNGLSAMPLERPARAMETERIVPROC glad_glSamplerParameteriv;
+#define glSamplerParameteriv glad_glSamplerParameteriv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFPROC)(GLuint sampler, GLenum pname, GLfloat param);
+GLAPI PFNGLSAMPLERPARAMETERFPROC glad_glSamplerParameterf;
+#define glSamplerParameterf glad_glSamplerParameterf
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, const GLfloat *param);
+GLAPI PFNGLSAMPLERPARAMETERFVPROC glad_glSamplerParameterfv;
+#define glSamplerParameterfv glad_glSamplerParameterfv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, const GLint *param);
+GLAPI PFNGLSAMPLERPARAMETERIIVPROC glad_glSamplerParameterIiv;
+#define glSamplerParameterIiv glad_glSamplerParameterIiv
+typedef void (APIENTRYP PFNGLSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, const GLuint *param);
+GLAPI PFNGLSAMPLERPARAMETERIUIVPROC glad_glSamplerParameterIuiv;
+#define glSamplerParameterIuiv glad_glSamplerParameterIuiv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIVPROC)(GLuint sampler, GLenum pname, GLint *params);
+GLAPI PFNGLGETSAMPLERPARAMETERIVPROC glad_glGetSamplerParameteriv;
+#define glGetSamplerParameteriv glad_glGetSamplerParameteriv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIIVPROC)(GLuint sampler, GLenum pname, GLint *params);
+GLAPI PFNGLGETSAMPLERPARAMETERIIVPROC glad_glGetSamplerParameterIiv;
+#define glGetSamplerParameterIiv glad_glGetSamplerParameterIiv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERFVPROC)(GLuint sampler, GLenum pname, GLfloat *params);
+GLAPI PFNGLGETSAMPLERPARAMETERFVPROC glad_glGetSamplerParameterfv;
+#define glGetSamplerParameterfv glad_glGetSamplerParameterfv
+typedef void (APIENTRYP PFNGLGETSAMPLERPARAMETERIUIVPROC)(GLuint sampler, GLenum pname, GLuint *params);
+GLAPI PFNGLGETSAMPLERPARAMETERIUIVPROC glad_glGetSamplerParameterIuiv;
+#define glGetSamplerParameterIuiv glad_glGetSamplerParameterIuiv
+typedef void (APIENTRYP PFNGLQUERYCOUNTERPROC)(GLuint id, GLenum target);
+GLAPI PFNGLQUERYCOUNTERPROC glad_glQueryCounter;
+#define glQueryCounter glad_glQueryCounter
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTI64VPROC)(GLuint id, GLenum pname, GLint64 *params);
+GLAPI PFNGLGETQUERYOBJECTI64VPROC glad_glGetQueryObjecti64v;
+#define glGetQueryObjecti64v glad_glGetQueryObjecti64v
+typedef void (APIENTRYP PFNGLGETQUERYOBJECTUI64VPROC)(GLuint id, GLenum pname, GLuint64 *params);
+GLAPI PFNGLGETQUERYOBJECTUI64VPROC glad_glGetQueryObjectui64v;
+#define glGetQueryObjectui64v glad_glGetQueryObjectui64v
+typedef void (APIENTRYP PFNGLVERTEXATTRIBDIVISORPROC)(GLuint index, GLuint divisor);
+GLAPI PFNGLVERTEXATTRIBDIVISORPROC glad_glVertexAttribDivisor;
+#define glVertexAttribDivisor glad_glVertexAttribDivisor
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP1UIPROC glad_glVertexAttribP1ui;
+#define glVertexAttribP1ui glad_glVertexAttribP1ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP1UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP1UIVPROC glad_glVertexAttribP1uiv;
+#define glVertexAttribP1uiv glad_glVertexAttribP1uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP2UIPROC glad_glVertexAttribP2ui;
+#define glVertexAttribP2ui glad_glVertexAttribP2ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP2UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP2UIVPROC glad_glVertexAttribP2uiv;
+#define glVertexAttribP2uiv glad_glVertexAttribP2uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP3UIPROC glad_glVertexAttribP3ui;
+#define glVertexAttribP3ui glad_glVertexAttribP3ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP3UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP3UIVPROC glad_glVertexAttribP3uiv;
+#define glVertexAttribP3uiv glad_glVertexAttribP3uiv
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIPROC)(GLuint index, GLenum type, GLboolean normalized, GLuint value);
+GLAPI PFNGLVERTEXATTRIBP4UIPROC glad_glVertexAttribP4ui;
+#define glVertexAttribP4ui glad_glVertexAttribP4ui
+typedef void (APIENTRYP PFNGLVERTEXATTRIBP4UIVPROC)(GLuint index, GLenum type, GLboolean normalized, const GLuint *value);
+GLAPI PFNGLVERTEXATTRIBP4UIVPROC glad_glVertexAttribP4uiv;
+#define glVertexAttribP4uiv glad_glVertexAttribP4uiv
+typedef void (APIENTRYP PFNGLVERTEXP2UIPROC)(GLenum type, GLuint value);
+GLAPI PFNGLVERTEXP2UIPROC glad_glVertexP2ui;
+#define glVertexP2ui glad_glVertexP2ui
+typedef void (APIENTRYP PFNGLVERTEXP2UIVPROC)(GLenum type, const GLuint *value);
+GLAPI PFNGLVERTEXP2UIVPROC glad_glVertexP2uiv;
+#define glVertexP2uiv glad_glVertexP2uiv
+typedef void (APIENTRYP PFNGLVERTEXP3UIPROC)(GLenum type, GLuint value);
+GLAPI PFNGLVERTEXP3UIPROC glad_glVertexP3ui;
+#define glVertexP3ui glad_glVertexP3ui
+typedef void (APIENTRYP PFNGLVERTEXP3UIVPROC)(GLenum type, const GLuint *value);
+GLAPI PFNGLVERTEXP3UIVPROC glad_glVertexP3uiv;
+#define glVertexP3uiv glad_glVertexP3uiv
+typedef void (APIENTRYP PFNGLVERTEXP4UIPROC)(GLenum type, GLuint value);
+GLAPI PFNGLVERTEXP4UIPROC glad_glVertexP4ui;
+#define glVertexP4ui glad_glVertexP4ui
+typedef void (APIENTRYP PFNGLVERTEXP4UIVPROC)(GLenum type, const GLuint *value);
+GLAPI PFNGLVERTEXP4UIVPROC glad_glVertexP4uiv;
+#define glVertexP4uiv glad_glVertexP4uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP1UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP1UIPROC glad_glTexCoordP1ui;
+#define glTexCoordP1ui glad_glTexCoordP1ui
+typedef void (APIENTRYP PFNGLTEXCOORDP1UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP1UIVPROC glad_glTexCoordP1uiv;
+#define glTexCoordP1uiv glad_glTexCoordP1uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP2UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP2UIPROC glad_glTexCoordP2ui;
+#define glTexCoordP2ui glad_glTexCoordP2ui
+typedef void (APIENTRYP PFNGLTEXCOORDP2UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP2UIVPROC glad_glTexCoordP2uiv;
+#define glTexCoordP2uiv glad_glTexCoordP2uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP3UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP3UIPROC glad_glTexCoordP3ui;
+#define glTexCoordP3ui glad_glTexCoordP3ui
+typedef void (APIENTRYP PFNGLTEXCOORDP3UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP3UIVPROC glad_glTexCoordP3uiv;
+#define glTexCoordP3uiv glad_glTexCoordP3uiv
+typedef void (APIENTRYP PFNGLTEXCOORDP4UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLTEXCOORDP4UIPROC glad_glTexCoordP4ui;
+#define glTexCoordP4ui glad_glTexCoordP4ui
+typedef void (APIENTRYP PFNGLTEXCOORDP4UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLTEXCOORDP4UIVPROC glad_glTexCoordP4uiv;
+#define glTexCoordP4uiv glad_glTexCoordP4uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP1UIPROC glad_glMultiTexCoordP1ui;
+#define glMultiTexCoordP1ui glad_glMultiTexCoordP1ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP1UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP1UIVPROC glad_glMultiTexCoordP1uiv;
+#define glMultiTexCoordP1uiv glad_glMultiTexCoordP1uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP2UIPROC glad_glMultiTexCoordP2ui;
+#define glMultiTexCoordP2ui glad_glMultiTexCoordP2ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP2UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP2UIVPROC glad_glMultiTexCoordP2uiv;
+#define glMultiTexCoordP2uiv glad_glMultiTexCoordP2uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP3UIPROC glad_glMultiTexCoordP3ui;
+#define glMultiTexCoordP3ui glad_glMultiTexCoordP3ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP3UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP3UIVPROC glad_glMultiTexCoordP3uiv;
+#define glMultiTexCoordP3uiv glad_glMultiTexCoordP3uiv
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIPROC)(GLenum texture, GLenum type, GLuint coords);
+GLAPI PFNGLMULTITEXCOORDP4UIPROC glad_glMultiTexCoordP4ui;
+#define glMultiTexCoordP4ui glad_glMultiTexCoordP4ui
+typedef void (APIENTRYP PFNGLMULTITEXCOORDP4UIVPROC)(GLenum texture, GLenum type, const GLuint *coords);
+GLAPI PFNGLMULTITEXCOORDP4UIVPROC glad_glMultiTexCoordP4uiv;
+#define glMultiTexCoordP4uiv glad_glMultiTexCoordP4uiv
+typedef void (APIENTRYP PFNGLNORMALP3UIPROC)(GLenum type, GLuint coords);
+GLAPI PFNGLNORMALP3UIPROC glad_glNormalP3ui;
+#define glNormalP3ui glad_glNormalP3ui
+typedef void (APIENTRYP PFNGLNORMALP3UIVPROC)(GLenum type, const GLuint *coords);
+GLAPI PFNGLNORMALP3UIVPROC glad_glNormalP3uiv;
+#define glNormalP3uiv glad_glNormalP3uiv
+typedef void (APIENTRYP PFNGLCOLORP3UIPROC)(GLenum type, GLuint color);
+GLAPI PFNGLCOLORP3UIPROC glad_glColorP3ui;
+#define glColorP3ui glad_glColorP3ui
+typedef void (APIENTRYP PFNGLCOLORP3UIVPROC)(GLenum type, const GLuint *color);
+GLAPI PFNGLCOLORP3UIVPROC glad_glColorP3uiv;
+#define glColorP3uiv glad_glColorP3uiv
+typedef void (APIENTRYP PFNGLCOLORP4UIPROC)(GLenum type, GLuint color);
+GLAPI PFNGLCOLORP4UIPROC glad_glColorP4ui;
+#define glColorP4ui glad_glColorP4ui
+typedef void (APIENTRYP PFNGLCOLORP4UIVPROC)(GLenum type, const GLuint *color);
+GLAPI PFNGLCOLORP4UIVPROC glad_glColorP4uiv;
+#define glColorP4uiv glad_glColorP4uiv
+typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIPROC)(GLenum type, GLuint color);
+GLAPI PFNGLSECONDARYCOLORP3UIPROC glad_glSecondaryColorP3ui;
+#define glSecondaryColorP3ui glad_glSecondaryColorP3ui
+typedef void (APIENTRYP PFNGLSECONDARYCOLORP3UIVPROC)(GLenum type, const GLuint *color);
+GLAPI PFNGLSECONDARYCOLORP3UIVPROC glad_glSecondaryColorP3uiv;
+#define glSecondaryColorP3uiv glad_glSecondaryColorP3uiv
+#endif
#ifdef __cplusplus
}
diff --git a/subprojects/d2tk/pugl/test/glad/khrplatform.h b/subprojects/d2tk/pugl/examples/glad/khrplatform.h
index 5b55ea2..5b55ea2 100644
--- a/subprojects/d2tk/pugl/test/glad/khrplatform.h
+++ b/subprojects/d2tk/pugl/examples/glad/khrplatform.h
diff --git a/subprojects/d2tk/pugl/test/pugl_cairo_test.c b/subprojects/d2tk/pugl/examples/pugl_cairo_demo.c
index 23c2b4e..6b43cb0 100644
--- a/subprojects/d2tk/pugl/test/pugl_cairo_test.c
+++ b/subprojects/d2tk/pugl/examples/pugl_cairo_demo.c
@@ -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
@@ -15,13 +15,14 @@
*/
/**
- @file pugl_cairo_test.c A simple Pugl test that creates a top-level window.
+ @file pugl_cairo_demo.c An example of drawing with Cairo.
*/
-#include "test_utils.h"
+#include "demo_utils.h"
+#include "test/test_utils.h"
#include "pugl/pugl.h"
-#include "pugl/pugl_cairo_backend.h"
+#include "pugl/pugl_cairo.h"
#include <cairo.h>
@@ -30,13 +31,14 @@
#include <stdio.h>
#include <string.h>
-static PuglWorld* world = NULL;
-PuglTestOptions opts = {0};
-
-static int quit = 0;
-static bool entered = false;
-static bool mouseDown = false;
-static unsigned framesDrawn = 0;
+typedef struct {
+ PuglTestOptions opts;
+ PuglWorld* world;
+ unsigned framesDrawn;
+ int quit;
+ bool entered;
+ bool mouseDown;
+} PuglTestApp;
typedef struct {
int x;
@@ -46,11 +48,11 @@ typedef struct {
const char* label;
} Button;
-static Button buttons[] = { { 128, 128, 64, 64, "1" },
- { 384, 128, 64, 64, "2" },
- { 128, 384, 64, 64, "3" },
- { 384, 384, 64, 64, "4" },
- { 0, 0, 0, 0, NULL } };
+static const Button buttons[] = {{128, 128, 64, 64, "1"},
+ {384, 128, 64, 64, "2"},
+ {128, 384, 64, 64, "3"},
+ {384, 384, 64, 64, "4"},
+ {0, 0, 0, 0, NULL}};
static void
roundedBox(cairo_t* cr, double x, double y, double w, double h)
@@ -76,14 +78,14 @@ roundedBox(cairo_t* cr, double x, double y, double w, double h)
}
static void
-buttonDraw(cairo_t* cr, const Button* but, const double time)
+buttonDraw(PuglTestApp* app, cairo_t* cr, const Button* but, const double time)
{
cairo_save(cr);
cairo_translate(cr, but->x, but->y);
cairo_rotate(cr, sin(time) * 3.141592);
// Draw base
- if (mouseDown) {
+ if (app->mouseDown) {
cairo_set_source_rgba(cr, 0.4, 0.9, 0.1, 1);
} else {
cairo_set_source_rgba(cr, 0.3, 0.5, 0.1, 1);
@@ -110,20 +112,42 @@ buttonDraw(cairo_t* cr, const Button* but, const double time)
}
static void
-onDisplay(PuglView* view)
+postButtonRedisplay(PuglView* view)
+{
+ const PuglRect frame = puglGetFrame(view);
+ const double width = frame.width;
+ const double height = frame.height;
+ const double scaleX = (width - (512 / width)) / 512.0;
+ const double scaleY = (height - (512 / height)) / 512.0;
+
+ for (const Button* b = buttons; b->label; ++b) {
+ const double span = sqrt(b->w * b->w + b->h * b->h);
+ const PuglRect rect = {(b->x - span) * scaleX,
+ (b->y - span) * scaleY,
+ span * 2.0 * scaleX,
+ span * 2.0 * scaleY};
+
+ puglPostRedisplayRect(view, rect);
+ }
+}
+
+static void
+onDisplay(PuglTestApp* app, PuglView* view, const PuglEventExpose* event)
{
cairo_t* cr = (cairo_t*)puglGetContext(view);
+ cairo_rectangle(cr, event->x, event->y, event->width, event->height);
+ cairo_clip_preserve(cr);
+
// Draw background
const PuglRect frame = puglGetFrame(view);
const double width = frame.width;
const double height = frame.height;
- if (entered) {
+ if (app->entered) {
cairo_set_source_rgb(cr, 0.1, 0.1, 0.1);
} else {
cairo_set_source_rgb(cr, 0, 0, 0);
}
- cairo_rectangle(cr, 0, 0, width, height);
cairo_fill(cr);
// Scale to view size
@@ -132,47 +156,60 @@ onDisplay(PuglView* view)
cairo_scale(cr, scaleX, scaleY);
// Draw button
- for (Button* b = buttons; b->label; ++b) {
- buttonDraw(cr, b, opts.continuous ? puglGetTime(world) : 0.0);
+ for (const Button* b = buttons; b->label; ++b) {
+ buttonDraw(app,
+ cr,
+ b,
+ app->opts.continuous ? puglGetTime(app->world) : 0.0);
}
- ++framesDrawn;
+ ++app->framesDrawn;
}
static void
onClose(PuglView* view)
{
- (void)view;
- quit = 1;
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+
+ app->quit = 1;
}
static PuglStatus
onEvent(PuglView* view, const PuglEvent* event)
{
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+
+ printEvent(event, "Event: ", app->opts.verbose);
+
switch (event->type) {
case PUGL_KEY_PRESS:
if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
- quit = 1;
+ app->quit = 1;
}
break;
case PUGL_BUTTON_PRESS:
- mouseDown = true;
- puglPostRedisplay(view);
+ app->mouseDown = true;
+ postButtonRedisplay(view);
break;
case PUGL_BUTTON_RELEASE:
- mouseDown = false;
- puglPostRedisplay(view);
+ app->mouseDown = false;
+ postButtonRedisplay(view);
break;
- case PUGL_ENTER_NOTIFY:
- entered = true;
+ case PUGL_POINTER_IN:
+ app->entered = true;
puglPostRedisplay(view);
break;
- case PUGL_LEAVE_NOTIFY:
- entered = false;
+ case PUGL_POINTER_OUT:
+ app->entered = false;
puglPostRedisplay(view);
break;
+ case PUGL_UPDATE:
+ if (app->opts.continuous) {
+ puglPostRedisplay(view);
+ }
+ break;
case PUGL_EXPOSE:
- onDisplay(view);
+ onDisplay(app, view, &event->expose);
break;
case PUGL_CLOSE:
onClose(view);
@@ -186,47 +223,47 @@ onEvent(PuglView* view, const PuglEvent* event)
int
main(int argc, char** argv)
{
- opts = puglParseTestOptions(&argc, &argv);
- if (opts.help) {
+ PuglTestApp app;
+ memset(&app, 0, sizeof(app));
+
+ app.opts = puglParseTestOptions(&argc, &argv);
+ if (app.opts.help) {
puglPrintTestUsage("pugl_test", "");
return 1;
}
- world = puglNewWorld();
- puglSetClassName(world, "PuglCairoTest");
+ app.world = puglNewWorld(PUGL_PROGRAM, 0);
+ puglSetClassName(app.world, "PuglCairoTest");
PuglRect frame = { 0, 0, 512, 512 };
- PuglView* view = puglNewView(world);
+ PuglView* view = puglNewView(app.world);
puglSetFrame(view, frame);
puglSetMinSize(view, 256, 256);
- puglSetViewHint(view, PUGL_RESIZABLE, opts.resizable);
+ puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable);
+ puglSetHandle(view, &app);
puglSetBackend(view, puglCairoBackend());
- puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat);
+ puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, app.opts.ignoreKeyRepeat);
puglSetEventFunc(view, onEvent);
- if (puglCreateWindow(view, "Pugl Test")) {
- return 1;
+ PuglStatus st = puglCreateWindow(view, "Pugl Test");
+ if (st) {
+ return logError("Failed to create window (%s)\n", puglStrerror(st));
}
puglShowWindow(view);
- PuglFpsPrinter fpsPrinter = { puglGetTime(world) };
- while (!quit) {
- if (opts.continuous) {
- puglPostRedisplay(view);
- } else {
- puglPollEvents(world, -1);
- }
-
- puglDispatchEvents(world);
+ PuglFpsPrinter fpsPrinter = { puglGetTime(app.world) };
+ const double timeout = app.opts.continuous ? (1 / 60.0) : -1.0;
+ while (!app.quit) {
+ puglUpdate(app.world, timeout);
- if (opts.continuous) {
- puglPrintFps(world, &fpsPrinter, &framesDrawn);
+ if (app.opts.continuous) {
+ puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
}
}
puglFreeView(view);
- puglFreeWorld(world);
+ puglFreeWorld(app.world);
return 0;
}
diff --git a/subprojects/d2tk/pugl/test/pugl_test.c b/subprojects/d2tk/pugl/examples/pugl_embed_demo.c
index 4bd5f80..313a92c 100644
--- a/subprojects/d2tk/pugl/test/pugl_test.c
+++ b/subprojects/d2tk/pugl/examples/pugl_embed_demo.c
@@ -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
@@ -15,16 +15,16 @@
*/
/**
- @file pugl_test.c A simple Pugl test that creates a top-level window.
+ @file pugl_embed_demo.c An example of embedding a view in another.
*/
-#define GL_SILENCE_DEPRECATION 1
-
-#include "test_utils.h"
+#include "cube_view.h"
+#include "demo_utils.h"
+#include "test/test_utils.h"
#include "pugl/gl.h"
#include "pugl/pugl.h"
-#include "pugl/pugl_gl_backend.h"
+#include "pugl/pugl_gl.h"
#include <math.h>
#include <stdbool.h>
@@ -32,7 +32,8 @@
#include <stdio.h>
#include <string.h>
-static const int borderWidth = 64;
+static const int borderWidth = 64;
+static const uintptr_t reverseTimerId = 1u;
typedef struct
{
@@ -41,16 +42,24 @@ typedef struct
PuglView* child;
bool continuous;
int quit;
- float xAngle;
- float yAngle;
+ double xAngle;
+ double yAngle;
float dist;
double lastMouseX;
double lastMouseY;
double lastDrawTime;
- unsigned framesDrawn;
bool mouseEntered;
+ bool verbose;
+ bool reversing;
} PuglTestApp;
+static const float backgroundVertices[] = {
+ -1.0f, 1.0f, -1.0f, // Top left
+ 1.0f, 1.0f, -1.0f, // Top right
+ -1.0f, -1.0f, -1.0f, // Bottom left
+ 1.0f, -1.0f, -1.0f, // Bottom right
+};
+
static PuglRect
getChildFrame(const PuglRect parentFrame)
{
@@ -65,72 +74,22 @@ getChildFrame(const PuglRect parentFrame)
}
static void
-onReshape(PuglView* view, int width, int height)
-{
- (void)view;
-
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
- glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
-
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- glViewport(0, 0, width, height);
-
- float projection[16];
- perspective(projection, 1.8f, width / (float)height, 1.0, 100.0f);
- glLoadMatrixf(projection);
-}
-
-static void
onDisplay(PuglView* view)
{
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
const double thisTime = puglGetTime(app->world);
if (app->continuous) {
- const double dTime = thisTime - app->lastDrawTime;
- app->xAngle = fmodf((float)(app->xAngle + dTime * 100.0f), 360.0f);
- app->yAngle = fmodf((float)(app->yAngle + dTime * 100.0f), 360.0f);
- }
+ const double dTime = (thisTime - app->lastDrawTime) *
+ (app->reversing ? -1.0 : 1.0);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- glTranslatef(0.0f, 0.0f, app->dist * -1);
- glRotatef(app->xAngle, 0.0f, 1.0f, 0.0f);
- glRotatef(app->yAngle, 1.0f, 0.0f, 0.0f);
-
- const float bg = app->mouseEntered ? 0.2f : 0.1f;
- glClearColor(bg, bg, bg, 1.0f);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-
- if (puglHasFocus(app->child)) {
- // Draw cube surfaces
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_COLOR_ARRAY);
- glVertexPointer(3, GL_FLOAT, 0, cubeStripVertices);
- glColorPointer(3, GL_FLOAT, 0, cubeStripVertices);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
- glDisableClientState(GL_COLOR_ARRAY);
- glDisableClientState(GL_VERTEX_ARRAY);
-
- glColor3f(0.0f, 0.0f, 0.0f);
- } else {
- glColor3f(1.0f, 1.0f, 1.0f);
+ app->xAngle = fmod(app->xAngle + dTime * 100.0, 360.0);
+ app->yAngle = fmod(app->yAngle + dTime * 100.0, 360.0);
}
- // Draw cube wireframe
- glEnableClientState(GL_VERTEX_ARRAY);
- glVertexPointer(3, GL_FLOAT, 0, cubeFrontLineLoop);
- glDrawArrays(GL_LINE_LOOP, 0, 4);
- glVertexPointer(3, GL_FLOAT, 0, cubeBackLineLoop);
- glDrawArrays(GL_LINE_LOOP, 0, 4);
- glVertexPointer(3, GL_FLOAT, 0, cubeSideLines);
- glDrawArrays(GL_LINES, 0, 8);
- glDisableClientState(GL_VERTEX_ARRAY);
+ displayCube(view, app->dist, app->xAngle, app->yAngle, app->mouseEntered);
app->lastDrawTime = thisTime;
- ++app->framesDrawn;
}
static void
@@ -142,8 +101,10 @@ swapFocus(PuglTestApp* app)
puglGrabFocus(app->parent);
}
- puglPostRedisplay(app->parent);
- puglPostRedisplay(app->child);
+ if (!app->continuous) {
+ puglPostRedisplay(app->parent);
+ puglPostRedisplay(app->child);
+ }
}
static void
@@ -199,16 +160,18 @@ onParentEvent(PuglView* view, const PuglEvent* event)
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
const PuglRect parentFrame = puglGetFrame(view);
- printEvent(event, "Parent: ");
+ printEvent(event, "Parent: ", app->verbose);
switch (event->type) {
case PUGL_CONFIGURE:
- onReshape(view,
- (int)event->configure.width,
- (int)event->configure.height);
-
+ reshapeCube((int)event->configure.width, (int)event->configure.height);
puglSetFrame(app->child, getChildFrame(parentFrame));
break;
+ case PUGL_UPDATE:
+ if (app->continuous) {
+ puglPostRedisplay(view);
+ }
+ break;
case PUGL_EXPOSE:
if (puglHasFocus(app->parent)) {
glMatrixMode(GL_MODELVIEW);
@@ -217,9 +180,9 @@ onParentEvent(PuglView* view, const PuglEvent* event)
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
- glVertexPointer(3, GL_FLOAT, 0, cubeStripVertices);
- glColorPointer(3, GL_FLOAT, 0, cubeStripVertices);
- glDrawArrays(GL_TRIANGLE_STRIP, 0, 14);
+ glVertexPointer(3, GL_FLOAT, 0, backgroundVertices);
+ glColorPointer(3, GL_FLOAT, 0, backgroundVertices);
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
} else {
@@ -230,7 +193,7 @@ onParentEvent(PuglView* view, const PuglEvent* event)
case PUGL_KEY_PRESS:
onKeyPress(view, &event->key, "Parent: ");
break;
- case PUGL_MOTION_NOTIFY:
+ case PUGL_MOTION:
break;
case PUGL_CLOSE:
app->quit = 1;
@@ -247,11 +210,16 @@ onEvent(PuglView* view, const PuglEvent* event)
{
PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
- printEvent(event, "Child: ");
+ printEvent(event, "Child: ", app->verbose);
switch (event->type) {
case PUGL_CONFIGURE:
- onReshape(view, (int)event->configure.width, (int)event->configure.height);
+ reshapeCube((int)event->configure.width, (int)event->configure.height);
+ break;
+ case PUGL_UPDATE:
+ if (app->continuous) {
+ puglPostRedisplay(view);
+ }
break;
case PUGL_EXPOSE:
onDisplay(view);
@@ -260,26 +228,33 @@ onEvent(PuglView* view, const PuglEvent* event)
app->quit = 1;
break;
case PUGL_KEY_PRESS:
- onKeyPress(view, &event->key, "Child: ");
+ onKeyPress(view, &event->key, "Child: ");
break;
- case PUGL_MOTION_NOTIFY:
- app->xAngle = fmodf(app->xAngle - (float)(event->motion.x - app->lastMouseX), 360.0f);
- app->yAngle = fmodf(app->yAngle + (float)(event->motion.y - app->lastMouseY), 360.0f);
+ case PUGL_MOTION:
+ app->xAngle -= event->motion.x - app->lastMouseX;
+ app->yAngle += event->motion.y - app->lastMouseY;
app->lastMouseX = event->motion.x;
app->lastMouseY = event->motion.y;
- puglPostRedisplay(view);
- puglPostRedisplay(app->parent);
+ if (!app->continuous) {
+ puglPostRedisplay(view);
+ puglPostRedisplay(app->parent);
+ }
break;
case PUGL_SCROLL:
app->dist = fmaxf(10.0f, app->dist + (float)event->scroll.dy);
- puglPostRedisplay(view);
+ if (!app->continuous) {
+ puglPostRedisplay(view);
+ }
break;
- case PUGL_ENTER_NOTIFY:
+ case PUGL_POINTER_IN:
app->mouseEntered = true;
break;
- case PUGL_LEAVE_NOTIFY:
+ case PUGL_POINTER_OUT:
app->mouseEntered = false;
break;
+ case PUGL_TIMER:
+ app->reversing = !app->reversing;
+ break;
default:
break;
}
@@ -291,6 +266,7 @@ int
main(int argc, char** argv)
{
PuglTestApp app = {0};
+
app.dist = 10;
const PuglTestOptions opts = puglParseTestOptions(&argc, &argv);
@@ -300,8 +276,9 @@ main(int argc, char** argv)
}
app.continuous = opts.continuous;
+ app.verbose = opts.verbose;
- app.world = puglNewWorld();
+ app.world = puglNewWorld(PUGL_PROGRAM, 0);
app.parent = puglNewView(app.world);
app.child = puglNewView(app.world);
@@ -313,54 +290,53 @@ main(int argc, char** argv)
puglSetAspectRatio(app.parent, 1, 1, 16, 9);
puglSetBackend(app.parent, puglGlBackend());
+ puglSetViewHint(app.parent, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking);
puglSetViewHint(app.parent, PUGL_RESIZABLE, opts.resizable);
puglSetViewHint(app.parent, PUGL_SAMPLES, opts.samples);
puglSetViewHint(app.parent, PUGL_DOUBLE_BUFFER, opts.doubleBuffer);
- puglSetViewHint(app.parent, PUGL_SWAP_INTERVAL, opts.doubleBuffer);
+ puglSetViewHint(app.parent, PUGL_SWAP_INTERVAL, opts.sync);
puglSetViewHint(app.parent, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat);
puglSetHandle(app.parent, &app);
puglSetEventFunc(app.parent, onParentEvent);
+ PuglStatus st = PUGL_SUCCESS;
const uint8_t title[] = { 'P', 'u', 'g', 'l', ' ',
'P', 'r', 0xC3, 0xBC, 'f', 'u', 'n', 'g', 0 };
- if (puglCreateWindow(app.parent, (const char*)title)) {
- fprintf(stderr, "error: Failed to create parent window\n");
- return 1;
+ if ((st = puglCreateWindow(app.parent, (const char*)title))) {
+ return logError("Failed to create parent window (%s)\n",
+ puglStrerror(st));
}
puglSetFrame(app.child, getChildFrame(parentFrame));
puglSetParentWindow(app.child, puglGetNativeWindow(app.parent));
+ puglSetViewHint(app.child, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking);
puglSetViewHint(app.child, PUGL_SAMPLES, opts.samples);
puglSetViewHint(app.child, PUGL_DOUBLE_BUFFER, opts.doubleBuffer);
- puglSetViewHint(app.child, PUGL_SWAP_INTERVAL, 0);
+ puglSetViewHint(app.child, PUGL_SWAP_INTERVAL, opts.sync);
puglSetBackend(app.child, puglGlBackend());
puglSetViewHint(app.child, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat);
puglSetHandle(app.child, &app);
puglSetEventFunc(app.child, onEvent);
- const int st = puglCreateWindow(app.child, NULL);
- if (st) {
- fprintf(stderr, "error: Failed to create child window (%d)\n", st);
- return 1;
+ if ((st = puglCreateWindow(app.child, NULL))) {
+ return logError("Failed to create child window (%s)\n",
+ puglStrerror(st));
}
puglShowWindow(app.parent);
puglShowWindow(app.child);
+ puglStartTimer(app.child, reverseTimerId, 3.6);
+
PuglFpsPrinter fpsPrinter = { puglGetTime(app.world) };
+ unsigned framesDrawn = 0;
bool requestedAttention = false;
while (!app.quit) {
const double thisTime = puglGetTime(app.world);
- if (app.continuous) {
- puglPostRedisplay(app.parent);
- puglPostRedisplay(app.child);
- } else {
- puglPollEvents(app.world, -1);
- }
-
- puglDispatchEvents(app.world);
+ puglUpdate(app.world, app.continuous ? 0.0 : -1.0);
+ ++framesDrawn;
if (!requestedAttention && thisTime > 5.0) {
puglRequestAttention(app.parent);
@@ -368,7 +344,7 @@ main(int argc, char** argv)
}
if (app.continuous) {
- puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
+ puglPrintFps(app.world, &fpsPrinter, &framesDrawn);
}
}
diff --git a/subprojects/d2tk/pugl/examples/pugl_gl3_demo.c b/subprojects/d2tk/pugl/examples/pugl_gl3_demo.c
new file mode 100644
index 0000000..26db852
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/pugl_gl3_demo.c
@@ -0,0 +1,429 @@
+/*
+ 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_gl3_demo.c An example of drawing with OpenGL 3.
+
+ This is an example of using OpenGL for pixel-perfect 2D drawing. It uses
+ pixel coordinates for positions and sizes so that things work roughly like a
+ typical 2D graphics API.
+
+ The program draws a bunch of rectangles with borders, using instancing.
+ Each rectangle has origin, size, and fill color attributes, which are shared
+ for all four vertices. On each frame, a single buffer with all the
+ rectangle data is sent to the GPU, and everything is drawn with a single
+ draw call.
+
+ This is not particularly realistic or optimal, but serves as a decent rough
+ benchmark for how much simple geometry you can draw. The number of
+ rectangles can be given on the command line. For reference, it begins to
+ struggle to maintain 60 FPS on my machine (1950x + Vega64) with more than
+ about 100000 rectangles.
+*/
+
+#include "demo_utils.h"
+#include "shader_utils.h"
+#include "test/test_utils.h"
+
+#include "glad/glad.h"
+
+#include "pugl/gl.h"
+#include "pugl/pugl.h"
+#include "pugl/pugl_gl.h"
+
+#include <math.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static const int defaultWidth = 512;
+static const int defaultHeight = 512;
+
+typedef struct
+{
+ float pos[2];
+ float size[2];
+ float fillColor[4];
+} Rect;
+
+// clang-format off
+static const GLfloat rectVertices[] = {
+ 0.0f, 0.0f, // TL
+ 1.0f, 0.0f, // TR
+ 0.0f, 1.0f, // BL
+ 1.0f, 1.0f, // BR
+};
+// clang-format on
+
+static const GLuint rectIndices[4] = {0, 1, 2, 3};
+
+typedef struct
+{
+ PuglTestOptions opts;
+ PuglWorld* world;
+ PuglView* view;
+ size_t numRects;
+ Rect* rects;
+ Program drawRect;
+ GLuint vao;
+ GLuint vbo;
+ GLuint instanceVbo;
+ GLuint ibo;
+ GLint u_projection;
+ unsigned framesDrawn;
+ int quit;
+} PuglTestApp;
+
+static PuglStatus
+setupGl(PuglTestApp* app);
+
+static void
+teardownGl(PuglTestApp* app);
+
+static void
+onConfigure(PuglView* view, double width, double height)
+{
+ (void)view;
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+ glViewport(0, 0, (int)width, (int)height);
+}
+
+static void
+onExpose(PuglView* view)
+{
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+ const PuglRect frame = puglGetFrame(view);
+ const double time = puglGetTime(puglGetWorld(view));
+
+ // Construct projection matrix for 2D window surface (in pixels)
+ mat4 proj;
+ mat4Ortho(proj,
+ 0.0f,
+ (float)frame.width,
+ 0.0f,
+ (float)frame.height,
+ -1.0f,
+ 1.0f);
+
+ // Clear and bind everything that is the same for every rect
+ glClear(GL_COLOR_BUFFER_BIT);
+ glUseProgram(app->drawRect.program);
+ glBindVertexArray(app->vao);
+
+ // Set projection matrix uniform
+ glUniformMatrix4fv(app->u_projection, 1, GL_FALSE, (const GLfloat*)&proj);
+
+ for (size_t i = 0; i < app->numRects; ++i) {
+ Rect* rect = &app->rects[i];
+ const float normal = i / (float)app->numRects;
+ const float offset[2] = {normal * 128.0f, normal * 128.0f};
+
+ // Move rect around in an arbitrary way that looks cool
+ rect->pos[0] =
+ (float)(frame.width - rect->size[0] + offset[0]) *
+ (sinf((float)time * rect->size[0] / 64.0f + normal) + 1.0f) /
+ 2.0f;
+ rect->pos[1] =
+ (float)(frame.height - rect->size[1] + offset[1]) *
+ (cosf((float)time * rect->size[1] / 64.0f + normal) + 1.0f) /
+ 2.0f;
+ }
+
+ glBufferSubData(GL_ARRAY_BUFFER,
+ 0,
+ (GLsizeiptr)(app->numRects * sizeof(Rect)),
+ app->rects);
+
+ glDrawElementsInstanced(GL_TRIANGLE_STRIP,
+ 4,
+ GL_UNSIGNED_INT,
+ NULL,
+ (GLsizei)(app->numRects * 4));
+
+ ++app->framesDrawn;
+}
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+
+ printEvent(event, "Event: ", app->opts.verbose);
+
+ switch (event->type) {
+ case PUGL_CREATE:
+ setupGl(app);
+ break;
+ case PUGL_DESTROY:
+ teardownGl(app);
+ break;
+ case PUGL_CONFIGURE:
+ onConfigure(view, event->configure.width, event->configure.height);
+ break;
+ case PUGL_UPDATE:
+ puglPostRedisplay(view);
+ break;
+ case PUGL_EXPOSE: onExpose(view); break;
+ case PUGL_CLOSE: app->quit = 1; break;
+ case PUGL_KEY_PRESS:
+ if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
+ app->quit = 1;
+ }
+ break;
+ default: break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+static Rect*
+makeRects(const size_t numRects)
+{
+ const float minSize = (float)defaultWidth / 64.0f;
+ const float maxSize = (float)defaultWidth / 6.0f;
+ const float boxAlpha = 0.2f;
+
+ Rect* rects = (Rect*)calloc(numRects, sizeof(Rect));
+ for (size_t i = 0; i < numRects; ++i) {
+ const float s = (sinf((float)i) / 2.0f + 0.5f);
+ const float c = (cosf((float)i) / 2.0f + 0.5f);
+
+ rects[i].size[0] = minSize + s * maxSize;
+ rects[i].size[1] = minSize + c * maxSize;
+ rects[i].fillColor[1] = s / 2.0f + 0.25f;
+ rects[i].fillColor[2] = c / 2.0f + 0.25f;
+ rects[i].fillColor[3] = boxAlpha;
+ }
+
+ return rects;
+}
+
+static char*
+loadShader(const char* const path)
+{
+ FILE* const file = fopen(path, "r");
+ if (!file) {
+ logError("Failed to open '%s'\n", path);
+ return NULL;
+ }
+
+ fseek(file, 0, SEEK_END);
+ const size_t fileSize = (size_t)ftell(file);
+
+ fseek(file, 0, SEEK_SET);
+ char* source = (char*)calloc(1, fileSize + 1u);
+
+ fread(source, 1, fileSize, file);
+ fclose(file);
+
+ return source;
+}
+
+static int
+parseOptions(PuglTestApp* app, int argc, char** argv)
+{
+ // Parse command line options
+ app->numRects = 1024;
+ app->opts = puglParseTestOptions(&argc, &argv);
+ if (app->opts.help) {
+ return 1;
+ }
+
+ // Parse number of rectangles argument, if given
+ if (argc == 1) {
+ char* endptr = NULL;
+
+ app->numRects = (size_t)strtol(argv[0], &endptr, 10);
+ if (endptr != argv[0] + strlen(argv[0])) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void
+setupPugl(PuglTestApp* app, const PuglRect frame)
+{
+ // Create world, view, and rect data
+ app->world = puglNewWorld(PUGL_PROGRAM, 0);
+ app->view = puglNewView(app->world);
+ app->rects = makeRects(app->numRects);
+
+ // Set up world and view
+ puglSetClassName(app->world, "PuglGL3Demo");
+ puglSetFrame(app->view, frame);
+ puglSetMinSize(app->view, defaultWidth / 4, defaultHeight / 4);
+ puglSetAspectRatio(app->view, 1, 1, 16, 9);
+ puglSetBackend(app->view, puglGlBackend());
+ puglSetViewHint(app->view, PUGL_USE_COMPAT_PROFILE, PUGL_FALSE);
+ puglSetViewHint(app->view, PUGL_USE_DEBUG_CONTEXT, app->opts.errorChecking);
+ puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MAJOR, 3);
+ puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MINOR, 3);
+ puglSetViewHint(app->view, PUGL_RESIZABLE, app->opts.resizable);
+ puglSetViewHint(app->view, PUGL_SAMPLES, app->opts.samples);
+ puglSetViewHint(app->view, PUGL_DOUBLE_BUFFER, app->opts.doubleBuffer);
+ puglSetViewHint(app->view, PUGL_SWAP_INTERVAL, app->opts.sync);
+ puglSetViewHint(app->view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
+ puglSetHandle(app->view, app);
+ puglSetEventFunc(app->view, onEvent);
+}
+
+static PuglStatus
+setupGl(PuglTestApp* app)
+{
+ // Load GL functions via GLAD
+ if (!gladLoadGLLoader((GLADloadproc)&puglGetProcAddress)) {
+ logError("Failed to load GL\n");
+ return PUGL_FAILURE;
+ }
+
+ // Load shader sources
+ char* const vertexSource = loadShader("shaders/rect.vert");
+ char* const fragmentSource = loadShader("shaders/rect.frag");
+ if (!vertexSource || !fragmentSource) {
+ logError("Failed to load shader sources\n");
+ return PUGL_FAILURE;
+ }
+
+ // Compile rectangle shaders and program
+ app->drawRect = compileProgram(vertexSource, fragmentSource);
+ free(fragmentSource);
+ free(vertexSource);
+ if (!app->drawRect.program) {
+ return PUGL_FAILURE;
+ }
+
+ // Get location of rectangle shader uniforms
+ app->u_projection =
+ glGetUniformLocation(app->drawRect.program, "u_projection");
+
+ // Generate/bind a VAO to track state
+ glGenVertexArrays(1, &app->vao);
+ glBindVertexArray(app->vao);
+
+ // Generate/bind a VBO to store vertex position data
+ glGenBuffers(1, &app->vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, app->vbo);
+ glBufferData(GL_ARRAY_BUFFER,
+ sizeof(rectVertices),
+ rectVertices,
+ GL_STATIC_DRAW);
+
+ // Attribute 0 is position, 2 floats from the VBO
+ glEnableVertexAttribArray(0);
+ glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL);
+
+ // Generate/bind a VBO to store instance attribute data
+ glGenBuffers(1, &app->instanceVbo);
+ glBindBuffer(GL_ARRAY_BUFFER, app->instanceVbo);
+ glBufferData(GL_ARRAY_BUFFER,
+ (GLsizeiptr)(app->numRects * sizeof(Rect)),
+ app->rects,
+ GL_STREAM_DRAW);
+
+ // Attribute 1 is Rect::position
+ glEnableVertexAttribArray(1);
+ glVertexAttribDivisor(1, 4);
+ glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Rect), NULL);
+
+ // Attribute 2 is Rect::size
+ glEnableVertexAttribArray(2);
+ glVertexAttribDivisor(2, 4);
+ glVertexAttribPointer(2,
+ 2,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(Rect),
+ (const void*)offsetof(Rect, size));
+
+ // Attribute 3 is Rect::fillColor
+ glEnableVertexAttribArray(3);
+ glVertexAttribDivisor(3, 4);
+ glVertexAttribPointer(3,
+ 4,
+ GL_FLOAT,
+ GL_FALSE,
+ sizeof(Rect),
+ (const void*)offsetof(Rect, fillColor));
+
+ // Set up the IBO to index into the VBO
+ glGenBuffers(1, &app->ibo);
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, app->ibo);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER,
+ sizeof(rectIndices),
+ rectIndices,
+ GL_STATIC_DRAW);
+
+ return PUGL_SUCCESS;
+}
+
+static void
+teardownGl(PuglTestApp* app)
+{
+ glDeleteBuffers(1, &app->ibo);
+ glDeleteBuffers(1, &app->vbo);
+ glDeleteBuffers(1, &app->instanceVbo);
+ glDeleteVertexArrays(1, &app->vao);
+ deleteProgram(app->drawRect);
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTestApp app;
+ memset(&app, 0, sizeof(app));
+
+ const PuglRect frame = {0, 0, defaultWidth, defaultHeight};
+
+ // Parse command line options
+ if (parseOptions(&app, argc, argv)) {
+ puglPrintTestUsage("pugl_gl3_demo", "[NUM_RECTS]");
+ return 1;
+ }
+
+ // Create and configure world and view
+ setupPugl(&app, frame);
+
+ // Create window (which will send a PUGL_CREATE event)
+ const PuglStatus st = puglCreateWindow(app.view, "Pugl OpenGL 3");
+ if (st) {
+ return logError("Failed to create window (%s)\n", puglStrerror(st));
+ }
+
+ // Show window
+ puglShowWindow(app.view);
+
+ // Grind away, drawing continuously
+ PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)};
+ while (!app.quit) {
+ puglUpdate(app.world, 0.0);
+ puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
+ }
+
+ // Destroy window (which will send a PUGL_DESTROY event)
+ puglFreeView(app.view);
+
+ // Free everything else
+ puglFreeWorld(app.world);
+ free(app.rects);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/examples/pugl_print_events.c b/subprojects/d2tk/pugl/examples/pugl_print_events.c
new file mode 100644
index 0000000..9c81033
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/pugl_print_events.c
@@ -0,0 +1,78 @@
+/*
+ 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_print_events.c A utility program that prints view events.
+*/
+
+#include "test/test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+#include <stdbool.h>
+#include <stdio.h>
+
+typedef struct
+{
+ PuglWorld* world;
+ PuglView* view;
+ int quit;
+} PuglPrintEventsApp;
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglPrintEventsApp* app = (PuglPrintEventsApp*)puglGetHandle(view);
+
+ printEvent(event, "Event: ", true);
+
+ switch (event->type) {
+ case PUGL_CLOSE: app->quit = 1; break;
+ default: break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+int
+main(void)
+{
+ PuglPrintEventsApp app = {NULL, NULL, 0};
+
+ app.world = puglNewWorld(PUGL_PROGRAM, 0);
+ app.view = puglNewView(app.world);
+
+ puglSetClassName(app.world, "Pugl Print Events");
+ puglSetBackend(app.view, puglStubBackend());
+ puglSetHandle(app.view, &app);
+ puglSetEventFunc(app.view, onEvent);
+
+ if (puglCreateWindow(app.view, "Pugl Event Printer")) {
+ return logError("Failed to create window\n");
+ }
+
+ puglShowWindow(app.view);
+
+ while (!app.quit) {
+ puglUpdate(app.world, -1.0);
+ }
+
+ puglFreeView(app.view);
+ puglFreeWorld(app.world);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/examples/pugl_window_demo.c b/subprojects/d2tk/pugl/examples/pugl_window_demo.c
new file mode 100644
index 0000000..7beb330
--- /dev/null
+++ b/subprojects/d2tk/pugl/examples/pugl_window_demo.c
@@ -0,0 +1,247 @@
+/*
+ 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_windows_demo.c A demonstration of multiple Pugl windows.
+*/
+
+#include "cube_view.h"
+#include "demo_utils.h"
+#include "test/test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_gl.h"
+
+#include <math.h>
+#include <stdbool.h>
+#include <string.h>
+
+typedef struct {
+ PuglView* view;
+ double xAngle;
+ double yAngle;
+ float dist;
+ double lastMouseX;
+ double lastMouseY;
+ double lastDrawTime;
+ bool entered;
+} CubeView;
+
+typedef struct {
+ PuglWorld* world;
+ CubeView cubes[2];
+ bool continuous;
+ int quit;
+ bool verbose;
+} PuglTestApp;
+
+static void
+onDisplay(PuglView* view)
+{
+ PuglWorld* world = puglGetWorld(view);
+ PuglTestApp* app = (PuglTestApp*)puglGetWorldHandle(world);
+ CubeView* cube = (CubeView*)puglGetHandle(view);
+
+ const double thisTime = puglGetTime(app->world);
+ if (app->continuous) {
+ const double dTime = thisTime - cube->lastDrawTime;
+
+ cube->xAngle = fmod(cube->xAngle + dTime * 100.0, 360.0);
+ cube->yAngle = fmod(cube->yAngle + dTime * 100.0, 360.0);
+ }
+
+ displayCube(view, cube->dist, cube->xAngle, cube->yAngle, cube->entered);
+
+ cube->lastDrawTime = thisTime;
+}
+
+static void
+onKeyPress(PuglView* view, const PuglEventKey* event)
+{
+ PuglWorld* world = puglGetWorld(view);
+ PuglTestApp* app = (PuglTestApp*)puglGetWorldHandle(world);
+ PuglRect frame = puglGetFrame(view);
+
+ if (event->key == 'q' || event->key == PUGL_KEY_ESCAPE) {
+ app->quit = 1;
+ } else if (event->state & PUGL_MOD_SHIFT) {
+ if (event->key == PUGL_KEY_UP) {
+ frame.height += 10;
+ } else if (event->key == PUGL_KEY_DOWN) {
+ frame.height -= 10;
+ } else if (event->key == PUGL_KEY_LEFT) {
+ frame.width -= 10;
+ } else if (event->key == PUGL_KEY_RIGHT) {
+ frame.width += 10;
+ } else {
+ return;
+ }
+ puglSetFrame(view, frame);
+ } else {
+ if (event->key == PUGL_KEY_UP) {
+ frame.y -= 10;
+ } else if (event->key == PUGL_KEY_DOWN) {
+ frame.y += 10;
+ } else if (event->key == PUGL_KEY_LEFT) {
+ frame.x -= 10;
+ } else if (event->key == PUGL_KEY_RIGHT) {
+ frame.x += 10;
+ } else {
+ return;
+ }
+ puglSetFrame(view, frame);
+ }
+}
+
+static void
+redisplayView(PuglTestApp* app, PuglView* view)
+{
+ if (!app->continuous) {
+ puglPostRedisplay(view);
+ }
+}
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglWorld* world = puglGetWorld(view);
+ PuglTestApp* app = (PuglTestApp*)puglGetWorldHandle(world);
+ CubeView* cube = (CubeView*)puglGetHandle(view);
+
+ const char* const prefix = cube == &app->cubes[0] ? "View 1: " : "View 2: ";
+ printEvent(event, prefix, app->verbose);
+
+ switch (event->type) {
+ case PUGL_CONFIGURE:
+ reshapeCube((int)event->configure.width, (int)event->configure.height);
+ break;
+ case PUGL_UPDATE:
+ if (app->continuous) {
+ puglPostRedisplay(view);
+ }
+ break;
+ case PUGL_EXPOSE:
+ onDisplay(view);
+ break;
+ case PUGL_CLOSE:
+ app->quit = 1;
+ break;
+ case PUGL_KEY_PRESS:
+ onKeyPress(view, &event->key);
+ break;
+ case PUGL_MOTION:
+ cube->xAngle -= event->motion.x - cube->lastMouseX;
+ cube->yAngle += event->motion.y - cube->lastMouseY;
+ cube->lastMouseX = event->motion.x;
+ cube->lastMouseY = event->motion.y;
+ redisplayView(app, view);
+ break;
+ case PUGL_SCROLL:
+ cube->dist = fmaxf(10.0f, cube->dist + (float)event->scroll.dy);
+ redisplayView(app, view);
+ break;
+ case PUGL_POINTER_IN:
+ cube->entered = true;
+ redisplayView(app, view);
+ break;
+ case PUGL_POINTER_OUT:
+ cube->entered = false;
+ redisplayView(app, view);
+ break;
+ case PUGL_FOCUS_IN:
+ case PUGL_FOCUS_OUT:
+ redisplayView(app, view);
+ break;
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTestApp app = {0};
+
+ const PuglTestOptions opts = puglParseTestOptions(&argc, &argv);
+ if (opts.help) {
+ puglPrintTestUsage(argv[0], "");
+ return 1;
+ }
+
+ app.continuous = opts.continuous;
+ app.verbose = opts.verbose;
+
+ app.world = puglNewWorld(PUGL_PROGRAM, 0);
+ app.cubes[0].view = puglNewView(app.world);
+ app.cubes[1].view = puglNewView(app.world);
+
+ puglSetWorldHandle(app.world, &app);
+ puglSetClassName(app.world, "Pugl Test");
+
+ PuglStatus st = PUGL_SUCCESS;
+ for (size_t i = 0; i < 2; ++i) {
+ CubeView* cube = &app.cubes[i];
+ PuglView* view = cube->view;
+ static const double pad = 64.0;
+ const PuglRect frame = {pad + (256.0 + pad) * i,
+ pad + (256.0 + pad) * i,
+ 256.0,
+ 256.0};
+
+ cube->dist = 10;
+
+ puglSetFrame(view, frame);
+ puglSetMinSize(view, 128, 128);
+ puglSetBackend(view, puglGlBackend());
+
+ puglSetViewHint(view, PUGL_USE_DEBUG_CONTEXT, opts.errorChecking);
+ puglSetViewHint(view, PUGL_RESIZABLE, opts.resizable);
+ puglSetViewHint(view, PUGL_SAMPLES, opts.samples);
+ puglSetViewHint(view, PUGL_DOUBLE_BUFFER, opts.doubleBuffer);
+ puglSetViewHint(view, PUGL_SWAP_INTERVAL, opts.sync);
+ puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, opts.ignoreKeyRepeat);
+ puglSetHandle(view, cube);
+ puglSetEventFunc(view, onEvent);
+
+ if ((st = puglCreateWindow(view, "Pugl"))) {
+ return logError("Failed to create window window (%s)\n",
+ puglStrerror(st));
+ }
+
+ puglShowWindow(view);
+ }
+
+ PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)};
+ unsigned framesDrawn = 0;
+ while (!app.quit) {
+ puglUpdate(app.world, app.continuous ? 0.0 : -1.0);
+ ++framesDrawn;
+
+ if (app.continuous) {
+ puglPrintFps(app.world, &fpsPrinter, &framesDrawn);
+ }
+ }
+
+ for (size_t i = 0; i < 2; ++i) {
+ puglFreeView(app.cubes[i].view);
+ }
+
+ puglFreeWorld(app.world);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/test/shader_utils.h b/subprojects/d2tk/pugl/examples/shader_utils.h
index 834d8fc..834d8fc 100644
--- a/subprojects/d2tk/pugl/test/shader_utils.h
+++ b/subprojects/d2tk/pugl/examples/shader_utils.h
diff --git a/subprojects/d2tk/pugl/pugl/detail/implementation.c b/subprojects/d2tk/pugl/pugl/detail/implementation.c
index 80a7a32..d7f1413 100644
--- a/subprojects/d2tk/pugl/pugl/detail/implementation.c
+++ b/subprojects/d2tk/pugl/pugl/detail/implementation.c
@@ -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
@@ -21,10 +21,57 @@
#include "pugl/detail/implementation.h"
#include "pugl/pugl.h"
+#include <assert.h>
#include <stdbool.h>
+#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+static const char*
+puglLogLevelPrefix(const PuglLogLevel level)
+{
+ switch (level) {
+ case PUGL_LOG_LEVEL_ERR:
+ return "error: ";
+ case PUGL_LOG_LEVEL_WARNING:
+ return "warning: ";
+ case PUGL_LOG_LEVEL_INFO:
+ case PUGL_LOG_LEVEL_DEBUG:
+ return "";
+ }
+
+ return "";
+}
+
+static void
+puglDefaultLogFunc(PuglWorld* PUGL_UNUSED(world),
+ PuglLogLevel level,
+ const char* msg)
+{
+ fprintf(stderr, "%s%s", puglLogLevelPrefix(level), msg);
+}
+
+const char*
+puglStrerror(const PuglStatus status)
+{
+ // clang-format off
+ switch (status) {
+ case PUGL_SUCCESS: return "Success";
+ case PUGL_FAILURE: return "Non-fatal failure";
+ case PUGL_UNKNOWN_ERROR: return "Unknown system error";
+ case PUGL_BAD_BACKEND: return "Invalid or missing backend";
+ case PUGL_BACKEND_FAILED: return "Backend initialisation failed";
+ case PUGL_REGISTRATION_FAILED: return "Window class registration failed";
+ case PUGL_CREATE_WINDOW_FAILED: return "Window creation failed";
+ case PUGL_SET_FORMAT_FAILED: return "Failed to set pixel format";
+ case PUGL_CREATE_CONTEXT_FAILED: return "Failed to create drawing context";
+ case PUGL_UNSUPPORTED_TYPE: return "Unsupported data type";
+ }
+ // clang-format on
+
+ return "Unknown error";
+}
+
void
puglSetString(char** dest, const char* string)
{
@@ -61,22 +108,25 @@ puglSetDefaultHints(PuglHints hints)
hints[PUGL_DEPTH_BITS] = 24;
hints[PUGL_STENCIL_BITS] = 8;
hints[PUGL_SAMPLES] = 0;
- hints[PUGL_DOUBLE_BUFFER] = PUGL_FALSE;
- hints[PUGL_SWAP_INTERVAL] = 0;
+ hints[PUGL_DOUBLE_BUFFER] = PUGL_TRUE;
+ hints[PUGL_SWAP_INTERVAL] = PUGL_DONT_CARE;
hints[PUGL_RESIZABLE] = PUGL_FALSE;
hints[PUGL_IGNORE_KEY_REPEAT] = PUGL_FALSE;
}
PuglWorld*
-puglNewWorld(void)
+puglNewWorld(PuglWorldType type, PuglWorldFlags flags)
{
PuglWorld* world = (PuglWorld*)calloc(1, sizeof(PuglWorld));
- if (!world || !(world->impl = puglInitWorldInternals())) {
+ if (!world || !(world->impl = puglInitWorldInternals(type, flags))) {
free(world);
return NULL;
}
world->startTime = puglGetTime(world);
+ world->logFunc = puglDefaultLogFunc;
+ world->logLevel = PUGL_LOG_LEVEL_INFO;
+
puglSetString(&world->className, "Pugl");
return world;
@@ -91,6 +141,34 @@ puglFreeWorld(PuglWorld* const world)
free(world);
}
+void
+puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle)
+{
+ world->handle = handle;
+}
+
+PuglWorldHandle
+puglGetWorldHandle(PuglWorld* world)
+{
+ return world->handle;
+}
+
+PuglStatus
+puglSetLogFunc(PuglWorld* world, PuglLogFunc logFunc)
+{
+ world->logFunc = logFunc;
+
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglSetLogLevel(PuglWorld* world, PuglLogLevel level)
+{
+ world->logLevel = level;
+
+ return PUGL_SUCCESS;
+}
+
PuglStatus
puglSetClassName(PuglWorld* const world, const char* const name)
{
@@ -117,6 +195,7 @@ puglNewView(PuglWorld* const world)
++world->numViews;
world->views = (PuglView**)realloc(world->views,
world->numViews * sizeof(PuglView*));
+
world->views[world->numViews - 1] = view;
return view;
@@ -125,6 +204,8 @@ puglNewView(PuglWorld* const world)
void
puglFreeView(PuglView* view)
{
+ puglDispatchSimpleEvent(view, PUGL_DESTROY);
+
// Remove from world view list
PuglWorld* world = view->world;
for (size_t i = 0; i < world->numViews; ++i) {
@@ -206,20 +287,44 @@ puglGetContext(PuglView* view)
return view->backend->getContext(view);
}
+#ifndef PUGL_DISABLE_DEPRECATED
+
+PuglStatus
+puglPollEvents(PuglWorld* world, double timeout)
+{
+ return puglUpdate(world, timeout);
+}
+
+PuglStatus
+puglDispatchEvents(PuglWorld* world)
+{
+ return puglUpdate(world, 0.0);
+}
+
PuglStatus
puglEnterContext(PuglView* view, bool drawing)
{
- view->backend->enter(view, drawing);
+ const PuglEventExpose expose = {
+ PUGL_EXPOSE, 0, 0, 0, view->frame.width, view->frame.height, 0};
+
+ view->backend->enter(view, drawing ? &expose : NULL);
+
return PUGL_SUCCESS;
}
PuglStatus
puglLeaveContext(PuglView* view, bool drawing)
{
- view->backend->leave(view, drawing);
+ const PuglEventExpose expose = {
+ PUGL_EXPOSE, 0, 0, 0, view->frame.width, view->frame.height, 0};
+
+ view->backend->leave(view, drawing ? &expose : NULL);
+
return PUGL_SUCCESS;
}
+#endif
+
PuglStatus
puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
{
@@ -240,45 +345,87 @@ puglDecodeUTF8(const uint8_t* buf)
} else if (buf[0] < 0xC2) {
return 0xFFFD;
} else if (buf[0] < 0xE0) {
- FAIL_IF((buf[1] & 0xC0) != 0x80);
- return (buf[0] << 6) + buf[1] - 0x3080u;
+ FAIL_IF((buf[1] & 0xC0u) != 0x80);
+ return ((uint32_t)buf[0] << 6u) + buf[1] - 0x3080u;
} else if (buf[0] < 0xF0) {
- FAIL_IF((buf[1] & 0xC0) != 0x80);
+ FAIL_IF((buf[1] & 0xC0u) != 0x80);
FAIL_IF(buf[0] == 0xE0 && buf[1] < 0xA0);
- FAIL_IF((buf[2] & 0xC0) != 0x80);
- return (buf[0] << 12) + (buf[1] << 6) + buf[2] - 0xE2080u;
+ FAIL_IF((buf[2] & 0xC0u) != 0x80);
+ return ((uint32_t)buf[0] << 12u) + //
+ ((uint32_t)buf[1] << 6u) + //
+ ((uint32_t)buf[2] - 0xE2080u);
} else if (buf[0] < 0xF5) {
- FAIL_IF((buf[1] & 0xC0) != 0x80);
+ FAIL_IF((buf[1] & 0xC0u) != 0x80);
FAIL_IF(buf[0] == 0xF0 && buf[1] < 0x90);
FAIL_IF(buf[0] == 0xF4 && buf[1] >= 0x90);
- FAIL_IF((buf[2] & 0xC0) != 0x80);
- FAIL_IF((buf[3] & 0xC0) != 0x80);
- return ((buf[0] << 18) +
- (buf[1] << 12) +
- (buf[2] << 6) +
- buf[3] - 0x3C82080u);
+ FAIL_IF((buf[2] & 0xC0u) != 0x80u);
+ FAIL_IF((buf[3] & 0xC0u) != 0x80u);
+ return (((uint32_t)buf[0] << 18u) + //
+ ((uint32_t)buf[1] << 12u) + //
+ ((uint32_t)buf[2] << 6u) + //
+ ((uint32_t)buf[3] - 0x3C82080u));
}
return 0xFFFD;
}
+static inline bool
+puglMustConfigure(PuglView* view, const PuglEventConfigure* configure)
+{
+ return memcmp(configure, &view->lastConfigure, sizeof(PuglEventConfigure));
+}
+
+void
+puglDispatchSimpleEvent(PuglView* view, const PuglEventType type)
+{
+ assert(type == PUGL_CREATE || type == PUGL_DESTROY || type == PUGL_MAP ||
+ type == PUGL_UNMAP || type == PUGL_UPDATE || type == PUGL_CLOSE);
+
+ const PuglEvent event = {{type, 0}};
+ puglDispatchEvent(view, &event);
+}
+
+void
+puglDispatchEventInContext(PuglView* view, const PuglEvent* event)
+{
+ if (event->type == PUGL_CONFIGURE) {
+ view->frame.x = event->configure.x;
+ view->frame.y = event->configure.y;
+ view->frame.width = event->configure.width;
+ view->frame.height = event->configure.height;
+
+ if (puglMustConfigure(view, &event->configure)) {
+ view->eventFunc(view, event);
+ view->lastConfigure = event->configure;
+ }
+ } else {
+ view->eventFunc(view, event);
+ }
+}
+
void
puglDispatchEvent(PuglView* view, const PuglEvent* event)
{
switch (event->type) {
case PUGL_NOTHING:
break;
- case PUGL_CONFIGURE:
- puglEnterContext(view, false);
+ case PUGL_CREATE:
+ case PUGL_DESTROY:
+ view->backend->enter(view, NULL);
view->eventFunc(view, event);
- puglLeaveContext(view, false);
+ view->backend->leave(view, NULL);
break;
- case PUGL_EXPOSE:
- if (event->expose.count == 0) {
- puglEnterContext(view, true);
- view->eventFunc(view, event);
- puglLeaveContext(view, true);
+ case PUGL_CONFIGURE:
+ if (puglMustConfigure(view, &event->configure)) {
+ view->backend->enter(view, NULL);
+ puglDispatchEventInContext(view, event);
+ view->backend->leave(view, NULL);
}
break;
+ case PUGL_EXPOSE:
+ view->backend->enter(view, &event->expose);
+ view->eventFunc(view, event);
+ view->backend->leave(view, &event->expose);
+ break;
default:
view->eventFunc(view, event);
}
diff --git a/subprojects/d2tk/pugl/pugl/detail/implementation.h b/subprojects/d2tk/pugl/pugl/detail/implementation.h
index 874e7e1..bcecd85 100644
--- a/subprojects/d2tk/pugl/pugl/detail/implementation.h
+++ b/subprojects/d2tk/pugl/pugl/detail/implementation.h
@@ -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
@@ -27,18 +27,17 @@
#include <stddef.h>
#include <stdint.h>
-#ifdef __cplusplus
-extern "C" {
-#endif
+PUGL_BEGIN_DECLS
/** Set `blob` to `data` with length `len`, reallocating if necessary. */
-void puglSetBlob(PuglBlob* blob, const void* data, size_t len);
+void puglSetBlob(PuglBlob* dest, const void* data, size_t len);
/** Reallocate and set `*dest` to `string`. */
void puglSetString(char** dest, const char* string);
/** Allocate and initialise world internals (implemented once per platform) */
-PuglWorldInternals* puglInitWorldInternals(void);
+PuglWorldInternals*
+puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags);
/** Destroy and free world internals (implemented once per platform) */
void puglFreeWorldInternals(PuglWorld* world);
@@ -52,7 +51,13 @@ void puglFreeViewInternals(PuglView* view);
/** Return the Unicode code point for `buf` or the replacement character. */
uint32_t puglDecodeUTF8(const uint8_t* buf);
-/** Dispatch `event` to `view`, optimising configure/expose if possible. */
+/** Dispatch an event with a simple `type` to `view`. */
+void puglDispatchSimpleEvent(PuglView* view, PuglEventType type);
+
+/** Dispatch `event` to `view` while already in the graphics context. */
+void puglDispatchEventInContext(PuglView* view, const PuglEvent* event);
+
+/** Dispatch `event` to `view`, entering graphics context if necessary. */
void puglDispatchEvent(PuglView* view, const PuglEvent* event);
/** Set internal (stored in view) clipboard contents. */
@@ -66,8 +71,6 @@ puglSetInternalClipboard(PuglView* view,
const void* data,
size_t len);
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
+PUGL_END_DECLS
#endif // PUGL_DETAIL_IMPLEMENTATION_H
diff --git a/subprojects/d2tk/pugl/pugl/detail/mac.h b/subprojects/d2tk/pugl/pugl/detail/mac.h
index 0652733..5bc1a2e 100644
--- a/subprojects/d2tk/pugl/pugl/detail/mac.h
+++ b/subprojects/d2tk/pugl/pugl/detail/mac.h
@@ -30,13 +30,12 @@
@public
PuglView* puglview;
NSTrackingArea* trackingArea;
- NSMutableAttributedString* markedText;
+ NSMutableAttributedString* markedText;
NSTimer* timer;
- NSTimer* urgentTimer;
+ NSMutableDictionary* userTimers;
bool reshaped;
}
-- (void) dispatchConfigure:(NSRect)bounds;
- (void) dispatchExpose:(NSRect)rect;
- (void) setReshaped;
diff --git a/subprojects/d2tk/pugl/pugl/detail/mac.m b/subprojects/d2tk/pugl/pugl/detail/mac.m
index ed85e0d..bd1fdcf 100644
--- a/subprojects/d2tk/pugl/pugl/detail/mac.m
+++ b/subprojects/d2tk/pugl/pugl/detail/mac.m
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
Permission to use, copy, modify, and/or distribute this software for any
@@ -98,18 +98,33 @@ updateViewRect(PuglView* view)
return YES;
}
-@end
+- (void) setIsVisible:(BOOL)flag
+{
+ if (flag && !puglview->visible) {
+ const PuglEventConfigure ev = {
+ PUGL_CONFIGURE,
+ 0,
+ puglview->frame.x,
+ puglview->frame.y,
+ puglview->frame.width,
+ puglview->frame.height,
+ };
-@implementation PuglWrapperView
+ puglDispatchEvent(puglview, (const PuglEvent*)&ev);
+ puglDispatchSimpleEvent(puglview, PUGL_MAP);
+ } else if (!flag && puglview->visible) {
+ puglDispatchSimpleEvent(puglview, PUGL_UNMAP);
+ }
-- (void) resizeWithOldSuperviewSize:(NSSize)oldSize
-{
- [super resizeWithOldSuperviewSize:oldSize];
+ puglview->visible = flag;
- const NSRect bounds = [self bounds];
- puglview->backend->resize(puglview, bounds.size.width, bounds.size.height);
+ [super setIsVisible:flag];
}
+@end
+
+@implementation PuglWrapperView
+
- (void) dispatchExpose:(NSRect)rect
{
if (reshaped) {
@@ -128,6 +143,10 @@ updateViewRect(PuglView* view)
reshaped = false;
}
+ if (![[puglview->impl->drawView window] isVisible]) {
+ return;
+ }
+
const PuglEventExpose ev = {
PUGL_EXPOSE,
0,
@@ -173,38 +192,34 @@ keySymToSpecial(const NSEvent* const ev)
NSString* chars = [ev charactersIgnoringModifiers];
if ([chars length] == 1) {
switch ([chars characterAtIndex:0]) {
- case NSDeleteCharacter: return PUGL_KEY_BACKSPACE;
- case NSTabCharacter: return PUGL_KEY_TAB;
- case NSCarriageReturnCharacter: return PUGL_KEY_RETURN;
- case 0x001B: return PUGL_KEY_ESCAPE;
- case NSDeleteFunctionKey: return PUGL_KEY_DELETE;
- case NSF1FunctionKey: return PUGL_KEY_F1;
- case NSF2FunctionKey: return PUGL_KEY_F2;
- case NSF3FunctionKey: return PUGL_KEY_F3;
- case NSF4FunctionKey: return PUGL_KEY_F4;
- case NSF5FunctionKey: return PUGL_KEY_F5;
- case NSF6FunctionKey: return PUGL_KEY_F6;
- case NSF7FunctionKey: return PUGL_KEY_F7;
- case NSF8FunctionKey: return PUGL_KEY_F8;
- case NSF9FunctionKey: return PUGL_KEY_F9;
- case NSF10FunctionKey: return PUGL_KEY_F10;
- case NSF11FunctionKey: return PUGL_KEY_F11;
- case NSF12FunctionKey: return PUGL_KEY_F12;
- case NSLeftArrowFunctionKey: return PUGL_KEY_LEFT;
- case NSUpArrowFunctionKey: return PUGL_KEY_UP;
- case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT;
- case NSDownArrowFunctionKey: return PUGL_KEY_DOWN;
- case NSPageUpFunctionKey: return PUGL_KEY_PAGE_UP;
- case NSPageDownFunctionKey: return PUGL_KEY_PAGE_DOWN;
- case NSHomeFunctionKey: return PUGL_KEY_HOME;
- case NSEndFunctionKey: return PUGL_KEY_END;
- case NSInsertFunctionKey: return PUGL_KEY_INSERT;
- case NSMenuFunctionKey: return PUGL_KEY_MENU;
- case NSScrollLockFunctionKey: return PUGL_KEY_SCROLL_LOCK;
- case NSClearLineFunctionKey: return PUGL_KEY_NUM_LOCK;
- case NSPrintScreenFunctionKey: return PUGL_KEY_PRINT_SCREEN;
- case NSPauseFunctionKey: return PUGL_KEY_PAUSE;
- default: break;
+ case NSF1FunctionKey: return PUGL_KEY_F1;
+ case NSF2FunctionKey: return PUGL_KEY_F2;
+ case NSF3FunctionKey: return PUGL_KEY_F3;
+ case NSF4FunctionKey: return PUGL_KEY_F4;
+ case NSF5FunctionKey: return PUGL_KEY_F5;
+ case NSF6FunctionKey: return PUGL_KEY_F6;
+ case NSF7FunctionKey: return PUGL_KEY_F7;
+ case NSF8FunctionKey: return PUGL_KEY_F8;
+ case NSF9FunctionKey: return PUGL_KEY_F9;
+ case NSF10FunctionKey: return PUGL_KEY_F10;
+ case NSF11FunctionKey: return PUGL_KEY_F11;
+ case NSF12FunctionKey: return PUGL_KEY_F12;
+ case NSDeleteCharacter: return PUGL_KEY_BACKSPACE;
+ case NSDeleteFunctionKey: return PUGL_KEY_DELETE;
+ case NSLeftArrowFunctionKey: return PUGL_KEY_LEFT;
+ case NSUpArrowFunctionKey: return PUGL_KEY_UP;
+ case NSRightArrowFunctionKey: return PUGL_KEY_RIGHT;
+ case NSDownArrowFunctionKey: return PUGL_KEY_DOWN;
+ case NSPageUpFunctionKey: return PUGL_KEY_PAGE_UP;
+ case NSPageDownFunctionKey: return PUGL_KEY_PAGE_DOWN;
+ case NSHomeFunctionKey: return PUGL_KEY_HOME;
+ case NSEndFunctionKey: return PUGL_KEY_END;
+ case NSInsertFunctionKey: return PUGL_KEY_INSERT;
+ case NSMenuFunctionKey: return PUGL_KEY_MENU;
+ case NSScrollLockFunctionKey: return PUGL_KEY_SCROLL_LOCK;
+ case NSClearLineFunctionKey: return PUGL_KEY_NUM_LOCK;
+ case NSPrintScreenFunctionKey: return PUGL_KEY_PRINT_SCREEN;
+ case NSPauseFunctionKey: return PUGL_KEY_PAUSE;
}
// SHIFT, CTRL, ALT, and SUPER are handled in [flagsChanged]
}
@@ -248,19 +263,20 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
- PUGL_CROSSING_NORMAL
+ PUGL_CROSSING_NORMAL,
};
+
puglDispatchEvent(view->puglview, (const PuglEvent*)&ev);
}
- (void) mouseEntered:(NSEvent*)event
{
- handleCrossing(self, event, PUGL_ENTER_NOTIFY);
+ handleCrossing(self, event, PUGL_POINTER_IN);
}
- (void) mouseExited:(NSEvent*)event
{
- handleCrossing(self, event, PUGL_LEAVE_NOTIFY);
+ handleCrossing(self, event, PUGL_POINTER_OUT);
}
- (void) mouseMoved:(NSEvent*)event
@@ -268,7 +284,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
const NSPoint wloc = [self eventLocation:event];
const NSPoint rloc = [NSEvent mouseLocation];
const PuglEventMotion ev = {
- PUGL_MOTION_NOTIFY,
+ PUGL_MOTION,
0,
[event timestamp],
wloc.x,
@@ -277,8 +293,9 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
0,
- 1
+ 1,
};
+
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
@@ -310,8 +327,9 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
- (uint32_t)[event buttonNumber] + 1
+ (uint32_t)[event buttonNumber] + 1,
};
+
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
@@ -328,8 +346,9 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
- (uint32_t)[event buttonNumber] + 1
+ (uint32_t)[event buttonNumber] + 1,
};
+
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
@@ -367,8 +386,9 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
[event scrollingDeltaX],
- [event scrollingDeltaY]
+ [event scrollingDeltaY],
};
+
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
@@ -378,13 +398,12 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
return;
}
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglKey spec = keySymToSpecial(event);
- const NSString* chars = [event charactersIgnoringModifiers];
- const char* str = [[chars lowercaseString] UTF8String];
- const uint32_t code = (
- spec ? spec : puglDecodeUTF8((const uint8_t*)str));
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const PuglKey spec = keySymToSpecial(event);
+ const NSString* chars = [event charactersIgnoringModifiers];
+ const char* str = [[chars lowercaseString] UTF8String];
+ const uint32_t code = (spec ? spec : puglDecodeUTF8((const uint8_t*)str));
const PuglEventKey ev = {
PUGL_KEY_PRESS,
@@ -396,7 +415,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
[event keyCode],
- (code != 0xFFFD) ? code : 0
+ (code != 0xFFFD) ? code : 0,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
@@ -406,15 +425,14 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
}
}
-- (void) keyUp:(NSEvent*)event
+- (void)keyUp:(NSEvent*)event
{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglKey spec = keySymToSpecial(event);
- const NSString* chars = [event charactersIgnoringModifiers];
- const char* str = [[chars lowercaseString] UTF8String];
- const uint32_t code =
- (spec ? spec : puglDecodeUTF8((const uint8_t*)str));
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const PuglKey spec = keySymToSpecial(event);
+ const NSString* chars = [event charactersIgnoringModifiers];
+ const char* str = [[chars lowercaseString] UTF8String];
+ const uint32_t code = (spec ? spec : puglDecodeUTF8((const uint8_t*)str));
const PuglEventKey ev = {
PUGL_KEY_RELEASE,
@@ -426,8 +444,9 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
[event keyCode],
- (code != 0xFFFD) ? code : 0
+ (code != 0xFFFD) ? code : 0,
};
+
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
@@ -527,17 +546,19 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
range:NSMakeRange(i, i + 1)
remainingRange:nil];
- PuglEventText ev = { PUGL_TEXT,
- 0,
- [event timestamp],
- wloc.x,
- wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(event),
- [event keyCode],
- code,
- { 0, 0, 0, 0, 0, 0, 0, 0 } };
+ PuglEventText ev = {
+ PUGL_TEXT,
+ 0,
+ [event timestamp],
+ wloc.x,
+ wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(event),
+ [event keyCode],
+ code,
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ };
memcpy(ev.string, utf8, len);
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
@@ -603,14 +624,23 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
[super viewWillStartLiveResize];
}
+- (void) viewWillDraw
+{
+ puglDispatchSimpleEvent(puglview, PUGL_UPDATE);
+ [super viewWillDraw];
+}
+
- (void) resizeTick
{
puglPostRedisplay(puglview);
}
-- (void) urgentTick
+- (void) timerTick:(NSTimer*)userTimer
{
- [puglview->world->impl->app requestUserAttention:NSInformationalRequest];
+ const NSNumber* userInfo = userTimer.userInfo;
+ const PuglEventTimer ev = {PUGL_TIMER, 0, userInfo.unsignedLongValue};
+
+ puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
- (void) viewDidEndLiveResize
@@ -646,9 +676,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
{
(void)sender;
- PuglEvent ev = { 0 };
- ev.type = PUGL_CLOSE;
- puglDispatchEvent(window->puglview, &ev);
+ puglDispatchSimpleEvent(window->puglview, PUGL_CLOSE);
return YES;
}
@@ -663,14 +691,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
{
(void)notification;
- PuglWrapperView* wrapperView = window->puglview->impl->wrapperView;
- if (wrapperView->urgentTimer) {
- [wrapperView->urgentTimer invalidate];
- wrapperView->urgentTimer = NULL;
- }
-
- PuglEvent ev = { 0 };
- ev.type = PUGL_FOCUS_IN;
+ PuglEvent ev = {{PUGL_FOCUS_IN, 0}};
ev.focus.grab = false;
puglDispatchEvent(window->puglview, &ev);
}
@@ -679,8 +700,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
{
(void)notification;
- PuglEvent ev = { 0 };
- ev.type = PUGL_FOCUS_OUT;
+ PuglEvent ev = {{PUGL_FOCUS_OUT, 0}};
ev.focus.grab = false;
puglDispatchEvent(window->puglview, &ev);
}
@@ -688,7 +708,8 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
@end
PuglWorldInternals*
-puglInitWorldInternals(void)
+puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
+ PuglWorldFlags PUGL_UNUSED(flags))
{
PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
1, sizeof(PuglWorldInternals));
@@ -706,6 +727,12 @@ puglFreeWorldInternals(PuglWorld* world)
free(world->impl);
}
+void*
+puglGetNativeWorld(PuglWorld* PUGL_UNUSED(world))
+{
+ return NULL;
+}
+
PuglInternals*
puglInitViewInternals(void)
{
@@ -733,6 +760,7 @@ puglCreateWindow(PuglView* view, const char* title)
// Create wrapper view to handle input
impl->wrapperView = [PuglWrapperView alloc];
impl->wrapperView->puglview = view;
+ impl->wrapperView->userTimers = [[NSMutableDictionary alloc] init];
impl->wrapperView->markedText = [[NSMutableAttributedString alloc] init];
[impl->wrapperView setAutoresizesSubviews:YES];
[impl->wrapperView initWithFrame:
@@ -749,7 +777,7 @@ puglCreateWindow(PuglView* view, const char* title)
return st;
}
- // Add draw view to wraper view
+ // Add draw view to wrapper view
[impl->wrapperView addSubview:impl->drawView];
[impl->wrapperView setHidden:NO];
[impl->drawView setHidden:NO];
@@ -803,19 +831,25 @@ puglCreateWindow(PuglView* view, const char* title)
[view->world->impl->app activateIgnoringOtherApps:YES];
[window makeFirstResponder:impl->wrapperView];
[window makeKeyAndOrderFront:window];
+ [impl->window setIsVisible:NO];
}
[impl->wrapperView updateTrackingAreas];
+ puglDispatchSimpleEvent(view, PUGL_CREATE);
+
return 0;
}
PuglStatus
puglShowWindow(PuglView* view)
{
- [view->impl->window setIsVisible:YES];
- updateViewRect(view);
- view->visible = true;
+ if (![view->impl->window isVisible]) {
+ [view->impl->window setIsVisible:YES];
+ [view->impl->drawView setNeedsDisplay: YES];
+ updateViewRect(view);
+ }
+
return PUGL_SUCCESS;
}
@@ -823,24 +857,30 @@ PuglStatus
puglHideWindow(PuglView* view)
{
[view->impl->window setIsVisible:NO];
- view->visible = false;
return PUGL_SUCCESS;
}
void
puglFreeViewInternals(PuglView* view)
{
- view->backend->destroy(view);
- [view->impl->wrapperView removeFromSuperview];
- view->impl->wrapperView->puglview = NULL;
- if (view->impl->window) {
- [view->impl->window close];
- }
- [view->impl->wrapperView release];
- if (view->impl->window) {
- [view->impl->window release];
+ if (view) {
+ if (view->backend) {
+ view->backend->destroy(view);
+ }
+
+ if (view->impl) {
+ [view->impl->wrapperView removeFromSuperview];
+ view->impl->wrapperView->puglview = NULL;
+ if (view->impl->window) {
+ [view->impl->window close];
+ }
+ [view->impl->wrapperView release];
+ if (view->impl->window) {
+ [view->impl->window release];
+ }
+ free(view->impl);
+ }
}
- free(view->impl);
}
PuglStatus
@@ -867,80 +907,145 @@ puglRequestAttention(PuglView* view)
{
if (![view->impl->window isKeyWindow]) {
[view->world->impl->app requestUserAttention:NSInformationalRequest];
- view->impl->wrapperView->urgentTimer =
- [NSTimer scheduledTimerWithTimeInterval:2.0
- target:view->impl->wrapperView
- selector:@selector(urgentTick)
- userInfo:nil
- repeats:YES];
}
return PUGL_SUCCESS;
}
PuglStatus
-puglPollEvents(PuglWorld* world, const double timeout)
+puglStartTimer(PuglView* view, uintptr_t id, double timeout)
{
- NSDate* date = ((timeout < 0) ? [NSDate distantFuture] :
- (timeout == 0) ? nil :
- [NSDate dateWithTimeIntervalSinceNow:timeout]);
+ puglStopTimer(view, id);
- /* Note that dequeue:NO is broken (it blocks forever even when events are
- pending), so we work around this by dequeueing the event then posting it
- back to the front of the queue. */
- NSEvent* event = [world->impl->app
- nextEventMatchingMask:NSAnyEventMask
- untilDate:date
- inMode:NSDefaultRunLoopMode
- dequeue:YES];
+ NSNumber* idNumber = [NSNumber numberWithUnsignedLong:id];
- [world->impl->app postEvent:event atStart:true];
+ NSTimer* timer = [NSTimer timerWithTimeInterval:timeout
+ target:view->impl->wrapperView
+ selector:@selector(timerTick:)
+ userInfo:idNumber
+ repeats:YES];
+
+ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
+
+ view->impl->wrapperView->userTimers[idNumber] = timer;
return PUGL_SUCCESS;
}
PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id)
+{
+ NSNumber* idNumber = [NSNumber numberWithUnsignedLong:id];
+ NSTimer* timer = view->impl->wrapperView->userTimers[idNumber];
+
+ if (timer) {
+ [view->impl->wrapperView->userTimers removeObjectForKey:timer];
+ [timer invalidate];
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_UNKNOWN_ERROR;
+}
+
+PuglStatus puglSendEvent(PuglView* view, const PuglEvent* event)
+{
+ if (event->type == PUGL_CLIENT) {
+ PuglWrapperView* wrapper = view->impl->wrapperView;
+ const NSWindow* window = [wrapper window];
+ const NSRect rect = [wrapper frame];
+ const NSPoint center = {NSMidX(rect), NSMidY(rect)};
+
+ NSEvent* nsevent = [NSEvent
+ otherEventWithType:NSApplicationDefined
+ location:center
+ modifierFlags:0
+ timestamp:[[NSProcessInfo processInfo] systemUptime]
+ windowNumber:window.windowNumber
+ context:nil
+ subtype:PUGL_CLIENT
+ data1:event->client.data1
+ data2:event->client.data2];
+
+ [view->world->impl->app postEvent:nsevent atStart:false];
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_UNSUPPORTED_TYPE;
+}
+
+#ifndef PUGL_DISABLE_DEPRECATED
+PuglStatus
puglWaitForEvent(PuglView* view)
{
return puglPollEvents(view->world, -1.0);
}
+#endif
-PUGL_API PuglStatus
-puglDispatchEvents(PuglWorld* world)
+static void
+dispatchClientEvent(PuglWorld* world, NSEvent* ev)
+{
+ NSWindow* win = [ev window];
+ NSPoint loc = [ev locationInWindow];
+ for (size_t i = 0; i < world->numViews; ++i) {
+ PuglView* view = world->views[i];
+ PuglWrapperView* wrapper = view->impl->wrapperView;
+ if ([wrapper window] == win && NSPointInRect(loc, [wrapper frame])) {
+ const PuglEventClient event = {PUGL_CLIENT,
+ 0,
+ [ev data1],
+ [ev data2]};
+
+ puglDispatchEvent(view, (const PuglEvent*)&event);
+ }
+ }
+}
+
+PuglStatus
+puglUpdate(PuglWorld* world, const double timeout)
{
+ NSDate* date = ((timeout < 0)
+ ? [NSDate distantFuture]
+ : [NSDate dateWithTimeIntervalSinceNow:timeout]);
+
for (NSEvent* ev = NULL;
(ev = [world->impl->app nextEventMatchingMask:NSAnyEventMask
- untilDate:nil
+ untilDate:date
inMode:NSDefaultRunLoopMode
dequeue:YES]);) {
+
+ if ([ev type] == NSApplicationDefined && [ev subtype] == PUGL_CLIENT) {
+ dispatchClientEvent(world, ev);
+ }
+
[world->impl->app sendEvent: ev];
+
+ if (timeout < 0) {
+ // Now that we've waited and got an event, set the date to now to
+ // avoid looping forever
+ date = [NSDate date];
+ }
+ }
+
+ for (size_t i = 0; i < world->numViews; ++i) {
+ PuglView* const view = world->views[i];
+
+ if ([[view->impl->drawView window] isVisible]) {
+ puglDispatchSimpleEvent(view, PUGL_UPDATE);
+ }
+
+ [view->impl->drawView displayIfNeeded];
}
return PUGL_SUCCESS;
}
+#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglProcessEvents(PuglView* view)
{
return puglDispatchEvents(view->world);
}
-
-PuglGlFunc
-puglGetProcAddress(const char *name)
-{
- CFBundleRef framework =
- CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
-
- CFStringRef symbol = CFStringCreateWithCString(
- kCFAllocatorDefault, name, kCFStringEncodingASCII);
-
- PuglGlFunc func = (PuglGlFunc)CFBundleGetFunctionPointerForName(
- framework, symbol);
-
- CFRelease(symbol);
-
- return func;
-}
+#endif
double
puglGetTime(const PuglWorld* world)
@@ -955,6 +1060,15 @@ puglPostRedisplay(PuglView* view)
return PUGL_SUCCESS;
}
+PuglStatus
+puglPostRedisplayRect(PuglView* view, const PuglRect rect)
+{
+ [view->impl->drawView setNeedsDisplayInRect:
+ CGRectMake(rect.x, rect.y, rect.width, rect.height)];
+
+ return PUGL_SUCCESS;
+}
+
PuglNativeWindow
puglGetNativeWindow(PuglView* view)
{
diff --git a/subprojects/d2tk/pugl/pugl/detail/mac_cairo.m b/subprojects/d2tk/pugl/pugl/detail/mac_cairo.m
index 143fbb0..51c1c13 100644
--- a/subprojects/d2tk/pugl/pugl/detail/mac_cairo.m
+++ b/subprojects/d2tk/pugl/pugl/detail/mac_cairo.m
@@ -20,7 +20,8 @@
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
-#include "pugl/pugl_cairo_backend.h"
+#include "pugl/pugl_cairo.h"
+#include "pugl/pugl_stub.h"
#include <cairo-quartz.h>
@@ -62,12 +63,6 @@
@end
static PuglStatus
-puglMacCairoConfigure(PuglView* PUGL_UNUSED(view))
-{
- return PUGL_SUCCESS;
-}
-
-static PuglStatus
puglMacCairoCreate(PuglView* view)
{
PuglInternals* impl = view->impl;
@@ -98,10 +93,10 @@ puglMacCairoDestroy(PuglView* view)
}
static PuglStatus
-puglMacCairoEnter(PuglView* view, bool drawing)
+puglMacCairoEnter(PuglView* view, const PuglEventExpose* expose)
{
PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView;
- if (!drawing) {
+ if (!expose) {
return PUGL_SUCCESS;
}
@@ -119,10 +114,10 @@ puglMacCairoEnter(PuglView* view, bool drawing)
}
static PuglStatus
-puglMacCairoLeave(PuglView* view, bool drawing)
+puglMacCairoLeave(PuglView* view, const PuglEventExpose* expose)
{
PuglCairoView* const drawView = (PuglCairoView*)view->impl->drawView;
- if (!drawing) {
+ if (!expose) {
return PUGL_SUCCESS;
}
@@ -141,15 +136,6 @@ puglMacCairoLeave(PuglView* view, bool drawing)
return PUGL_SUCCESS;
}
-static PuglStatus
-puglMacCairoResize(PuglView* PUGL_UNUSED(view),
- int PUGL_UNUSED(width),
- int PUGL_UNUSED(height))
-{
- // No need to resize, the surface is created for the drawing context
- return PUGL_SUCCESS;
-}
-
static void*
puglMacCairoGetContext(PuglView* view)
{
@@ -158,15 +144,12 @@ puglMacCairoGetContext(PuglView* view)
const PuglBackend* puglCairoBackend(void)
{
- static const PuglBackend backend = {
- puglMacCairoConfigure,
- puglMacCairoCreate,
- puglMacCairoDestroy,
- puglMacCairoEnter,
- puglMacCairoLeave,
- puglMacCairoResize,
- puglMacCairoGetContext
- };
+ static const PuglBackend backend = {puglStubConfigure,
+ puglMacCairoCreate,
+ puglMacCairoDestroy,
+ puglMacCairoEnter,
+ puglMacCairoLeave,
+ puglMacCairoGetContext};
return &backend;
}
diff --git a/subprojects/d2tk/pugl/pugl/detail/mac_gl.m b/subprojects/d2tk/pugl/pugl/detail/mac_gl.m
index cb9117e..54f0fdd 100644
--- a/subprojects/d2tk/pugl/pugl/detail/mac_gl.m
+++ b/subprojects/d2tk/pugl/pugl/detail/mac_gl.m
@@ -1,5 +1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-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
@@ -20,7 +20,8 @@
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
-#include "pugl/pugl_gl_backend.h"
+#include "pugl/pugl_gl.h"
+#include "pugl/pugl_stub.h"
#ifndef __MAC_10_10
# define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core
@@ -92,12 +93,6 @@
@end
static PuglStatus
-puglMacGlConfigure(PuglView* PUGL_UNUSED(view))
-{
- return PUGL_SUCCESS;
-}
-
-static PuglStatus
puglMacGlCreate(PuglView* view)
{
PuglInternals* impl = view->impl;
@@ -130,7 +125,7 @@ puglMacGlDestroy(PuglView* view)
}
static PuglStatus
-puglMacGlEnter(PuglView* view, bool PUGL_UNUSED(drawing))
+puglMacGlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
{
PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView;
@@ -139,11 +134,11 @@ puglMacGlEnter(PuglView* view, bool PUGL_UNUSED(drawing))
}
static PuglStatus
-puglMacGlLeave(PuglView* view, bool drawing)
+puglMacGlLeave(PuglView* view, const PuglEventExpose* expose)
{
PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView;
- if (drawing) {
+ if (expose) {
[[drawView openGLContext] flushBuffer];
}
@@ -152,33 +147,31 @@ puglMacGlLeave(PuglView* view, bool drawing)
return PUGL_SUCCESS;
}
-static PuglStatus
-puglMacGlResize(PuglView* view, int PUGL_UNUSED(width), int PUGL_UNUSED(height))
+PuglGlFunc
+puglGetProcAddress(const char *name)
{
- PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView;
+ CFBundleRef framework =
+ CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl"));
- [drawView reshape];
+ CFStringRef symbol = CFStringCreateWithCString(
+ kCFAllocatorDefault, name, kCFStringEncodingASCII);
- return PUGL_SUCCESS;
-}
+ PuglGlFunc func = (PuglGlFunc)CFBundleGetFunctionPointerForName(
+ framework, symbol);
-static void*
-puglMacGlGetContext(PuglView* PUGL_UNUSED(view))
-{
- return NULL;
+ CFRelease(symbol);
+
+ return func;
}
const PuglBackend* puglGlBackend(void)
{
- static const PuglBackend backend = {
- puglMacGlConfigure,
- puglMacGlCreate,
- puglMacGlDestroy,
- puglMacGlEnter,
- puglMacGlLeave,
- puglMacGlResize,
- puglMacGlGetContext
- };
+ static const PuglBackend backend = {puglStubConfigure,
+ puglMacGlCreate,
+ puglMacGlDestroy,
+ puglMacGlEnter,
+ puglMacGlLeave,
+ puglStubGetContext};
return &backend;
}
diff --git a/subprojects/d2tk/pugl/pugl/detail/mac_stub.m b/subprojects/d2tk/pugl/pugl/detail/mac_stub.m
new file mode 100644
index 0000000..71a54b8
--- /dev/null
+++ b/subprojects/d2tk/pugl/pugl/detail/mac_stub.m
@@ -0,0 +1,95 @@
+/*
+ Copyright 2019-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 mac_stub.m Stub graphics backend for MacOS.
+*/
+
+#include "pugl/detail/implementation.h"
+#include "pugl/detail/mac.h"
+#include "pugl/pugl_stub.h"
+
+#import <Cocoa/Cocoa.h>
+
+@interface PuglStubView : NSView
+{
+@public
+ PuglView* puglview;
+}
+
+@end
+
+@implementation PuglStubView
+
+- (void) resizeWithOldSuperviewSize:(NSSize)oldSize
+{
+ PuglWrapperView* wrapper = (PuglWrapperView*)[self superview];
+
+ [super resizeWithOldSuperviewSize:oldSize];
+ [wrapper setReshaped];
+}
+
+- (void) drawRect:(NSRect)rect
+{
+ PuglWrapperView* wrapper = (PuglWrapperView*)[self superview];
+
+ [wrapper dispatchExpose:rect];
+}
+
+@end
+
+static PuglStatus
+puglMacStubCreate(PuglView* view)
+{
+ PuglInternals* impl = view->impl;
+ PuglStubView* drawView = [PuglStubView alloc];
+
+ drawView->puglview = view;
+ [drawView initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)];
+ if (view->hints[PUGL_RESIZABLE]) {
+ [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
+ } else {
+ [drawView setAutoresizingMask:NSViewNotSizable];
+ }
+
+ impl->drawView = drawView;
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
+puglMacStubDestroy(PuglView* view)
+{
+ PuglStubView* const drawView = (PuglStubView*)view->impl->drawView;
+
+ [drawView removeFromSuperview];
+ [drawView release];
+
+ view->impl->drawView = nil;
+ return PUGL_SUCCESS;
+}
+
+const PuglBackend*
+puglStubBackend(void)
+{
+ static const PuglBackend backend = {puglStubConfigure,
+ puglMacStubCreate,
+ puglMacStubDestroy,
+ puglStubEnter,
+ puglStubLeave,
+ puglStubGetContext};
+
+ return &backend;
+}
diff --git a/subprojects/d2tk/pugl/pugl/detail/types.h b/subprojects/d2tk/pugl/pugl/detail/types.h
index d018be5..e750ca1 100644
--- a/subprojects/d2tk/pugl/pugl/detail/types.h
+++ b/subprojects/d2tk/pugl/pugl/detail/types.h
@@ -28,12 +28,12 @@
#include <stdint.h>
// Unused parameter macro to suppresses warnings and make it impossible to use
-#if defined(__cplusplus) || defined(_MSC_VER)
+#if defined(__cplusplus)
# define PUGL_UNUSED(name)
#elif defined(__GNUC__)
# define PUGL_UNUSED(name) name##_unused __attribute__((__unused__))
#else
-# define PUGL_UNUSED(name)
+# define PUGL_UNUSED(name) name
#endif
/** Platform-specific world internals. */
@@ -64,6 +64,7 @@ struct PuglViewImpl {
uintptr_t transientParent;
PuglHints hints;
PuglRect frame;
+ PuglEventConfigure lastConfigure;
int minWidth;
int minHeight;
int minAspectX;
@@ -71,16 +72,18 @@ struct PuglViewImpl {
int maxAspectX;
int maxAspectY;
bool visible;
- bool redisplay;
};
/** Cross-platform world definition. */
struct PuglWorldImpl {
PuglWorldInternals* impl;
+ PuglWorldHandle handle;
+ PuglLogFunc logFunc;
char* className;
double startTime;
size_t numViews;
PuglView** views;
+ PuglLogLevel logLevel;
};
/** Opaque surface used by graphics backend. */
@@ -97,14 +100,11 @@ struct PuglBackendImpl {
/** Destroy surface and drawing context. */
PuglStatus (*destroy)(PuglView*);
- /** Enter drawing context, for drawing if parameter is true. */
- PuglStatus (*enter)(PuglView*, bool);
-
- /** Leave drawing context, after drawing if parameter is true. */
- PuglStatus (*leave)(PuglView*, bool);
+ /** Enter drawing context, for drawing if expose is non-null. */
+ PuglStatus (*enter)(PuglView*, const PuglEventExpose*);
- /** Resize drawing context to the given width and height. */
- PuglStatus (*resize)(PuglView*, int, int);
+ /** Leave drawing context, after drawing if expose is non-null. */
+ PuglStatus (*leave)(PuglView*, const PuglEventExpose*);
/** Return the puglGetContext() handle for the application, if any. */
void* (*getContext)(PuglView*);
diff --git a/subprojects/d2tk/pugl/pugl/detail/win.c b/subprojects/d2tk/pugl/pugl/detail/win.c
index 8886496..4606d60 100644
--- a/subprojects/d2tk/pugl/pugl/detail/win.c
+++ b/subprojects/d2tk/pugl/pugl/detail/win.c
@@ -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
@@ -18,15 +18,17 @@
@file win.c Windows implementation.
*/
-#include "pugl/detail/implementation.h"
#include "pugl/detail/win.h"
+
+#include "pugl/detail/implementation.h"
#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
#include <windows.h>
#include <windowsx.h>
+#include <math.h>
#include <stdbool.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <wctype.h>
@@ -44,10 +46,11 @@
# define GWLP_USERDATA (-21)
#endif
-#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
-#define PUGL_LOCAL_MARK_MSG (WM_USER + 51)
-#define PUGL_RESIZE_TIMER_ID 9461
-#define PUGL_URGENT_TIMER_ID 9462
+#define PUGL_LOCAL_CLOSE_MSG (WM_USER + 50)
+#define PUGL_LOCAL_MARK_MSG (WM_USER + 51)
+#define PUGL_LOCAL_CLIENT_MSG (WM_USER + 52)
+#define PUGL_RESIZE_TIMER_ID 9461
+#define PUGL_USER_TIMER_MIN 9470
typedef BOOL (WINAPI *PFN_SetProcessDPIAware)(void);
@@ -102,7 +105,8 @@ puglRegisterWindowClass(const char* name)
}
PuglWorldInternals*
-puglInitWorldInternals(void)
+puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
+ PuglWorldFlags PUGL_UNUSED(flags))
{
PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
1, sizeof(PuglWorldInternals));
@@ -129,14 +133,20 @@ puglInitWorldInternals(void)
return impl;
}
+void*
+puglGetNativeWorld(PuglWorld* PUGL_UNUSED(world))
+{
+ return GetModuleHandle(NULL);
+}
+
PuglInternals*
puglInitViewInternals(void)
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
-PuglStatus
-puglPollEvents(PuglWorld* world, const double timeout)
+static PuglStatus
+puglPollWinEvents(PuglWorld* world, const double timeout)
{
(void)world;
@@ -171,7 +181,7 @@ puglCreateWindow(PuglView* view, const char* title)
}
PuglStatus st = view->backend->configure(view);
- if (st || !impl->surface) {
+ if (st) {
return PUGL_SET_FORMAT_FAILED;
} else if ((st = view->backend->create(view))) {
return PUGL_CREATE_CONTEXT_FAILED;
@@ -181,8 +191,11 @@ puglCreateWindow(PuglView* view, const char* title)
puglSetWindowTitle(view, title);
}
+ puglSetFrame(view, view->frame);
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
+ puglDispatchSimpleEvent(view, PUGL_CREATE);
+
return PUGL_SUCCESS;
}
@@ -193,7 +206,6 @@ puglShowWindow(PuglView* view)
ShowWindow(impl->hwnd, SW_SHOWNORMAL);
SetFocus(impl->hwnd);
- view->visible = true;
return PUGL_SUCCESS;
}
@@ -203,7 +215,6 @@ puglHideWindow(PuglView* view)
PuglInternals* impl = view->impl;
ShowWindow(impl->hwnd, SW_HIDE);
- view->visible = false;
return PUGL_SUCCESS;
}
@@ -211,7 +222,10 @@ void
puglFreeViewInternals(PuglView* view)
{
if (view) {
- view->backend->destroy(view);
+ if (view->backend) {
+ view->backend->destroy(view);
+ }
+
ReleaseDC(view->impl->hwnd, view->impl->hdc);
DestroyWindow(view->impl->hwnd);
free(view->impl);
@@ -228,12 +242,8 @@ puglFreeWorldInternals(PuglWorld* world)
static PuglKey
keySymToSpecial(WPARAM sym)
{
+ // clang-format off
switch (sym) {
- case VK_BACK: return PUGL_KEY_BACKSPACE;
- case VK_TAB: return PUGL_KEY_TAB;
- case VK_RETURN: return PUGL_KEY_RETURN;
- case VK_ESCAPE: return PUGL_KEY_ESCAPE;
- case VK_DELETE: return PUGL_KEY_DELETE;
case VK_F1: return PUGL_KEY_F1;
case VK_F2: return PUGL_KEY_F2;
case VK_F3: return PUGL_KEY_F3;
@@ -246,6 +256,8 @@ keySymToSpecial(WPARAM sym)
case VK_F10: return PUGL_KEY_F10;
case VK_F11: return PUGL_KEY_F11;
case VK_F12: return PUGL_KEY_F12;
+ case VK_BACK: return PUGL_KEY_BACKSPACE;
+ case VK_DELETE: return PUGL_KEY_DELETE;
case VK_LEFT: return PUGL_KEY_LEFT;
case VK_UP: return PUGL_KEY_UP;
case VK_RIGHT: return PUGL_KEY_RIGHT;
@@ -271,19 +283,22 @@ keySymToSpecial(WPARAM sym)
case VK_NUMLOCK: return PUGL_KEY_NUM_LOCK;
case VK_SNAPSHOT: return PUGL_KEY_PRINT_SCREEN;
case VK_PAUSE: return PUGL_KEY_PAUSE;
- default: break;
}
+ // clang-format on
+
return (PuglKey)0;
}
static uint32_t
getModifiers(void)
{
+ // clang-format off
return (((GetKeyState(VK_SHIFT) < 0) ? PUGL_MOD_SHIFT : 0u) |
((GetKeyState(VK_CONTROL) < 0) ? PUGL_MOD_CTRL : 0u) |
((GetKeyState(VK_MENU) < 0) ? PUGL_MOD_ALT : 0u) |
((GetKeyState(VK_LWIN) < 0) ? PUGL_MOD_SUPER : 0u) |
((GetKeyState(VK_RWIN) < 0) ? PUGL_MOD_SUPER : 0u));
+ // clang-format on
}
static void
@@ -392,8 +407,9 @@ initKeyEvent(PuglEventKey* event,
// Translate unshifted key
BYTE keyboardState[256] = {0};
wchar_t buf[5] = {0};
- const int ulen = ToUnicode(vkey, vcode, keyboardState, buf, 4, 1<<2);
- event->key = puglDecodeUTF16(buf, ulen);
+
+ event->key = puglDecodeUTF16(
+ buf, ToUnicode(vkey, vcode, keyboardState, buf, 4, 1 << 2));
}
}
@@ -428,20 +444,23 @@ handleConfigure(PuglView* view, PuglEvent* event)
(LPPOINT)&rect,
2);
- view->frame.x = rect.left;
- view->frame.y = rect.top;
- view->frame.width = rect.right - rect.left;
- view->frame.height = rect.bottom - rect.top;
+ const LONG width = rect.right - rect.left;
+ const LONG height = rect.bottom - rect.top;
+
+ view->frame.x = rect.left;
+ view->frame.y = rect.top;
event->configure.type = PUGL_CONFIGURE;
event->configure.x = view->frame.x;
event->configure.y = view->frame.y;
- event->configure.width = view->frame.width;
- event->configure.height = view->frame.height;
+ event->configure.width = width;
+ event->configure.height = height;
+
+ if (view->frame.width != width || view->frame.height != height) {
+ view->frame.width = width;
+ view->frame.height = height;
+ }
- view->backend->resize(view,
- rect.right - rect.left,
- rect.bottom - rect.top);
return rect;
}
@@ -460,18 +479,10 @@ handleCrossing(PuglView* view, const PuglEventType type, POINT pos)
(double)root_pos.x,
(double)root_pos.y,
getModifiers(),
- PUGL_CROSSING_NORMAL
+ PUGL_CROSSING_NORMAL,
};
- puglDispatchEvent(view, (const PuglEvent*)&ev);
-}
-static void
-stopFlashing(PuglView* view)
-{
- if (view->impl->flashing) {
- KillTimer(view->impl->hwnd, PUGL_URGENT_TIMER_ID);
- FlashWindow(view->impl->hwnd, FALSE);
- }
+ puglDispatchEvent(view, (const PuglEvent*)&ev);
}
static void
@@ -531,12 +542,22 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
switch (message) {
case WM_SHOWWINDOW:
- rect = handleConfigure(view, &event);
- RedrawWindow(view->impl->hwnd, NULL, NULL,
- RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT);
+ if (wParam) {
+ handleConfigure(view, &event);
+ puglDispatchEvent(view, &event);
+ event.type = PUGL_NOTHING;
+
+ RedrawWindow(view->impl->hwnd, NULL, NULL,
+ RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT);
+ }
+
+ if ((bool)wParam != view->visible) {
+ view->visible = wParam;
+ event.any.type = wParam ? PUGL_MAP : PUGL_UNMAP;
+ }
break;
case WM_SIZE:
- rect = handleConfigure(view, &event);
+ handleConfigure(view, &event);
InvalidateRect(view->impl->hwnd, NULL, false);
break;
case WM_SIZING:
@@ -557,8 +578,9 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
if (wParam == PUGL_RESIZE_TIMER_ID) {
RedrawWindow(view->impl->hwnd, NULL, NULL,
RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT);
- } else if (wParam == PUGL_URGENT_TIMER_ID) {
- FlashWindow(view->impl->hwnd, TRUE);
+ } else if (wParam >= PUGL_USER_TIMER_MIN) {
+ const PuglEventTimer ev = {PUGL_TIMER, 0, wParam - PUGL_USER_TIMER_MIN};
+ puglDispatchEvent(view, (const PuglEvent*)&ev);
}
break;
case WM_EXITSIZEMOVE:
@@ -589,18 +611,18 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
if (!view->impl->mouseTracked) {
TRACKMOUSEEVENT tme = {0};
+
tme.cbSize = sizeof(tme);
tme.dwFlags = TME_LEAVE;
tme.hwndTrack = view->impl->hwnd;
TrackMouseEvent(&tme);
- stopFlashing(view);
- handleCrossing(view, PUGL_ENTER_NOTIFY, pt);
+ handleCrossing(view, PUGL_POINTER_IN, pt);
view->impl->mouseTracked = true;
}
ClientToScreen(view->impl->hwnd, &pt);
- event.motion.type = PUGL_MOTION_NOTIFY;
+ event.motion.type = PUGL_MOTION;
event.motion.time = GetMessageTime() / 1e3;
event.motion.x = GET_X_LPARAM(lParam);
event.motion.y = GET_Y_LPARAM(lParam);
@@ -612,7 +634,7 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
case WM_MOUSELEAVE:
GetCursorPos(&pt);
ScreenToClient(view->impl->hwnd, &pt);
- handleCrossing(view, PUGL_LEAVE_NOTIFY, pt);
+ handleCrossing(view, PUGL_POINTER_OUT, pt);
view->impl->mouseTracked = false;
break;
case WM_LBUTTONDOWN:
@@ -653,7 +675,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
initCharEvent(&event, view, wParam, lParam);
break;
case WM_SETFOCUS:
- stopFlashing(view);
event.type = PUGL_FOCUS_IN;
break;
case WM_KILLFOCUS:
@@ -667,9 +688,14 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
break;
case WM_SYSCHAR:
return TRUE;
+ case PUGL_LOCAL_CLIENT_MSG:
+ event.client.type = PUGL_CLIENT;
+ event.client.data1 = (uintptr_t)wParam;
+ event.client.data2 = (uintptr_t)lParam;
+ break;
case WM_QUIT:
case PUGL_LOCAL_CLOSE_MSG:
- event.close.type = PUGL_CLOSE;
+ event.any.type = PUGL_CLOSE;
break;
default:
return DefWindowProc(view->impl->hwnd, message, wParam, lParam);
@@ -696,43 +722,72 @@ puglHasFocus(const PuglView* view)
PuglStatus
puglRequestAttention(PuglView* view)
{
- if (!view->impl->mouseTracked || !puglHasFocus(view)) {
- FlashWindow(view->impl->hwnd, TRUE);
- SetTimer(view->impl->hwnd, PUGL_URGENT_TIMER_ID, 500, NULL);
- view->impl->flashing = true;
- }
+ FLASHWINFO info = {sizeof(FLASHWINFO),
+ view->impl->hwnd,
+ FLASHW_ALL|FLASHW_TIMERNOFG,
+ 1,
+ 0};
+
+ FlashWindowEx(&info);
return PUGL_SUCCESS;
}
PuglStatus
+puglStartTimer(PuglView* view, uintptr_t id, double timeout)
+{
+ const UINT msec = (UINT)floor(timeout * 1000.0);
+
+ return (SetTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id, msec, NULL)
+ ? PUGL_SUCCESS
+ : PUGL_UNKNOWN_ERROR);
+}
+
+PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id)
+{
+ return (KillTimer(view->impl->hwnd, PUGL_USER_TIMER_MIN + id)
+ ? PUGL_SUCCESS
+ : PUGL_UNKNOWN_ERROR);
+}
+
+PuglStatus
+puglSendEvent(PuglView* view, const PuglEvent* event)
+{
+ if (event->type == PUGL_CLIENT) {
+ PostMessage(view->impl->hwnd,
+ PUGL_LOCAL_CLIENT_MSG,
+ (WPARAM)event->client.data1,
+ (LPARAM)event->client.data2);
+
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_UNSUPPORTED_TYPE;
+}
+
+#ifndef PUGL_DISABLE_DEPRECATED
+PuglStatus
puglWaitForEvent(PuglView* PUGL_UNUSED(view))
{
WaitMessage();
return PUGL_SUCCESS;
}
+#endif
-PUGL_API PuglStatus
-puglDispatchEvents(PuglWorld* world)
+static PuglStatus
+puglDispatchViewEvents(PuglView* view)
{
- for (size_t i = 0; i < world->numViews; ++i) {
- if (world->views[i]->redisplay) {
- UpdateWindow(world->views[i]->impl->hwnd);
- world->views[i]->redisplay = false;
- }
- }
-
/* Windows has no facility to process only currently queued messages, which
causes the event loop to run forever in cases like mouse movement where
the queue is constantly being filled with new messages. To work around
this, we post a message to ourselves before starting, record its time
when it is received, then break the loop on the first message that was
created afterwards. */
- PostMessage(NULL, PUGL_LOCAL_MARK_MSG, 0, 0);
long markTime = 0;
MSG msg;
- while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
+ while (PeekMessage(&msg, view->impl->hwnd, 0, 0, PM_REMOVE)) {
if (msg.message == PUGL_LOCAL_MARK_MSG) {
markTime = GetMessageTime();
} else {
@@ -747,11 +802,59 @@ puglDispatchEvents(PuglWorld* world)
return PUGL_SUCCESS;
}
+static PuglStatus
+puglDispatchWinEvents(PuglWorld* world)
+{
+ for (size_t i = 0; i < world->numViews; ++i) {
+ PostMessage(world->views[i]->impl->hwnd, PUGL_LOCAL_MARK_MSG, 0, 0);
+ }
+
+ for (size_t i = 0; i < world->numViews; ++i) {
+ puglDispatchViewEvents(world->views[i]);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglUpdate(PuglWorld* world, double timeout)
+{
+ const double startTime = puglGetTime(world);
+ PuglStatus st = PUGL_SUCCESS;
+
+ if (timeout < 0.0) {
+ st = puglPollWinEvents(world, timeout);
+ st = st ? st : puglDispatchWinEvents(world);
+ } else if (timeout == 0.0) {
+ st = puglDispatchWinEvents(world);
+ } else {
+ const double endTime = startTime + timeout - 0.001;
+ for (double t = startTime; t < endTime; t = puglGetTime(world)) {
+ if ((st = puglPollWinEvents(world, endTime - t)) ||
+ (st = puglDispatchWinEvents(world))) {
+ break;
+ }
+ }
+ }
+
+ for (size_t i = 0; i < world->numViews; ++i) {
+ if (world->views[i]->visible) {
+ puglDispatchSimpleEvent(world->views[i], PUGL_UPDATE);
+ }
+
+ UpdateWindow(world->views[i]->impl->hwnd);
+ }
+
+ return st;
+}
+
+#ifndef PUGL_DISABLE_DEPRECATED
PuglStatus
puglProcessEvents(PuglView* view)
{
- return puglDispatchEvents(view->world);
+ return puglUpdate(view->world, 0.0);
}
+#endif
LRESULT CALLBACK
wndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
@@ -789,7 +892,19 @@ PuglStatus
puglPostRedisplay(PuglView* view)
{
InvalidateRect(view->impl->hwnd, NULL, false);
- view->redisplay = true;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglPostRedisplayRect(PuglView* view, const PuglRect rect)
+{
+ const RECT r = {(long)floor(rect.x),
+ (long)floor(rect.y),
+ (long)ceil(rect.x + rect.width),
+ (long)ceil(rect.y + rect.height)};
+
+ InvalidateRect(view->impl->hwnd, &r, false);
+
return PUGL_SUCCESS;
}
@@ -828,10 +943,13 @@ puglSetFrame(PuglView* view, const PuglRect frame)
FALSE,
puglWinGetWindowExFlags(view));
- if (!SetWindowPos(view->impl->hwnd, HWND_TOP,
- rect.left, rect.top,
- rect.right - rect.left, rect.bottom - rect.top,
- (SWP_NOACTIVATE|SWP_NOOWNERZORDER|SWP_NOZORDER))) {
+ if (!SetWindowPos(view->impl->hwnd,
+ HWND_TOP,
+ rect.left,
+ rect.top,
+ rect.right - rect.left,
+ rect.bottom - rect.top,
+ SWP_NOACTIVATE | SWP_NOOWNERZORDER | SWP_NOZORDER)) {
return PUGL_UNKNOWN_ERROR;
}
}
@@ -928,3 +1046,39 @@ puglSetClipboard(PuglView* const view,
CloseClipboard();
return PUGL_SUCCESS;
}
+
+static PuglStatus
+puglWinStubEnter(PuglView* view, const PuglEventExpose* expose)
+{
+ if (expose) {
+ PAINTSTRUCT ps;
+ BeginPaint(view->impl->hwnd, &ps);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
+puglWinStubLeave(PuglView* view, const PuglEventExpose* expose)
+{
+ if (expose) {
+ PAINTSTRUCT ps;
+ EndPaint(view->impl->hwnd, &ps);
+ SwapBuffers(view->impl->hdc);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+const PuglBackend*
+puglStubBackend(void)
+{
+ static const PuglBackend backend = {puglWinStubConfigure,
+ puglStubCreate,
+ puglStubDestroy,
+ puglWinStubEnter,
+ puglWinStubLeave,
+ puglStubGetContext};
+
+ return &backend;
+}
diff --git a/subprojects/d2tk/pugl/pugl/detail/win.h b/subprojects/d2tk/pugl/pugl/detail/win.h
index 6d89759..283f39e 100644
--- a/subprojects/d2tk/pugl/pugl/detail/win.h
+++ b/subprojects/d2tk/pugl/pugl/detail/win.h
@@ -45,16 +45,17 @@ struct PuglInternalsImpl {
static inline PuglWinPFD
puglWinGetPixelFormatDescriptor(const PuglHints hints)
{
- const int rgbBits = (hints[PUGL_RED_BITS] +
- hints[PUGL_GREEN_BITS] +
+ const int rgbBits = (hints[PUGL_RED_BITS] + //
+ hints[PUGL_GREEN_BITS] + //
hints[PUGL_BLUE_BITS]);
+ const DWORD dwFlags = hints[PUGL_DOUBLE_BUFFER] ? PFD_DOUBLEBUFFER : 0u;
+
PuglWinPFD pfd;
ZeroMemory(&pfd, sizeof(pfd));
pfd.nSize = sizeof(pfd);
pfd.nVersion = 1;
- pfd.dwFlags = PFD_DRAW_TO_WINDOW|PFD_SUPPORT_OPENGL;
- pfd.dwFlags |= hints[PUGL_DOUBLE_BUFFER] ? PFD_DOUBLEBUFFER : 0;
+ pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | dwFlags;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = (BYTE)rgbBits;
pfd.cRedBits = (BYTE)hints[PUGL_RED_BITS];
@@ -70,12 +71,13 @@ puglWinGetPixelFormatDescriptor(const PuglHints hints)
static inline unsigned
puglWinGetWindowFlags(const PuglView* const view)
{
- const bool resizable = view->hints[PUGL_RESIZABLE];
+ const bool resizable = view->hints[PUGL_RESIZABLE];
+ const unsigned sizeFlags = resizable ? (WS_SIZEBOX | WS_MAXIMIZEBOX) : 0u;
+
return (WS_CLIPCHILDREN | WS_CLIPSIBLINGS |
(view->parent
- ? WS_CHILD
- : (WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX |
- (resizable ? (WS_SIZEBOX | WS_MAXIMIZEBOX) : 0))));
+ ? WS_CHILD
+ : (WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | sizeFlags)));
}
static inline unsigned
@@ -113,3 +115,27 @@ puglWinCreateWindow(const PuglView* const view,
return PUGL_SUCCESS;
}
+
+static inline PuglStatus
+puglWinStubConfigure(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglStatus st = PUGL_SUCCESS;
+
+ if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) {
+ return st;
+ }
+
+ impl->pfd = puglWinGetPixelFormatDescriptor(view->hints);
+ impl->pfId = ChoosePixelFormat(impl->hdc, &impl->pfd);
+
+ if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) {
+ ReleaseDC(impl->hwnd, impl->hdc);
+ DestroyWindow(impl->hwnd);
+ impl->hwnd = NULL;
+ impl->hdc = NULL;
+ return PUGL_SET_FORMAT_FAILED;
+ }
+
+ return PUGL_SUCCESS;
+}
diff --git a/subprojects/d2tk/pugl/pugl/detail/win_cairo.c b/subprojects/d2tk/pugl/pugl/detail/win_cairo.c
index 497711b..a8b371f 100644
--- a/subprojects/d2tk/pugl/pugl/detail/win_cairo.c
+++ b/subprojects/d2tk/pugl/pugl/detail/win_cairo.c
@@ -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
@@ -20,7 +20,8 @@
#include "pugl/detail/types.h"
#include "pugl/detail/win.h"
-#include "pugl/pugl_cairo_backend.h"
+#include "pugl/pugl_cairo.h"
+#include "pugl/pugl_stub.h"
#include <cairo-win32.h>
#include <cairo.h>
@@ -46,15 +47,6 @@ puglWinCairoCreateDrawContext(PuglView* view)
DeleteObject(SelectObject(surface->drawDc, surface->drawBitmap));
- cairo_status_t st = CAIRO_STATUS_SUCCESS;
- if (!(surface->surface = cairo_win32_surface_create(surface->drawDc)) ||
- (st = cairo_surface_status(surface->surface)) ||
- !(surface->cr = cairo_create(surface->surface)) ||
- (st = cairo_status(surface->cr))) {
- return PUGL_CREATE_CONTEXT_FAILED;
- }
-
- cairo_save(surface->cr);
return PUGL_SUCCESS;
}
@@ -66,11 +58,7 @@ puglWinCairoDestroyDrawContext(PuglView* view)
DeleteDC(surface->drawDc);
DeleteObject(surface->drawBitmap);
- cairo_destroy(surface->cr);
- cairo_surface_destroy(surface->surface);
- surface->surface = NULL;
- surface->cr = NULL;
surface->drawDc = NULL;
surface->drawBitmap = NULL;
@@ -80,34 +68,42 @@ puglWinCairoDestroyDrawContext(PuglView* view)
static PuglStatus
puglWinCairoConfigure(PuglView* view)
{
- PuglInternals* const impl = view->impl;
- PuglStatus st = PUGL_SUCCESS;
+ const PuglStatus st = puglWinStubConfigure(view);
- if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) {
- return st;
+ if (!st) {
+ view->impl->surface = (PuglWinCairoSurface*)calloc(
+ 1, sizeof(PuglWinCairoSurface));
}
- impl->pfd = puglWinGetPixelFormatDescriptor(view->hints);
- impl->pfId = ChoosePixelFormat(impl->hdc, &impl->pfd);
+ return st;
+}
- if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) {
- ReleaseDC(impl->hwnd, impl->hdc);
- DestroyWindow(impl->hwnd);
- impl->hwnd = NULL;
- impl->hdc = NULL;
- return PUGL_SET_FORMAT_FAILED;
- }
+static void
+puglWinCairoClose(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface;
- impl->surface = (PuglWinCairoSurface*)calloc(
- 1, sizeof(PuglWinCairoSurface));
+ cairo_surface_destroy(surface->surface);
- return PUGL_SUCCESS;
+ surface->surface = NULL;
}
static PuglStatus
-puglWinCairoCreate(PuglView* view)
+puglWinCairoOpen(PuglView* view)
{
- return puglWinCairoCreateDrawContext(view);
+ PuglInternals* const impl = view->impl;
+ PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface;
+
+ cairo_status_t st = CAIRO_STATUS_SUCCESS;
+ if (!(surface->surface = cairo_win32_surface_create(surface->drawDc)) ||
+ (st = cairo_surface_status(surface->surface)) ||
+ !(surface->cr = cairo_create(surface->surface)) ||
+ (st = cairo_status(surface->cr))) {
+ return PUGL_CREATE_CONTEXT_FAILED;
+ }
+
+ return PUGL_SUCCESS;
}
static PuglStatus
@@ -116,6 +112,7 @@ puglWinCairoDestroy(PuglView* view)
PuglInternals* const impl = view->impl;
PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface;
+ puglWinCairoClose(view);
puglWinCairoDestroyDrawContext(view);
free(surface);
impl->surface = NULL;
@@ -124,52 +121,38 @@ puglWinCairoDestroy(PuglView* view)
}
static PuglStatus
-puglWinCairoEnter(PuglView* view, bool drawing)
+puglWinCairoEnter(PuglView* view, const PuglEventExpose* expose)
{
- PuglInternals* const impl = view->impl;
- PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface;
- if (!drawing) {
- return PUGL_SUCCESS;
- }
+ PuglStatus st = PUGL_SUCCESS;
- PAINTSTRUCT ps;
- BeginPaint(view->impl->hwnd, &ps);
- cairo_save(surface->cr);
+ if (expose &&
+ !(st = puglWinCairoCreateDrawContext(view)) &&
+ !(st = puglWinCairoOpen(view))) {
+ PAINTSTRUCT ps;
+ BeginPaint(view->impl->hwnd, &ps);
+ }
- return PUGL_SUCCESS;
+ return st;
}
static PuglStatus
-puglWinCairoLeave(PuglView* view, bool drawing)
+puglWinCairoLeave(PuglView* view, const PuglEventExpose* expose)
{
PuglInternals* const impl = view->impl;
PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface;
- if (!drawing) {
- return PUGL_SUCCESS;
- }
-
- cairo_restore(surface->cr);
- cairo_surface_flush(surface->surface);
- BitBlt(impl->hdc,
- 0, 0, (int)view->frame.width, (int)view->frame.height,
- surface->drawDc, 0, 0, SRCCOPY);
- PAINTSTRUCT ps;
- EndPaint(view->impl->hwnd, &ps);
- SwapBuffers(view->impl->hdc);
+ if (expose) {
+ cairo_surface_flush(surface->surface);
+ BitBlt(impl->hdc,
+ 0, 0, (int)view->frame.width, (int)view->frame.height,
+ surface->drawDc, 0, 0, SRCCOPY);
- return PUGL_SUCCESS;
-}
+ puglWinCairoClose(view);
+ puglWinCairoDestroyDrawContext(view);
-static PuglStatus
-puglWinCairoResize(PuglView* view,
- int PUGL_UNUSED(width),
- int PUGL_UNUSED(height))
-{
- PuglStatus st = PUGL_SUCCESS;
- if ((st = puglWinCairoDestroyDrawContext(view)) ||
- (st = puglWinCairoCreateDrawContext(view))) {
- return st;
+ PAINTSTRUCT ps;
+ EndPaint(view->impl->hwnd, &ps);
+ SwapBuffers(view->impl->hdc);
}
return PUGL_SUCCESS;
@@ -184,15 +167,12 @@ puglWinCairoGetContext(PuglView* view)
const PuglBackend*
puglCairoBackend()
{
- static const PuglBackend backend = {
- puglWinCairoConfigure,
- puglWinCairoCreate,
- puglWinCairoDestroy,
- puglWinCairoEnter,
- puglWinCairoLeave,
- puglWinCairoResize,
- puglWinCairoGetContext
- };
+ static const PuglBackend backend = {puglWinCairoConfigure,
+ puglStubCreate,
+ puglWinCairoDestroy,
+ puglWinCairoEnter,
+ puglWinCairoLeave,
+ puglWinCairoGetContext};
return &backend;
}
diff --git a/subprojects/d2tk/pugl/pugl/detail/win_gl.c b/subprojects/d2tk/pugl/pugl/detail/win_gl.c
index 4cbb796..f5acfd6 100644
--- a/subprojects/d2tk/pugl/pugl/detail/win_gl.c
+++ b/subprojects/d2tk/pugl/pugl/detail/win_gl.c
@@ -20,7 +20,8 @@
#include "pugl/detail/types.h"
#include "pugl/detail/win.h"
-#include "pugl/pugl_gl_backend.h"
+#include "pugl/pugl_gl.h"
+#include "pugl/pugl_stub.h"
#include <windows.h>
@@ -108,6 +109,7 @@ puglWinGlConfigure(PuglView* view)
{
PuglInternals* impl = view->impl;
+ // clang-format off
const int pixelAttrs[] = {
WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB,
@@ -124,16 +126,17 @@ puglWinGlConfigure(PuglView* view)
WGL_STENCIL_BITS_ARB, view->hints[PUGL_STENCIL_BITS],
0,
};
+ // clang-format on
PuglWinGlSurface* const surface =
(PuglWinGlSurface*)calloc(1, sizeof(PuglWinGlSurface));
impl->surface = surface;
// Create fake window for getting at GL context
- PuglStatus st = PUGL_SUCCESS;
- PuglFakeWindow fakeWin = { 0, 0 };
- if ((st = puglWinCreateWindow(view, "Pugl Configuration",
- &fakeWin.hwnd, &fakeWin.hdc))) {
+ PuglStatus st = PUGL_SUCCESS;
+ PuglFakeWindow fakeWin = {0, 0};
+ static const char* title = "Pugl Configuration";
+ if ((st = puglWinCreateWindow(view, title, &fakeWin.hwnd, &fakeWin.hdc))) {
return puglWinError(&fakeWin, st);
}
@@ -189,16 +192,21 @@ puglWinGlCreate(PuglView* view)
PuglStatus st = PUGL_SUCCESS;
const int contextAttribs[] = {
- WGL_CONTEXT_MAJOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MAJOR],
- WGL_CONTEXT_MINOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MINOR],
- WGL_CONTEXT_FLAGS_ARB, (view->hints[PUGL_USE_DEBUG_CONTEXT]
- ? WGL_CONTEXT_DEBUG_BIT_ARB
- : 0),
- (view->hints[PUGL_USE_COMPAT_PROFILE]
- ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
- : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB),
- 0
- };
+ WGL_CONTEXT_MAJOR_VERSION_ARB,
+ view->hints[PUGL_CONTEXT_VERSION_MAJOR],
+
+ WGL_CONTEXT_MINOR_VERSION_ARB,
+ view->hints[PUGL_CONTEXT_VERSION_MINOR],
+
+ WGL_CONTEXT_FLAGS_ARB,
+ (view->hints[PUGL_USE_DEBUG_CONTEXT] ? WGL_CONTEXT_DEBUG_BIT_ARB : 0),
+
+ WGL_CONTEXT_PROFILE_MASK_ARB,
+ (view->hints[PUGL_USE_COMPAT_PROFILE]
+ ? WGL_CONTEXT_CORE_PROFILE_BIT_ARB
+ : WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB),
+
+ 0};
// Create real window with desired pixel format
if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) {
@@ -222,8 +230,9 @@ puglWinGlCreate(PuglView* view)
// Enter context and set swap interval
wglMakeCurrent(impl->hdc, surface->hglrc);
- if (surface->procs.wglSwapInterval) {
- surface->procs.wglSwapInterval(view->hints[PUGL_SWAP_INTERVAL]);
+ const int swapInterval = view->hints[PUGL_SWAP_INTERVAL];
+ if (surface->procs.wglSwapInterval && swapInterval != PUGL_DONT_CARE) {
+ surface->procs.wglSwapInterval(swapInterval);
}
return PUGL_SUCCESS;
@@ -244,13 +253,13 @@ puglWinGlDestroy(PuglView* view)
}
static PuglStatus
-puglWinGlEnter(PuglView* view, bool drawing)
+puglWinGlEnter(PuglView* view, const PuglEventExpose* expose)
{
PuglWinGlSurface* surface = (PuglWinGlSurface*)view->impl->surface;
wglMakeCurrent(view->impl->hdc, surface->hglrc);
- if (drawing) {
+ if (expose) {
PAINTSTRUCT ps;
BeginPaint(view->impl->hwnd, &ps);
}
@@ -259,9 +268,9 @@ puglWinGlEnter(PuglView* view, bool drawing)
}
static PuglStatus
-puglWinGlLeave(PuglView* view, bool drawing)
+puglWinGlLeave(PuglView* view, const PuglEventExpose* expose)
{
- if (drawing) {
+ if (expose) {
PAINTSTRUCT ps;
EndPaint(view->impl->hwnd, &ps);
SwapBuffers(view->impl->hdc);
@@ -271,20 +280,6 @@ puglWinGlLeave(PuglView* view, bool drawing)
return PUGL_SUCCESS;
}
-static PuglStatus
-puglWinGlResize(PuglView* PUGL_UNUSED(view),
- int PUGL_UNUSED(width),
- int PUGL_UNUSED(height))
-{
- return PUGL_SUCCESS;
-}
-
-static void*
-puglWinGlGetContext(PuglView* PUGL_UNUSED(view))
-{
- return NULL;
-}
-
PuglGlFunc
puglGetProcAddress(const char* name)
{
@@ -302,15 +297,12 @@ puglGetProcAddress(const char* name)
const PuglBackend*
puglGlBackend()
{
- static const PuglBackend backend = {
- puglWinGlConfigure,
- puglWinGlCreate,
- puglWinGlDestroy,
- puglWinGlEnter,
- puglWinGlLeave,
- puglWinGlResize,
- puglWinGlGetContext
- };
+ static const PuglBackend backend = {puglWinGlConfigure,
+ puglWinGlCreate,
+ puglWinGlDestroy,
+ puglWinGlEnter,
+ puglWinGlLeave,
+ puglStubGetContext};
return &backend;
}
diff --git a/subprojects/d2tk/pugl/pugl/detail/x11.c b/subprojects/d2tk/pugl/pugl/detail/x11.c
index cc84bc0..6f85215 100644
--- a/subprojects/d2tk/pugl/pugl/detail/x11.c
+++ b/subprojects/d2tk/pugl/pugl/detail/x11.c
@@ -1,5 +1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <http://drobilla.net>
Copyright 2013 Robin Gareus <robin@gareus.org>
Copyright 2011-2012 Ben Loftis, Harrison Consoles
@@ -22,10 +22,12 @@
#define _POSIX_C_SOURCE 199309L
+#include "pugl/detail/x11.h"
+
#include "pugl/detail/implementation.h"
#include "pugl/detail/types.h"
-#include "pugl/detail/x11.h"
#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
#include <X11/X.h>
#include <X11/Xatom.h>
@@ -33,13 +35,16 @@
#include <X11/Xutil.h>
#include <X11/keysym.h>
+#ifdef HAVE_XSYNC
+# include <X11/extensions/sync.h>
+#endif
+
#include <sys/select.h>
#include <sys/time.h>
-#include <limits.h>
+#include <math.h>
#include <stdbool.h>
#include <stdint.h>
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
@@ -59,13 +64,49 @@ enum WmClientStateMessageAction {
};
static const long eventMask =
- (ExposureMask | StructureNotifyMask | FocusChangeMask |
+ (ExposureMask | StructureNotifyMask |
+ VisibilityChangeMask | FocusChangeMask |
EnterWindowMask | LeaveWindowMask | PointerMotionMask |
ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask);
+static bool
+puglInitXSync(PuglWorldInternals* impl)
+{
+#ifdef HAVE_XSYNC
+ int syncMajor;
+ int syncMinor;
+ int errorBase;
+ XSyncSystemCounter* counters;
+ int numCounters;
+
+ if (XSyncQueryExtension(impl->display, &impl->syncEventBase, &errorBase) &&
+ XSyncInitialize(impl->display, &syncMajor, &syncMinor) &&
+ (counters = XSyncListSystemCounters(impl->display, &numCounters))) {
+
+ for (int n = 0; n < numCounters; ++n) {
+ if (!strcmp(counters[n].name, "SERVERTIME")) {
+ impl->serverTimeCounter = counters[n].counter;
+ impl->syncSupported = true;
+ break;
+ }
+ }
+
+ XSyncFreeSystemCounterList(counters);
+ }
+#else
+ (void)impl;
+#endif
+
+ return false;
+}
+
PuglWorldInternals*
-puglInitWorldInternals(void)
+puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags)
{
+ if (type == PUGL_PROGRAM && (flags & PUGL_WORLD_THREADS)) {
+ XInitThreads();
+ }
+
Display* display = XOpenDisplay(NULL);
if (!display) {
return NULL;
@@ -81,6 +122,7 @@ puglInitWorldInternals(void)
impl->atoms.UTF8_STRING = XInternAtom(display, "UTF8_STRING", 0);
impl->atoms.WM_PROTOCOLS = XInternAtom(display, "WM_PROTOCOLS", 0);
impl->atoms.WM_DELETE_WINDOW = XInternAtom(display, "WM_DELETE_WINDOW", 0);
+ impl->atoms.PUGL_CLIENT_MSG = XInternAtom(display, "_PUGL_CLIENT_MSG", 0);
impl->atoms.NET_WM_NAME = XInternAtom(display, "_NET_WM_NAME", 0);
impl->atoms.NET_WM_STATE = XInternAtom(display, "_NET_WM_STATE", 0);
impl->atoms.NET_WM_STATE_DEMANDS_ATTENTION =
@@ -90,45 +132,51 @@ puglInitWorldInternals(void)
XSetLocaleModifiers("");
if (!(impl->xim = XOpenIM(display, NULL, NULL, NULL))) {
XSetLocaleModifiers("@im=");
- if (!(impl->xim = XOpenIM(display, NULL, NULL, NULL))) {
- fprintf(stderr, "warning: XOpenIM failed\n");
- }
+ impl->xim = XOpenIM(display, NULL, NULL, NULL);
}
+ puglInitXSync(impl);
XFlush(display);
return impl;
}
+void*
+puglGetNativeWorld(PuglWorld* world)
+{
+ return world->impl->display;
+}
+
PuglInternals*
puglInitViewInternals(void)
{
return (PuglInternals*)calloc(1, sizeof(PuglInternals));
}
-PuglStatus
-puglPollEvents(PuglWorld* world, const double timeout)
+static PuglStatus
+puglPollX11Socket(PuglWorld* world, const double timeout)
{
- XFlush(world->impl->display);
+ if (XPending(world->impl->display) > 0) {
+ return PUGL_SUCCESS;
+ }
const int fd = ConnectionNumber(world->impl->display);
const int nfds = fd + 1;
int ret = 0;
fd_set fds;
- FD_ZERO(&fds);
+ FD_ZERO(&fds); // NOLINT
FD_SET(fd, &fds);
if (timeout < 0.0) {
ret = select(nfds, &fds, NULL, NULL, NULL);
} else {
const long sec = (long)timeout;
- const long msec = (long)((timeout - (double)sec) * 1e6);
- struct timeval tv = {sec, msec};
+ const long usec = (long)((timeout - (double)sec) * 1e6);
+ struct timeval tv = {sec, usec};
ret = select(nfds, &fds, NULL, NULL, &tv);
}
- return ret < 0 ? PUGL_UNKNOWN_ERROR
- : ret == 0 ? PUGL_FAILURE : PUGL_SUCCESS;
+ return ret < 0 ? PUGL_UNKNOWN_ERROR : PUGL_SUCCESS;
}
static PuglView*
@@ -179,8 +227,6 @@ puglCreateWindow(PuglView* view, const char* title)
PuglWorld* const world = view->world;
PuglX11Atoms* const atoms = &view->world->impl->atoms;
Display* const display = world->impl->display;
- const int width = (int)view->frame.width;
- const int height = (int)view->frame.height;
impl->display = display;
impl->screen = DefaultScreen(display);
@@ -207,7 +253,8 @@ puglCreateWindow(PuglView* view, const char* title)
const Window win = impl->win = XCreateWindow(
display, xParent,
- (int)view->frame.x, (int)view->frame.y, width, height,
+ (int)view->frame.x, (int)view->frame.y,
+ (unsigned)view->frame.width, (unsigned)view->frame.height,
0, impl->vi->depth, InputOutput,
impl->vi->visual, CWColormap | CWEventMask, &attr);
@@ -240,9 +287,13 @@ puglCreateWindow(PuglView* view, const char* title)
XNClientWindow, win,
XNFocusWindow, win,
NULL))) {
- fprintf(stderr, "warning: XCreateIC failed\n");
+ view->world->logFunc(view->world,
+ PUGL_LOG_LEVEL_WARNING,
+ "XCreateID failed\n");
}
+ puglDispatchSimpleEvent(view, PUGL_CREATE);
+
return PUGL_SUCCESS;
}
@@ -251,7 +302,6 @@ puglShowWindow(PuglView* view)
{
XMapRaised(view->impl->display, view->impl->win);
puglPostRedisplay(view);
- view->visible = true;
return PUGL_SUCCESS;
}
@@ -259,19 +309,22 @@ PuglStatus
puglHideWindow(PuglView* view)
{
XUnmapWindow(view->impl->display, view->impl->win);
- view->visible = false;
return PUGL_SUCCESS;
}
void
puglFreeViewInternals(PuglView* view)
{
- if (view) {
+ if (view && view->impl) {
if (view->impl->xic) {
XDestroyIC(view->impl->xic);
}
- view->backend->destroy(view);
- XDestroyWindow(view->impl->display, view->impl->win);
+ if (view->backend) {
+ view->backend->destroy(view);
+ }
+ if (view->impl->display) {
+ XDestroyWindow(view->impl->display, view->impl->win);
+ }
XFree(view->impl->vi);
free(view->impl);
}
@@ -284,6 +337,7 @@ puglFreeWorldInternals(PuglWorld* world)
XCloseIM(world->impl->xim);
}
XCloseDisplay(world->impl->display);
+ free(world->impl->timers);
free(world->impl);
}
@@ -291,11 +345,6 @@ static PuglKey
keySymToSpecial(KeySym sym)
{
switch (sym) {
- case XK_BackSpace: return PUGL_KEY_BACKSPACE;
- case XK_Tab: return PUGL_KEY_TAB;
- case XK_Return: return PUGL_KEY_RETURN;
- case XK_Escape: return PUGL_KEY_ESCAPE;
- case XK_Delete: return PUGL_KEY_DELETE;
case XK_F1: return PUGL_KEY_F1;
case XK_F2: return PUGL_KEY_F2;
case XK_F3: return PUGL_KEY_F3;
@@ -332,7 +381,7 @@ keySymToSpecial(KeySym sym)
case XK_Num_Lock: return PUGL_KEY_NUM_LOCK;
case XK_Print: return PUGL_KEY_PRINT_SCREEN;
case XK_Pause: return PUGL_KEY_PAUSE;
- default: break;
+ default: break;
}
return (PuglKey)0;
}
@@ -402,7 +451,7 @@ translateEvent(PuglView* view, XEvent xevent)
{
const PuglX11Atoms* atoms = &view->world->impl->atoms;
- PuglEvent event = {0};
+ PuglEvent event = {{PUGL_NOTHING, 0}};
event.any.flags = xevent.xany.send_event ? PUGL_IS_SEND_EVENT : 0;
switch (xevent.type) {
@@ -412,18 +461,22 @@ translateEvent(PuglView* view, XEvent xevent)
if (protocol == atoms->WM_DELETE_WINDOW) {
event.type = PUGL_CLOSE;
}
+ } else if (xevent.xclient.message_type == atoms->PUGL_CLIENT_MSG) {
+ event.type = PUGL_CLIENT;
+ event.client.data1 = (uintptr_t)xevent.xclient.data.l[0];
+ event.client.data2 = (uintptr_t)xevent.xclient.data.l[1];
}
break;
- case MapNotify: {
- XWindowAttributes attrs = {0};
- XGetWindowAttributes(view->impl->display, view->impl->win, &attrs);
- event.type = PUGL_CONFIGURE;
- event.configure.x = attrs.x;
- event.configure.y = attrs.y;
- event.configure.width = attrs.width;
- event.configure.height = attrs.height;
+ case VisibilityNotify:
+ view->visible = xevent.xvisibility.state != VisibilityFullyObscured;
+ break;
+ case MapNotify:
+ event.type = PUGL_MAP;
+ break;
+ case UnmapNotify:
+ event.type = PUGL_UNMAP;
+ view->visible = false;
break;
- }
case ConfigureNotify:
event.type = PUGL_CONFIGURE;
event.configure.x = xevent.xconfigure.x;
@@ -440,7 +493,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.expose.count = xevent.xexpose.count;
break;
case MotionNotify:
- event.type = PUGL_MOTION_NOTIFY;
+ event.type = PUGL_MOTION;
event.motion.time = xevent.xmotion.time / 1e3;
event.motion.x = xevent.xmotion.x;
event.motion.y = xevent.xmotion.y;
@@ -499,8 +552,8 @@ translateEvent(PuglView* view, XEvent xevent)
case EnterNotify:
case LeaveNotify:
event.type = ((xevent.type == EnterNotify)
- ? PUGL_ENTER_NOTIFY
- : PUGL_LEAVE_NOTIFY);
+ ? PUGL_POINTER_IN
+ : PUGL_POINTER_OUT);
event.crossing.time = xevent.xcrossing.time / 1e3;
event.crossing.x = xevent.xcrossing.x;
event.crossing.y = xevent.xcrossing.y;
@@ -557,7 +610,7 @@ puglRequestAttention(PuglView* view)
event.xclient.format = 32;
event.xclient.message_type = atoms->NET_WM_STATE;
event.xclient.data.l[0] = WM_STATE_ADD;
- event.xclient.data.l[1] = atoms->NET_WM_STATE_DEMANDS_ATTENTION;
+ event.xclient.data.l[1] = (long)atoms->NET_WM_STATE_DEMANDS_ATTENTION;
event.xclient.data.l[2] = 0;
event.xclient.data.l[3] = 1;
event.xclient.data.l[4] = 0;
@@ -573,15 +626,148 @@ puglRequestAttention(PuglView* view)
}
PuglStatus
+puglStartTimer(PuglView* view, uintptr_t id, double timeout)
+{
+#ifdef HAVE_XSYNC
+ if (view->world->impl->syncSupported) {
+ XSyncValue value;
+ XSyncIntToValue(&value, (int)floor(timeout * 1000.0));
+
+ PuglWorldInternals* w = view->world->impl;
+ Display* const display = w->display;
+ const XSyncCounter counter = w->serverTimeCounter;
+ const XSyncTrigger trigger = {counter, XSyncRelative, value, 0};
+ XSyncAlarmAttributes attr = {trigger, value, True, XSyncAlarmActive};
+ const XSyncAlarm alarm = XSyncCreateAlarm(display, 0x17, &attr);
+ const PuglTimer timer = {alarm, view, id};
+
+ if (alarm != None) {
+ for (size_t i = 0; i < w->numTimers; ++i) {
+ if (w->timers[i].view == view && w->timers[i].id == id) {
+ // Replace existing timer
+ XSyncDestroyAlarm(w->display, w->timers[i].alarm);
+ w->timers[i] = timer;
+ return PUGL_SUCCESS;
+ }
+ }
+
+ // Add new timer
+ const size_t size = ++w->numTimers * sizeof(timer);
+ w->timers = (PuglTimer*)realloc(w->timers, size);
+ w->timers[w->numTimers - 1] = timer;
+ return PUGL_SUCCESS;
+ }
+ }
+#else
+ (void)view;
+ (void)id;
+ (void)timeout;
+#endif
+
+ return PUGL_FAILURE;
+}
+
+PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id)
+{
+#ifdef HAVE_XSYNC
+ PuglWorldInternals* w = view->world->impl;
+
+ for (size_t i = 0; i < w->numTimers; ++i) {
+ if (w->timers[i].view == view && w->timers[i].id == id) {
+ XSyncDestroyAlarm(w->display, w->timers[i].alarm);
+
+ if (i == w->numTimers - 1) {
+ memset(&w->timers[i], 0, sizeof(PuglTimer));
+ } else {
+ memmove(w->timers + i,
+ w->timers + i + 1,
+ sizeof(PuglTimer) * (w->numTimers - i - 1));
+
+ memset(&w->timers[i], 0, sizeof(PuglTimer));
+ }
+
+ --w->numTimers;
+ return PUGL_SUCCESS;
+ }
+ }
+#else
+ (void)view;
+ (void)id;
+#endif
+
+ return PUGL_FAILURE;
+}
+
+static XEvent
+puglEventToX(PuglView* view, const PuglEvent* event)
+{
+ XEvent xev = {0};
+ xev.xany.send_event = True;
+
+ switch (event->type) {
+ case PUGL_EXPOSE: {
+ const double x = floor(event->expose.x);
+ const double y = floor(event->expose.y);
+ const double w = ceil(event->expose.x + event->expose.width) - x;
+ const double h = ceil(event->expose.y + event->expose.height) - y;
+
+ xev.xexpose.type = Expose;
+ xev.xexpose.serial = 0;
+ xev.xexpose.display = view->impl->display;
+ xev.xexpose.window = view->impl->win;
+ xev.xexpose.x = (int)x;
+ xev.xexpose.y = (int)y;
+ xev.xexpose.width = (int)w;
+ xev.xexpose.height = (int)h;
+ break;
+ }
+
+ case PUGL_CLIENT:
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.display = view->impl->display;
+ xev.xclient.window = view->impl->win;
+ xev.xclient.message_type = view->world->impl->atoms.PUGL_CLIENT_MSG;
+ xev.xclient.format = 32;
+ xev.xclient.data.l[0] = (long)event->client.data1;
+ xev.xclient.data.l[1] = (long)event->client.data2;
+ break;
+
+ default:
+ break;
+ }
+
+ return xev;
+}
+
+PuglStatus
+puglSendEvent(PuglView* view, const PuglEvent* event)
+{
+ XEvent xev = puglEventToX(view, event);
+
+ if (xev.type) {
+ if (XSendEvent(view->impl->display, view->impl->win, False, 0, &xev)) {
+ return PUGL_SUCCESS;
+ }
+ }
+
+ return PUGL_UNSUPPORTED_TYPE;
+}
+
+#ifndef PUGL_DISABLE_DEPRECATED
+PuglStatus
puglWaitForEvent(PuglView* view)
{
XEvent xevent;
XPeekEvent(view->impl->display, &xevent);
return PUGL_SUCCESS;
}
+#endif
static void
-merge_expose_events(PuglEvent* dst, const PuglEvent* src)
+mergeExposeEvents(PuglEvent* dst, const PuglEvent* src)
{
if (!dst->type) {
*dst = *src;
@@ -599,20 +785,140 @@ merge_expose_events(PuglEvent* dst, const PuglEvent* src)
}
}
-PUGL_API PuglStatus
-puglDispatchEvents(PuglWorld* world)
+static void
+handleSelectionNotify(const PuglWorld* world, PuglView* view)
+{
+ uint8_t* str = NULL;
+ Atom type = 0;
+ int fmt = 0;
+ unsigned long len = 0;
+ unsigned long left = 0;
+
+ XGetWindowProperty(world->impl->display,
+ view->impl->win,
+ XA_PRIMARY,
+ 0,
+ 0x1FFFFFFF,
+ False,
+ AnyPropertyType,
+ &type,
+ &fmt,
+ &len,
+ &left,
+ &str);
+
+ if (str && fmt == 8 && type == world->impl->atoms.UTF8_STRING &&
+ left == 0) {
+ puglSetBlob(&view->clipboard, str, len);
+ }
+
+ XFree(str);
+}
+
+static void
+handleSelectionRequest(const PuglWorld* world,
+ PuglView* view,
+ const XSelectionRequestEvent* request)
+{
+ XSelectionEvent note = {SelectionNotify,
+ request->serial,
+ False,
+ world->impl->display,
+ request->requestor,
+ request->selection,
+ request->target,
+ None,
+ request->time};
+
+ const char* type = NULL;
+ size_t len = 0;
+ const void* data = puglGetInternalClipboard(view, &type, &len);
+ if (data && request->selection == world->impl->atoms.CLIPBOARD &&
+ request->target == world->impl->atoms.UTF8_STRING) {
+ note.property = request->property;
+ XChangeProperty(world->impl->display,
+ note.requestor,
+ note.property,
+ note.target,
+ 8,
+ PropModeReplace,
+ (const uint8_t*)data,
+ (int)len);
+ } else {
+ note.property = None;
+ }
+
+ XSendEvent(world->impl->display, note.requestor, True, 0, (XEvent*)&note);
+}
+
+/// Flush pending configure and expose events for all views
+static void
+flushExposures(PuglWorld* world)
+{
+ for (size_t i = 0; i < world->numViews; ++i) {
+ PuglView* const view = world->views[i];
+
+ if (view->visible) {
+ puglDispatchSimpleEvent(view, PUGL_UPDATE);
+ }
+
+ const PuglEvent configure = view->impl->pendingConfigure;
+ const PuglEvent expose = view->impl->pendingExpose;
+
+ view->impl->pendingConfigure.type = PUGL_NOTHING;
+ view->impl->pendingExpose.type = PUGL_NOTHING;
+
+ if (configure.type || expose.type) {
+ view->backend->enter(view, expose.type ? &expose.expose : NULL);
+ puglDispatchEventInContext(view, &configure);
+ puglDispatchEventInContext(view, &expose);
+ view->backend->leave(view, expose.type ? &expose.expose : NULL);
+ }
+ }
+}
+
+static bool
+handleTimerEvent(PuglWorld* world, XEvent xevent)
+{
+#ifdef HAVE_XSYNC
+ if (xevent.type == world->impl->syncEventBase + XSyncAlarmNotify) {
+ XSyncAlarmNotifyEvent* notify = ((XSyncAlarmNotifyEvent*)&xevent);
+
+ for (size_t i = 0; i < world->impl->numTimers; ++i) {
+ if (world->impl->timers[i].alarm == notify->alarm) {
+ const PuglEventTimer ev = {PUGL_TIMER, 0, world->impl->timers[i].id};
+ puglDispatchEvent(world->impl->timers[i].view, (const PuglEvent*)&ev);
+ }
+ }
+
+ return true;
+ }
+#else
+ (void)world;
+ (void)xevent;
+#endif
+
+ return false;
+}
+
+static PuglStatus
+puglDispatchX11Events(PuglWorld* world)
{
const PuglX11Atoms* const atoms = &world->impl->atoms;
- // Flush just once at the start to fill event queue
+ // Flush output to the server once at the start
Display* display = world->impl->display;
XFlush(display);
- // Process all queued events (locally, without flushing or reading)
- while (XEventsQueued(display, QueuedAlready) > 0) {
+ // Process all queued events (without further flushing)
+ while (XEventsQueued(display, QueuedAfterReading) > 0) {
XEvent xevent;
XNextEvent(display, &xevent);
+ if (handleTimerEvent(world, xevent)) {
+ continue;
+ }
+
PuglView* view = puglFindView(world, xevent.xany.window);
if (!view) {
continue;
@@ -634,53 +940,13 @@ puglDispatchEvents(PuglWorld* world)
XUnsetICFocus(impl->xic);
} else if (xevent.type == SelectionClear) {
puglSetBlob(&view->clipboard, NULL, 0);
- continue;
} else if (xevent.type == SelectionNotify &&
xevent.xselection.selection == atoms->CLIPBOARD &&
xevent.xselection.target == atoms->UTF8_STRING &&
xevent.xselection.property == XA_PRIMARY) {
-
- uint8_t* str = NULL;
- Atom type = 0;
- int fmt = 0;
- unsigned long len = 0;
- unsigned long left = 0;
- XGetWindowProperty(impl->display, impl->win, XA_PRIMARY,
- 0, 8, False, AnyPropertyType,
- &type, &fmt, &len, &left, &str);
-
- if (str && fmt == 8 && type == atoms->UTF8_STRING && left == 0) {
- puglSetBlob(&view->clipboard, str, len);
- }
-
- XFree(str);
- continue;
+ handleSelectionNotify(world, view);
} else if (xevent.type == SelectionRequest) {
- const XSelectionRequestEvent* request = &xevent.xselectionrequest;
-
- XSelectionEvent note = {0};
- note.type = SelectionNotify;
- note.requestor = request->requestor;
- note.selection = request->selection;
- note.target = request->target;
- note.time = request->time;
-
- const char* type = NULL;
- size_t len = 0;
- const void* data = puglGetInternalClipboard(view, &type, &len);
- if (data &&
- request->selection == atoms->CLIPBOARD &&
- request->target == atoms->UTF8_STRING) {
- note.property = request->property;
- XChangeProperty(impl->display, note.requestor,
- note.property, note.target, 8, PropModeReplace,
- (const uint8_t*)data, len);
- } else {
- note.property = None;
- }
-
- XSendEvent(impl->display, note.requestor, True, 0, (XEvent*)&note);
- continue;
+ handleSelectionRequest(world, view, &xevent.xselectionrequest);
}
// Translate X11 event to Pugl event
@@ -688,61 +954,68 @@ puglDispatchEvents(PuglWorld* world)
if (event.type == PUGL_EXPOSE) {
// Expand expose event to be dispatched after loop
- merge_expose_events(&view->impl->pendingExpose, &event);
+ mergeExposeEvents(&view->impl->pendingExpose, &event);
} else if (event.type == PUGL_CONFIGURE) {
// Expand configure event to be dispatched after loop
view->impl->pendingConfigure = event;
+ view->frame.x = event.configure.x;
+ view->frame.y = event.configure.y;
+ view->frame.width = event.configure.width;
+ view->frame.height = event.configure.height;
+ } else if (event.type == PUGL_MAP && view->parent) {
+ XWindowAttributes attrs;
+ XGetWindowAttributes(view->impl->display, view->impl->win, &attrs);
+
+ const PuglEventConfigure configure = {
+ PUGL_CONFIGURE, 0, attrs.x, attrs.y, attrs.width, attrs.height};
+
+ puglDispatchEvent(view, (const PuglEvent*)&configure);
+ puglDispatchEvent(view, &event);
} else {
// Dispatch event to application immediately
puglDispatchEvent(view, &event);
}
}
- // Flush pending configure and expose events for all views
- for (size_t i = 0; i < world->numViews; ++i) {
- PuglView* const view = world->views[i];
- PuglEvent* const configure = &view->impl->pendingConfigure;
- PuglEvent* const expose = &view->impl->pendingExpose;
-
- if (view->redisplay)
- {
- expose->type = PUGL_EXPOSE;
- view->redisplay = false;
- }
+ return PUGL_SUCCESS;
+}
- if (configure->type || expose->type) {
- const bool mustExpose = expose->type && expose->expose.count == 0;
- puglEnterContext(view, mustExpose);
+#ifndef PUGL_DISABLE_DEPRECATED
+PuglStatus
+puglProcessEvents(PuglView* view)
+{
+ return puglUpdate(view->world, 0.0);
+}
+#endif
- if (configure->type) {
- view->frame.x = configure->configure.x;
- view->frame.y = configure->configure.y;
- view->frame.width = configure->configure.width;
- view->frame.height = configure->configure.height;
+PuglStatus
+puglUpdate(PuglWorld* world, double timeout)
+{
+ const double startTime = puglGetTime(world);
+ PuglStatus st = PUGL_SUCCESS;
- view->backend->resize(view,
- (int)view->frame.width,
- (int)view->frame.height);
- view->eventFunc(view, &view->impl->pendingConfigure);
- }
+ world->impl->dispatchingEvents = true;
- if (mustExpose) {
- view->eventFunc(view, &view->impl->pendingExpose);
+ if (timeout < 0.0) {
+ st = puglPollX11Socket(world, timeout);
+ st = st ? st : puglDispatchX11Events(world);
+ } else if (timeout == 0.0) {
+ st = puglDispatchX11Events(world);
+ } else {
+ const double endTime = startTime + timeout - 0.001;
+ for (double t = startTime; t < endTime; t = puglGetTime(world)) {
+ if ((st = puglPollX11Socket(world, endTime - t)) ||
+ (st = puglDispatchX11Events(world))) {
+ break;
}
-
- puglLeaveContext(view, mustExpose);
- configure->type = 0;
- expose->type = 0;
}
}
- return PUGL_SUCCESS;
-}
+ flushExposures(world);
-PuglStatus
-puglProcessEvents(PuglView* view)
-{
- return puglDispatchEvents(view->world);
+ world->impl->dispatchingEvents = false;
+
+ return st;
}
double
@@ -756,7 +1029,26 @@ puglGetTime(const PuglWorld* world)
PuglStatus
puglPostRedisplay(PuglView* view)
{
- view->redisplay = true;
+ const PuglRect rect = { 0, 0, view->frame.width, view->frame.height };
+
+ return puglPostRedisplayRect(view, rect);
+}
+
+PuglStatus
+puglPostRedisplayRect(PuglView* view, PuglRect rect)
+{
+ const PuglEventExpose event = {
+ PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height, 0
+ };
+
+ if (view->world->impl->dispatchingEvents) {
+ // Currently dispatching events, add/expand expose for the loop end
+ mergeExposeEvents(&view->impl->pendingExpose, (const PuglEvent*)&event);
+ } else if (view->visible) {
+ // Not dispatching events, send an X expose so we wake up next time
+ return puglSendEvent(view, (const PuglEvent*)&event);
+ }
+
return PUGL_SUCCESS;
}
@@ -787,9 +1079,9 @@ puglSetFrame(PuglView* view, const PuglRect frame)
view->frame = frame;
if (view->impl->win &&
- XMoveResizeWindow(view->world->impl->display, view->impl->win,
- (int)frame.x, (int)frame.y,
- (int)frame.width, (int)frame.height)) {
+ !XMoveResizeWindow(view->world->impl->display, view->impl->win,
+ (int)frame.x, (int)frame.y,
+ (unsigned)frame.width, (unsigned)frame.height)) {
return PUGL_UNKNOWN_ERROR;
}
@@ -872,8 +1164,7 @@ puglGetClipboard(PuglView* const view,
// Run event loop until data is received
while (!view->clipboard.data) {
- puglPollEvents(view->world, -1);
- puglDispatchEvents(view->world);
+ puglUpdate(view->world, -1.0);
}
}
@@ -897,3 +1188,18 @@ puglSetClipboard(PuglView* const view,
XSetSelectionOwner(impl->display, atoms->CLIPBOARD, impl->win, CurrentTime);
return PUGL_SUCCESS;
}
+
+const PuglBackend*
+puglStubBackend(void)
+{
+ static const PuglBackend backend = {
+ puglX11StubConfigure,
+ puglStubCreate,
+ puglStubDestroy,
+ puglStubEnter,
+ puglStubLeave,
+ puglStubGetContext,
+ };
+
+ return &backend;
+}
diff --git a/subprojects/d2tk/pugl/pugl/detail/x11.h b/subprojects/d2tk/pugl/pugl/detail/x11.h
index bfdbf60..6b7a150 100644
--- a/subprojects/d2tk/pugl/pugl/detail/x11.h
+++ b/subprojects/d2tk/pugl/pugl/detail/x11.h
@@ -18,25 +18,44 @@
@file x11.h Shared definitions for X11 implementation.
*/
-#include "pugl/detail/implementation.h"
+#include "pugl/detail/types.h"
+#include "pugl/pugl.h"
+#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
typedef struct {
Atom CLIPBOARD;
Atom UTF8_STRING;
Atom WM_PROTOCOLS;
Atom WM_DELETE_WINDOW;
+ Atom PUGL_CLIENT_MSG;
Atom NET_WM_NAME;
Atom NET_WM_STATE;
Atom NET_WM_STATE_DEMANDS_ATTENTION;
} PuglX11Atoms;
+typedef struct {
+ XID alarm;
+ PuglView* view;
+ uint64_t id;
+} PuglTimer;
+
struct PuglWorldInternalsImpl {
Display* display;
PuglX11Atoms atoms;
XIM xim;
+ PuglTimer* timers;
+ size_t numTimers;
+ XID serverTimeCounter;
+ int syncEventBase;
+ bool syncSupported;
+ bool dispatchingEvents;
};
struct PuglInternalsImpl {
@@ -49,3 +68,16 @@ struct PuglInternalsImpl {
PuglEvent pendingConfigure;
PuglEvent pendingExpose;
};
+
+static inline PuglStatus
+puglX11StubConfigure(PuglView* view)
+{
+ PuglInternals* const impl = view->impl;
+ XVisualInfo pat = {0};
+ int n = 0;
+
+ pat.screen = impl->screen;
+ impl->vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n);
+
+ return PUGL_SUCCESS;
+}
diff --git a/subprojects/d2tk/pugl/pugl/detail/x11_cairo.c b/subprojects/d2tk/pugl/pugl/detail/x11_cairo.c
index 0753317..0229d97 100644
--- a/subprojects/d2tk/pugl/pugl/detail/x11_cairo.c
+++ b/subprojects/d2tk/pugl/pugl/detail/x11_cairo.c
@@ -21,132 +21,128 @@
#include "pugl/detail/types.h"
#include "pugl/detail/x11.h"
#include "pugl/pugl.h"
-#include "pugl/pugl_cairo_backend.h"
+#include "pugl/pugl_cairo.h"
#include <X11/Xutil.h>
#include <cairo-xlib.h>
#include <cairo.h>
-#include <stdbool.h>
-#include <stdio.h>
#include <stdlib.h>
-typedef struct {
+typedef struct {
cairo_surface_t* back;
- cairo_t* backCr;
cairo_surface_t* front;
- cairo_t* frontCr;
+ cairo_t* cr;
} PuglX11CairoSurface;
-static PuglStatus
-puglX11CairoConfigure(PuglView* view)
+static void
+puglX11CairoClose(PuglView* view)
{
- PuglInternals* const impl = view->impl;
-
- XVisualInfo pat;
- int n;
- pat.screen = impl->screen;
- impl->vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n);
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- return PUGL_SUCCESS;
+ cairo_surface_destroy(surface->front);
+ cairo_surface_destroy(surface->back);
+ surface->front = surface->back = NULL;
}
static PuglStatus
-puglX11CairoCreate(PuglView* view)
+puglX11CairoOpen(PuglView* view)
{
- PuglInternals* const impl = view->impl;
- const int width = (int)view->frame.width;
- const int height = (int)view->frame.height;
- PuglX11CairoSurface surface = { 0 };
-
- surface.back = cairo_xlib_surface_create(
- impl->display, impl->win, impl->vi->visual, width, height);
- surface.front = cairo_surface_create_similar(
- surface.back, CAIRO_CONTENT_COLOR, width, height);
- surface.backCr = cairo_create(surface.back);
- surface.frontCr = cairo_create(surface.front);
-
- cairo_status_t st = CAIRO_STATUS_SUCCESS;
- if (!surface.back || !surface.backCr ||
- !surface.front || !surface.frontCr ||
- (st = cairo_surface_status(surface.back)) ||
- (st = cairo_surface_status(surface.front)) ||
- (st = cairo_status(surface.backCr)) ||
- (st = cairo_status(surface.frontCr))) {
- cairo_destroy(surface.frontCr);
- cairo_destroy(surface.backCr);
- cairo_surface_destroy(surface.front);
- cairo_surface_destroy(surface.back);
+ PuglInternals* const impl = view->impl;
+ PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+
+ surface->back = cairo_xlib_surface_create(impl->display,
+ impl->win,
+ impl->vi->visual,
+ (int)view->frame.width,
+ (int)view->frame.height);
+
+ surface->front = cairo_surface_create_similar(
+ surface->back,
+ cairo_surface_get_content(surface->back),
+ (int)view->frame.width,
+ (int)view->frame.height);
+
+ if (cairo_surface_status(surface->back) ||
+ cairo_surface_status(surface->front)) {
+ puglX11CairoClose(view);
return PUGL_CREATE_CONTEXT_FAILED;
}
- impl->surface = calloc(1, sizeof(PuglX11CairoSurface));
- *(PuglX11CairoSurface*)impl->surface = surface;
-
return PUGL_SUCCESS;
}
static PuglStatus
-puglX11CairoDestroy(PuglView* view)
+puglX11CairoCreate(PuglView* view)
{
- PuglInternals* const impl = view->impl;
- PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+ PuglInternals* const impl = view->impl;
+
+ impl->surface = (cairo_surface_t*)calloc(1, sizeof(PuglX11CairoSurface));
- cairo_destroy(surface->frontCr);
- cairo_destroy(surface->backCr);
- cairo_surface_destroy(surface->front);
- cairo_surface_destroy(surface->back);
- free(surface);
- impl->surface = NULL;
return PUGL_SUCCESS;
}
static PuglStatus
-puglX11CairoEnter(PuglView* view, bool drawing)
+puglX11CairoDestroy(PuglView* view)
{
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- if (drawing) {
- cairo_save(surface->frontCr);
- }
+ puglX11CairoClose(view);
+ free(surface);
return PUGL_SUCCESS;
}
static PuglStatus
-puglX11CairoLeave(PuglView* view, bool drawing)
+puglX11CairoEnter(PuglView* view, const PuglEventExpose* expose)
{
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
+ PuglStatus st = PUGL_SUCCESS;
+
+ if (expose && !(st = puglX11CairoOpen(view))) {
+ surface->cr = cairo_create(surface->front);
- if (drawing) {
- cairo_set_source_surface(surface->backCr, surface->front, 0, 0);
- cairo_paint(surface->backCr);
- cairo_restore(surface->frontCr);
+ if (cairo_status(surface->cr)) {
+ st = PUGL_CREATE_CONTEXT_FAILED;
+ }
}
- return PUGL_SUCCESS;
+ return st;
}
static PuglStatus
-puglX11CairoResize(PuglView* view, int width, int height)
+puglX11CairoLeave(PuglView* view, const PuglEventExpose* expose)
{
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- cairo_xlib_surface_set_size(surface->back, width, height);
-
- cairo_destroy(surface->frontCr);
- cairo_surface_destroy(surface->front);
- if (!(surface->front = cairo_surface_create_similar(
- surface->back, CAIRO_CONTENT_COLOR, width, height))) {
- return PUGL_CREATE_CONTEXT_FAILED;
+ if (expose) {
+ // Destroy front context and create a new one for drawing to the back
+ cairo_destroy(surface->cr);
+ surface->cr = cairo_create(surface->back);
+
+ // Clip to expose region
+ cairo_rectangle(surface->cr,
+ expose->x,
+ expose->y,
+ expose->width,
+ expose->height);
+ cairo_clip(surface->cr);
+
+ // Paint front onto back
+ cairo_set_source_surface(surface->cr, surface->front, 0, 0);
+ cairo_paint(surface->cr);
+
+ // Flush to X and close everything
+ cairo_destroy(surface->cr);
+ cairo_surface_flush(surface->back);
+ puglX11CairoClose(view);
+ surface->cr = NULL;
}
- surface->frontCr = cairo_create(surface->front);
- cairo_save(surface->frontCr);
-
return PUGL_SUCCESS;
}
@@ -156,21 +152,18 @@ puglX11CairoGetContext(PuglView* view)
PuglInternals* const impl = view->impl;
PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface;
- return surface->frontCr;
+ return surface->cr;
}
const PuglBackend*
puglCairoBackend(void)
{
- static const PuglBackend backend = {
- puglX11CairoConfigure,
- puglX11CairoCreate,
- puglX11CairoDestroy,
- puglX11CairoEnter,
- puglX11CairoLeave,
- puglX11CairoResize,
- puglX11CairoGetContext
- };
+ static const PuglBackend backend = {puglX11StubConfigure,
+ puglX11CairoCreate,
+ puglX11CairoDestroy,
+ puglX11CairoEnter,
+ puglX11CairoLeave,
+ puglX11CairoGetContext};
return &backend;
}
diff --git a/subprojects/d2tk/pugl/pugl/detail/x11_gl.c b/subprojects/d2tk/pugl/pugl/detail/x11_gl.c
index f5dbe2c..33a05df 100644
--- a/subprojects/d2tk/pugl/pugl/detail/x11_gl.c
+++ b/subprojects/d2tk/pugl/pugl/detail/x11_gl.c
@@ -21,15 +21,15 @@
#include "pugl/detail/types.h"
#include "pugl/detail/x11.h"
#include "pugl/pugl.h"
-#include "pugl/pugl_gl_backend.h"
+#include "pugl/pugl_gl.h"
+#include "pugl/pugl_stub.h"
-#include <GL/gl.h>
#include <GL/glx.h>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
-#include <stdbool.h>
+#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
@@ -39,16 +39,16 @@ typedef struct {
int double_buffered;
} PuglX11GlSurface;
-static PuglStatus
+static int
puglX11GlHintValue(const int value)
{
return value == PUGL_DONT_CARE ? (int)GLX_DONT_CARE : value;
}
-static PuglStatus
-puglX11GlGetAttrib(Display* const display,
- const GLXFBConfig fb_config,
- const int attrib)
+static int
+puglX11GlGetAttrib(Display* const display,
+ GLXFBConfig fb_config,
+ const int attrib)
{
int value = 0;
glXGetFBConfigAttrib(display, fb_config, attrib, &value);
@@ -85,23 +85,28 @@ puglX11GlConfigure(PuglView* view)
int n_fbc = 0;
GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc);
if (n_fbc <= 0) {
- fprintf(stderr, "error: Failed to create GL context\n");
return PUGL_CREATE_CONTEXT_FAILED;
}
surface->fb_config = fbc[0];
impl->vi = glXGetVisualFromFBConfig(impl->display, fbc[0]);
- printf("Using visual 0x%lX: R=%d G=%d B=%d A=%d D=%d"
- " DOUBLE=%d SAMPLES=%d\n",
- impl->vi->visualid,
- puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE),
- puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE),
- puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE),
- puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE),
- puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE),
- puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER),
- puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES));
+ char msg[128];
+
+ snprintf(
+ msg,
+ sizeof(msg),
+ "Using visual 0x%lX: R=%d G=%d B=%d A=%d D=%d DOUBLE=%d SAMPLES=%d\n",
+ impl->vi->visualid,
+ puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE),
+ puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER),
+ puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES));
+
+ view->world->logFunc(view->world, PUGL_LOG_LEVEL_INFO, msg);
XFree(fbc);
@@ -114,7 +119,7 @@ puglX11GlCreate(PuglView* view)
PuglInternals* const impl = view->impl;
PuglX11GlSurface* const surface = (PuglX11GlSurface*)impl->surface;
Display* const display = impl->display;
- const GLXFBConfig fb_config = surface->fb_config;
+ GLXFBConfig fb_config = surface->fb_config;
const int ctx_attrs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MAJOR],
@@ -127,14 +132,15 @@ puglX11GlCreate(PuglView* view)
: GLX_CONTEXT_CORE_PROFILE_BIT_ARB),
0};
- typedef GLXContext (*CreateContextAttribs)(
- Display*, GLXFBConfig, GLXContext, Bool, const int*);
+ PFNGLXCREATECONTEXTATTRIBSARBPROC create_context =
+ (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress(
+ (const uint8_t*)"glXCreateContextAttribsARB");
- CreateContextAttribs create_context =
- (CreateContextAttribs)glXGetProcAddress(
- (const GLubyte*)"glXCreateContextAttribsARB");
+ PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT =
+ (PFNGLXSWAPINTERVALEXTPROC) glXGetProcAddress(
+ (const uint8_t*)"glXSwapIntervalEXT");
- surface->ctx = create_context(display, fb_config, 0, GL_TRUE, ctx_attrs);
+ surface->ctx = create_context(display, fb_config, 0, True, ctx_attrs);
if (!surface->ctx) {
surface->ctx =
glXCreateNewContext(display, fb_config, GLX_RGBA_TYPE, 0, True);
@@ -144,6 +150,11 @@ puglX11GlCreate(PuglView* view)
return PUGL_CREATE_CONTEXT_FAILED;
}
+ const int swapInterval = view->hints[PUGL_SWAP_INTERVAL];
+ if (glXSwapIntervalEXT && swapInterval != PUGL_DONT_CARE) {
+ glXSwapIntervalEXT(display, impl->win, swapInterval);
+ }
+
glXGetConfig(impl->display,
impl->vi,
GLX_DOUBLEBUFFER,
@@ -165,7 +176,7 @@ puglX11GlDestroy(PuglView* view)
}
static PuglStatus
-puglX11GlEnter(PuglView* view, bool PUGL_UNUSED(drawing))
+puglX11GlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
{
PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
@@ -173,14 +184,12 @@ puglX11GlEnter(PuglView* view, bool PUGL_UNUSED(drawing))
}
static PuglStatus
-puglX11GlLeave(PuglView* view, bool drawing)
+puglX11GlLeave(PuglView* view, const PuglEventExpose* expose)
{
PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
- if (drawing && surface->double_buffered) {
+ if (expose && surface->double_buffered) {
glXSwapBuffers(view->impl->display, view->impl->win);
- } else if (drawing) {
- glFlush();
}
glXMakeCurrent(view->impl->display, None, NULL);
@@ -188,37 +197,20 @@ puglX11GlLeave(PuglView* view, bool drawing)
return PUGL_SUCCESS;
}
-static PuglStatus
-puglX11GlResize(PuglView* PUGL_UNUSED(view),
- int PUGL_UNUSED(width),
- int PUGL_UNUSED(height))
-{
- return PUGL_SUCCESS;
-}
-
-static void*
-puglX11GlGetContext(PuglView* PUGL_UNUSED(view))
-{
- return NULL;
-}
-
PuglGlFunc
puglGetProcAddress(const char* name)
{
- return glXGetProcAddress((const GLubyte*)name);
+ return glXGetProcAddress((const uint8_t*)name);
}
const PuglBackend* puglGlBackend(void)
{
- static const PuglBackend backend = {
- puglX11GlConfigure,
- puglX11GlCreate,
- puglX11GlDestroy,
- puglX11GlEnter,
- puglX11GlLeave,
- puglX11GlResize,
- puglX11GlGetContext
- };
+ static const PuglBackend backend = {puglX11GlConfigure,
+ puglX11GlCreate,
+ puglX11GlDestroy,
+ puglX11GlEnter,
+ puglX11GlLeave,
+ puglStubGetContext};
return &backend;
}
diff --git a/subprojects/d2tk/pugl/pugl/pugl.h b/subprojects/d2tk/pugl/pugl/pugl.h
index 1299fa7..f1ddfdb 100644
--- a/subprojects/d2tk/pugl/pugl/pugl.h
+++ b/subprojects/d2tk/pugl/pugl/pugl.h
@@ -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
@@ -15,11 +15,11 @@
*/
/**
- @file pugl.h Public C API.
+ @file pugl.h Pugl API.
*/
-#ifndef PUGL_H_INCLUDED
-#define PUGL_H_INCLUDED
+#ifndef PUGL_PUGL_H
+#define PUGL_PUGL_H
#include <stdbool.h>
#include <stddef.h>
@@ -42,68 +42,31 @@
# define PUGL_API
#endif
-#ifdef __cplusplus
-extern "C" {
+#ifndef PUGL_DISABLE_DEPRECATED
+# if defined(__clang__)
+# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("", rep)))
+# elif defined(__GNUC__)
+# define PUGL_DEPRECATED_BY(rep) __attribute__((deprecated("Use " rep)))
+# else
+# define PUGL_DEPRECATED_BY(rep)
+# endif
#endif
-/**
- @defgroup pugl Pugl
- Pugl C API.
- @{
-*/
-
-/**
- Handle for opaque user data.
-*/
-typedef void* PuglHandle;
-
-/**
- Return status code.
-*/
-typedef enum {
- PUGL_SUCCESS, /**< Success */
- PUGL_FAILURE, /**< Non-fatal failure */
- PUGL_UNKNOWN_ERROR, /**< Unknown system error */
- PUGL_BAD_BACKEND, /**< Invalid or missing backend */
- PUGL_BACKEND_FAILED, /**< Backend initialisation failed */
- PUGL_REGISTRATION_FAILED, /**< Window class registration failed */
- PUGL_CREATE_WINDOW_FAILED, /**< Window creation failed */
- PUGL_SET_FORMAT_FAILED, /**< Failed to set pixel format */
- PUGL_CREATE_CONTEXT_FAILED, /**< Failed to create drawing context */
- PUGL_UNSUPPORTED_TYPE, /**< Unsupported data type */
-} PuglStatus;
-
-/**
- Window hint.
-*/
-typedef enum {
- PUGL_USE_COMPAT_PROFILE, /**< Use compatible (not core) OpenGL profile */
- PUGL_USE_DEBUG_CONTEXT, /**< True to use a debug OpenGL context */
- PUGL_CONTEXT_VERSION_MAJOR, /**< OpenGL context major version */
- PUGL_CONTEXT_VERSION_MINOR, /**< OpenGL context minor version */
- PUGL_RED_BITS, /**< Number of bits for red channel */
- PUGL_GREEN_BITS, /**< Number of bits for green channel */
- PUGL_BLUE_BITS, /**< Number of bits for blue channel */
- PUGL_ALPHA_BITS, /**< Number of bits for alpha channel */
- PUGL_DEPTH_BITS, /**< Number of bits for depth buffer */
- PUGL_STENCIL_BITS, /**< Number of bits for stencil buffer */
- PUGL_SAMPLES, /**< Number of samples per pixel (AA) */
- PUGL_DOUBLE_BUFFER, /**< True if double buffering should be used */
- PUGL_SWAP_INTERVAL, /**< Number of frames between buffer swaps */
- PUGL_RESIZABLE, /**< True if window should be resizable */
- PUGL_IGNORE_KEY_REPEAT, /**< True if key repeat events are ignored */
+#ifdef __cplusplus
+# define PUGL_BEGIN_DECLS extern "C" {
+# define PUGL_END_DECLS }
+#else
+# define PUGL_BEGIN_DECLS
+# define PUGL_END_DECLS
+#endif
- PUGL_NUM_WINDOW_HINTS
-} PuglViewHint;
+PUGL_BEGIN_DECLS
/**
- Special window hint value.
+ @defgroup pugl_api Pugl
+ A minimal portable API for embeddable GUIs.
+ @{
*/
-typedef enum {
- PUGL_DONT_CARE = -1, /**< Use best available value */
- PUGL_FALSE = 0, /**< Explicitly false */
- PUGL_TRUE = 1 /**< Explicitly true */
-} PuglViewHintValue;
/**
A rectangle.
@@ -119,32 +82,50 @@ typedef struct {
} PuglRect;
/**
+ @defgroup events Events
+
+ 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
+ underlying window system, but some are constructed by Pugl itself so there
+ is not necessarily a direct correspondence.
+
+ @{
+*/
+
+/**
Keyboard modifier flags.
*/
typedef enum {
- PUGL_MOD_SHIFT = 1, /**< Shift key */
- PUGL_MOD_CTRL = 1 << 1, /**< Control key */
- PUGL_MOD_ALT = 1 << 2, /**< Alt/Option key */
- PUGL_MOD_SUPER = 1 << 3 /**< Mod4/Command/Windows key */
+ PUGL_MOD_SHIFT = 1, ///< Shift key
+ PUGL_MOD_CTRL = 1 << 1, ///< Control key
+ PUGL_MOD_ALT = 1 << 2, ///< Alt/Option key
+ PUGL_MOD_SUPER = 1 << 3 ///< Mod4/Command/Windows key
} PuglMod;
/**
- Special keyboard keys.
+ Bitwise OR of #PuglMod values.
+*/
+typedef uint32_t PuglMods;
+
+/**
+ Keyboard key codepoints.
- All keys, special or not, are expressed as a Unicode code point. This
+ All keys are identified by a Unicode code point in PuglEventKey::key. This
enumeration defines constants for special keys that do not have a standard
- code point, and some convenience constants for control characters.
+ code point, and some convenience constants for control characters. Note
+ that all keys are handled in the same way, this enumeration is just for
+ convenience when writing hard-coded key bindings.
Keys that do not have a standard code point use values in the Private Use
- Area in the Basic Multilingual Plane (U+E000 to U+F8FF). Applications must
- take care to not interpret these values beyond key detection, the mapping
- used here is arbitrary and specific to Pugl.
+ Area in the Basic Multilingual Plane (`U+E000` to `U+F8FF`). Applications
+ must take care to not interpret these values beyond key detection, the
+ mapping used here is arbitrary and specific to Pugl.
*/
typedef enum {
// ASCII control codes
PUGL_KEY_BACKSPACE = 0x08,
- PUGL_KEY_TAB = 0x09,
- PUGL_KEY_RETURN = 0x0D,
PUGL_KEY_ESCAPE = 0x1B,
PUGL_KEY_DELETE = 0x7F,
@@ -194,248 +175,412 @@ typedef enum {
The type of a PuglEvent.
*/
typedef enum {
- PUGL_NOTHING, /**< No event */
- PUGL_BUTTON_PRESS, /**< Mouse button press */
- PUGL_BUTTON_RELEASE, /**< Mouse button release */
- PUGL_CONFIGURE, /**< View moved and/or resized */
- PUGL_EXPOSE, /**< View exposed, redraw required */
- PUGL_CLOSE, /**< Close view */
- PUGL_KEY_PRESS, /**< Key press */
- PUGL_KEY_RELEASE, /**< Key release */
- PUGL_TEXT, /**< Character entry */
- PUGL_ENTER_NOTIFY, /**< Pointer entered view */
- PUGL_LEAVE_NOTIFY, /**< Pointer left view */
- PUGL_MOTION_NOTIFY, /**< Pointer motion */
- PUGL_SCROLL, /**< Scroll */
- PUGL_FOCUS_IN, /**< Keyboard focus entered view */
- PUGL_FOCUS_OUT /**< Keyboard focus left view */
+ PUGL_NOTHING, ///< No event
+ PUGL_CREATE, ///< View created, a #PuglEventAny
+ PUGL_DESTROY, ///< View destroyed, a #PuglEventAny
+ PUGL_CONFIGURE, ///< View moved/resized, a #PuglEventConfigure
+ PUGL_MAP, ///< View made visible, a #PuglEventAny
+ PUGL_UNMAP, ///< View made invisible, a #PuglEventAny
+ PUGL_UPDATE, ///< View ready to draw, a #PuglEventAny
+ PUGL_EXPOSE, ///< View must be drawn, a #PuglEventExpose
+ PUGL_CLOSE, ///< View will be closed, a #PuglEventAny
+ PUGL_FOCUS_IN, ///< Keyboard focus entered view, a #PuglEventFocus
+ PUGL_FOCUS_OUT, ///< Keyboard focus left view, a #PuglEventFocus
+ PUGL_KEY_PRESS, ///< Key pressed, a #PuglEventKey
+ PUGL_KEY_RELEASE, ///< Key released, a #PuglEventKey
+ PUGL_TEXT, ///< Character entered, a #PuglEventText
+ PUGL_POINTER_IN, ///< Pointer entered view, a #PuglEventCrossing
+ PUGL_POINTER_OUT, ///< Pointer left view, a #PuglEventCrossing
+ PUGL_BUTTON_PRESS, ///< Mouse button pressed, a #PuglEventButton
+ PUGL_BUTTON_RELEASE, ///< Mouse button released, a #PuglEventButton
+ PUGL_MOTION, ///< Pointer moved, a #PuglEventMotion
+ PUGL_SCROLL, ///< Scrolled, a #PuglEventScroll
+ PUGL_CLIENT, ///< Custom client message, a #PuglEventClient
+ PUGL_TIMER, ///< Timer triggered, a #PuglEventTimer
+
+#ifndef PUGL_DISABLE_DEPRECATED
+ PUGL_ENTER_NOTIFY PUGL_DEPRECATED_BY("PUGL_POINTER_IN") = PUGL_POINTER_IN,
+ PUGL_LEAVE_NOTIFY PUGL_DEPRECATED_BY("PUGL_POINTER_OUT") = PUGL_POINTER_OUT,
+ PUGL_MOTION_NOTIFY PUGL_DEPRECATED_BY("PUGL_MOTION") = PUGL_MOTION,
+#endif
+
} PuglEventType;
+/**
+ Common flags for all event types.
+*/
typedef enum {
- PUGL_IS_SEND_EVENT = 1
+ PUGL_IS_SEND_EVENT = 1 ///< Event is synthetic
} PuglEventFlag;
/**
+ Bitwise OR of #PuglEventFlag values.
+*/
+typedef uint32_t PuglEventFlags;
+
+/**
Reason for a PuglEventCrossing.
*/
typedef enum {
- PUGL_CROSSING_NORMAL, /**< Crossing due to pointer motion. */
- PUGL_CROSSING_GRAB, /**< Crossing due to a grab. */
- PUGL_CROSSING_UNGRAB /**< Crossing due to a grab release. */
+ PUGL_CROSSING_NORMAL, ///< Crossing due to pointer motion
+ PUGL_CROSSING_GRAB, ///< Crossing due to a grab
+ PUGL_CROSSING_UNGRAB ///< Crossing due to a grab release
} PuglCrossingMode;
/**
Common header for all event structs.
*/
typedef struct {
- PuglEventType type; /**< Event type. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
+ PuglEventType type; ///< Event type
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
} PuglEventAny;
/**
- Button press or release event.
-
- For event types PUGL_BUTTON_PRESS and PUGL_BUTTON_RELEASE.
-*/
-typedef struct {
- PuglEventType type; /**< PUGL_BUTTON_PRESS or PUGL_BUTTON_RELEASE. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double time; /**< Time in seconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double xRoot; /**< Root-relative X coordinate. */
- double yRoot; /**< Root-relative Y coordinate. */
- uint32_t state; /**< Bitwise OR of PuglMod flags. */
- uint32_t button; /**< 1-relative button number. */
-} PuglEventButton;
+ Window resize or move event.
-/**
- Configure event for when window size or position has changed.
+ A configure event is sent whenever the window is resized or moved. When a
+ configure event is received, the graphics context is active but not set up
+ for drawing. For example, it is valid to adjust the OpenGL viewport or
+ otherwise configure the context, but not to draw anything.
*/
typedef struct {
- PuglEventType type; /**< PUGL_CONFIGURE. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double x; /**< New parent-relative X coordinate. */
- double y; /**< New parent-relative Y coordinate. */
- double width; /**< New width. */
- double height; /**< New height. */
+ PuglEventType type; ///< #PUGL_CONFIGURE
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double x; ///< New parent-relative X coordinate
+ double y; ///< New parent-relative Y coordinate
+ double width; ///< New width
+ double height; ///< New height
} PuglEventConfigure;
/**
Expose event for when a region must be redrawn.
+
+ When an expose event is received, the graphics context is active, and the
+ view must draw the entire specified region. The contents of the region are
+ undefined, there is no preservation of anything drawn previously.
*/
typedef struct {
- PuglEventType type; /**< PUGL_EXPOSE. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double width; /**< Width of exposed region. */
- double height; /**< Height of exposed region. */
- int count; /**< Number of expose events to follow. */
+ PuglEventType type; ///< #PUGL_EXPOSE
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double width; ///< Width of exposed region
+ double height; ///< Height of exposed region
+ int count; ///< Number of expose events to follow
} PuglEventExpose;
/**
- Window close event.
+ Keyboard focus event.
+
+ This event is sent whenever the view gains or loses the keyboard focus. The
+ view with the keyboard focus will receive any key press or release events.
*/
typedef struct {
- PuglEventType type; /**< PUGL_CLOSE. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
-} PuglEventClose;
+ PuglEventType type; ///< #PUGL_FOCUS_IN or #PUGL_FOCUS_OUT
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ bool grab; ///< True iff this is a grab/ungrab event
+} PuglEventFocus;
/**
- Key press/release event.
+ Key press or release event.
- This represents low-level key press and release events. This event type
- should be used for "raw" keyboard handing (key bindings, for example), but
- must not be interpreted as text input.
+ This event represents low-level key presses and releases. This can be used
+ for "direct" keyboard handing like key bindings, but must not be interpreted
+ as text input.
- Keys are represented as Unicode code points, using the "natural" code point
- for the key wherever possible (see @ref PuglKey for details). The `key`
- field will be set to the code for the pressed key, without any modifiers
- applied (by the shift or control keys).
+ Keys are represented portably as Unicode code points, using the "natural"
+ 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')
+ regardless of whether shift or control are being held.
+
+ 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.
*/
typedef struct {
- PuglEventType type; /**< PUGL_KEY_PRESS or PUGL_KEY_RELEASE. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double time; /**< Time in seconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double xRoot; /**< Root-relative X coordinate. */
- double yRoot; /**< Root-relative Y coordinate. */
- uint32_t state; /**< Bitwise OR of PuglMod flags. */
- uint32_t keycode; /**< Raw key code. */
- uint32_t key; /**< Unshifted Unicode character code, or 0. */
+ PuglEventType type; ///< #PUGL_KEY_PRESS or #PUGL_KEY_RELEASE
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ uint32_t keycode; ///< Raw key code
+ uint32_t key; ///< Unshifted Unicode character code, or 0
} PuglEventKey;
/**
Character input event.
- This represents text input, usually as the result of a key press. The text
- is given both as a Unicode character code and a UTF-8 string.
+ This event represents text input, usually as the result of a key press. The
+ text is given both as a Unicode character code and a UTF-8 string.
+
+ Note that this event is generated by the platform's input system, so there
+ is not necessarily a direct correspondence between text events and physical
+ key presses. For example, with some input methods a sequence of several key
+ presses will generate a single character.
*/
typedef struct {
- PuglEventType type; /**< PUGL_CHAR. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double time; /**< Time in seconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double xRoot; /**< Root-relative X coordinate. */
- double yRoot; /**< Root-relative Y coordinate. */
- uint32_t state; /**< Bitwise OR of PuglMod flags. */
- uint32_t keycode; /**< Raw key code. */
- uint32_t character; /**< Unicode character code */
- char string[8]; /**< UTF-8 string. */
+ PuglEventType type; ///< #PUGL_TEXT
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ uint32_t keycode; ///< Raw key code
+ uint32_t character; ///< Unicode character code
+ char string[8]; ///< UTF-8 string
} PuglEventText;
/**
- Pointer crossing event (enter and leave).
+ Pointer enter or leave event.
+
+ 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.
*/
typedef struct {
- PuglEventType type; /**< PUGL_ENTER_NOTIFY or PUGL_LEAVE_NOTIFY. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double time; /**< Time in seconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double xRoot; /**< Root-relative X coordinate. */
- double yRoot; /**< Root-relative Y coordinate. */
- uint32_t state; /**< Bitwise OR of PuglMod flags. */
- PuglCrossingMode mode; /**< Reason for crossing. */
+ PuglEventType type; ///< #PUGL_POINTER_IN or #PUGL_POINTER_OUT
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ PuglCrossingMode mode; ///< Reason for crossing
} PuglEventCrossing;
/**
+ Button press or release event.
+*/
+typedef struct {
+ PuglEventType type; ///< #PUGL_BUTTON_PRESS or #PUGL_BUTTON_RELEASE
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ uint32_t button; ///< Button number starting from 1
+} PuglEventButton;
+
+/**
Pointer motion event.
*/
typedef struct {
- PuglEventType type; /**< PUGL_MOTION_NOTIFY. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double time; /**< Time in seconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double xRoot; /**< Root-relative X coordinate. */
- double yRoot; /**< Root-relative Y coordinate. */
- uint32_t state; /**< Bitwise OR of PuglMod flags. */
- bool isHint; /**< True iff this event is a motion hint. */
- bool focus; /**< True iff this is the focused window. */
+ PuglEventType type; ///< #PUGL_MOTION
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ bool isHint; ///< True iff this event is a motion hint
+ bool focus; ///< True iff this is the focused window
} PuglEventMotion;
/**
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.
- */
+*/
typedef struct {
- PuglEventType type; /**< PUGL_SCROLL. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- double time; /**< Time in seconds. */
- double x; /**< View-relative X coordinate. */
- double y; /**< View-relative Y coordinate. */
- double xRoot; /**< Root-relative X coordinate. */
- double yRoot; /**< Root-relative Y coordinate. */
- uint32_t state; /**< Bitwise OR of PuglMod flags. */
- double dx; /**< Scroll X distance in lines. */
- double dy; /**< Scroll Y distance in lines. */
+ PuglEventType type; ///< #PUGL_SCROLL
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ double time; ///< Time in seconds
+ double x; ///< View-relative X coordinate
+ double y; ///< View-relative Y coordinate
+ double xRoot; ///< Root-relative X coordinate
+ double yRoot; ///< Root-relative Y coordinate
+ PuglMods state; ///< Bitwise OR of #PuglMod flags
+ double dx; ///< Scroll X distance in lines
+ double dy; ///< Scroll Y distance in lines
} PuglEventScroll;
/**
- Keyboard focus event.
+ Custom client message event.
+
+ This can be used to send a custom message to a view, which is delivered via
+ the window system and processed in the event loop as usual. Among other
+ things, this makes it possible to wake up the event loop for any reason.
*/
typedef struct {
- PuglEventType type; /**< PUGL_FOCUS_IN or PUGL_FOCUS_OUT. */
- uint32_t flags; /**< Bitwise OR of PuglEventFlag values. */
- bool grab; /**< True iff this is a grab/ungrab event. */
-} PuglEventFocus;
+ PuglEventType type; ///< #PUGL_CLIENT
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ uintptr_t data1; ///< Client-specific data
+ uintptr_t data2; ///< Client-specific data
+} PuglEventClient;
/**
- Interface event.
+ Timer event.
+
+ This event is sent at the regular interval specified in the call to
+ puglStartTimer() that activated it.
- This is a union of all event structs. 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.
+ 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.
+*/
+typedef struct {
+ PuglEventType type; ///< #PUGL_TIMER
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ uintptr_t id; ///< Timer ID
+} PuglEventTimer;
+
+/**
+ View event.
+
+ 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.
+
+ The graphics system may only be accessed when handling certain events. The
+ graphics context is active for #PUGL_CREATE, #PUGL_DESTROY, #PUGL_CONFIGURE,
+ and #PUGL_EXPOSE, but only enabled for drawing for #PUGL_EXPOSE.
*/
typedef union {
- PuglEventType type; /**< Event type. */
- PuglEventAny any; /**< Valid for all event types. */
- PuglEventButton button; /**< PUGL_BUTTON_PRESS, PUGL_BUTTON_RELEASE. */
- PuglEventConfigure configure; /**< PUGL_CONFIGURE. */
- PuglEventExpose expose; /**< PUGL_EXPOSE. */
- PuglEventClose close; /**< PUGL_CLOSE. */
- PuglEventKey key; /**< PUGL_KEY_PRESS, PUGL_KEY_RELEASE. */
- PuglEventText text; /**< PUGL_TEXT. */
- PuglEventCrossing crossing; /**< PUGL_ENTER_NOTIFY, PUGL_LEAVE_NOTIFY. */
- PuglEventMotion motion; /**< PUGL_MOTION_NOTIFY. */
- PuglEventScroll scroll; /**< PUGL_SCROLL. */
- PuglEventFocus focus; /**< PUGL_FOCUS_IN, PUGL_FOCUS_OUT. */
+ PuglEventAny any; ///< Valid for all event types
+ PuglEventType type; ///< Event type
+ PuglEventButton button; ///< #PUGL_BUTTON_PRESS, #PUGL_BUTTON_RELEASE
+ PuglEventConfigure configure; ///< #PUGL_CONFIGURE
+ PuglEventExpose expose; ///< #PUGL_EXPOSE
+ PuglEventKey key; ///< #PUGL_KEY_PRESS, #PUGL_KEY_RELEASE
+ PuglEventText text; ///< #PUGL_TEXT
+ PuglEventCrossing crossing; ///< #PUGL_POINTER_IN, #PUGL_POINTER_OUT
+ PuglEventMotion motion; ///< #PUGL_MOTION
+ PuglEventScroll scroll; ///< #PUGL_SCROLL
+ PuglEventFocus focus; ///< #PUGL_FOCUS_IN, #PUGL_FOCUS_OUT
+ PuglEventClient client; ///< #PUGL_CLIENT
+ PuglEventTimer timer; ///< #PUGL_TIMER
} PuglEvent;
/**
- @anchor world
- @name World
- The top level context of a Pugl application.
+ @}
+ @defgroup status Status
+
+ Status codes and error handling.
+
+ @{
+*/
+
+/**
+ Return status code.
+*/
+typedef enum {
+ PUGL_SUCCESS, ///< Success
+ PUGL_FAILURE, ///< Non-fatal failure
+ PUGL_UNKNOWN_ERROR, ///< Unknown system error
+ PUGL_BAD_BACKEND, ///< Invalid or missing backend
+ PUGL_BACKEND_FAILED, ///< Backend initialisation failed
+ PUGL_REGISTRATION_FAILED, ///< Window class registration failed
+ PUGL_CREATE_WINDOW_FAILED, ///< Window creation failed
+ PUGL_SET_FORMAT_FAILED, ///< Failed to set pixel format
+ PUGL_CREATE_CONTEXT_FAILED, ///< Failed to create drawing context
+ PUGL_UNSUPPORTED_TYPE, ///< Unsupported data type
+} PuglStatus;
+
+/**
+ Return a string describing a status code.
+*/
+PUGL_API
+const char*
+puglStrerror(PuglStatus status);
+
+/**
+ @}
+ @defgroup world World
+
+ The top-level context of a Pugl application or plugin.
+
+ The world contains all library-wide state. There is no static data in Pugl,
+ so it is safe to use multiple worlds in a single process. This is to
+ facilitate plugins or other situations where it is not possible to share a
+ world, but a single world should be shared for all views where possible.
+
@{
*/
/**
The "world" of application state.
- The world represents things that are not associated with a particular view.
- Several worlds can be created in a process (which is the case when many
- plugins use Pugl, for example), but code using different worlds must be
- isolated so they are never mixed. Views are strongly associated with the
- world they were created for.
+ The world represents everything that is not associated with a particular
+ view. Several worlds can be created in a single process, but code using
+ different worlds must be isolated so they are never mixed. Views are
+ strongly associated with the world they were created in.
*/
typedef struct PuglWorldImpl PuglWorld;
/**
+ Handle for the world's opaque user data.
+*/
+typedef void* PuglWorldHandle;
+
+/**
+ The type of a PuglWorld.
+*/
+typedef enum {
+ PUGL_PROGRAM, ///< Top-level application
+ PUGL_MODULE ///< Plugin or module within a larger application
+} PuglWorldType;
+
+/**
+ World flags.
+*/
+typedef enum {
+ /**
+ Set up support for threads if necessary.
+
+ - X11: Calls XInitThreads() which is required for some drivers.
+ */
+ PUGL_WORLD_THREADS = 1 << 0
+} PuglWorldFlag;
+
+/**
+ Bitwise OR of #PuglWorldFlag values.
+*/
+typedef uint32_t PuglWorldFlags;
+
+/**
+ A log message level, compatible with syslog.
+*/
+typedef enum {
+ PUGL_LOG_LEVEL_ERR = 3, ///< Error
+ PUGL_LOG_LEVEL_WARNING = 4, ///< Warning
+ PUGL_LOG_LEVEL_INFO = 6, ///< Informational message
+ PUGL_LOG_LEVEL_DEBUG = 7 ///< Debug message
+} PuglLogLevel;
+
+/**
+ A function called to report log messages.
+
+ @param world The world that produced this log message.
+ @param level Log level.
+ @param msg Message string.
+*/
+typedef void (*PuglLogFunc)(PuglWorld* world,
+ PuglLogLevel level,
+ const char* msg);
+
+/**
Create a new world.
- @return A newly created world.
+ @param type The type, which dictates what this world is responsible for.
+ @param flags Flags to control world features.
+ @return A new world, which must be later freed with puglFreeWorld().
*/
PUGL_API PuglWorld*
-puglNewWorld(void);
+puglNewWorld(PuglWorldType type, PuglWorldFlags flags);
/**
Free a world allocated with puglNewWorld().
@@ -444,6 +589,51 @@ PUGL_API void
puglFreeWorld(PuglWorld* world);
/**
+ Set the user data for the world.
+
+ This is usually a pointer to a struct that contains all the state which must
+ be accessed by several views.
+
+ The handle is opaque to Pugl and is not interpreted in any way.
+*/
+PUGL_API void
+puglSetWorldHandle(PuglWorld* world, PuglWorldHandle handle);
+
+/**
+ Get the user data for the world.
+*/
+PUGL_API PuglWorldHandle
+puglGetWorldHandle(PuglWorld* world);
+
+/**
+ Return a pointer to the native handle of the world.
+
+ @return
+ - X11: A pointer to the `Display`.
+ - MacOS: `NULL`.
+ - Windows: The `HMODULE` of the calling process.
+*/
+PUGL_API void*
+puglGetNativeWorld(PuglWorld* world);
+
+/**
+ Set the function to call to log a message.
+
+ This will be called to report any log messages generated internally by Pugl
+ which are enabled according to the log level.
+*/
+PUGL_API PuglStatus
+puglSetLogFunc(PuglWorld* world, PuglLogFunc logFunc);
+
+/**
+ Set the level of log messages to emit.
+
+ Any log messages with a level less than or equal to `level` will be emitted.
+*/
+PUGL_API PuglStatus
+puglSetLogLevel(PuglWorld* world, PuglLogLevel level);
+
+/**
Set the class name of the application.
This is a stable identifier for the application, used as the window
@@ -466,49 +656,127 @@ PUGL_API double
puglGetTime(const PuglWorld* world);
/**
- Poll for events that are ready to be processed.
+ Update by processing events from the window system.
- This polls for events that are ready for any view in the application,
- potentially blocking depending on `timeout`.
+ 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.
+
+ @param world The world to update.
- @param world The world for all the views to poll.
@param timeout Maximum time to wait, in seconds. If zero, the call returns
immediately, if negative, the call blocks indefinitely.
- @return PUGL_SUCCESS if events are read, PUGL_FAILURE if not, or an error.
-*/
-PUGL_API PuglStatus
-puglPollEvents(PuglWorld* world, double timeout);
-
-/**
- Dispatch any pending events to views.
- This processes all pending events, dispatching them to the appropriate
- views. View event handlers will be called in the scope of this call. This
- function does not block, if no events are pending it will return
- immediately.
+ @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error.
*/
PUGL_API PuglStatus
-puglDispatchEvents(PuglWorld* world);
+puglUpdate(PuglWorld* world, double timeout);
/**
@}
- @anchor view
- @name View
- A view is a drawing region that receives events, which may correspond to a
- top-level window or be embedded in some other window.
+
+ @defgroup view View
+
+ A drawable region that receives events.
+
+ A view can be thought of as a window, but does not necessarily correspond to
+ a top-level window in a desktop environment. For example, a view can be
+ embedded in some other window, or represent an embedded system where there
+ is no concept of multiple windows at all.
+
@{
*/
/**
- A Pugl view.
+ A drawable region that receives events.
*/
typedef struct PuglViewImpl PuglView;
/**
+ A graphics backend.
+
+ The backend dictates how graphics are set up for a view, and how drawing is
+ performed. A backend must be set by calling puglSetBackend() before
+ realising a view.
+
+ If you are using a local copy of Pugl, it is possible to implement a custom
+ backend. See the definition of `PuglBackendImpl` in the source code for
+ details.
+*/
+typedef struct PuglBackendImpl PuglBackend;
+
+/**
+ A native window handle.
+
+ X11: This is a `Window`.
+
+ MacOS: This is a pointer to an `NSView*`.
+
+ Windows: This is a `HWND`.
+*/
+typedef uintptr_t PuglNativeWindow;
+
+/**
+ Handle for a view's opaque user data.
+*/
+typedef void* PuglHandle;
+
+/**
+ A hint for configuring a view.
+*/
+typedef enum {
+ PUGL_USE_COMPAT_PROFILE, ///< Use compatible (not core) OpenGL profile
+ PUGL_USE_DEBUG_CONTEXT, ///< True to use a debug OpenGL context
+ PUGL_CONTEXT_VERSION_MAJOR, ///< OpenGL context major version
+ PUGL_CONTEXT_VERSION_MINOR, ///< OpenGL context minor version
+ PUGL_RED_BITS, ///< Number of bits for red channel
+ PUGL_GREEN_BITS, ///< Number of bits for green channel
+ PUGL_BLUE_BITS, ///< Number of bits for blue channel
+ PUGL_ALPHA_BITS, ///< Number of bits for alpha channel
+ PUGL_DEPTH_BITS, ///< Number of bits for depth buffer
+ PUGL_STENCIL_BITS, ///< Number of bits for stencil buffer
+ PUGL_SAMPLES, ///< Number of samples per pixel (AA)
+ PUGL_DOUBLE_BUFFER, ///< True if double buffering should be used
+ PUGL_SWAP_INTERVAL, ///< Number of frames between buffer swaps
+ PUGL_RESIZABLE, ///< True if window should be resizable
+ PUGL_IGNORE_KEY_REPEAT, ///< True if key repeat events are ignored
+
+ PUGL_NUM_WINDOW_HINTS
+} PuglViewHint;
+
+/**
+ A special view hint value.
+*/
+typedef enum {
+ PUGL_DONT_CARE = -1, ///< Use best available value
+ PUGL_FALSE = 0, ///< Explicitly false
+ PUGL_TRUE = 1 ///< Explicitly true
+} PuglViewHintValue;
+
+/**
+ A function called when an event occurs.
+*/
+typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event);
+
+/**
+ @name Setup
+ Functions for creating and destroying a view.
+ @{
+*/
+
+/**
Create a new view.
- A view represents a window, but a window will not be shown until configured
- with the various puglInit functions and shown with puglShowWindow().
+ A newly created view does not correspond to a real system view or window.
+ It must first be configured, then be realised by calling puglCreateWindow().
*/
PUGL_API PuglView*
puglNewView(PuglWorld* world);
@@ -526,39 +794,55 @@ PUGL_API PuglWorld*
puglGetWorld(PuglView* view);
/**
- Set the handle to be passed to all callbacks.
+ Set the user data for a view.
- This is generally a pointer to a struct which contains all necessary state.
- Everything needed in callbacks should be here, not in static variables.
+ This is usually a pointer to a struct that contains all the state which must
+ be accessed by a view. Everything needed to process events should be stored
+ here, not in static variables.
+
+ The handle is opaque to Pugl and is not interpreted in any way.
*/
PUGL_API void
puglSetHandle(PuglView* view, PuglHandle handle);
/**
- Get the handle to be passed to all callbacks.
+ Get the user data for a view.
*/
PUGL_API PuglHandle
puglGetHandle(PuglView* view);
/**
- Set a hint to configure window properties.
+ Set the graphics backend to use for a view.
- This only has an effect when called before puglCreateWindow().
+ This must be called once to set the graphics backend before calling
+ puglCreateWindow().
+
+ Pugl includes the following backends:
+
+ - puglGlBackend(), declared in pugl_gl.h
+ - puglCairoBackend(), declared in pugl_cairo.h
+
+ Note that backends are modular and not compiled into the main Pugl library
+ to avoid unnecessary dependencies. To use a particular backend,
+ applications must link against the appropriate backend library, or be sure
+ to compile in the appropriate code if using a local copy of Pugl.
*/
PUGL_API PuglStatus
-puglSetViewHint(PuglView* view, PuglViewHint hint, int value);
+puglSetBackend(PuglView* view, const PuglBackend* backend);
/**
- Return true iff the view is currently visible.
+ Set the function to call when an event occurs.
*/
-PUGL_API bool
-puglGetVisible(PuglView* view);
+PUGL_API PuglStatus
+puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc);
/**
- Request a redisplay on the next call to puglDispatchEvents().
+ Set a hint to configure window properties.
+
+ This only has an effect when called before puglCreateWindow().
*/
PUGL_API PuglStatus
-puglPostRedisplay(PuglView* view);
+puglSetViewHint(PuglView* view, PuglViewHint hint, int value);
/**
@}
@@ -570,12 +854,16 @@ puglPostRedisplay(PuglView* view);
/**
Get the current position and size of the view.
+
+ The position is in screen coordinates with an upper left origin.
*/
PUGL_API PuglRect
puglGetFrame(const PuglView* view);
/**
Set the current position and size of the view.
+
+ The position is in screen coordinates with an upper left origin.
*/
PUGL_API PuglStatus
puglSetFrame(PuglView* view, PuglRect frame);
@@ -583,7 +871,9 @@ puglSetFrame(PuglView* view, PuglRect frame);
/**
Set the minimum size of the view.
- To avoid stutter, this should be called before creating the window.
+ If an initial minimum size is known, this should be called before
+ puglCreateWindow() to avoid stutter, though it can be called afterwards as
+ well.
*/
PUGL_API PuglStatus
puglSetMinSize(PuglView* view, int width, int height);
@@ -597,6 +887,10 @@ puglSetMinSize(PuglView* view, int width, int height);
Note that setting different minimum and maximum constraints does not
currenty work on MacOS (the minimum is used), so only setting a fixed aspect
ratio works properly across all platforms.
+
+ If an initial aspect ratio is known, this should be called before
+ puglCreateWindow() to avoid stutter, though it can be called afterwards as
+ well.
*/
PUGL_API PuglStatus
puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY);
@@ -609,15 +903,6 @@ puglSetAspectRatio(PuglView* view, int minX, int minY, int maxX, int maxY);
*/
/**
- A native window handle.
-
- On X11, this is a Window.
- On OSX, this is an NSView*.
- On Windows, this is a HWND.
-*/
-typedef intptr_t PuglNativeWindow;
-
-/**
Set the title of the window.
This only makes sense for non-embedded views that will have a corresponding
@@ -628,10 +913,9 @@ PUGL_API PuglStatus
puglSetWindowTitle(PuglView* view, const char* title);
/**
- Set the parent window before creating a window (for embedding).
+ Set the parent window for embedding a view in an existing window.
- This only works when called before creating the window with
- puglCreateWindow(), reparenting is not supported.
+ This must be called before puglCreateWindow(), reparenting is not supported.
*/
PUGL_API PuglStatus
puglSetParentWindow(PuglView* view, PuglNativeWindow parent);
@@ -639,22 +923,27 @@ puglSetParentWindow(PuglView* view, PuglNativeWindow parent);
/**
Set the transient parent of the window.
- This is used for things like dialogs, to have them associated with the
- window they are a transient child of properly.
+ Set this for transient children like dialogs, to have them properly
+ associated with their parent window. This should be called before
+ puglCreateWindow().
*/
PUGL_API PuglStatus
puglSetTransientFor(PuglView* view, PuglNativeWindow parent);
/**
- Create a window with the settings given by the various puglInit functions.
+ Realise a view by creating a corresponding system view or window.
- @return 1 (pugl does not currently support multiple windows).
+ The view should be fully configured using the above functions before this is
+ called. This function may only be called once per view.
*/
PUGL_API PuglStatus
puglCreateWindow(PuglView* view, const char* title);
/**
Show the current window.
+
+ If the window is currently hidden, it will be shown and possibly raised to
+ the top depending on the platform.
*/
PUGL_API PuglStatus
puglShowWindow(PuglView* view);
@@ -666,6 +955,12 @@ PUGL_API PuglStatus
puglHideWindow(PuglView* view);
/**
+ Return true iff the view is currently visible.
+*/
+PUGL_API bool
+puglGetVisible(PuglView* view);
+
+/**
Return the native window handle.
*/
PUGL_API PuglNativeWindow
@@ -673,108 +968,88 @@ puglGetNativeWindow(PuglView* view);
/**
@}
- @name Graphics Context
- Functions for working with the drawing context.
+ @name Graphics
+ Functions for working with the graphics context and scheduling redisplays.
@{
*/
/**
- Graphics backend interface.
-*/
-typedef struct PuglBackendImpl PuglBackend;
+ Get the graphics context.
-/**
- OpenGL extension function.
-*/
-typedef void (*PuglGlFunc)(void);
+ This is a backend-specific context used for drawing if the backend graphics
+ API requires one. It is only available during an expose.
-/**
- Set the graphics backend to use.
-
- This needs to be called once before creating the window to set the graphics
- backend. There are two backend accessors included with pugl:
- puglGlBackend() and puglCairoBackend(), declared in pugl_gl_backend.h and
- pugl_cairo_backend.h, respectively.
-*/
-PUGL_API PuglStatus
-puglSetBackend(PuglView* view, const PuglBackend* backend);
-
-/**
- Return the address of an OpenGL extension function.
-*/
-PUGL_API PuglGlFunc
-puglGetProcAddress(const char* name);
-
-/**
- Get the drawing context.
-
- The context is only guaranteed to be available during an expose.
-
- For OpenGL backends, this is unused and returns NULL.
- For Cairo backends, this returns a pointer to a `cairo_t`.
+ @return
+ - OpenGL: `NULL`.
+ - Cairo: A pointer to a
+ [`cairo_t`](http://www.cairographics.org/manual/cairo-cairo-t.html).
*/
PUGL_API void*
puglGetContext(PuglView* view);
/**
- Enter the drawing context.
-
- Note that pugl automatically enters and leaves the drawing context during
- configure and expose events, so it is not normally necessary to call this.
- However, it can be used to enter the drawing context elsewhere, for example
- to call any GL functions during setup.
+ Request a redisplay for the entire view.
- @param view The view being entered.
- @param drawing If true, prepare for drawing.
+ This will cause an expose event to be dispatched later. If called from
+ within the event handler, the expose should arrive at the end of the current
+ event loop iteration, though this is not strictly guaranteed on all
+ platforms. If called elsewhere, an expose will be enqueued to be processed
+ in the next event loop iteration.
*/
PUGL_API PuglStatus
-puglEnterContext(PuglView* view, bool drawing);
+puglPostRedisplay(PuglView* view);
/**
- Leave the drawing context.
+ Request a redisplay of the given rectangle within the view.
- This must be called after puglEnterContext() with a matching `drawing`
- parameter.
-
- @param view The view being left.
- @param drawing If true, finish drawing, for example by swapping buffers.
+ This has the same semantics as puglPostRedisplay(), but allows giving a
+ precise region for redrawing only a portion of the view.
*/
PUGL_API PuglStatus
-puglLeaveContext(PuglView* view, bool drawing);
+puglPostRedisplayRect(PuglView* view, PuglRect rect);
/**
@}
@anchor interaction
@name Interaction
- Interacting with the system and user with events.
+ Functions for interacting with the user and window system.
@{
*/
/**
- A function called when an event occurs.
-*/
-typedef PuglStatus (*PuglEventFunc)(PuglView* view, const PuglEvent* event);
-
-/**
- Set the function to call when an event occurs.
+ Grab the keyboard input focus.
*/
PUGL_API PuglStatus
-puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc);
+puglGrabFocus(PuglView* view);
/**
- Return true iff `view` has the input focus.
+ Return whether `view` has the keyboard input focus.
*/
PUGL_API bool
puglHasFocus(const PuglView* view);
/**
- Grab the input focus.
+ Set the clipboard contents.
+
+ This sets the system clipboard contents, which can be retrieved with
+ puglGetClipboard() or pasted into other applications.
+
+ @param view The view.
+ @param type The MIME type of the data, "text/plain" is assumed if `NULL`.
+ @param data The data to copy to the clipboard.
+ @param len The length of data in bytes (including terminator if necessary).
*/
PUGL_API PuglStatus
-puglGrabFocus(PuglView* view);
+puglSetClipboard(PuglView* view,
+ const char* type,
+ const void* data,
+ size_t len);
/**
- Get clipboard contents.
+ Get the clipboard contents.
+
+ This gets the system clipboard contents, which may have been set with
+ puglSetClipboard() or copied from another application.
@param view The view.
@param[out] type Set to the MIME type of the data.
@@ -785,28 +1060,68 @@ PUGL_API const void*
puglGetClipboard(PuglView* view, const char** type, size_t* len);
/**
- Set clipboard contents.
+ Request user attention.
- @param view The view.
- @param type The MIME type of the data, "text/plain" is assumed if NULL.
- @param data The data to copy to the clipboard.
- @param len The length of data in bytes (including terminator if necessary).
+ This hints to the system that the window or application requires attention
+ from the user. The exact effect depends on the platform, but is usually
+ something like a flashing task bar entry or bouncing application icon.
*/
PUGL_API PuglStatus
-puglSetClipboard(PuglView* view,
- const char* type,
- const void* data,
- size_t len);
+puglRequestAttention(PuglView* view);
/**
- Request user attention.
+ Activate a repeating timer event.
- This hints to the system that the window or application requires attention
- from the user. The exact effect depends on the platform, but is usually
- something like flashing a task bar entry.
+ This starts a timer which will send a #PuglEventTimer to `view` every
+ `timeout` seconds. This can be used to perform some action in a view at a
+ regular interval with relatively low frequency. Note that the frequency of
+ timer events may be limited by how often puglUpdate() is called.
+
+ If the given timer already exists, it is replaced.
+
+ @param view The view to begin seding #PUGL_TIMER events to.
+
+ @param id The identifier for this timer. This is an application-specific ID
+ that should be a low number, typically the value of a constant or `enum`
+ that starts from 0. There is a platform-specific limit to the number of
+ supported timers, and overhead associated with each, so applications should
+ create only a few timers and perform several tasks in one if necessary.
+
+ @param timeout The period, in seconds, of this timer. This is not
+ guaranteed to have a resolution better than 10ms (the maximum timer
+ resolution on Windows) and may be rounded up if it is too short. On X11 and
+ MacOS, a resolution of about 1ms can usually be relied on.
+
+ @return #PUGL_SUCCESS, #PUGL_FAILURE if timers are not supported on this
+ system, or an error code.
*/
PUGL_API PuglStatus
-puglRequestAttention(PuglView* view);
+puglStartTimer(PuglView* view, uintptr_t id, double timeout);
+
+/**
+ Stop an active timer.
+
+ @param view The view that the timer is set for.
+ @param id The ID previously passed to puglStartTimer().
+ @return #PUGL_SUCCESS, or #PUGL_FAILURE if no such timer was found.
+*/
+PUGL_API PuglStatus
+puglStopTimer(PuglView* view, uintptr_t id);
+
+/**
+ Send an event to a view via the window system.
+
+ If supported, the event will be delivered to the view via the event loop
+ like other events. Note that this function only works for certain event
+ types, and will return PUGL_UNSUPPORTED_TYPE for events that are not
+ supported.
+*/
+PUGL_API PuglStatus
+puglSendEvent(PuglView* view, const PuglEvent* event);
+
+/**
+ @}
+*/
#ifndef PUGL_DISABLE_DEPRECATED
@@ -816,14 +1131,6 @@ puglRequestAttention(PuglView* view);
@{
*/
-#if defined(__clang__)
-# define PUGL_DEPRECATED_BY(name) __attribute__((deprecated("", name)))
-#elif defined(__GNUC__)
-# define PUGL_DEPRECATED_BY(name) __attribute__((deprecated("Use " name)))
-#else
-# define PUGL_DEPRECATED_BY(name)
-#endif
-
/**
Create a Pugl application and view.
@@ -842,7 +1149,7 @@ puglInit(const int* pargc, char** argv)
(void)pargc;
(void)argv;
- return puglNewView(puglNewWorld());
+ return puglNewView(puglNewWorld(PUGL_MODULE, 0));
}
/**
@@ -928,7 +1235,7 @@ puglInitTransientFor(PuglView* view, uintptr_t parent)
/**
Enable or disable resizing before creating a window.
- @deprecated Use puglSetViewHint() with @ref PUGL_RESIZABLE.
+ @deprecated Use puglSetViewHint() with #PUGL_RESIZABLE.
*/
static inline PUGL_DEPRECATED_BY("puglSetViewHint") void
puglInitResizable(PuglView* view, bool resizable)
@@ -954,7 +1261,7 @@ puglGetSize(PuglView* view, int* width, int* height)
/**
Ignore synthetic repeated key events.
- @deprecated Use puglSetViewHint() with @ref PUGL_IGNORE_KEY_REPEAT.
+ @deprecated Use puglSetViewHint() with #PUGL_IGNORE_KEY_REPEAT.
*/
static inline PUGL_DEPRECATED_BY("puglSetViewHint") void
puglIgnoreKeyRepeat(PuglView* view, bool ignore)
@@ -992,7 +1299,7 @@ puglInitWindowParent(PuglView* view, PuglNativeWindow parent)
static inline PUGL_DEPRECATED_BY("puglSetBackend") int
puglInitBackend(PuglView* view, const PuglBackend* backend)
{
- return puglSetBackend(view, backend);
+ return (int)puglSetBackend(view, backend);
}
/**
@@ -1020,6 +1327,70 @@ puglWaitForEvent(PuglView* view);
PUGL_API PUGL_DEPRECATED_BY("puglDispatchEvents") PuglStatus
puglProcessEvents(PuglView* view);
+/**
+ Poll for events that are ready to be processed.
+
+ This polls for events that are ready for any view in the world, potentially
+ blocking depending on `timeout`.
+
+ @param world The world to poll for events.
+
+ @param timeout Maximum time to wait, in seconds. If zero, the call returns
+ immediately, if negative, the call blocks indefinitely.
+
+ @return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error.
+
+ @deprecated Use puglUpdate().
+*/
+PUGL_API PUGL_DEPRECATED_BY("puglUpdate") PuglStatus
+puglPollEvents(PuglWorld* world, double timeout);
+
+/**
+ Dispatch any pending events to views.
+
+ This processes all pending events, dispatching them to the appropriate
+ views. View event handlers will be called in the scope of this call. This
+ function does not block, if no events are pending then it will return
+ immediately.
+
+ @deprecated Use puglUpdate().
+*/
+PUGL_API PUGL_DEPRECATED_BY("puglUpdate") PuglStatus
+puglDispatchEvents(PuglWorld* world);
+
+/**
+ Enter the graphics context.
+
+ Note that, unlike some similar libraries, Pugl automatically enters and
+ leaves the graphics context when required and application should not
+ normally do this. Drawing in Pugl is only allowed during the processing of
+ an expose event.
+
+ However, this can be used to enter the graphics context elsewhere, for
+ example to call any GL functions during setup.
+
+ @param view The view being entered.
+ @param drawing If true, prepare for drawing.
+
+ @deprecated Set up graphics when a #PUGL_CREATE event is received.
+*/
+PUGL_API PUGL_DEPRECATED_BY("PUGL_CREATE") PuglStatus
+puglEnterContext(PuglView* view, bool drawing);
+
+/**
+ Leave the graphics context.
+
+ This must be called after puglEnterContext() with a matching `drawing`
+ parameter.
+
+ @param view The view being left.
+ @param drawing If true, finish drawing, for example by swapping buffers.
+
+ @deprecated Shut down graphics when a #PUGL_DESTROY event is received.
+*/
+PUGL_API PUGL_DEPRECATED_BY("PUGL_DESTROY") PuglStatus
+puglLeaveContext(PuglView* view, bool drawing);
+
#endif /* PUGL_DISABLE_DEPRECATED */
/**
@@ -1027,8 +1398,6 @@ puglProcessEvents(PuglView* view);
@}
*/
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
+PUGL_END_DECLS
-#endif /* PUGL_H_INCLUDED */
+#endif /* PUGL_PUGL_H */
diff --git a/subprojects/d2tk/pugl/pugl/pugl.hpp b/subprojects/d2tk/pugl/pugl/pugl.hpp
index dee8c17..73cfe2a 100644
--- a/subprojects/d2tk/pugl/pugl/pugl.hpp
+++ b/subprojects/d2tk/pugl/pugl/pugl.hpp
@@ -18,19 +18,32 @@
@file pugl.hpp Pugl C++ API wrapper.
*/
-#ifndef PUGL_HPP_INCLUDED
-#define PUGL_HPP_INCLUDED
+#ifndef PUGL_PUGL_HPP
+#define PUGL_PUGL_HPP
#include "pugl/pugl.h"
/**
- @defgroup puglmm Puglmm
- Pugl C++ API wrapper.
+ @defgroup puglxx C++
+
+ C++ API wrapper.
+
+ @ingroup pugl_api
@{
*/
+/**
+ Pugl C++ API namespace.
+*/
namespace pugl {
+/**
+ A drawable region that receives events.
+
+ This is a thin wrapper for a PuglView that contains only a pointer.
+
+ @ingroup puglxx
+*/
class View {
public:
View(int* pargc, char** argv)
@@ -104,4 +117,4 @@ private:
@}
*/
-#endif /* PUGL_HPP_INCLUDED */
+#endif /* PUGL_PUGL_HPP */
diff --git a/subprojects/d2tk/pugl/pugl/pugl_cairo.h b/subprojects/d2tk/pugl/pugl/pugl_cairo.h
new file mode 100644
index 0000000..e71072e
--- /dev/null
+++ b/subprojects/d2tk/pugl/pugl/pugl_cairo.h
@@ -0,0 +1,49 @@
+/*
+ Copyright 2012-2019 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.h Declaration of Cairo backend accessor.
+*/
+
+#ifndef PUGL_PUGL_CAIRO_H
+#define PUGL_PUGL_CAIRO_H
+
+#include "pugl/pugl.h"
+
+PUGL_BEGIN_DECLS
+
+/**
+ @defgroup cairo Cairo
+ Cairo graphics support.
+ @ingroup pugl_api
+ @{
+*/
+
+/**
+ Cairo graphics backend accessor.
+
+ Pass the return value to puglInitBackend() to draw to a view with Cairo.
+*/
+PUGL_API const PuglBackend*
+puglCairoBackend(void);
+
+/**
+ @}
+*/
+
+PUGL_END_DECLS
+
+#endif // PUGL_PUGL_CAIRO_H
diff --git a/subprojects/d2tk/pugl/pugl/pugl_cairo_backend.h b/subprojects/d2tk/pugl/pugl/pugl_cairo_backend.h
index 3330c08..3f8cec3 100644
--- a/subprojects/d2tk/pugl/pugl/pugl_cairo_backend.h
+++ b/subprojects/d2tk/pugl/pugl/pugl_cairo_backend.h
@@ -14,29 +14,10 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/**
- @file pugl_cairo_backend.h Declaration of Cairo backend accessor.
-*/
-
-#ifndef PUGL_CAIRO_BACKEND_H
-#define PUGL_CAIRO_BACKEND_H
-
-#include "pugl/pugl.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- Cairo graphics backend accessor.
-
- Pass the return value to puglInitBackend() to draw to a view with Cairo.
-*/
-PUGL_API const PuglBackend*
-puglCairoBackend(void);
+#ifndef PUGL_PUGL_CAIRO_BACKEND_H
+#define PUGL_PUGL_CAIRO_BACKEND_H
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
+#warning "This header is deprecated, use pugl/pugl_cairo.h instead."
+#include "pugl/pugl_cairo.h"
-#endif // PUGL_CAIRO_BACKEND_H
+#endif // PUGL_PUGL_CAIRO_BACKEND_H
diff --git a/subprojects/d2tk/pugl/pugl/pugl_gl.h b/subprojects/d2tk/pugl/pugl/pugl_gl.h
new file mode 100644
index 0000000..9c5fa94
--- /dev/null
+++ b/subprojects/d2tk/pugl/pugl/pugl_gl.h
@@ -0,0 +1,60 @@
+/*
+ Copyright 2012-2019 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.h OpenGL-specific API.
+*/
+
+#ifndef PUGL_PUGL_GL_H
+#define PUGL_PUGL_GL_H
+
+#include "pugl/pugl.h"
+
+PUGL_BEGIN_DECLS
+
+/**
+ @defgroup gl OpenGL
+ OpenGL graphics support.
+ @ingroup pugl_api
+ @{
+*/
+
+/**
+ OpenGL extension function.
+*/
+typedef void (*PuglGlFunc)(void);
+
+/**
+ Return the address of an OpenGL extension function.
+*/
+PUGL_API PuglGlFunc
+puglGetProcAddress(const char* name);
+
+/**
+ OpenGL graphics backend.
+
+ Pass the return value to puglSetBackend() to draw to a view with OpenGL.
+*/
+PUGL_API const PuglBackend*
+puglGlBackend(void);
+
+PUGL_END_DECLS
+
+/**
+ @}
+*/
+
+#endif // PUGL_PUGL_GL_H
diff --git a/subprojects/d2tk/pugl/pugl/pugl_gl_backend.h b/subprojects/d2tk/pugl/pugl/pugl_gl_backend.h
index 5913b95..e1b9a15 100644
--- a/subprojects/d2tk/pugl/pugl/pugl_gl_backend.h
+++ b/subprojects/d2tk/pugl/pugl/pugl_gl_backend.h
@@ -14,29 +14,10 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/**
- @file pugl_gl_backend.h Declaration of OpenGL backend accessor.
-*/
-
-#ifndef PUGL_GL_BACKEND_H
-#define PUGL_GL_BACKEND_H
-
-#include "pugl/pugl.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/**
- OpenGL graphics backend.
-
- Pass the return value to puglInitBackend() to draw to a view with OpenGL.
-*/
-PUGL_API const PuglBackend*
-puglGlBackend(void);
+#ifndef PUGL_PUGL_GL_BACKEND_H
+#define PUGL_PUGL_GL_BACKEND_H
-#ifdef __cplusplus
-} /* extern "C" */
-#endif
+#warning "This header is deprecated, use pugl/pugl_gl.h instead."
+#include "pugl/pugl_gl.h"
-#endif // PUGL_GL_BACKEND_H
+#endif // PUGL_PUGL_GL_BACKEND_H
diff --git a/subprojects/d2tk/pugl/pugl/pugl_stub.h b/subprojects/d2tk/pugl/pugl/pugl_stub.h
new file mode 100644
index 0000000..da918aa
--- /dev/null
+++ b/subprojects/d2tk/pugl/pugl/pugl_stub.h
@@ -0,0 +1,103 @@
+/*
+ Copyright 2019 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.h Stub backend functions and accessor declaration.
+*/
+
+#ifndef PUGL_PUGL_STUB_H
+#define PUGL_PUGL_STUB_H
+
+#include "pugl/pugl.h"
+
+PUGL_BEGIN_DECLS
+
+/**
+ @defgroup stub Stub
+
+ Stub graphics backend.
+
+ The stub backend functions do nothing and always
+ return success. These do not make for a usable backend on their own since
+ the platform implementation would fail to create a window, but are useful
+ for other backends to reuse since not all need non-trivial implementations
+ of every backend function.
+
+ @ingroup pugl_api
+ @{
+*/
+
+/**
+ Stub graphics backend.
+
+ This backend just creates a simple native window without setting up any
+ portable graphics API.
+*/
+PUGL_API
+const PuglBackend*
+puglStubBackend(void);
+
+static inline PuglStatus
+puglStubConfigure(PuglView* view)
+{
+ (void)view;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubCreate(PuglView* view)
+{
+ (void)view;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubDestroy(PuglView* view)
+{
+ (void)view;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubEnter(PuglView* view, const PuglEventExpose* expose)
+{
+ (void)view;
+ (void)expose;
+ return PUGL_SUCCESS;
+}
+
+static inline PuglStatus
+puglStubLeave(PuglView* view, const PuglEventExpose* expose)
+{
+ (void)view;
+ (void)expose;
+ return PUGL_SUCCESS;
+}
+
+static inline void*
+puglStubGetContext(PuglView* view)
+{
+ (void)view;
+ return NULL;
+}
+
+/**
+ @}
+*/
+
+PUGL_END_DECLS
+
+#endif // PUGL_PUGL_STUB_H
diff --git a/subprojects/d2tk/pugl/pugl/pugl_stub_backend.h b/subprojects/d2tk/pugl/pugl/pugl_stub_backend.h
new file mode 100644
index 0000000..e5aa513
--- /dev/null
+++ b/subprojects/d2tk/pugl/pugl/pugl_stub_backend.h
@@ -0,0 +1,23 @@
+/*
+ Copyright 2012-2019 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.
+*/
+
+#ifndef PUGL_PUGL_STUB_BACKEND_H
+#define PUGL_PUGL_STUB_BACKEND_H
+
+#warning "This header is deprecated, use pugl/pugl_stub.h instead."
+#include "pugl/pugl_stub.h"
+
+#endif // PUGL_PUGL_STUB_BACKEND_H
diff --git a/subprojects/d2tk/pugl/shaders/rect.frag b/subprojects/d2tk/pugl/shaders/rect.frag
new file mode 100644
index 0000000..5e3af9d
--- /dev/null
+++ b/subprojects/d2tk/pugl/shaders/rect.frag
@@ -0,0 +1,35 @@
+#version 330 core
+
+/* The fragment shader uses the UV coordinates to calculate whether it is in
+ the T, R, B, or L border. These are then mixed with the border color, and
+ their inverse is mixed with the fill color, to calculate the fragment color.
+ For example, if we are in the top border, then T=1, so the border mix factor
+ TRBL=1, and the fill mix factor (1-TRBL) is 0.
+
+ The use of pixel units here is handy because the border width can be
+ specified precisely in pixels to draw sharp lines. The border width is just
+ hardcoded, but could be made a uniform or vertex attribute easily enough. */
+
+noperspective in vec2 f_uv;
+noperspective in vec2 f_size;
+noperspective in vec4 f_fillColor;
+
+layout(location = 0) out vec4 FragColor;
+
+void
+main()
+{
+ const float borderWidth = 2.0;
+
+ vec4 borderColor = f_fillColor + vec4(0.0, 0.4, 0.4, 0.0);
+ float t = step(borderWidth, f_uv[1]);
+ float r = step(borderWidth, f_size.x - f_uv[0]);
+ float b = step(borderWidth, f_size.y - f_uv[1]);
+ float l = step(borderWidth, f_uv[0]);
+ float fillMix = t * r * b * l;
+ float borderMix = 1.0 - fillMix;
+ vec4 fill = fillMix * f_fillColor;
+ vec4 border = borderMix * borderColor;
+
+ FragColor = fill + border;
+}
diff --git a/subprojects/d2tk/pugl/shaders/rect.vert b/subprojects/d2tk/pugl/shaders/rect.vert
new file mode 100644
index 0000000..bf2e951
--- /dev/null
+++ b/subprojects/d2tk/pugl/shaders/rect.vert
@@ -0,0 +1,34 @@
+#version 330 core
+
+/* The vertex shader is trivial, but forwards scaled UV coordinates (in pixels)
+ to the fragment shader for drawing the border. */
+
+uniform mat4 u_projection;
+
+layout(location = 0) in vec2 v_position;
+layout(location = 1) in vec2 v_origin;
+layout(location = 2) in vec2 v_size;
+layout(location = 3) in vec4 v_fillColor;
+
+noperspective out vec2 f_uv;
+noperspective out vec2 f_size;
+noperspective out vec4 f_fillColor;
+
+void
+main()
+{
+ // clang-format off
+ mat4 m = mat4(v_size[0], 0.0, 0.0, 0.0,
+ 0.0, v_size[1], 0.0, 0.0,
+ 0.0, 0.0, 1.0, 0.0,
+ v_origin[0], v_origin[1], 0.0, 1.0);
+ // clang-format on
+
+ mat4 MVP = u_projection * m;
+
+ f_uv = v_position * v_size;
+ f_size = v_size;
+ f_fillColor = v_fillColor;
+
+ gl_Position = MVP * vec4(v_position, 0.0, 1.0);
+}
diff --git a/subprojects/d2tk/pugl/test/pugl_gl3_test.c b/subprojects/d2tk/pugl/test/pugl_gl3_test.c
deleted file mode 100644
index c939930..0000000
--- a/subprojects/d2tk/pugl/test/pugl_gl3_test.c
+++ /dev/null
@@ -1,395 +0,0 @@
-/*
- Copyright 2012-2019 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_gl3_test.c A simple test of OpenGL 3 with Pugl.
-
- This is an example of using OpenGL for pixel-perfect 2D drawing. It uses
- pixel coordinates for positions and sizes so that things work roughly like a
- typical 2D graphics API.
-
- The program draws a bunch of rectangles with borders, with one draw call per
- rectangle (the shader draws the borders). Rectangle attributes are
- controlled via uniform variables. This is certainly not the fastest way to
- do this: it is probably CPU and/or I/O bound, but serves as a decent very
- rough benchmark for how many draw calls you can get away with.
-
- A better (if slightly more GPU memory intensive) way to do this would be to
- put everything in vertex attributes, jam all the rectangle data into a
- single buffer, and draw the whole thing with a single draw call. That way
- would probably be GPU bound instead, and show a difference between alpha
- blending and depth testing for many overlapped rectangles.
-*/
-
-#define GL_SILENCE_DEPRECATION 1
-
-#include "shader_utils.h"
-#include "test_utils.h"
-
-#include "glad/glad.h"
-
-#include "pugl/gl.h"
-#include "pugl/pugl.h"
-#include "pugl/pugl_gl_backend.h"
-
-#include <math.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-static const int defaultWidth = 512;
-static const int defaultHeight = 512;
-
-typedef struct
-{
- float pos[2];
- float size[2];
- float fillColor[4];
- float borderColor[4];
-} Rect;
-
-// clang-format off
-static const GLfloat rectVertices[] = {
- 0.0f, 0.0f, // TL
- 1.0f, 0.0f, // TR
- 0.0f, 1.0f, // BL
- 1.0f, 1.0f, // BR
-};
-// clang-format on
-
-static const GLuint rectIndices[4] = {0, 1, 2, 3};
-
-/* The vertex shader is trivial, but forwards scaled UV coordinates (in pixels)
- to the fragment shader for drawing the border. */
-static const char* vertexSource = //
- "#version 330\n"
- "uniform mat4 MVP;\n"
- "uniform vec2 u_size;\n"
- "in vec2 v_position;\n"
- "noperspective out vec2 f_uv;\n"
- "void main() {\n"
- " f_uv = v_position * u_size;\n"
- " gl_Position = MVP * vec4(v_position, 0.0, 1.0);\n"
- "}\n";
-
-/* The fragment shader uses the UV coordinates to calculate whether it is in
- the T, R, B, or L border. These are then mixed with the border color, and
- their inverse is mixed with the fill color, to calculate the fragment color.
- For example, if we are in the top border, then T=1, so the border mix factor
- TRBL=1, and the fill mix factor (1-TRBL) is 0.
-
- The use of pixel units here is handy because the border width can be
- specified precisely in pixels to draw sharp lines. The border width is just
- hardcoded, but could be made a uniform or vertex attribute easily enough. */
-static const char* fragmentSource = //
- "#version 330\n"
- "uniform vec2 u_size;\n"
- "uniform vec4 u_borderColor;\n"
- "uniform vec4 u_fillColor;\n"
- "noperspective in vec2 f_uv;\n"
- "layout(location = 0) out vec4 FragColor;\n"
- "void main() {\n"
- " const float border_width = 2.0;\n"
- "\n"
- " float t = step(border_width, f_uv[1]);\n"
- " float r = step(border_width, u_size.x - f_uv[0]);\n"
- " float b = step(border_width, u_size.y - f_uv[1]);\n"
- " float l = step(border_width, f_uv[0]);\n"
- " float fill_mix = t * r * b * l;\n"
- " float border_mix = 1.0 - fill_mix;\n"
- " vec4 fill = fill_mix * u_fillColor;\n"
- " vec4 border = border_mix * u_borderColor;\n"
- " FragColor = fill + border;\n"
- "}\n";
-
-typedef struct
-{
- PuglTestOptions opts;
- PuglWorld* world;
- PuglView* view;
- size_t numRects;
- Rect* rects;
- Program drawRect;
- GLuint vao;
- GLuint vbo;
- GLuint ibo;
- GLint u_MVP;
- GLint u_size;
- GLint u_fillColor;
- GLint u_borderColor;
- unsigned framesDrawn;
- int quit;
-} PuglTestApp;
-
-static void
-onConfigure(PuglView* view, double width, double height)
-{
- (void)view;
-
- glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- glViewport(0, 0, (int)width, (int)height);
-}
-
-static void
-drawRect(const PuglTestApp* app, const Rect* rect, mat4 projection)
-{
- /* The vertex data is always the same: a normalized rectangle from (0, 0)
- to (1, 1). We use the MVP matrix to scale and translate this to the
- desired screen coordinates. */
-
- // Construct model matrix to scale/translate to screen coordinates
- mat4 m;
- mat4Identity(m);
- mat4Translate(m, rect->pos[0], rect->pos[1], 0);
- m[0][0] = rect->size[0];
- m[1][1] = rect->size[1];
-
- // Combine them into the final MVP matrix and set uniform
- mat4 mvp;
- mat4Mul(mvp, projection, m);
- glUniformMatrix4fv(app->u_MVP, 1, GL_FALSE, (const GLfloat*)&mvp);
-
- // Set uniforms for the various rectangle attributes
- glUniform2fv(app->u_size, 1, rect->size);
- glUniform4fv(app->u_fillColor, 1, rect->fillColor);
- glUniform4fv(app->u_borderColor, 1, rect->borderColor);
-
- // Draw
- glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_INT, 0);
-}
-
-static void
-onExpose(PuglView* view)
-{
- PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
- const PuglRect frame = puglGetFrame(view);
- const double time = puglGetTime(puglGetWorld(view));
-
- // Construct projection matrix for 2D window surface (in pixels)
- mat4 proj;
- mat4Ortho(proj,
- 0.0f,
- (float)frame.width,
- 0.0f,
- (float)frame.height,
- -1.0f,
- 1.0f);
-
- // Clear and bind everything that is the same for every rect
- glClear(GL_COLOR_BUFFER_BIT);
- glUseProgram(app->drawRect.program);
- glBindVertexArray(app->vao);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, app->ibo);
-
- for (size_t i = 0; i < app->numRects; ++i) {
- Rect* rect = &app->rects[i];
-
- // Move rect around in an arbitrary way that looks cool
- rect->pos[0] = (float)(frame.width - rect->size[0]) *
- (sinf((float)time * rect->size[0] / 100.0f) + 1.0f) /
- 2.0f;
- rect->pos[1] = (float)(frame.height - rect->size[1]) *
- (cosf((float)time * rect->size[1] / 100.0f) + 1.0f) /
- 2.0f;
-
- drawRect(app, rect, proj);
- }
-
- ++app->framesDrawn;
-}
-
-static PuglStatus
-onEvent(PuglView* view, const PuglEvent* event)
-{
- PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
-
- printEvent(event, "Event: ");
-
- switch (event->type) {
- case PUGL_CONFIGURE:
- onConfigure(view, event->configure.width, event->configure.height);
- break;
- case PUGL_EXPOSE: onExpose(view); break;
- case PUGL_CLOSE: app->quit = 1; break;
- case PUGL_KEY_PRESS:
- if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
- app->quit = 1;
- }
- break;
- default: break;
- }
-
- return PUGL_SUCCESS;
-}
-
-static Rect*
-makeRects(const size_t numRects, const int width, const int height)
-{
- const int minSize = width / 32;
- const int maxSize = width / 4;
- const float boxAlpha = 0.6f;
-
- Rect* rects = (Rect*)calloc(numRects, sizeof(Rect));
- for (size_t i = 0; i < numRects; ++i) {
- rects[i].pos[0] = (float)(rand() % width);
- rects[i].pos[1] = (float)(rand() % height);
- rects[i].size[0] = (float)minSize + rand() % (maxSize - minSize);
- rects[i].size[1] = (float)minSize + rand() % (maxSize - minSize);
-
- rects[i].fillColor[1] = (rand() % numRects) / ((float)numRects - 0.4f);
- rects[i].fillColor[2] = (rand() % numRects) / ((float)numRects - 0.4f);
- rects[i].fillColor[3] = boxAlpha;
-
- rects[i].borderColor[1] = rects[i].fillColor[1] + 0.4f;
- rects[i].borderColor[2] = rects[i].fillColor[1] + 0.4f;
- rects[i].borderColor[3] = boxAlpha;
- }
-
- return rects;
-}
-
-int
-main(int argc, char** argv)
-{
- PuglTestApp app;
- memset(&app, 0, sizeof(app));
-
- const PuglRect frame = {0, 0, defaultWidth, defaultHeight};
-
- // Parse command line options
- app.numRects = 1024;
- app.opts = puglParseTestOptions(&argc, &argv);
- if (app.opts.help) {
- puglPrintTestUsage("pugl_gl3_test", "[NUM_RECTS]");
- return 1;
- }
-
- // Parse number of rectangles argument, if given
- if (argc == 1) {
- char* endptr = NULL;
- app.numRects = (size_t)strtol(argv[0], &endptr, 10);
- if (endptr != argv[0] + strlen(argv[0])) {
- puglPrintTestUsage("pugl_gl3_test", "[NUM_RECTS]");
- return 1;
- }
- }
-
- // Create world, view, and rect data
- app.world = puglNewWorld();
- app.view = puglNewView(app.world);
- app.rects = makeRects(app.numRects, defaultWidth, defaultHeight);
-
- // Set up world and view
- puglSetClassName(app.world, "PuglGL3Test");
- puglSetFrame(app.view, frame);
- puglSetMinSize(app.view, defaultWidth / 4, defaultHeight / 4);
- puglSetAspectRatio(app.view, 1, 1, 16, 9);
- puglSetBackend(app.view, puglGlBackend());
- puglSetViewHint(app.view, PUGL_USE_COMPAT_PROFILE, PUGL_TRUE);
- puglSetViewHint(app.view, PUGL_CONTEXT_VERSION_MAJOR, 3);
- puglSetViewHint(app.view, PUGL_CONTEXT_VERSION_MINOR, 3);
- puglSetViewHint(app.view, PUGL_RESIZABLE, app.opts.resizable);
- puglSetViewHint(app.view, PUGL_SAMPLES, app.opts.samples);
- puglSetViewHint(app.view, PUGL_DOUBLE_BUFFER, app.opts.doubleBuffer);
- puglSetViewHint(app.view, PUGL_SWAP_INTERVAL, app.opts.doubleBuffer);
- puglSetViewHint(app.view, PUGL_IGNORE_KEY_REPEAT, PUGL_TRUE);
- puglSetHandle(app.view, &app);
- puglSetEventFunc(app.view, onEvent);
-
- if (puglCreateWindow(app.view, "Pugl OpenGL 3")) {
- fprintf(stderr, "error: Failed to create window window\n");
- return 1;
- }
-
- // Enter context to set up GL stuff
- puglEnterContext(app.view, false);
-
- // Load GL functions via GLAD
- if (!gladLoadGLLoader((GLADloadproc)&puglGetProcAddress)) {
- fprintf(stderr, "error: Failed to load GL\n");
- puglFreeView(app.view);
- puglFreeWorld(app.world);
- return 1;
- }
-
- // Compile rectangle shaders and program
- app.drawRect = compileProgram(vertexSource, fragmentSource);
- if (!app.drawRect.program) {
- puglFreeView(app.view);
- puglFreeWorld(app.world);
- return 1;
- }
-
- // Get location of rectangle shader uniforms
- app.u_MVP = glGetUniformLocation(app.drawRect.program, "MVP");
- app.u_size = glGetUniformLocation(app.drawRect.program, "u_size");
- app.u_fillColor = glGetUniformLocation(app.drawRect.program, "u_fillColor");
- app.u_borderColor =
- glGetUniformLocation(app.drawRect.program, "u_borderColor");
-
- // Generate/bind a VAO to track state
- glGenVertexArrays(1, &app.vao);
- glBindVertexArray(app.vao);
-
- // Generate/bind a VBO to store vertex position data
- glGenBuffers(1, &app.vbo);
- glBindBuffer(GL_ARRAY_BUFFER, app.vbo);
- glBufferData(GL_ARRAY_BUFFER,
- sizeof(rectVertices),
- rectVertices,
- GL_STATIC_DRAW);
-
- // Set up the first/only attribute, position, as 2 floats from the VBO
- glEnableVertexAttribArray(0);
- glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), NULL);
-
- // Set up the IBO to index into the VBO
- glGenBuffers(1, &app.ibo);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, app.ibo);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER,
- sizeof(rectIndices),
- rectIndices,
- GL_STATIC_DRAW);
-
- // Finally ready to go, leave GL context and show the window
- puglLeaveContext(app.view, false);
- puglShowWindow(app.view);
-
- // Grind away, drawing continuously
- PuglFpsPrinter fpsPrinter = {puglGetTime(app.world)};
- while (!app.quit) {
- puglPostRedisplay(app.view);
- puglDispatchEvents(app.world);
- puglPrintFps(app.world, &fpsPrinter, &app.framesDrawn);
- }
-
- // Delete GL stuff
- puglEnterContext(app.view, false);
- glDeleteBuffers(1, &app.ibo);
- glDeleteBuffers(1, &app.vbo);
- glDeleteVertexArrays(1, &app.vao);
- deleteProgram(app.drawRect);
- puglLeaveContext(app.view, false);
-
- // Tear down view and world
- puglFreeView(app.view);
- puglFreeWorld(app.world);
- return 0;
-}
diff --git a/subprojects/d2tk/pugl/test/test_redisplay.c b/subprojects/d2tk/pugl/test/test_redisplay.c
new file mode 100644
index 0000000..6ec5bb8
--- /dev/null
+++ b/subprojects/d2tk/pugl/test/test_redisplay.c
@@ -0,0 +1,132 @@
+/*
+ Copyright 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.
+*/
+
+/*
+ Tests that redisplays posted in the event handler are dispatched at the end
+ of the same event loop iteration.
+*/
+
+#undef NDEBUG
+
+#include "test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __APPLE__
+static const double timeout = 1 / 60.0;
+#else
+static const double timeout = -1.0;
+#endif
+
+typedef enum {
+ START,
+ EXPOSED,
+ SHOULD_REDISPLAY,
+ POSTED_REDISPLAY,
+ REDISPLAYED,
+} State;
+
+typedef struct
+{
+ PuglTestOptions opts;
+ PuglWorld* world;
+ PuglView* view;
+ State state;
+} PuglTest;
+
+static const PuglRect redisplayRect = {1, 2, 3, 4};
+static const uintptr_t postRedisplayId = 42;
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTest* test = (PuglTest*)puglGetHandle(view);
+
+ if (test->opts.verbose) {
+ printEvent(event, "Event: ", true);
+ }
+
+ switch (event->type) {
+ case PUGL_EXPOSE:
+ if (test->state == START) {
+ test->state = EXPOSED;
+ } else if (test->state == POSTED_REDISPLAY &&
+ event->expose.x == redisplayRect.x &&
+ event->expose.y == redisplayRect.y &&
+ event->expose.width == redisplayRect.width &&
+ event->expose.height == redisplayRect.height) {
+ test->state = REDISPLAYED;
+ }
+ break;
+
+ case PUGL_CLIENT:
+ if (event->client.data1 == postRedisplayId) {
+ puglPostRedisplayRect(view, redisplayRect);
+ test->state = POSTED_REDISPLAY;
+ }
+ break;
+
+ default: break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTest app = {puglParseTestOptions(&argc, &argv),
+ puglNewWorld(PUGL_PROGRAM, 0),
+ NULL,
+ START};
+
+ // Set up view
+ app.view = puglNewView(app.world);
+ puglSetClassName(app.world, "Pugl Test");
+ puglSetBackend(app.view, puglStubBackend());
+ puglSetHandle(app.view, &app);
+ puglSetEventFunc(app.view, onEvent);
+
+ // Create and show window
+ assert(!puglCreateWindow(app.view, "Pugl Test"));
+ assert(!puglShowWindow(app.view));
+ while (app.state != EXPOSED) {
+ assert(!puglUpdate(app.world, timeout));
+ }
+
+ // Send a custom event to trigger a redisplay in the event loop
+ const PuglEventClient client = { PUGL_CLIENT, 0, postRedisplayId, 0 };
+ assert(!puglSendEvent(app.view, (const PuglEvent*)&client));
+
+ // Loop until an expose happens in the same iteration as the redisplay
+ app.state = SHOULD_REDISPLAY;
+ while (app.state != REDISPLAYED) {
+ assert(!puglUpdate(app.world, timeout));
+ assert(app.state != POSTED_REDISPLAY);
+ }
+
+ // Tear down
+ puglFreeView(app.view);
+ puglFreeWorld(app.world);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/test/test_show_hide.c b/subprojects/d2tk/pugl/test/test_show_hide.c
new file mode 100644
index 0000000..ed5ede7
--- /dev/null
+++ b/subprojects/d2tk/pugl/test/test_show_hide.c
@@ -0,0 +1,147 @@
+/*
+ Copyright 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.
+*/
+
+/*
+ Tests the basic sanity of view/window create, configure, map, expose, unmap,
+ and destroy events.
+*/
+
+#undef NDEBUG
+
+#include "test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+typedef enum {
+ START,
+ CREATED,
+ CONFIGURED,
+ MAPPED,
+ EXPOSED,
+ UNMAPPED,
+ DESTROYED,
+} State;
+
+typedef struct {
+ PuglTestOptions opts;
+ PuglWorld* world;
+ PuglView* view;
+ State state;
+} PuglTest;
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTest* test = (PuglTest*)puglGetHandle(view);
+
+ if (test->opts.verbose) {
+ printEvent(event, "Event: ", true);
+ }
+
+ switch (event->type) {
+ case PUGL_CREATE:
+ assert(test->state == START);
+ test->state = CREATED;
+ break;
+ case PUGL_CONFIGURE:
+ if (test->state == CREATED) {
+ test->state = CONFIGURED;
+ }
+ break;
+ case PUGL_MAP:
+ assert(test->state == CONFIGURED || test->state == UNMAPPED);
+ test->state = MAPPED;
+ break;
+ case PUGL_EXPOSE:
+ assert(test->state == MAPPED);
+ test->state = EXPOSED;
+ break;
+ case PUGL_UNMAP:
+ assert(test->state == EXPOSED);
+ test->state = UNMAPPED;
+ break;
+ case PUGL_DESTROY:
+ assert(test->state == UNMAPPED);
+ test->state = DESTROYED;
+ break;
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+static void
+tick(PuglWorld* world)
+{
+#ifdef __APPLE__
+ // FIXME: Expose events are not events on MacOS, so we can't block
+ // indefinitely here since it will block forever
+ assert(!puglUpdate(world, 1 / 30.0));
+#else
+ assert(!puglUpdate(world, -1));
+#endif
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTest test = {puglParseTestOptions(&argc, &argv),
+ puglNewWorld(PUGL_PROGRAM, 0),
+ NULL,
+ START};
+
+ // Set up view
+ test.view = puglNewView(test.world);
+ puglSetClassName(test.world, "Pugl Test");
+ puglSetBackend(test.view, puglStubBackend());
+ puglSetHandle(test.view, &test);
+ puglSetEventFunc(test.view, onEvent);
+
+ // Create initially invisible window
+ assert(!puglCreateWindow(test.view, "Pugl Test"));
+ assert(!puglGetVisible(test.view));
+ while (test.state < CREATED) {
+ tick(test.world);
+ }
+
+ // Show and hide window a couple of times
+ for (unsigned i = 0u; i < 2u; ++i) {
+ assert(!puglShowWindow(test.view));
+ while (test.state != EXPOSED) {
+ tick(test.world);
+ }
+
+ assert(puglGetVisible(test.view));
+ assert(!puglHideWindow(test.view));
+ while (test.state != UNMAPPED) {
+ tick(test.world);
+ }
+ }
+
+ // Tear down
+ assert(!puglGetVisible(test.view));
+ puglFreeView(test.view);
+ assert(test.state == DESTROYED);
+ puglFreeWorld(test.world);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/test/test_timer.c b/subprojects/d2tk/pugl/test/test_timer.c
new file mode 100644
index 0000000..11666ec
--- /dev/null
+++ b/subprojects/d2tk/pugl/test/test_timer.c
@@ -0,0 +1,160 @@
+/*
+ Copyright 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.
+*/
+
+/*
+ Tests that update events are received and than redisplays they trigger happen
+ immediately in the same event loop iteration.
+*/
+
+#undef NDEBUG
+
+#include "test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+#include <assert.h>
+#include <math.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#ifdef __APPLE__
+static const double timeout = 1 / 60.0;
+#else
+static const double timeout = -1.0;
+#endif
+
+#ifdef _WIN32
+// Windows SetTimer has a maximum resolution of 10ms
+static const double tolerance = 0.011;
+#else
+static const double tolerance = 0.002;
+#endif
+
+static const uintptr_t timerId = 1u;
+static const double timerPeriod = 1 / 60.0;
+
+typedef enum {
+ START,
+ EXPOSED,
+} State;
+
+typedef struct {
+ PuglTestOptions opts;
+ PuglWorld* world;
+ PuglView* view;
+ size_t numAlarms;
+ State state;
+} PuglTest;
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTest* test = (PuglTest*)puglGetHandle(view);
+
+ if (test->opts.verbose) {
+ printEvent(event, "Event: ", true);
+ }
+
+ switch (event->type) {
+ case PUGL_EXPOSE:
+ test->state = EXPOSED;
+ break;
+
+ case PUGL_TIMER:
+ assert(event->timer.id == timerId);
+ ++test->numAlarms;
+ break;
+
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+static double
+roundPeriod(const double period)
+{
+ return floor(period * 1000.0) / 1000.0; // Round down to milliseconds
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTest app = {puglParseTestOptions(&argc, &argv),
+ puglNewWorld(PUGL_PROGRAM, 0),
+ NULL,
+ 0,
+ START};
+
+ // Set up view
+ app.view = puglNewView(app.world);
+ puglSetClassName(app.world, "Pugl Test");
+ puglSetBackend(app.view, puglStubBackend());
+ puglSetHandle(app.view, &app);
+ puglSetEventFunc(app.view, onEvent);
+
+ // Create and show window
+ assert(!puglCreateWindow(app.view, "Pugl Test"));
+ assert(!puglShowWindow(app.view));
+ while (app.state != EXPOSED) {
+ assert(!puglUpdate(app.world, timeout));
+ }
+
+ // Register a timer with a longer period first
+ assert(!puglStartTimer(app.view, timerId, timerPeriod * 2.0));
+
+ // Replace it with the one we want (to ensure timers are replaced)
+ assert(!puglStartTimer(app.view, timerId, timerPeriod));
+
+ const double startTime = puglGetTime(app.world);
+
+ puglUpdate(app.world, 1.0);
+
+ // Calculate the actual period of the timer
+ const double endTime = puglGetTime(app.world);
+ const double duration = endTime - startTime;
+ const double expectedPeriod = roundPeriod(timerPeriod);
+ const double actualPeriod = roundPeriod(duration / (double)app.numAlarms);
+ const double difference = fabs(actualPeriod - expectedPeriod);
+
+ if (difference > tolerance) {
+ fprintf(stderr,
+ "error: Period not within %f of %f\n",
+ tolerance,
+ expectedPeriod);
+ fprintf(stderr, "note: Actual period %f\n", actualPeriod);
+ }
+
+ assert(difference <= tolerance);
+
+ // Deregister timer and tick once to synchronize
+ assert(!puglStopTimer(app.view, timerId));
+ puglUpdate(app.world, 0.0);
+
+ // Update for a half second and check that we receive no more alarms
+ app.numAlarms = 0;
+ puglUpdate(app.world, 0.5);
+ assert(app.numAlarms == 0);
+
+ puglFreeView(app.view);
+ puglFreeWorld(app.world);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/test/test_update.c b/subprojects/d2tk/pugl/test/test_update.c
new file mode 100644
index 0000000..ada3da8
--- /dev/null
+++ b/subprojects/d2tk/pugl/test/test_update.c
@@ -0,0 +1,124 @@
+/*
+ Copyright 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.
+*/
+
+/*
+ Tests that update events are received and that the redisplays they trigger
+ happen immediately in the same event loop iteration.
+*/
+
+#undef NDEBUG
+
+#include "test_utils.h"
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+#include <assert.h>
+#include <stdbool.h>
+#include <stddef.h>
+
+#ifdef __APPLE__
+static const double timeout = 1 / 60.0;
+#else
+static const double timeout = -1.0;
+#endif
+
+typedef enum {
+ START,
+ EXPOSED1,
+ UPDATED,
+ EXPOSED2,
+} State;
+
+typedef struct {
+ PuglTestOptions opts;
+ PuglWorld* world;
+ PuglView* view;
+ State state;
+} PuglTest;
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTest* test = (PuglTest*)puglGetHandle(view);
+
+ if (test->opts.verbose) {
+ printEvent(event, "Event: ", true);
+ }
+
+ switch (event->type) {
+ case PUGL_EXPOSE:
+ switch (test->state) {
+ case START:
+ test->state = EXPOSED1;
+ break;
+ case UPDATED:
+ test->state = EXPOSED2;
+ break;
+ default:
+ break;
+ }
+ break;
+
+ case PUGL_UPDATE:
+ if (test->state == EXPOSED1) {
+ puglPostRedisplay(view);
+ test->state = UPDATED;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTest app = {puglParseTestOptions(&argc, &argv),
+ puglNewWorld(PUGL_PROGRAM, 0),
+ NULL,
+ START};
+
+ // Set up view
+ app.view = puglNewView(app.world);
+ puglSetClassName(app.world, "Pugl Test");
+ puglSetBackend(app.view, puglStubBackend());
+ puglSetHandle(app.view, &app);
+ puglSetEventFunc(app.view, onEvent);
+
+ // Create and show window
+ assert(!puglCreateWindow(app.view, "Pugl Test"));
+ assert(!puglShowWindow(app.view));
+
+ // Tick until an expose happens
+ while (app.state < EXPOSED1) {
+ assert(!puglUpdate(app.world, timeout));
+ assert(app.state != UPDATED);
+ }
+
+ // Tick once and ensure the update and the expose it posted both happened
+ assert(!puglUpdate(app.world, 0.0));
+ assert(app.state == EXPOSED2);
+
+ // Tear down
+ puglFreeView(app.view);
+ puglFreeWorld(app.world);
+
+ return 0;
+}
diff --git a/subprojects/d2tk/pugl/test/test_utils.h b/subprojects/d2tk/pugl/test/test_utils.h
index 8e9c7ff..34b66c0 100644
--- a/subprojects/d2tk/pugl/test/test_utils.h
+++ b/subprojects/d2tk/pugl/test/test_utils.h
@@ -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
@@ -14,143 +14,40 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define __STDC_FORMAT_MACROS 1
+
#include "pugl/pugl.h"
-#include <math.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
typedef struct {
- double lastReportTime;
-} PuglFpsPrinter;
-
-typedef struct {
int samples;
int doubleBuffer;
+ int sync;
bool continuous;
bool help;
bool ignoreKeyRepeat;
bool resizable;
+ bool verbose;
+ bool errorChecking;
} PuglTestOptions;
-typedef float vec4[4];
-typedef vec4 mat4[4];
-
-static const float cubeStripVertices[] = {
- -1.0f, 1.0f, 1.0f, // Front top left
- 1.0f, 1.0f, 1.0f, // Front top right
- -1.0f, -1.0f, 1.0f, // Front bottom left
- 1.0f, -1.0f, 1.0f, // Front bottom right
- 1.0f, -1.0f, -1.0f, // Back bottom right
- 1.0f, 1.0f, 1.0f, // Front top right
- 1.0f, 1.0f, -1.0f, // Back top right
- -1.0f, 1.0f, 1.0f, // Front top left
- -1.0f, 1.0f, -1.0f, // Back top left
- -1.0f, -1.0f, 1.0f, // Front bottom left
- -1.0f, -1.0f, -1.0f, // Back bottom left
- 1.0f, -1.0f, -1.0f, // Back bottom right
- -1.0f, 1.0f, -1.0f, // Back top left
- 1.0f, 1.0f, -1.0f // Back top right
-};
-
-static const float cubeFrontLineLoop[] = {
- -1.0f, 1.0f, 1.0f, // Front top left
- 1.0f, 1.0f, 1.0f, // Front top right
- 1.0f, -1.0f, 1.0f, // Front bottom right
- -1.0f, -1.0f, 1.0f, // Front bottom left
-};
-
-static const float cubeBackLineLoop[] = {
- -1.0f, 1.0f, -1.0f, // Back top left
- 1.0f, 1.0f, -1.0f, // Back top right
- 1.0f, -1.0f, -1.0f, // Back bottom right
- -1.0f, -1.0f, -1.0f, // Back bottom left
-};
-
-static const float cubeSideLines[] = {
- -1.0f, 1.0f, 1.0f, // Front top left
- -1.0f, 1.0f, -1.0f, // Back top left
-
- -1.0f, -1.0f, 1.0f, // Front bottom left
- -1.0f, -1.0f, -1.0f, // Back bottom left
-
- 1.0f, 1.0f, 1.0f, // Front top right
- 1.0f, 1.0f, -1.0f, // Back top right
-
- 1.0f, -1.0f, 1.0f, // Front bottom right
- 1.0f, -1.0f, -1.0f, // Back bottom right
-};
-
-static inline void
-mat4Identity(mat4 m)
-{
- for (int c = 0; c < 4; ++c) {
- for (int r = 0; r < 4; ++r) {
- m[c][r] = c == r ? 1.0f : 0.0f;
- }
- }
-}
-
-static inline void
-mat4Translate(mat4 m, const float x, const float y, const float z)
-{
- m[3][0] = x;
- m[3][1] = y;
- m[3][2] = z;
-}
-
-static inline void
-mat4Mul(mat4 m, mat4 a, mat4 b)
-{
- for (int c = 0; c < 4; ++c) {
- for (int r = 0; r < 4; ++r) {
- m[c][r] = 0.0f;
- for (int k = 0; k < 4; ++k) {
- m[c][r] += a[k][r] * b[c][k];
- }
- }
- }
-}
-
-static inline void
-mat4Ortho(mat4 m,
- const float l,
- const float r,
- const float b,
- const float t,
- const float n,
- const float f)
+static inline int
+logError(const char* fmt, ...)
{
- m[0][0] = 2.0f / (r - l);
- m[0][1] = m[0][2] = m[0][3] = 0.0f;
-
- m[1][1] = 2.0f / (t - b);
- m[1][0] = m[1][2] = m[1][3] = 0.0f;
+ fprintf(stderr, "error: ");
- m[2][2] = -2.0f / (f - n);
- m[2][0] = m[2][1] = m[2][3] = 0.0f;
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
- m[3][0] = -(r + l) / (r - l);
- m[3][1] = -(t + b) / (t - b);
- m[3][2] = -(f + n) / (f - n);
- m[3][3] = 1.0f;
-}
-
-/** Calculate a projection matrix for a given perspective. */
-static inline void
-perspective(float* m, float fov, float aspect, float zNear, float zFar)
-{
- const float h = tanf(fov);
- const float w = h / aspect;
- const float depth = zNear - zFar;
- const float q = (zFar + zNear) / depth;
- const float qn = 2 * zFar * zNear / depth;
-
- m[0] = w; m[1] = 0; m[2] = 0; m[3] = 0;
- m[4] = 0; m[5] = h; m[6] = 0; m[7] = 0;
- m[8] = 0; m[9] = 0; m[10] = q; m[11] = -1;
- m[12] = 0; m[13] = 0; m[14] = qn; m[15] = 0;
+ return 1;
}
static inline int
@@ -164,49 +61,120 @@ printModifiers(const uint32_t mods)
}
static inline int
-printEvent(const PuglEvent* event, const char* prefix)
+printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
{
+#define FFMT "%6.1f"
+#define PFMT FFMT " " FFMT
+#define PRINT(fmt, ...) fprintf(stderr, fmt, __VA_ARGS__)
+
switch (event->type) {
+ case PUGL_NOTHING:
+ return 0;
case PUGL_KEY_PRESS:
- return fprintf(stderr, "%sKey press code %3u key U+%04X\n",
- prefix, event->key.keycode, event->key.key);
+ return PRINT("%sKey press code %3u key U+%04X\n",
+ prefix,
+ event->key.keycode,
+ event->key.key);
case PUGL_KEY_RELEASE:
- return fprintf(stderr, "%sKey release code %3u key U+%04X\n",
- prefix, event->key.keycode, event->key.key);
+ return PRINT("%sKey release code %3u key U+%04X\n",
+ prefix,
+ event->key.keycode,
+ event->key.key);
case PUGL_TEXT:
- return fprintf(stderr, "%sText entry code %3u char U+%04X (%s)\n",
- prefix, event->text.keycode,
- event->text.character, event->text.string);
+ return PRINT("%sText entry code %3u char U+%04X (%s)\n",
+ prefix,
+ event->text.keycode,
+ event->text.character,
+ event->text.string);
case PUGL_BUTTON_PRESS:
case PUGL_BUTTON_RELEASE:
- return (fprintf(stderr, "%sMouse %d %s at %f,%f ",
- prefix,
- event->button.button,
- (event->type == PUGL_BUTTON_PRESS) ? "down" : "up",
- event->button.x,
- event->button.y) +
+ return (PRINT("%sMouse %d %s at " PFMT " ",
+ prefix,
+ event->button.button,
+ (event->type == PUGL_BUTTON_PRESS) ? "down" : "up ",
+ event->button.x,
+ event->button.y) +
printModifiers(event->scroll.state));
case PUGL_SCROLL:
- return (fprintf(stderr, "%sScroll %f %f %f %f ",
- prefix,
- event->scroll.x, event->scroll.y,
- event->scroll.dx, event->scroll.dy) +
+ return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
+ prefix,
+ event->scroll.dx,
+ event->scroll.dy,
+ event->scroll.x,
+ event->scroll.y) +
printModifiers(event->scroll.state));
- case PUGL_ENTER_NOTIFY:
- return fprintf(stderr, "%sMouse enter at %f,%f\n",
- prefix, event->crossing.x, event->crossing.y);
- case PUGL_LEAVE_NOTIFY:
- return fprintf(stderr, "%sMouse leave at %f,%f\n",
- prefix, event->crossing.x, event->crossing.y);
+ case PUGL_POINTER_IN:
+ return PRINT("%sMouse enter at " PFMT "\n",
+ prefix,
+ event->crossing.x,
+ event->crossing.y);
+ case PUGL_POINTER_OUT:
+ return PRINT("%sMouse leave at " PFMT "\n",
+ prefix,
+ event->crossing.x,
+ event->crossing.y);
case PUGL_FOCUS_IN:
- return fprintf(stderr, "%sFocus in%s\n",
- prefix, event->focus.grab ? " (grab)" : "");
+ return PRINT("%sFocus in%s\n",
+ prefix,
+ event->focus.grab ? " (grab)" : "");
case PUGL_FOCUS_OUT:
- return fprintf(stderr, "%sFocus out%s\n",
- prefix, event->focus.grab ? " (ungrab)" : "");
- default: break;
+ return PRINT("%sFocus out%s\n",
+ prefix,
+ event->focus.grab ? " (ungrab)" : "");
+ case PUGL_CLIENT:
+ return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
+ prefix,
+ event->client.data1,
+ event->client.data2);
+ case PUGL_TIMER:
+ return PRINT("%sTimer %" PRIuPTR "\n", prefix, event->timer.id);
+ default:
+ break;
}
+ if (verbose) {
+ switch (event->type) {
+ case PUGL_CREATE:
+ return fprintf(stderr, "%sCreate\n", prefix);
+ case PUGL_DESTROY:
+ return fprintf(stderr, "%sDestroy\n", prefix);
+ case PUGL_MAP:
+ return fprintf(stderr, "%sMap\n", prefix);
+ case PUGL_UNMAP:
+ return fprintf(stderr, "%sUnmap\n", prefix);
+ case PUGL_UPDATE:
+ return fprintf(stderr, "%sUpdate\n", prefix);
+ case PUGL_CONFIGURE:
+ return PRINT("%sConfigure " PFMT " " PFMT "\n",
+ prefix,
+ event->configure.x,
+ event->configure.y,
+ event->configure.width,
+ event->configure.height);
+ case PUGL_EXPOSE:
+ return PRINT("%sExpose " PFMT " " PFMT "\n",
+ prefix,
+ event->expose.x,
+ event->expose.y,
+ event->expose.width,
+ event->expose.height);
+ case PUGL_CLOSE:
+ return PRINT("%sClose\n", prefix);
+ case PUGL_MOTION:
+ return PRINT("%sMouse motion at " PFMT "\n",
+ prefix,
+ event->motion.x,
+ event->motion.y);
+ default:
+ fprintf(stderr, "%sUnknown event type %d\n", prefix, event->type);
+ break;
+ }
+ }
+
+#undef PRINT
+#undef PFMT
+#undef FFMT
+
return 0;
}
@@ -216,17 +184,31 @@ puglPrintTestUsage(const char* prog, const char* posHelp)
printf("Usage: %s [OPTION]... %s\n\n"
" -a Enable anti-aliasing\n"
" -c Continuously animate and draw\n"
- " -d Enable double-buffering\n"
+ " -d Directly draw to window (no double-buffering)\n"
+ " -e Enable platform error-checking\n"
+ " -f Fast drawing, explicitly disable vertical sync\n"
" -h Display this help\n"
" -i Ignore key repeat\n"
- " -r Resizable window\n",
+ " -v Print verbose output\n"
+ " -r Resizable window\n"
+ " -s Explicitly enable vertical sync\n",
prog, posHelp);
}
static inline PuglTestOptions
puglParseTestOptions(int* pargc, char*** pargv)
{
- PuglTestOptions opts = { 0, 0, false, false, false, false };
+ PuglTestOptions opts = {
+ 0,
+ PUGL_TRUE,
+ PUGL_DONT_CARE,
+ false,
+ false,
+ false,
+ false,
+ false,
+ false,
+ };
char** const argv = *pargv;
int i = 1;
@@ -236,7 +218,11 @@ puglParseTestOptions(int* pargc, char*** pargv)
} else if (!strcmp(argv[i], "-c")) {
opts.continuous = true;
} else if (!strcmp(argv[i], "-d")) {
- opts.doubleBuffer = PUGL_TRUE;
+ opts.doubleBuffer = PUGL_FALSE;
+ } else if (!strcmp(argv[i], "-e")) {
+ opts.errorChecking = PUGL_TRUE;
+ } else if (!strcmp(argv[i], "-f")) {
+ opts.sync = PUGL_FALSE;
} else if (!strcmp(argv[i], "-h")) {
opts.help = true;
return opts;
@@ -244,11 +230,15 @@ puglParseTestOptions(int* pargc, char*** pargv)
opts.ignoreKeyRepeat = true;
} else if (!strcmp(argv[i], "-r")) {
opts.resizable = true;
+ } else if (!strcmp(argv[i], "-s")) {
+ opts.sync = PUGL_TRUE;
+ } else if (!strcmp(argv[i], "-v")) {
+ opts.verbose = true;
} else if (argv[i][0] != '-') {
break;
} else {
opts.help = true;
- fprintf(stderr, "error: Unknown option: %s\n", argv[i]);
+ logError("Unknown option: %s\n", argv[i]);
}
}
@@ -257,22 +247,3 @@ puglParseTestOptions(int* pargc, char*** pargv)
return opts;
}
-
-static inline void
-puglPrintFps(const PuglWorld* world,
- PuglFpsPrinter* printer,
- unsigned* const framesDrawn)
-{
- const double thisTime = puglGetTime(world);
- if (thisTime > printer->lastReportTime + 5) {
- const double fps = *framesDrawn / (thisTime - printer->lastReportTime);
- fprintf(stderr,
- "%u frames in %.0f seconds = %.3f FPS\n",
- *framesDrawn,
- thisTime - printer->lastReportTime,
- fps);
-
- printer->lastReportTime = thisTime;
- *framesDrawn = 0;
- }
-}
diff --git a/subprojects/d2tk/pugl/wscript b/subprojects/d2tk/pugl/wscript
index 5fce00e..797f321 100644
--- a/subprojects/d2tk/pugl/wscript
+++ b/subprojects/d2tk/pugl/wscript
@@ -34,65 +34,120 @@ def options(ctx):
'no-cairo': 'do not build Cairo support',
'no-static': 'do not build static library',
'no-shared': 'do not build shared library',
- 'log': 'print GL information to console',
'grab-focus': 'work around keyboard issues by grabbing focus'})
+ ctx.get_option_group('Test options').add_option(
+ '--gui-tests', action='store_true', help='Run GUI tests')
+
def configure(conf):
- conf.env.ALL_HEADERS = Options.options.all_headers
- conf.env.TARGET_PLATFORM = Options.options.target or sys.platform
conf.load('compiler_c', cache=True)
conf.load('autowaf', cache=True)
+ autowaf.set_c_lang(conf, 'c99')
+
+ conf.env.ALL_HEADERS = Options.options.all_headers
+ conf.env.TARGET_PLATFORM = Options.options.target or sys.platform
+ platform = conf.env.TARGET_PLATFORM
- if conf.env.TARGET_PLATFORM == 'win32':
- if conf.env.MSVC_COMPILER:
- conf.env.append_unique('CFLAGS', ['/wd4191'])
- elif conf.env.TARGET_PLATFORM == 'darwin':
+ if platform == 'darwin':
conf.env.append_unique('CFLAGS', ['-Wno-deprecated-declarations'])
- if not conf.env.MSVC_COMPILER:
+ if conf.env.MSVC_COMPILER:
+ conf.env.append_unique('CFLAGS', ['/wd4191'])
+ else:
conf.env.append_value('LINKFLAGS', ['-fvisibility=hidden'])
- for f in ('CFLAGS', 'CXXFLAGS'):
- conf.env.append_value(f, ['-fvisibility=hidden'])
- if Options.options.strict:
- conf.env.append_value(f, ['-Wunused-parameter', '-Wno-pedantic'])
-
- autowaf.set_c_lang(conf, 'c99')
-
- if not Options.options.no_gl:
- # TODO: Portable check for OpenGL
- conf.define('HAVE_GL', 1)
- conf.define('PUGL_HAVE_GL', 1)
-
- conf.check(features='c cshlib', lib='m', uselib_store='M', mandatory=False)
- conf.check(features='c cshlib', lib='dl', uselib_store='DL', mandatory=False)
+ conf.env.append_value('CFLAGS', ['-fvisibility=hidden'])
+ if Options.options.strict:
+ conf.env.append_value('CFLAGS', ['-Wunused-parameter',
+ '-Wno-pedantic'])
+
+ if conf.env.TARGET_PLATFORM == 'darwin':
+ conf.env.append_unique('CFLAGS', ['-DGL_SILENCE_DEPRECATION'])
+ conf.env.append_unique('CXXFLAGS', ['-DGL_SILENCE_DEPRECATION'])
+
+ if Options.options.ultra_strict and 'clang' in conf.env.CC:
+ for var in ['CFLAGS', 'CXXFLAGS']:
+ flags = conf.env[var]
+ conf.env[var] = [f for f in flags if not f.startswith('-W')]
+ conf.env.append_value(var, [
+ '-Weverything',
+ '-Wno-bad-function-cast',
+ '-Wno-double-promotion',
+ '-Wno-float-equal',
+ '-Wno-format-nonliteral',
+ '-Wno-padded',
+ '-Wno-reserved-id-macro',
+ '-Wno-switch-enum',
+ ])
+
+ conf.env.append_value('CXXFLAGS', ['-Wno-c++98-compat',
+ '-Wno-c++98-compat-pedantic'])
+
+ conf.check_cc(lib='m', uselib_store='M', mandatory=False)
+ conf.check_cc(lib='dl', uselib_store='DL', mandatory=False)
+
+ # Check for "native" platform dependencies
+ conf.env.HAVE_GL = False
+ if platform == 'darwin':
+ conf.check_cc(framework_name='Cocoa', framework='Cocoa',
+ uselib_store='COCOA')
+ if not Options.options.no_gl:
+ conf.check_cc(framework_name='OpenGL', uselib_store='GL',
+ mandatory=False)
+ conf.env.HAVE_GL = conf.env.FRAMEWORK_GL
+
+ elif platform == 'win32':
+ conf.check_cc(lib='gdi32', uselib_store='GDI32')
+ conf.check_cc(lib='user32', uselib_store='USER32')
+ if not Options.options.no_gl:
+ conf.check_cc(lib='opengl32', uselib_store='GL', mandatory=False)
+ conf.env.HAVE_GL = conf.env.LIB_GL
+ else:
+ conf.check_cc(lib='X11', uselib_store='X11')
+
+ xsync_fragment = """#include <X11/Xlib.h>
+ #include <X11/extensions/sync.h>
+ int main(void) { XSyncQueryExtension(0, 0, 0); return 0; }"""
+ if conf.check_cc(fragment=xsync_fragment,
+ uselib_store='XSYNC',
+ lib='Xext',
+ mandatory=False,
+ msg='Checking for function XSyncQueryExtension'):
+ conf.define('HAVE_XSYNC', 1)
+
+ if not Options.options.no_gl:
+ glx_fragment = """#include <GL/glx.h>
+ int main(void) { glXSwapBuffers(0, 0); return 0; }"""
+
+ conf.check_cc(lib='GLX', uselib_store='GLX', mandatory=False)
+ conf.check_cc(lib='GL', uselib_store='GL', mandatory=False)
+ conf.check_cc(fragment=glx_fragment,
+ lib='GLX' if conf.env.LIB_GLX else 'GL',
+ mandatory=False,
+ msg='Checking for GLX')
+ conf.env.HAVE_GL = conf.env.LIB_GL or conf.env.LIB_GLX
+
+ # Check for Cairo via pkg-config
if not Options.options.no_cairo:
autowaf.check_pkg(conf, 'cairo',
uselib_store = 'CAIRO',
atleast_version = '1.0.0',
+ system = True,
mandatory = False)
- if conf.is_defined('HAVE_CAIRO'):
- conf.define('PUGL_HAVE_CAIRO', 1)
-
- if Options.options.log:
- conf.define('PUGL_VERBOSE', 1)
- # Shared library building is broken on win32 for some reason
conf.env.update({
'BUILD_SHARED': not Options.options.no_shared,
'BUILD_STATIC': conf.env.BUILD_TESTS or not Options.options.no_static})
autowaf.set_lib_env(conf, 'pugl', PUGL_VERSION)
- conf.write_config_header('pugl_config.h', remove=False)
autowaf.display_summary(
conf,
{"Build static library": bool(conf.env.BUILD_STATIC),
"Build shared library": bool(conf.env.BUILD_SHARED),
- "OpenGL support": conf.is_defined('HAVE_GL'),
- "Cairo support": conf.is_defined('HAVE_CAIRO'),
- "Verbose console output": conf.is_defined('PUGL_VERBOSE')})
+ "OpenGL support": bool(conf.env.HAVE_GL),
+ "Cairo support": bool(conf.env.HAVE_CAIRO)})
def _build_pc_file(bld, name, desc, target, libname, deps={}, requires=[]):
@@ -128,6 +183,9 @@ def _build_pc_file(bld, name, desc, target, libname, deps={}, requires=[]):
LIBS=' '.join(link_flags))
+tests = ['redisplay', 'show_hide', 'update', 'timer']
+
+
def build(bld):
# C Headers
includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION
@@ -138,7 +196,7 @@ def build(bld):
bld.install_files(detaildir, bld.path.ant_glob('pugl/detail/*.h'))
bld.install_files(detaildir, bld.path.ant_glob('pugl/detail/*.c'))
- # Library dependencies of pugl libraries (for buiding tests)
+ # Library dependencies of pugl libraries (for building examples)
deps = {}
def build_pugl_lib(name, **kwargs):
@@ -156,14 +214,14 @@ def build(bld):
bld(features = 'c cshlib',
name = name,
target = 'pugl_' + name,
- cflags = ['-DPUGL_INTERNAL', '-DPUGL_SHARED'],
+ defines = ['PUGL_INTERNAL', 'PUGL_SHARED'],
**args)
if bld.env.BUILD_STATIC:
bld(features = 'c cstlib',
name = 'pugl_%s_static' % name,
target = 'pugl_' + name,
- cflags = ['-DPUGL_INTERNAL'],
+ defines = ['PUGL_INTERNAL', 'PUGL_DISABLE_DEPRECATED'],
**args)
def build_platform(platform, **kwargs):
@@ -186,18 +244,17 @@ def build(bld):
if bld.env.TARGET_PLATFORM == 'win32':
platform = 'win'
build_platform('win',
- lib=['gdi32', 'user32'],
+ uselib=['GDI32', 'USER32'],
source=lib_source + ['pugl/detail/win.c'])
- if bld.is_defined('HAVE_GL'):
+ if bld.env.HAVE_GL:
build_backend('win', 'gl',
- lib=['gdi32', 'user32', 'opengl32'],
+ uselib=['GDI32', 'USER32', 'GL'],
source=['pugl/detail/win_gl.c'])
- if bld.is_defined('HAVE_CAIRO'):
+ if bld.env.HAVE_CAIRO:
build_backend('win', 'cairo',
- uselib=['CAIRO'],
- lib=['gdi32', 'user32'],
+ uselib=['CAIRO', 'GDI32', 'USER32'],
source=['pugl/detail/win_cairo.c'])
elif bld.env.TARGET_PLATFORM == 'darwin':
@@ -206,12 +263,16 @@ def build(bld):
framework=['Cocoa'],
source=lib_source + ['pugl/detail/mac.m'])
- if bld.is_defined('HAVE_GL'):
+ build_backend('mac', 'stub',
+ framework=['Cocoa'],
+ source=['pugl/detail/mac_stub.m'])
+
+ if bld.env.HAVE_GL:
build_backend('mac', 'gl',
framework=['Cocoa', 'OpenGL'],
source=['pugl/detail/mac_gl.m'])
- if bld.is_defined('HAVE_CAIRO'):
+ if bld.env.HAVE_CAIRO:
build_backend('mac', 'cairo',
framework=['Cocoa'],
uselib=['CAIRO'],
@@ -219,21 +280,21 @@ def build(bld):
else:
platform = 'x11'
build_platform('x11',
- lib=['X11'],
+ uselib=['M', 'X11', 'XSYNC'],
source=lib_source + ['pugl/detail/x11.c'])
- if bld.is_defined('HAVE_GL'):
+ if bld.env.HAVE_GL:
+ glx_lib = 'GLX' if bld.env.LIB_GLX else 'GL'
build_backend('x11', 'gl',
- lib=['X11', 'GL'],
+ uselib=[glx_lib, 'X11'],
source=['pugl/detail/x11_gl.c'])
- if bld.is_defined('HAVE_CAIRO'):
+ if bld.env.HAVE_CAIRO:
build_backend('x11', 'cairo',
- lib=['X11'],
- uselib=['CAIRO'],
+ uselib=['CAIRO', 'X11'],
source=['pugl/detail/x11_cairo.c'])
- def build_test(prog, source, platform, backend, **kwargs):
+ def build_example(prog, source, platform, backend, **kwargs):
use = ['pugl_%s_static' % platform,
'pugl_%s_%s_static' % (platform, backend)]
@@ -261,58 +322,64 @@ def build(bld):
**kwargs)
if bld.env.BUILD_TESTS:
- if bld.is_defined('HAVE_GL'):
- build_test('pugl_test', ['test/pugl_test.c'],
- platform, 'gl', uselib=['M'])
- build_test('pugl_gl3_test',
- ['test/pugl_gl3_test.c', 'test/glad/glad.c'],
- platform, 'gl', uselib=['M', 'DL'])
-
- if bld.is_defined('HAVE_CAIRO'):
- build_test('pugl_cairo_test', ['test/pugl_cairo_test.c'],
- platform, 'cairo',
- uselib=['M', 'CAIRO'])
+ for s in ('rect.vert', 'rect.frag'):
+ # Copy shaders to build directory for example programs
+ bld(features = 'subst',
+ is_copy = True,
+ source = 'shaders/%s' % s,
+ target = 'shaders/%s' % s)
+
+ if bld.env.HAVE_GL:
+ build_example('pugl_embed_demo', ['examples/pugl_embed_demo.c'],
+ platform, 'gl', uselib=['GL', 'M'])
+ build_example('pugl_window_demo', ['examples/pugl_window_demo.c'],
+ platform, 'gl', uselib=['GL', 'M'])
+ build_example('pugl_print_events',
+ ['examples/pugl_print_events.c'],
+ platform, 'stub')
+ build_example('pugl_gl3_demo',
+ ['examples/pugl_gl3_demo.c', 'examples/glad/glad.c'],
+ platform, 'gl', uselib=['DL', 'GL', 'M'])
+
+ if bld.env.HAVE_CAIRO:
+ build_example('pugl_cairo_demo', ['examples/pugl_cairo_demo.c'],
+ platform, 'cairo',
+ uselib=['M', 'CAIRO'])
+
+ for test in tests:
+ bld(features = 'c cprogram',
+ source = 'test/test_%s.c' % test,
+ target = 'test/test_%s' % test,
+ install_path = '',
+ use = ['pugl_%s_static' % platform,
+ 'pugl_%s_stub_static' % platform],
+ uselib = deps[platform]['uselib'] + ['CAIRO'])
if bld.env.DOCS:
- bld(features = 'subst',
- source = 'doc/reference.doxygen.in',
- target = 'doc/reference.doxygen',
- install_path = '',
- PUGL_VERSION = PUGL_VERSION,
- PUGL_SRCDIR = os.path.abspath(bld.path.srcpath()))
-
- bld(features = 'doxygen',
- doxyfile = 'doc/reference.doxygen')
+ autowaf.build_dox(bld, 'PUGL', PUGL_VERSION, top, out)
def test(tst):
- pass
+ if tst.options.gui_tests:
+ with tst.group('gui') as check:
+ for test in tests:
+ check(['test/test_%s' % test])
def lint(ctx):
"checks code for style issues"
+ import json
import subprocess
subprocess.call("flake8 wscript --ignore E221,W504,E251,E241",
shell=True)
- cmd = ("clang-tidy -p=. -header-filter=.* -checks=\"*," +
- "-bugprone-suspicious-string-compare," +
- "-clang-analyzer-alpha.*," +
- "-cppcoreguidelines-avoid-magic-numbers," +
- "-google-readability-todo," +
- "-hicpp-multiway-paths-covered," +
- "-hicpp-signed-bitwise," +
- "-hicpp-uppercase-literal-suffix," +
- "-llvm-header-guard," +
- "-misc-misplaced-const," +
- "-misc-unused-parameters," +
- "-readability-else-after-return," +
- "-readability-magic-numbers," +
- "-readability-uppercase-literal-suffix\" " +
- "../pugl/detail/*.c")
-
- subprocess.call(cmd, cwd='build', shell=True)
+ with open('build/compile_commands.json', 'r') as db:
+ commands = json.load(db)
+ files = [c['file'] for c in commands
+ if os.path.basename(c['file']) != 'glad.c']
+
+ subprocess.call(['clang-tidy'] + files, cwd='build')
try:
subprocess.call(['iwyu_tool.py', '-o', 'clang', '-p', 'build'])