M .clang-format => .clang-format +2 -2
@@ 59,7 59,7 @@ Cpp11BracedListStyle: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
-FixNamespaceComments: false
+FixNamespaceComments: true
ForEachMacros:
- foreach
- Q_FOREACH
@@ 104,7 104,7 @@ SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
-SpaceBeforeCpp11BracedList: true
+SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
M .clang-tidy => .clang-tidy +7 -2
@@ 1,5 1,6 @@
Checks: >
*,
+ -*avoid-c-arrays,
-*magic-numbers,
-*uppercase-literal-suffix,
-android-cloexec-fopen,
@@ 7,10 8,14 @@ Checks: >
-cert-flp30-c,
-clang-analyzer-alpha.*,
-clang-analyzer-security.FloatLoopCounter,
+ -google-runtime-references,
-hicpp-multiway-paths-covered,
-hicpp-signed-bitwise,
-llvm-header-guard,
- -readability-else-after-return
+ -modernize-use-trailing-return-type,
+ -readability-else-after-return,
+ -readability-implicit-bool-conversion,
+ -readability-named-parameter,
WarningsAsErrors: ''
-HeaderFilterRegex: 'pugl/.*|test/.*'
+HeaderFilterRegex: 'pugl/.*|test/.*|examples/.*'
FormatStyle: file
A .editorconfig => .editorconfig +18 -0
@@ 0,0 1,18 @@
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.{c,h,m,cpp,hpp,mm,glsl,frag,vert}]
+indent_style = tab
+
+[wscript]
+indent_style = space
+indent_size = 4
+
+[doc/**.{html,xml,css}]
+indent_style = space
+indent_size = 2
M .gitlab-ci.yml => .gitlab-ci.yml +14 -14
@@ 11,7 11,7 @@ variables:
arm32_dbg:
<<: *build_definition
image: lv2plugin/debian-arm32
- script: python ./waf configure build -dsT --no-coverage
+ script: python ./waf configure build -dST --werror --no-coverage
variables:
CC: "arm-linux-gnueabihf-gcc"
CXX: "arm-linux-gnueabihf-g++"
@@ 19,7 19,7 @@ arm32_dbg:
arm32_rel:
<<: *build_definition
image: lv2plugin/debian-arm32
- script: python ./waf configure build -sT --no-coverage
+ script: python ./waf configure build -ST --werror --no-coverage
variables:
CC: "arm-linux-gnueabihf-gcc"
CXX: "arm-linux-gnueabihf-g++"
@@ 27,7 27,7 @@ arm32_rel:
arm64_dbg:
<<: *build_definition
image: lv2plugin/debian-arm64
- script: python ./waf configure build -dsT --no-coverage
+ script: python ./waf configure build -dST --werror --no-coverage
variables:
CC: "aarch64-linux-gnu-gcc"
CXX: "aarch64-linux-gnu-g++"
@@ 35,7 35,7 @@ arm64_dbg:
arm64_rel:
<<: *build_definition
image: lv2plugin/debian-arm64
- script: python ./waf configure build -sT --no-coverage
+ script: python ./waf configure build -ST --werror --no-coverage
variables:
CC: "aarch64-linux-gnu-gcc"
CXX: "aarch64-linux-gnu-g++"
@@ 43,7 43,7 @@ arm64_rel:
x64_dbg:
<<: *build_definition
image: lv2plugin/debian-x64
- script: python ./waf configure build -dsT --no-coverage --docs
+ script: python ./waf configure build -dST --werror --no-coverage --docs
artifacts:
paths:
- build/doc
@@ 51,12 51,12 @@ x64_dbg:
x64_rel:
<<: *build_definition
image: lv2plugin/debian-x64
- script: python ./waf configure build -sT --no-coverage
+ script: python ./waf configure build -ST --werror --no-coverage
mingw32_dbg:
<<: *build_definition
image: lv2plugin/debian-mingw32
- script: python ./waf configure build -dsT --no-coverage --target=win32
+ script: python ./waf configure build -dST --werror --no-coverage --target=win32
variables:
CC: "i686-w64-mingw32-gcc"
CXX: "i686-w64-mingw32-g++"
@@ 64,7 64,7 @@ mingw32_dbg:
mingw32_rel:
<<: *build_definition
image: lv2plugin/debian-mingw32
- script: python ./waf configure build -sT --no-coverage --target=win32
+ script: python ./waf configure build -ST --werror --no-coverage --target=win32
variables:
CC: "i686-w64-mingw32-gcc"
CXX: "i686-w64-mingw32-g++"
@@ 72,7 72,7 @@ mingw32_rel:
mingw64_dbg:
<<: *build_definition
image: lv2plugin/debian-mingw64
- script: python ./waf configure build -dsT --no-coverage --target=win32
+ script: python ./waf configure build -dST --werror --no-coverage --target=win32
variables:
CC: "x86_64-w64-mingw32-gcc"
CXX: "x86_64-w64-mingw32-g++"
@@ 80,30 80,30 @@ mingw64_dbg:
mingw64_rel:
<<: *build_definition
image: lv2plugin/debian-mingw64
- script: python ./waf configure build -sT --no-coverage --target=win32
+ script: python ./waf configure build -ST --werror --no-coverage --target=win32
variables:
CC: "x86_64-w64-mingw32-gcc"
CXX: "x86_64-w64-mingw32-g++"
mac_dbg:
<<: *build_definition
- script: python ./waf configure build -dsT --no-coverage
+ script: python ./waf configure build -dST --werror --no-coverage
tags: [macos]
mac_rel:
<<: *build_definition
- script: python ./waf configure build -sT --no-coverage
+ script: python ./waf configure build -ST --werror --no-coverage
tags: [macos]
win_dbg:
<<: *build_definition
script:
- - python ./waf configure build -dT --no-coverage
+ - python ./waf configure build -dST --werror --no-coverage
tags: [windows,msvc,python]
win_rel:
<<: *build_definition
- script: python ./waf configure build -T --no-coverage
+ script: python ./waf configure build -ST --werror --no-coverage
tags: [windows,msvc,python]
pages:
M AUTHORS => AUTHORS +2 -1
@@ 8,4 8,5 @@ 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
+Zoë Sparks <zoe@milky.flowers>
+Jean Pierre Cimalando <jp-dev@inbox.ru>
M COPYING => COPYING +1 -1
@@ 1,4 1,4 @@
-Copyright 2011-2020 David Robillard <http://drobilla.net>
+Copyright 2011-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
M README.md => README.md +15 -5
@@ 70,8 70,16 @@ 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.
+There are a few unit tests included, but unfortunately manual testing is still
+required. The tests and example programs will be built if you pass the
+`--test` option when configuring:
+
+ ./waf configure --test
+
+Then, after building, the unit tests can be run:
+
+ ./waf
+ ./waf test --gui-tests
Several example programs are included that serve as both manual tests and
demonstrations:
@@ 84,9 92,9 @@ demonstrations:
* `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_shader_demo` demonstrates using more modern OpenGL (version 3 or 4)
+ 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.
@@ 94,6 102,8 @@ demonstrations:
* `pugl_print_events` is a utility that prints all received events to the
console in a human readable format.
+ * `pugl_cxx_demo` is a simple cube demo that uses the C++ API.
+
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.
M doc/layout.xml => doc/layout.xml +2 -1
@@ 25,6 25,7 @@
<!-- Layout definition for a class page -->
<class>
<briefdescription visible="yes"/>
+ <detaileddescription title=""/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<inheritancegraph visible="$CLASS_GRAPH"/>
<collaborationgraph visible="$COLLABORATION_GRAPH"/>
@@ 107,6 108,7 @@
<!-- Layout definition for a file page -->
<file>
<briefdescription visible="yes"/>
+ <detaileddescription title=""/>
<includes visible="$SHOW_INCLUDE_FILES"/>
<includegraph visible="$INCLUDE_GRAPH"/>
<includedbygraph visible="$INCLUDED_BY_GRAPH"/>
@@ 122,7 124,6 @@
<variables title=""/>
<membergroups visible="yes"/>
</memberdecl>
- <detaileddescription title=""/>
<memberdef>
<inlineclasses title=""/>
<defines title=""/>
M doc/mainpage.md => doc/mainpage.md +2 -2
@@ 1,6 1,6 @@
This is the API documentation for Pugl.
-This documentation is based around the [C API](@ref pugl_api),
-there is also a [C++ API](@ref pugl) in the `pugl` namespace.
+This page refers to the [C API](@ref pugl_c),
+there is also a [C++ API](@ref pugl_cxx) in the `pugl` namespace.
The Pugl API revolves around two main objects:
the [World](@ref world) and the [View](@ref view).
M doc/reference.doxygen.in => doc/reference.doxygen.in +11 -17
@@ 243,12 243,6 @@ TAB_SIZE = 4
ALIASES =
-# This tag can be used to specify a number of word-keyword mappings (TCL only).
-# A mapping has the form "name=value". For example adding "class=itcl::class"
-# will allow you to use the command class in the itcl::class meaning.
-
-TCL_SUBST =
-
# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
# only. Doxygen will then generate output that is more tailored for C. For
# instance, some of the names that are used will be different. The list of all
@@ 447,7 441,7 @@ LOOKUP_CACHE_SIZE = 0
# normally produced when WARNINGS is set to YES.
# The default value is: NO.
-EXTRACT_ALL = YES
+EXTRACT_ALL = NO
# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
# be included in the documentation.
@@ 498,7 492,7 @@ EXTRACT_ANON_NSPACES = NO
# section is generated. This option has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
-HIDE_UNDOC_MEMBERS = YES
+HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
@@ 506,7 500,7 @@ HIDE_UNDOC_MEMBERS = YES
# has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
-HIDE_UNDOC_CLASSES = YES
+HIDE_UNDOC_CLASSES = NO
# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
# (class|struct|union) declarations. If set to NO, these declarations will be
@@ 543,7 537,7 @@ CASE_SENSE_NAMES = YES
# scope will be hidden.
# The default value is: NO.
-HIDE_SCOPE_NAMES = NO
+HIDE_SCOPE_NAMES = YES
# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
# append additional text to a page's title, such as Class Reference. If set to
@@ 752,7 746,7 @@ WARNINGS = YES
# will automatically be disabled.
# The default value is: YES.
-WARN_IF_UNDOCUMENTED = YES
+WARN_IF_UNDOCUMENTED = NO
# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
# potential errors in the documentation, such as not documenting some parameters
@@ 769,7 763,7 @@ WARN_IF_DOC_ERROR = YES
# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
# The default value is: NO.
-WARN_NO_PARAMDOC = YES
+WARN_NO_PARAMDOC = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered.
@@ 1165,7 1159,7 @@ HTML_EXTRA_FILES =
# Minimum value: 0, maximum value: 359, default value: 220.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_COLORSTYLE_HUE = 160
+HTML_COLORSTYLE_HUE = 120
# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
# in the HTML output. For a value of 0 the output will use grayscales only. A
@@ 1173,7 1167,7 @@ HTML_COLORSTYLE_HUE = 160
# Minimum value: 0, maximum value: 255, default value: 100.
# This tag requires that the tag GENERATE_HTML is set to YES.
-HTML_COLORSTYLE_SAT = 100
+HTML_COLORSTYLE_SAT = 32
# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
# luminance component of the colors in the HTML output. Values below 100
@@ 1685,7 1679,7 @@ COMPACT_LATEX = NO
# The default value is: a4.
# This tag requires that the tag GENERATE_LATEX is set to YES.
-PAPER_TYPE = a4wide
+PAPER_TYPE = a4
# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
# that should be included in the LaTeX output. The package can be specified just
@@ 1930,7 1924,7 @@ MAN_LINKS = NO
# captures the structure of the code including all documentation.
# The default value is: NO.
-GENERATE_XML = NO
+GENERATE_XML = YES
# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
@@ 2090,7 2084,7 @@ INCLUDE_FILE_PATTERNS =
# recursively expanded use the := operator instead of the = operator.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
-PREDEFINED = PUGL_API PUGL_DISABLE_DEPRECATED
+PREDEFINED = PUGL_API PUGL_DISABLE_DEPRECATED PUGL_CONST_FUNC=
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
# tag can be used to specify a list of macro names that should be expanded. The
M doc/style.css => doc/style.css +64 -9
@@ 233,6 233,14 @@ div.groupHeader {
font-weight: 700;
}
+h2.groupheader {
+ line-height: 1.18em;
+ font-size: 1em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
+}
+
a + h2.groupheader {
display: none;
}
@@ 387,6 395,18 @@ table.memberdecls {
line-height: 1.3em;
}
+table.memberdecls h3 {
+ line-height: 1.18em;
+ font-size: 1em;
+ font-weight: 600;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ margin: 1.25em 0 0.5em 0;
+}
+
+tr.inherit_header td {
+ padding: 1em 0 0.5em 0;
+}
+
.mdescLeft,.mdescRight,.memItemLeft,.memItemRight,.memTemplItemLeft,.memTemplItemRight,.memTemplParams {
margin: 0;
padding: 0;
@@ 419,13 439,28 @@ td.memSeparator {
display: none;
}
+td.mlabels-left {
+ margin-left: 0;
+ padding-left: 0;
+}
+
td.mlabels-right {
- vertical-align: top;
- padding-top: 4px;
color: #B4C342;
+ font-weight: normal;
+ margin-left: 1em;
+ vertical-align: bottom;
}
.memtitle {
+ border-bottom: 1px solid #EEE;
+ font-family: Helvetica, Arial, "DejaVu Sans Condensed", Verdana, sans-serif;
+ font-size: 1.18em;
+ font-weight: 600;
+ line-height: 1.41em;
+ margin: 1.5em 0 0 0;
+}
+
+.permalink {
display: none;
}
@@ 446,18 481,20 @@ td.mlabels-right {
}
.memitem {
- padding: 0.5em 0.5em 0.25em 0.5em;
- margin: 1em 0 2em 0;
+ padding: 0;
+ margin: 0 0 3em 0;
}
.memproto {
border-bottom: 1px solid #EEE;
+ border-left: 1px solid #EEE;
+ color: #444;
+ float: right;
font-family: "SF Mono", Menlo, Consolas, "DejaVu Sans Mono", monospace, fixed;
- font-size: 1.09em;
- font-weight: 600;
- line-height: 1.41em;
- margin-bottom: 0.25em;
- padding-bottom: 0.125em;
+ font-size: small;
+ margin-bottom: 1em;
+ margin-left: 1em;
+ padding: 0.25em 0 0.25em 0.25em;
}
.memproto .paramname {
@@ 465,6 502,11 @@ td.mlabels-right {
padding-right: 0.25em;
}
+.mlabels {
+ padding-left: 0;
+ padding-right: 0;
+}
+
.memdoc {
padding: 0;
}
@@ 473,6 515,19 @@ td.mlabels-right {
font-style: italic;
color: #444;
margin-bottom: 0.75em;
+ margin-top: 0;
+ padding-top: 0.25em;
+ font-weight: normal;
+}
+
+.memdoc > p:first-child, .memdoc .textblock > h3:first-child {
+ color: #444;
+ margin-bottom: 0.75em;
+ margin-top: 0;
+ padding-top: 0.25em;
+ font-weight: normal;
+ color: #444;
+ font-size: 0.9em;
}
.paramkey {
M examples/cube_view.h => examples/cube_view.h +1 -1
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
M examples/demo_utils.h => examples/demo_utils.h +2 -2
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2019 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 135,7 135,7 @@ mat4Ortho(mat4 m,
m[3][3] = 1.0f;
}
-/** Calculate a projection matrix for a given perspective. */
+/// Calculate a projection matrix for a given perspective
static inline void
perspective(float* m, float fov, float aspect, float zNear, float zFar)
{
M examples/pugl_cairo_demo.c => examples/pugl_cairo_demo.c +6 -6
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_cairo_demo.c An example of drawing with Cairo.
+ @file pugl_cairo_demo.c
+ @brief An example of drawing with Cairo.
*/
#include "demo_utils.h"
@@ 32,8 33,8 @@
#include <string.h>
typedef struct {
- PuglTestOptions opts;
PuglWorld* world;
+ PuglTestOptions opts;
unsigned framesDrawn;
int quit;
bool entered;
@@ 235,11 236,10 @@ main(int argc, char** argv)
app.world = puglNewWorld(PUGL_PROGRAM, 0);
puglSetClassName(app.world, "PuglCairoTest");
- PuglRect frame = { 0, 0, 512, 512 };
- PuglView* view = puglNewView(app.world);
+ PuglView* view = puglNewView(app.world);
puglSetWindowTitle(view, "Pugl Cairo Demo");
- puglSetFrame(view, frame);
+ puglSetDefaultSize(view, 512, 512);
puglSetMinSize(view, 256, 256);
puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable);
puglSetHandle(view, &app);
A examples/pugl_cursor_demo.c => examples/pugl_cursor_demo.c +171 -0
@@ 0,0 1,171 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_cursor_demo.c
+ @brief An example of changing the mouse cursor.
+*/
+
+#include "test/test_utils.h"
+
+#include "pugl/gl.h"
+#include "pugl/pugl.h"
+#include "pugl/pugl_gl.h"
+
+#include <stdbool.h>
+
+static const int N_CURSORS = 7;
+static const int N_ROWS = 2;
+static const int N_COLS = 4;
+
+typedef struct {
+ PuglWorld* world;
+ PuglTestOptions opts;
+ bool quit;
+} PuglTestApp;
+
+static void
+onConfigure(const double width, const double height)
+{
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ glClearColor(0.2f, 0.2f, 0.2f, 1.0f);
+
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glViewport(0, 0, (int)width, (int)height);
+}
+
+static void
+onExpose(void)
+{
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ glColor3f(0.6f, 0.6f, 0.6f);
+
+ for (int row = 1; row < N_ROWS; ++row) {
+ const float y = (float)row * (2.0f / (float)N_ROWS) - 1.0f;
+ glBegin(GL_LINES);
+ glVertex2f(-1.0f, y);
+ glVertex2f(1.0f, y);
+ glEnd();
+ }
+
+ for (int col = 1; col < N_COLS; ++col) {
+ const float x = (float)col * (2.0f / (float)N_COLS) - 1.0f;
+ glBegin(GL_LINES);
+ glVertex2f(x, -1.0f);
+ glVertex2f(x, 1.0f);
+ glEnd();
+ }
+}
+
+static void
+onMotion(PuglView* view, double x, double y)
+{
+ const PuglRect frame = puglGetFrame(view);
+ int row = (int)(y * N_ROWS / frame.height);
+ int col = (int)(x * N_COLS / frame.width);
+
+ row = (row < 0) ? 0 : (row >= N_ROWS) ? (N_ROWS - 1) : row;
+ col = (col < 0) ? 0 : (col >= N_COLS) ? (N_COLS - 1) : col;
+
+ const PuglCursor cursor = (PuglCursor)((row * N_COLS + col) % N_CURSORS);
+ puglSetCursor(view, cursor);
+}
+
+static PuglStatus
+onEvent(PuglView* view, const PuglEvent* event)
+{
+ PuglTestApp* app = (PuglTestApp*)puglGetHandle(view);
+
+ printEvent(event, "Event: ", app->opts.verbose);
+
+ switch (event->type) {
+ case PUGL_CONFIGURE:
+ onConfigure(event->configure.width, event->configure.height);
+ break;
+ case PUGL_KEY_PRESS:
+ if (event->key.key == 'q' || event->key.key == PUGL_KEY_ESCAPE) {
+ app->quit = 1;
+ }
+ break;
+ case PUGL_MOTION:
+ onMotion(view, event->motion.x, event->motion.y);
+ break;
+ case PUGL_EXPOSE:
+ onExpose();
+ break;
+ case PUGL_CLOSE:
+ app->quit = 1;
+ break;
+ default:
+ break;
+ }
+
+ return PUGL_SUCCESS;
+}
+
+int
+main(int argc, char** argv)
+{
+ PuglTestApp app = {0};
+
+ app.opts = puglParseTestOptions(&argc, &argv);
+ if (app.opts.help) {
+ puglPrintTestUsage(argv[0], "");
+ return 1;
+ }
+
+ app.world = puglNewWorld(PUGL_PROGRAM, 0);
+
+ puglSetWorldHandle(app.world, &app);
+ puglSetClassName(app.world, "Pugl Test");
+
+ PuglView* view = puglNewView(app.world);
+
+ puglSetWindowTitle(view, "Pugl Window Demo");
+ puglSetDefaultSize(view, 512, 256);
+ puglSetMinSize(view, 128, 64);
+ puglSetBackend(view, puglGlBackend());
+
+ puglSetViewHint(view, PUGL_USE_DEBUG_CONTEXT, app.opts.errorChecking);
+ puglSetViewHint(view, PUGL_RESIZABLE, app.opts.resizable);
+ puglSetViewHint(view, PUGL_SAMPLES, app.opts.samples);
+ puglSetViewHint(view, PUGL_DOUBLE_BUFFER, app.opts.doubleBuffer);
+ puglSetViewHint(view, PUGL_SWAP_INTERVAL, app.opts.sync);
+ puglSetViewHint(view, PUGL_IGNORE_KEY_REPEAT, app.opts.ignoreKeyRepeat);
+ puglSetHandle(view, &app);
+ puglSetEventFunc(view, onEvent);
+
+ const PuglStatus st = puglRealize(view);
+ if (st) {
+ return logError("Failed to create window (%s)\n", puglStrerror(st));
+ }
+
+ puglShowWindow(view);
+
+ while (!app.quit) {
+ puglUpdate(app.world, -1.0);
+ }
+
+ puglFreeView(view);
+ puglFreeWorld(app.world);
+
+ return 0;
+}
A examples/pugl_cxx_demo.cpp => examples/pugl_cxx_demo.cpp +146 -0
@@ 0,0 1,146 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_cxx_demo.cpp
+ @brief A simple demo of the Pugl C++ API.
+*/
+
+#include "cube_view.h"
+#include "demo_utils.h"
+#include "test/test_utils.h"
+
+#include "pugl/gl.h"
+#include "pugl/pugl.h"
+#include "pugl/pugl.hpp"
+#include "pugl/pugl.ipp"
+#include "pugl/pugl_gl.hpp"
+
+#include <cmath>
+
+class CubeView : public pugl::View
+{
+public:
+ explicit CubeView(pugl::World& world)
+ : pugl::View{world}
+ {}
+
+ pugl::Status onConfigure(const pugl::ConfigureEvent& event) override;
+ pugl::Status onUpdate(const pugl::UpdateEvent& event) override;
+ pugl::Status onExpose(const pugl::ExposeEvent& event) override;
+ pugl::Status onKeyPress(const pugl::KeyPressEvent& event) override;
+ pugl::Status onClose(const pugl::CloseEvent& event) override;
+
+ bool quit() const { return _quit; }
+
+private:
+ double _xAngle{0.0};
+ double _yAngle{0.0};
+ double _lastDrawTime{0.0};
+ bool _quit{false};
+};
+
+pugl::Status
+CubeView::onConfigure(const pugl::ConfigureEvent& event)
+{
+ reshapeCube(static_cast<float>(event.width),
+ static_cast<float>(event.height));
+
+ return pugl::Status::success;
+}
+
+pugl::Status
+CubeView::onUpdate(const pugl::UpdateEvent&)
+{
+ return postRedisplay();
+}
+
+pugl::Status
+CubeView::onExpose(const pugl::ExposeEvent&)
+{
+ const double thisTime = world().time();
+ const double dTime = thisTime - _lastDrawTime;
+ const double dAngle = dTime * 100.0;
+
+ _xAngle = fmod(_xAngle + dAngle, 360.0);
+ _yAngle = fmod(_yAngle + dAngle, 360.0);
+ displayCube(cobj(),
+ 8.0f,
+ static_cast<float>(_xAngle),
+ static_cast<float>(_yAngle),
+ false);
+
+ _lastDrawTime = thisTime;
+
+ return pugl::Status::success;
+}
+
+pugl::Status
+CubeView::onKeyPress(const pugl::KeyPressEvent& event)
+{
+ if (event.key == PUGL_KEY_ESCAPE || event.key == 'q') {
+ _quit = true;
+ }
+
+ return pugl::Status::success;
+}
+
+pugl::Status
+CubeView::onClose(const pugl::CloseEvent&)
+{
+ _quit = true;
+
+ return pugl::Status::success;
+}
+
+int
+main(int argc, char** argv)
+{
+ const PuglTestOptions opts = puglParseTestOptions(&argc, &argv);
+ if (opts.help) {
+ puglPrintTestUsage("pugl_cxx_demo", "");
+ return 1;
+ }
+
+ pugl::World world{pugl::WorldType::program};
+ CubeView view{world};
+ PuglFpsPrinter fpsPrinter{};
+
+ world.setClassName("PuglCppTest");
+
+ view.setWindowTitle("Pugl C++ Test");
+ view.setDefaultSize(512, 512);
+ view.setMinSize(64, 64);
+ view.setAspectRatio(1, 1, 16, 9);
+ view.setBackend(pugl::glBackend());
+ view.setHint(pugl::ViewHint::resizable, opts.resizable);
+ view.setHint(pugl::ViewHint::samples, opts.samples);
+ view.setHint(pugl::ViewHint::doubleBuffer, opts.doubleBuffer);
+ view.setHint(pugl::ViewHint::swapInterval, opts.sync);
+ view.setHint(pugl::ViewHint::ignoreKeyRepeat, opts.ignoreKeyRepeat);
+ view.realize();
+ view.showWindow();
+
+ unsigned framesDrawn = 0;
+ while (!view.quit()) {
+ world.update(0.0);
+
+ ++framesDrawn;
+ puglPrintFps(world.cobj(), &fpsPrinter, &framesDrawn);
+ }
+
+ return 0;
+}
M examples/pugl_embed_demo.c => examples/pugl_embed_demo.c +8 -6
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_embed_demo.c An example of embedding a view in another.
+ @file pugl_embed_demo.c
+ @brief An example of embedding a view in another.
*/
#include "cube_view.h"
@@ 40,14 41,14 @@ typedef struct
PuglWorld* world;
PuglView* parent;
PuglView* child;
- bool continuous;
- int quit;
double xAngle;
double yAngle;
- float dist;
double lastMouseX;
double lastMouseY;
double lastDrawTime;
+ float dist;
+ int quit;
+ bool continuous;
bool mouseEntered;
bool verbose;
bool reversing;
@@ 292,8 293,9 @@ main(int argc, char** argv)
puglSetClassName(app.world, "Pugl Test");
const PuglRect parentFrame = { 0, 0, 512, 512 };
- puglSetFrame(app.parent, parentFrame);
+ puglSetDefaultSize(app.parent, 512, 512);
puglSetMinSize(app.parent, borderWidth * 3, borderWidth * 3);
+ puglSetMaxSize(app.parent, 1024, 1024);
puglSetAspectRatio(app.parent, 1, 1, 16, 9);
puglSetBackend(app.parent, puglGlBackend());
M examples/pugl_print_events.c => examples/pugl_print_events.c +3 -2
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_print_events.c A utility program that prints view events.
+ @file pugl_print_events.c
+ @brief A utility program that prints view events.
*/
#include "test/test_utils.h"
R examples/pugl_gl3_demo.c => examples/pugl_shader_demo.c +58 -65
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_gl3_demo.c An example of drawing with OpenGL 3.
+ @file pugl_shader_demo.c
+ @brief An example of drawing with OpenGL 3/4.
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
@@ 35,6 36,7 @@
*/
#include "demo_utils.h"
+#include "rects.h"
#include "shader_utils.h"
#include "test/test_utils.h"
@@ 44,7 46,6 @@
#include "pugl/pugl.h"
#include "pugl/pugl_gl.h"
-#include <math.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
@@ 55,27 56,14 @@ 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};
+ mat4 projection;
+} RectUniforms;
typedef struct
{
- PuglTestOptions opts;
PuglWorld* world;
PuglView* view;
+ PuglTestOptions opts;
size_t numRects;
Rect* rects;
Program drawRect;
@@ 83,8 71,9 @@ typedef struct
GLuint vbo;
GLuint instanceVbo;
GLuint ibo;
- GLint u_projection;
unsigned framesDrawn;
+ int glMajorVersion;
+ int glMinorVersion;
int quit;
} PuglTestApp;
@@ 100,7 89,8 @@ onConfigure(PuglView* view, double width, double height)
(void)view;
glEnable(GL_BLEND);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
+ glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glViewport(0, 0, (int)width, (int)height);
}
@@ 129,25 119,12 @@ onExpose(PuglView* view)
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] = (width - rect->size[0] + offset[0]) *
- (sinf((float)time * rect->size[0] / 64.0f + normal) +
- 1.0f) /
- 2.0f;
- rect->pos[1] = (height - rect->size[1] + offset[1]) *
- (cosf((float)time * rect->size[1] / 64.0f + normal) +
- 1.0f) /
- 2.0f;
+ moveRect(&app->rects[i], i, app->numRects, width, height, time);
}
+ glBufferData(GL_UNIFORM_BUFFER, sizeof(proj), &proj, GL_STREAM_DRAW);
+
glBufferSubData(GL_ARRAY_BUFFER,
0,
(GLsizeiptr)(app->numRects * sizeof(Rect)),
@@ 198,20 175,9 @@ onEvent(PuglView* view, const PuglEvent* event)
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;
+ rects[i] = makeRect(i, (float)defaultWidth);
}
return rects;
@@ 241,6 207,8 @@ loadShader(const char* const path)
static int
parseOptions(PuglTestApp* app, int argc, char** argv)
{
+ char* endptr = NULL;
+
// Parse command line options
app->numRects = 1024;
app->opts = puglParseTestOptions(&argc, &argv);
@@ 249,11 217,24 @@ parseOptions(PuglTestApp* app, int argc, char** argv)
}
// Parse number of rectangles argument, if given
- if (argc == 1) {
- char* endptr = NULL;
-
+ if (argc >= 1) {
app->numRects = (size_t)strtol(argv[0], &endptr, 10);
if (endptr != argv[0] + strlen(argv[0])) {
+ logError("Invalid number of rectangles: %s\n", argv[0]);
+ return 1;
+ }
+ }
+
+ // Parse OpenGL major version argument, if given
+ if (argc >= 2) {
+ app->glMajorVersion = (int)strtol(argv[1], &endptr, 10);
+ if (endptr != argv[1] + strlen(argv[1])) {
+ logError("Invalid GL major version: %s\n", argv[1]);
+ return 1;
+ } else if (app->glMajorVersion == 4) {
+ app->glMinorVersion = 2;
+ } else if (app->glMajorVersion != 3) {
+ logError("Unsupported GL major version %d\n", app->glMajorVersion);
return 1;
}
}
@@ 262,7 243,7 @@ parseOptions(PuglTestApp* app, int argc, char** argv)
}
static void
-setupPugl(PuglTestApp* app, const PuglRect frame)
+setupPugl(PuglTestApp* app)
{
// Create world, view, and rect data
app->world = puglNewWorld(PUGL_PROGRAM, 0);
@@ 272,14 253,14 @@ setupPugl(PuglTestApp* app, const PuglRect frame)
// Set up world and view
puglSetClassName(app->world, "PuglGL3Demo");
puglSetWindowTitle(app->view, "Pugl OpenGL 3");
- puglSetFrame(app->view, frame);
+ puglSetDefaultSize(app->view, defaultWidth, defaultHeight);
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_CONTEXT_VERSION_MAJOR, app->glMajorVersion);
+ puglSetViewHint(app->view, PUGL_CONTEXT_VERSION_MINOR, app->glMinorVersion);
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);
@@ 298,7 279,12 @@ setupGl(PuglTestApp* app)
return PUGL_FAILURE;
}
+ const char* const headerFile = (app->glMajorVersion == 3
+ ? "shaders/header_330.glsl"
+ : "shaders/header_420.glsl");
+
// Load shader sources
+ char* const headerSource = loadShader(headerFile);
char* const vertexSource = loadShader("shaders/rect.vert");
char* const fragmentSource = loadShader("shaders/rect.frag");
if (!vertexSource || !fragmentSource) {
@@ 307,16 293,23 @@ setupGl(PuglTestApp* app)
}
// Compile rectangle shaders and program
- app->drawRect = compileProgram(vertexSource, fragmentSource);
+ app->drawRect = compileProgram(headerSource, vertexSource, fragmentSource);
free(fragmentSource);
free(vertexSource);
+ free(headerSource);
if (!app->drawRect.program) {
return PUGL_FAILURE;
}
- // Get location of rectangle shader uniforms
- app->u_projection =
- glGetUniformLocation(app->drawRect.program, "u_projection");
+ // Get location of rectangle shader uniform block
+ const GLuint globalsIndex = glGetUniformBlockIndex(app->drawRect.program,
+ "UniformBufferObject");
+
+ // Generate/bind a uniform buffer for setting rectangle properties
+ GLuint uboHandle = 0;
+ glGenBuffers(1, &uboHandle);
+ glBindBuffer(GL_UNIFORM_BUFFER, uboHandle);
+ glBindBufferBase(GL_UNIFORM_BUFFER, globalsIndex, uboHandle);
// Generate/bind a VAO to track state
glGenVertexArrays(1, &app->vao);
@@ 391,19 384,19 @@ teardownGl(PuglTestApp* app)
int
main(int argc, char** argv)
{
- PuglTestApp app;
- memset(&app, 0, sizeof(app));
+ PuglTestApp app = {0};
- const PuglRect frame = {0, 0, defaultWidth, defaultHeight};
+ app.glMajorVersion = 3;
+ app.glMinorVersion = 3;
// Parse command line options
if (parseOptions(&app, argc, argv)) {
- puglPrintTestUsage("pugl_gl3_demo", "[NUM_RECTS]");
+ puglPrintTestUsage("pugl_shader_demo", "[NUM_RECTS] [GL_MAJOR]");
return 1;
}
// Create and configure world and view
- setupPugl(&app, frame);
+ setupPugl(&app);
// Create window (which will send a PUGL_CREATE event)
const PuglStatus st = puglRealize(app.view);
M examples/pugl_window_demo.c => examples/pugl_window_demo.c +18 -10
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_window_demo.c A demonstration of multiple Pugl windows.
+ @file pugl_window_demo.c
+ @brief A demonstration of multiple Pugl windows.
*/
#include "cube_view.h"
@@ 33,21 34,23 @@ typedef struct {
PuglView* view;
double xAngle;
double yAngle;
- float dist;
double lastMouseX;
double lastMouseY;
double lastDrawTime;
+ float dist;
bool entered;
} CubeView;
typedef struct {
PuglWorld* world;
CubeView cubes[2];
- bool continuous;
int quit;
+ bool continuous;
bool verbose;
} PuglTestApp;
+static const double pad = 64.0;
+
static void
onDisplay(PuglView* view)
{
@@ 199,19 202,19 @@ main(int argc, char** argv)
puglSetClassName(app.world, "Pugl Test");
PuglStatus st = PUGL_SUCCESS;
- for (size_t i = 0; i < 2; ++i) {
+ for (unsigned 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};
+ const PuglRect frame = {pad + (128.0 + pad) * i,
+ pad + (128.0 + pad) * i,
+ 512.0,
+ 512.0};
cube->dist = 10;
puglSetWindowTitle(view, "Pugl Window Demo");
puglSetFrame(view, frame);
+ puglSetDefaultSize(view, 512, 512);
puglSetMinSize(view, 128, 128);
puglSetBackend(view, puglGlBackend());
@@ 224,6 227,11 @@ main(int argc, char** argv)
puglSetHandle(view, cube);
puglSetEventFunc(view, onEvent);
+ if (i == 1) {
+ puglSetTransientFor(app.cubes[1].view,
+ puglGetNativeWindow(app.cubes[0].view));
+ }
+
if ((st = puglRealize(view))) {
return logError("Failed to create window (%s)\n", puglStrerror(st));
}
A examples/rects.h => examples/rects.h +82 -0
@@ 0,0 1,82 @@
+/*
+ Copyright 2019-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file rects.h
+ @brief Utilities for rectangle animation demos.
+
+ This file contains common definitions for demos that show an animation of
+ many 2D rectangles.
+*/
+
+#include <math.h>
+#include <stddef.h>
+
+typedef float vec2[2];
+
+typedef struct {
+ float pos[2];
+ float size[2];
+ float fillColor[4];
+} Rect;
+
+static const vec2 rectVertices[] = {
+ {0.0f, 0.0f}, // TL
+ {1.0f, 0.0f}, // TR
+ {0.0f, 1.0f}, // BL
+ {1.0f, 1.0f} // BR
+};
+
+static const unsigned rectIndices[4] = {0, 1, 2, 3};
+
+/// Make a new rectangle with the given index (each is slightly different)
+static inline Rect
+makeRect(const size_t index, const float frameWidth)
+{
+ static const float alpha = 0.3f;
+ const float minSize = frameWidth / 64.0f;
+ const float maxSize = frameWidth / 6.0f;
+ const float s = (sinf((float)index) / 2.0f + 0.5f);
+ const float c = (cosf((float)index) / 2.0f + 0.5f);
+
+ const Rect rect = {
+ {0.0f, 0.0f}, // Position is set later during expose
+ {minSize + s * maxSize, minSize + c * maxSize},
+ {0.0f, s / 2.0f + 0.25f, c / 2.0f + 0.25f, alpha},
+ };
+
+ return rect;
+}
+
+/// Move `rect` with the given index around in an arbitrary way that looks cool
+static inline void
+moveRect(Rect* const rect,
+ const size_t index,
+ const size_t numRects,
+ const float frameWidth,
+ const float frameHeight,
+ const double time)
+{
+ const float normal = (float)index / (float)numRects;
+ const float offset[2] = {normal * 128.0f, normal * 128.0f};
+
+ rect->pos[0] = (frameWidth - rect->size[0] + offset[0]) *
+ (sinf((float)time * rect->size[0] / 64.0f + normal) + 1.0f) /
+ 2.0f;
+ rect->pos[1] = (frameHeight - rect->size[1] + offset[1]) *
+ (cosf((float)time * rect->size[1] / 64.0f + normal) + 1.0f) /
+ 2.0f;
+}
M examples/shader_utils.h => examples/shader_utils.h +18 -13
@@ 1,5 1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 28,17 28,18 @@ typedef struct
} Program;
static GLuint
-compileShader(const char* source, const GLenum type)
+compileShader(const char* header, const char* source, const GLenum type)
{
- GLuint shader = glCreateShader(type);
- const int sourceLength = (int)strlen(source);
- glShaderSource(shader, 1, &source, &sourceLength);
+ const GLchar* sources[] = {header, source};
+ const GLint lengths[] = {(GLint)strlen(header), (GLint)strlen(source)};
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 2, sources, lengths);
glCompileShader(shader);
- int status;
+ int status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
- GLint length;
+ GLint length = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
char* log = (char*)calloc(1, (size_t)length);
@@ 61,13 62,17 @@ deleteProgram(Program program)
}
static Program
-compileProgram(const char* vertexSource, const char* fragmentSource)
+compileProgram(const char* headerSource,
+ const char* vertexSource,
+ const char* fragmentSource)
{
static const Program nullProgram = {0, 0, 0};
- Program program = {compileShader(vertexSource, GL_VERTEX_SHADER),
- compileShader(fragmentSource, GL_FRAGMENT_SHADER),
- glCreateProgram()};
+ Program program = {
+ compileShader(headerSource, vertexSource, GL_VERTEX_SHADER),
+ compileShader(headerSource, fragmentSource, GL_FRAGMENT_SHADER),
+ glCreateProgram(),
+ };
if (!program.vertexShader || !program.fragmentShader || !program.program) {
deleteProgram(program);
@@ 78,10 83,10 @@ compileProgram(const char* vertexSource, const char* fragmentSource)
glAttachShader(program.program, program.fragmentShader);
glLinkProgram(program.program);
- GLint status;
+ GLint status = 0;
glGetProgramiv(program.program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
- GLint length;
+ GLint length = 0;
glGetProgramiv(program.program, GL_INFO_LOG_LENGTH, &length);
char* log = (char*)calloc(1, (size_t)length);
M pugl/detail/implementation.c => pugl/detail/implementation.c +10 -8
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file implementation.c Platform-independent implementation.
+ @file implementation.c
+ @brief Platform-independent implementation.
*/
#include "pugl/detail/implementation.h"
@@ 60,6 61,7 @@ puglStrerror(const PuglStatus status)
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_BAD_CONFIGURATION: return "Invalid view configuration";
case PUGL_BAD_PARAMETER: return "Invalid parameter";
case PUGL_BACKEND_FAILED: return "Backend initialisation failed";
case PUGL_REGISTRATION_FAILED: return "Class registration failed";
@@ 188,9 190,9 @@ puglNewView(PuglWorld* const world)
return NULL;
}
- view->world = world;
- view->frame.width = 640;
- view->frame.height = 480;
+ view->world = world;
+ view->minWidth = 1;
+ view->minHeight = 1;
puglSetDefaultHints(view->hints);
@@ 309,7 311,7 @@ PuglStatus
puglEnterContext(PuglView* view, bool drawing)
{
const PuglEventExpose expose = {
- PUGL_EXPOSE, 0, 0, 0, view->frame.width, view->frame.height, 0};
+ PUGL_EXPOSE, 0, 0.0, 0.0, view->frame.width, view->frame.height};
view->backend->enter(view, drawing ? &expose : NULL);
@@ 320,7 322,7 @@ PuglStatus
puglLeaveContext(PuglView* view, bool drawing)
{
const PuglEventExpose expose = {
- PUGL_EXPOSE, 0, 0, 0, view->frame.width, view->frame.height, 0};
+ PUGL_EXPOSE, 0, 0.0, 0.0, view->frame.width, view->frame.height};
view->backend->leave(view, drawing ? &expose : NULL);
@@ 336,7 338,7 @@ puglSetEventFunc(PuglView* view, PuglEventFunc eventFunc)
return PUGL_SUCCESS;
}
-/** Return the code point for buf, or the replacement character on error. */
+/// Return the code point for buf, or the replacement character on error
uint32_t
puglDecodeUTF8(const uint8_t* buf)
{
M pugl/detail/implementation.h => pugl/detail/implementation.h +15 -14
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file implementation.h Shared declarations for implementation.
+ @file implementation.h
+ @brief Shared declarations for implementation.
*/
#ifndef PUGL_DETAIL_IMPLEMENTATION_H
@@ 29,42 30,42 @@
PUGL_BEGIN_DECLS
-/** Set `blob` to `data` with length `len`, reallocating if necessary. */
+/// Set `blob` to `data` with length `len`, reallocating if necessary
void puglSetBlob(PuglBlob* dest, const void* data, size_t len);
-/** Reallocate and set `*dest` to `string`. */
+/// Reallocate and set `*dest` to `string`
void puglSetString(char** dest, const char* string);
-/** Allocate and initialise world internals (implemented once per platform) */
+/// Allocate and initialise world internals (implemented once per platform)
PuglWorldInternals*
puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags);
-/** Destroy and free world internals (implemented once per platform) */
+/// Destroy and free world internals (implemented once per platform)
void puglFreeWorldInternals(PuglWorld* world);
-/** Allocate and initialise view internals (implemented once per platform) */
+/// Allocate and initialise view internals (implemented once per platform)
PuglInternals* puglInitViewInternals(void);
-/** Destroy and free view internals (implemented once per platform) */
+/// Destroy and free view internals (implemented once per platform)
void puglFreeViewInternals(PuglView* view);
-/** Return the Unicode code point for `buf` or the replacement character. */
+/// Return the Unicode code point for `buf` or the replacement character
uint32_t puglDecodeUTF8(const uint8_t* buf);
-/** Dispatch an event with a simple `type` to `view`. */
+/// 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. */
+/// 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. */
+/// Dispatch `event` to `view`, entering graphics context if necessary
void puglDispatchEvent(PuglView* view, const PuglEvent* event);
-/** Set internal (stored in view) clipboard contents. */
+/// Set internal (stored in view) clipboard contents
const void*
puglGetInternalClipboard(const PuglView* view, const char** type, size_t* len);
-/** Set internal (stored in view) clipboard contents. */
+/// Set internal (stored in view) clipboard contents
PuglStatus
puglSetInternalClipboard(PuglView* view,
const char* type,
M pugl/detail/mac.h => pugl/detail/mac.h +5 -15
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
Permission to use, copy, modify, and/or distribute this software for any
@@ 16,7 16,8 @@
*/
/**
- @file mac.h Shared definitions for MacOS implementation.
+ @file mac.h
+ @brief Shared definitions for MacOS implementation.
*/
#include "pugl/pugl.h"
@@ 26,15 27,6 @@
#include <stdint.h>
@interface PuglWrapperView : NSView<NSTextInputClient>
-{
-@public
- PuglView* puglview;
- NSTrackingArea* trackingArea;
- NSMutableAttributedString* markedText;
- NSTimer* timer;
- NSMutableDictionary* userTimers;
- bool reshaped;
-}
- (void) dispatchExpose:(NSRect)rect;
- (void) setReshaped;
@@ 42,10 34,6 @@
@end
@interface PuglWindow : NSWindow
-{
-@public
- PuglView* puglview;
-}
- (void) setPuglview:(PuglView*)view;
@@ 60,6 48,8 @@ struct PuglInternalsImpl {
NSApplication* app;
PuglWrapperView* wrapperView;
NSView* drawView;
+ NSCursor* cursor;
PuglWindow* window;
uint32_t mods;
+ bool mouseTracked;
};
M pugl/detail/mac.m => pugl/detail/mac.m +287 -72
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Copyright 2017 Hanspeter Portner <dev@open-music-kontrollers.ch>
Permission to use, copy, modify, and/or distribute this software for any
@@ 16,7 16,8 @@
*/
/**
- @file mac.m MacOS implementation.
+ @file mac.m
+ @brief MacOS implementation.
*/
#define GL_SILENCE_DEPRECATION 1
@@ 40,31 41,88 @@ typedef NSUInteger NSWindowStyleMask;
#endif
static NSRect
-rectToScreen(NSRect rect)
+rectToScreen(NSScreen* screen, NSRect rect)
{
- const double screenHeight = [[NSScreen mainScreen] frame].size.height;
+ const double screenHeight = [screen frame].size.height;
rect.origin.y = screenHeight - rect.origin.y - rect.size.height;
return rect;
}
+static NSScreen*
+viewScreen(PuglView* view)
+{
+ return view->impl->window ? [view->impl->window screen] : [NSScreen mainScreen];
+}
+
+static NSRect
+nsRectToPoints(PuglView* view, const NSRect rect)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakeRect(rect.origin.x / scaleFactor,
+ rect.origin.y / scaleFactor,
+ rect.size.width / scaleFactor,
+ rect.size.height / scaleFactor);
+}
+
+static NSRect
+nsRectFromPoints(PuglView* view, const NSRect rect)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakeRect(rect.origin.x * scaleFactor,
+ rect.origin.y * scaleFactor,
+ rect.size.width * scaleFactor,
+ rect.size.height * scaleFactor);
+}
+
+static NSPoint
+nsPointFromPoints(PuglView* view, const NSPoint point)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakePoint(point.x * scaleFactor, point.y * scaleFactor);
+}
+
+static NSRect
+rectToNsRect(const PuglRect rect)
+{
+ return NSMakeRect(rect.x, rect.y, rect.width, rect.height);
+}
+
+static NSSize
+sizePoints(PuglView* view, const double width, const double height)
+{
+ const double scaleFactor = [viewScreen(view) backingScaleFactor];
+
+ return NSMakeSize(width / scaleFactor, height / scaleFactor);
+}
+
static void
updateViewRect(PuglView* view)
{
NSWindow* const window = view->impl->window;
if (window) {
- const double screenHeight = [[NSScreen mainScreen] frame].size.height;
- const NSRect frame = [window frame];
- const NSRect content = [window contentRectForFrameRect:frame];
-
- view->frame.x = content.origin.x;
- view->frame.y = screenHeight - content.origin.y - content.size.height;
- view->frame.width = content.size.width;
- view->frame.height = content.size.height;
+ const NSRect screenFramePt = [[NSScreen mainScreen] frame];
+ const NSRect screenFramePx = nsRectFromPoints(view, screenFramePt);
+ const NSRect framePt = [window frame];
+ const NSRect contentPt = [window contentRectForFrameRect:framePt];
+ const NSRect contentPx = nsRectFromPoints(view, contentPt);
+ const double screenHeight = screenFramePx.size.height;
+
+ view->frame.x = contentPx.origin.x;
+ view->frame.y = screenHeight - contentPx.origin.y - contentPx.size.height;
+ view->frame.width = contentPx.size.width;
+ view->frame.height = contentPx.size.height;
}
}
@implementation PuglWindow
+{
+@public
+ PuglView* puglview;
+}
- (id) initWithContentRect:(NSRect)contentRect
styleMask:(NSWindowStyleMask)aStyle
@@ 74,9 132,9 @@ updateViewRect(PuglView* view)
(void)flag;
NSWindow* result = [super initWithContentRect:contentRect
- styleMask:aStyle
- backing:bufferingType
- defer:NO];
+ styleMask:aStyle
+ backing:bufferingType
+ defer:NO];
[result setAcceptsMouseMovedEvents:YES];
return (PuglWindow*)result;
@@ 85,7 143,9 @@ updateViewRect(PuglView* view)
- (void)setPuglview:(PuglView*)view
{
puglview = view;
- [self setContentSize:NSMakeSize(view->frame.width, view->frame.height)];
+
+ [self
+ setContentSize:sizePoints(view, view->frame.width, view->frame.height)];
}
- (BOOL) canBecomeKeyWindow
@@ 124,13 184,24 @@ updateViewRect(PuglView* view)
@end
@implementation PuglWrapperView
+{
+@public
+ PuglView* puglview;
+ NSTrackingArea* trackingArea;
+ NSMutableAttributedString* markedText;
+ NSTimer* timer;
+ NSMutableDictionary* userTimers;
+ bool reshaped;
+}
- (void) dispatchExpose:(NSRect)rect
{
+ const double scaleFactor = [[NSScreen mainScreen] backingScaleFactor];
+
if (reshaped) {
updateViewRect(puglview);
- const PuglEventConfigure ev = {
+ const PuglEventConfigure ev = {
PUGL_CONFIGURE,
0,
puglview->frame.x,
@@ 150,16 221,26 @@ updateViewRect(PuglView* view)
const PuglEventExpose ev = {
PUGL_EXPOSE,
0,
- rect.origin.x,
- rect.origin.y,
- rect.size.width,
- rect.size.height,
- 0
+ rect.origin.x * scaleFactor,
+ rect.origin.y * scaleFactor,
+ rect.size.width * scaleFactor,
+ rect.size.height * scaleFactor,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
}
+- (NSSize) intrinsicContentSize
+{
+ if (puglview->defaultWidth || puglview->defaultHeight) {
+ return sizePoints(puglview,
+ puglview->defaultWidth,
+ puglview->defaultHeight);
+ }
+
+ return NSMakeSize(NSViewNoInstrinsicMetric, NSViewNoInstrinsicMetric);
+}
+
- (BOOL) isFlipped
{
return YES;
@@ 246,7 327,9 @@ keySymToSpecial(const NSEvent* const ev)
- (NSPoint) eventLocation:(NSEvent*)event
{
- return [self convertPoint:[event locationInWindow] fromView:nil];
+ return nsPointFromPoints(puglview,
+ [self convertPoint:[event locationInWindow]
+ fromView:nil]);
}
static void
@@ 272,11 355,15 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
- (void) mouseEntered:(NSEvent*)event
{
handleCrossing(self, event, PUGL_POINTER_IN);
+ [puglview->impl->cursor set];
+ puglview->impl->mouseTracked = true;
}
- (void) mouseExited:(NSEvent*)event
{
+ [[NSCursor arrowCursor] set];
handleCrossing(self, event, PUGL_POINTER_OUT);
+ puglview->impl->mouseTracked = false;
}
- (void) mouseMoved:(NSEvent*)event
@@ 292,8 379,6 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
rloc.x,
[[NSScreen mainScreen] frame].size.height - rloc.y,
getModifiers(event),
- 0,
- 1,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
@@ 374,19 459,32 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
- (void) scrollWheel:(NSEvent*)event
{
- const NSPoint wloc = [self eventLocation:event];
- const NSPoint rloc = [NSEvent mouseLocation];
- const PuglEventScroll ev = {
- PUGL_SCROLL,
- 0,
- [event timestamp],
- wloc.x,
- wloc.y,
- rloc.x,
- [[NSScreen mainScreen] frame].size.height - rloc.y,
- getModifiers(event),
- [event scrollingDeltaX],
- [event scrollingDeltaY],
+ const NSPoint wloc = [self eventLocation:event];
+ const NSPoint rloc = [NSEvent mouseLocation];
+ const double dx = [event scrollingDeltaX];
+ const double dy = [event scrollingDeltaY];
+ const PuglScrollDirection dir =
+ ((dx == 0.0 && dy > 0.0)
+ ? PUGL_SCROLL_UP
+ : ((dx == 0.0 && dy < 0.0)
+ ? PUGL_SCROLL_DOWN
+ : ((dy == 0.0 && dx > 0.0)
+ ? PUGL_SCROLL_RIGHT
+ : ((dy == 0.0 && dx < 0.0) ? PUGL_SCROLL_LEFT
+ : PUGL_SCROLL_SMOOTH))));
+
+ const PuglEventScroll ev = {
+ PUGL_SCROLL,
+ 0,
+ [event timestamp],
+ wloc.x,
+ wloc.y,
+ rloc.x,
+ [[NSScreen mainScreen] frame].size.height - rloc.y,
+ getModifiers(event),
+ [event hasPreciseScrollingDeltas] ? PUGL_SCROLL_SMOOTH : dir,
+ dx,
+ dy,
};
puglDispatchEvent(puglview, (const PuglEvent*)&ev);
@@ 653,15 751,15 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
@end
@interface PuglWindowDelegate : NSObject<NSWindowDelegate>
-{
- PuglWindow* window;
-}
- (instancetype) initWithPuglWindow:(PuglWindow*)window;
@end
@implementation PuglWindowDelegate
+{
+ PuglWindow* window;
+}
- (instancetype) initWithPuglWindow:(PuglWindow*)puglWindow
{
@@ 692,7 790,7 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
(void)notification;
PuglEvent ev = {{PUGL_FOCUS_IN, 0}};
- ev.focus.grab = false;
+ ev.focus.mode = PUGL_CROSSING_NORMAL;
puglDispatchEvent(window->puglview, &ev);
}
@@ 701,21 799,23 @@ handleCrossing(PuglWrapperView* view, NSEvent* event, const PuglEventType type)
(void)notification;
PuglEvent ev = {{PUGL_FOCUS_OUT, 0}};
- ev.focus.grab = false;
+ ev.focus.mode = PUGL_CROSSING_NORMAL;
puglDispatchEvent(window->puglview, &ev);
}
@end
PuglWorldInternals*
-puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
- PuglWorldFlags PUGL_UNUSED(flags))
+puglInitWorldInternals(PuglWorldType type, PuglWorldFlags PUGL_UNUSED(flags))
{
PuglWorldInternals* impl = (PuglWorldInternals*)calloc(
1, sizeof(PuglWorldInternals));
- impl->app = [NSApplication sharedApplication];
- impl->autoreleasePool = [NSAutoreleasePool new];
+ impl->app = [NSApplication sharedApplication];
+
+ if (type == PUGL_PROGRAM) {
+ impl->autoreleasePool = [NSAutoreleasePool new];
+ }
return impl;
}
@@ 723,7 823,10 @@ puglInitWorldInternals(PuglWorldType PUGL_UNUSED(type),
void
puglFreeWorldInternals(PuglWorld* world)
{
- [world->impl->autoreleasePool drain];
+ if (world->impl->autoreleasePool) {
+ [world->impl->autoreleasePool drain];
+ }
+
free(world->impl);
}
@@ 736,7 839,11 @@ puglGetNativeWorld(PuglWorld* PUGL_UNUSED(world))
PuglInternals*
puglInitViewInternals(void)
{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+
+ impl->cursor = [NSCursor arrowCursor];
+
+ return impl;
}
static NSLayoutConstraint*
@@ 749,13 856,35 @@ puglConstraint(id item, NSLayoutAttribute attribute, float constant)
toItem: nil
attribute: NSLayoutAttributeNotAnAttribute
multiplier: 1.0
- constant: constant];
+ constant: (CGFloat)constant];
}
PuglStatus
puglRealize(PuglView* view)
{
- PuglInternals* impl = view->impl;
+ PuglInternals* impl = view->impl;
+ const NSScreen* const screen = [NSScreen mainScreen];
+ const double scaleFactor = [screen backingScaleFactor];
+
+ if (view->frame.width == 0.0 && view->frame.height == 0.0) {
+ if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) {
+ return PUGL_BAD_CONFIGURATION;
+ }
+
+ const double screenWidthPx = [screen frame].size.width * scaleFactor;
+ const double screenHeightPx = [screen frame].size.height * scaleFactor;
+
+ view->frame.width = view->defaultWidth;
+ view->frame.height = view->defaultHeight;
+ view->frame.x = screenWidthPx / 2.0 - view->frame.width / 2.0;
+ view->frame.y = screenHeightPx / 2.0 - view->frame.height / 2.0;
+ }
+
+ const NSRect framePx = rectToNsRect(view->frame);
+ const NSRect framePt = NSMakeRect(framePx.origin.x / scaleFactor,
+ framePx.origin.y / scaleFactor,
+ framePx.size.width / scaleFactor,
+ framePx.size.height / scaleFactor);
// Create wrapper view to handle input
impl->wrapperView = [PuglWrapperView alloc];
@@ 763,8 892,7 @@ puglRealize(PuglView* view)
impl->wrapperView->userTimers = [[NSMutableDictionary alloc] init];
impl->wrapperView->markedText = [[NSMutableAttributedString alloc] init];
[impl->wrapperView setAutoresizesSubviews:YES];
- [impl->wrapperView initWithFrame:
- NSMakeRect(0, 0, view->frame.width, view->frame.height)];
+ [impl->wrapperView initWithFrame:framePt];
[impl->wrapperView addConstraint:
puglConstraint(impl->wrapperView, NSLayoutAttributeWidth, view->minWidth)];
[impl->wrapperView addConstraint:
@@ 788,10 916,6 @@ puglRealize(PuglView* view)
[impl->drawView setHidden:NO];
[[impl->drawView window] makeFirstResponder:impl->wrapperView];
} else {
- const NSRect frame = rectToScreen(
- NSMakeRect(view->frame.x, view->frame.y,
- view->minWidth, view->minHeight));
-
unsigned style = (NSClosableWindowMask |
NSTitledWindowMask |
NSMiniaturizableWindowMask );
@@ 800,7 924,7 @@ puglRealize(PuglView* view)
}
PuglWindow* window = [[[PuglWindow alloc]
- initWithContentRect:frame
+ initWithContentRect:rectToScreen([NSScreen mainScreen], framePt)
styleMask:style
backing:NSBackingStoreBuffered
defer:NO
@@ 817,7 941,8 @@ puglRealize(PuglView* view)
}
if (view->minWidth || view->minHeight) {
- [window setContentMinSize:NSMakeSize(view->minWidth,
+ [window setContentMinSize:sizePoints(view,
+ view->minWidth,
view->minHeight)];
}
impl->window = window;
@@ 826,10 951,13 @@ puglRealize(PuglView* view)
initWithPuglWindow:window];
if (view->minAspectX && view->minAspectY) {
- [window setContentAspectRatio:NSMakeSize(view->minAspectX,
+ [window setContentAspectRatio:sizePoints(view,
+ view->minAspectX,
view->minAspectY)];
}
+ puglSetFrame(view, view->frame);
+
[window setContentView:impl->wrapperView];
[view->world->impl->app activateIgnoringOtherApps:YES];
[window makeFirstResponder:impl->wrapperView];
@@ 1066,8 1194,9 @@ puglPostRedisplay(PuglView* view)
PuglStatus
puglPostRedisplayRect(PuglView* view, const PuglRect rect)
{
- [view->impl->drawView setNeedsDisplayInRect:
- NSMakeRect(rect.x, rect.y, rect.width, rect.height)];
+ const NSRect rectPx = rectToNsRect(rect);
+
+ [view->impl->drawView setNeedsDisplayInRect:nsRectToPoints(view, rectPx)];
return PUGL_SUCCESS;
}
@@ 1103,32 1232,59 @@ puglSetFrame(PuglView* view, const PuglRect frame)
// Update view frame to exactly the requested frame in Pugl coordinates
view->frame = frame;
- const NSRect rect = NSMakeRect(frame.x, frame.y, frame.width, frame.height);
+ const NSRect framePx = rectToNsRect(frame);
+ const NSRect framePt = nsRectToPoints(view, framePx);
if (impl->window) {
// Resize window to fit new content rect
- const NSRect windowFrame = [
- impl->window frameRectForContentRect:rectToScreen(rect)];
+ const NSRect screenPt = rectToScreen(viewScreen(view), framePt);
+ const NSRect winFrame = [impl->window frameRectForContentRect:screenPt];
- [impl->window setFrame:windowFrame display:NO];
+ [impl->window setFrame:winFrame display:NO];
}
// Resize views
- const NSRect drawRect = NSMakeRect(0, 0, frame.width, frame.height);
- [impl->wrapperView setFrame:(impl->window ? drawRect : rect)];
- [impl->drawView setFrame:drawRect];
+ const NSRect sizePx = NSMakeRect(0, 0, frame.width, frame.height);
+ const NSRect sizePt = [impl->drawView convertRectFromBacking:sizePx];
+
+ [impl->wrapperView setFrame:(impl->window ? sizePt : framePt)];
+ [impl->drawView setFrame:sizePt];
return PUGL_SUCCESS;
}
PuglStatus
+puglSetDefaultSize(PuglView* const view, const int width, const int height)
+{
+ view->defaultWidth = width;
+ view->defaultHeight = height;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
puglSetMinSize(PuglView* const view, const int width, const int height)
{
view->minWidth = width;
view->minHeight = height;
if (view->impl->window && (view->minWidth || view->minHeight)) {
- [view->impl->window
- setContentMinSize:NSMakeSize(view->minWidth, view->minHeight)];
+ [view->impl->window setContentMinSize:sizePoints(view,
+ view->minWidth,
+ view->minHeight)];
+ }
+
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
+puglSetMaxSize(PuglView* const view, const int width, const int height)
+{
+ view->maxWidth = width;
+ view->maxHeight = height;
+
+ if (view->impl->window && (view->maxWidth || view->maxHeight)) {
+ [view->impl->window setContentMaxSize:sizePoints(view,
+ view->maxWidth,
+ view->maxHeight)];
}
return PUGL_SUCCESS;
@@ 1147,13 1303,31 @@ puglSetAspectRatio(PuglView* const view,
view->maxAspectY = maxY;
if (view->impl->window && view->minAspectX && view->minAspectY) {
- [view->impl->window setContentAspectRatio:NSMakeSize(view->minAspectX,
+ [view->impl->window setContentAspectRatio:sizePoints(view,
+ view->minAspectX,
view->minAspectY)];
}
return PUGL_SUCCESS;
}
+PuglStatus
+puglSetTransientFor(PuglView* view, PuglNativeView parent)
+{
+ view->transientParent = parent;
+
+ if (view->impl->window) {
+ NSWindow* parentWindow = [(NSView*)parent window];
+ if (parentWindow) {
+ [parentWindow addChildWindow:view->impl->window
+ ordered:NSWindowAbove];
+ return PUGL_SUCCESS;
+ }
+ }
+
+ return PUGL_FAILURE;
+}
+
const void*
puglGetClipboard(PuglView* const view,
const char** const type,
@@ 1171,6 1345,47 @@ puglGetClipboard(PuglView* const view,
return puglGetInternalClipboard(view, type, len);
}
+static NSCursor*
+puglGetNsCursor(const PuglCursor cursor)
+{
+ switch (cursor) {
+ case PUGL_CURSOR_ARROW:
+ return [NSCursor arrowCursor];
+ case PUGL_CURSOR_CARET:
+ return [NSCursor IBeamCursor];
+ case PUGL_CURSOR_CROSSHAIR:
+ return [NSCursor crosshairCursor];
+ case PUGL_CURSOR_HAND:
+ return [NSCursor pointingHandCursor];
+ case PUGL_CURSOR_NO:
+ return [NSCursor operationNotAllowedCursor];
+ case PUGL_CURSOR_LEFT_RIGHT:
+ return [NSCursor resizeLeftRightCursor];
+ case PUGL_CURSOR_UP_DOWN:
+ return [NSCursor resizeUpDownCursor];
+ }
+
+ return NULL;
+}
+
+PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor)
+{
+ PuglInternals* const impl = view->impl;
+ NSCursor* const cur = puglGetNsCursor(cursor);
+ if (!cur) {
+ return PUGL_FAILURE;
+ }
+
+ impl->cursor = cur;
+
+ if (impl->mouseTracked) {
+ [cur set];
+ }
+
+ return PUGL_SUCCESS;
+}
+
PuglStatus
puglSetClipboard(PuglView* const view,
const char* const type,
M pugl/detail/mac_cairo.m => pugl/detail/mac_cairo.m +19 -10
@@ 1,5 1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,13 15,14 @@
*/
/**
- @file mac_cairo.m Cairo graphics backend for MacOS.
+ @file mac_cairo.m
+ @brief Cairo graphics backend for MacOS.
*/
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl_cairo.h"
-#include "pugl/pugl_stub.h"
#include <cairo-quartz.h>
@@ 30,6 31,9 @@
#include <assert.h>
@interface PuglCairoView : NSView
+@end
+
+@implementation PuglCairoView
{
@public
PuglView* puglview;
@@ 37,13 41,11 @@
cairo_t* cr;
}
-@end
-
-@implementation PuglCairoView
-
- (id) initWithFrame:(NSRect)frame
{
- return (self = [super initWithFrame:frame]);
+ self = [super initWithFrame:frame];
+
+ return self;
}
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize
@@ 69,7 71,7 @@ puglMacCairoCreate(PuglView* view)
PuglCairoView* drawView = [PuglCairoView alloc];
drawView->puglview = view;
- [drawView initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)];
+ [drawView initWithFrame:[impl->wrapperView bounds]];
if (view->hints[PUGL_RESIZABLE]) {
[drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
} else {
@@ 103,10 105,17 @@ puglMacCairoEnter(PuglView* view, const PuglEventExpose* expose)
assert(!drawView->surface);
assert(!drawView->cr);
+ const double scale = 1.0 / [[NSScreen mainScreen] backingScaleFactor];
CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort];
+ const CGSize sizePx = {view->frame.width, view->frame.height};
+ const CGSize sizePt = CGContextConvertSizeToUserSpace(context, sizePx);
+
+ // Convert coordinates to standard Cairo space
+ CGContextTranslateCTM(context, 0.0, -sizePt.height);
+ CGContextScaleCTM(context, scale, -scale);
drawView->surface = cairo_quartz_surface_create_for_cg_context(
- context, view->frame.width, view->frame.height);
+ context, (unsigned)sizePx.width, (unsigned)sizePx.height);
drawView->cr = cairo_create(drawView->surface);
M pugl/detail/mac_gl.m => pugl/detail/mac_gl.m +10 -10
@@ 1,5 1,5 @@
/*
- Copyright 2019-2020 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,28 15,28 @@
*/
/**
- @file mac_gl.m OpenGL graphics backend for MacOS.
+ @file mac_gl.m
+ @brief OpenGL graphics backend for MacOS.
*/
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl_gl.h"
-#include "pugl/pugl_stub.h"
#ifndef __MAC_10_10
# define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core
#endif
@interface PuglOpenGLView : NSOpenGLView
+@end
+
+@implementation PuglOpenGLView
{
@public
PuglView* puglview;
}
-@end
-
-@implementation PuglOpenGLView
-
- (id) initWithFrame:(NSRect)frame
{
const bool compat = puglview->hints[PUGL_USE_COMPAT_PROFILE];
@@ 69,6 69,8 @@
self = [super initWithFrame:frame];
}
+ [self setWantsBestResolutionOpenGLSurface:YES];
+
if (self) {
[[self openGLContext] makeCurrentContext];
[self reshape];
@@ 97,11 99,9 @@ puglMacGlCreate(PuglView* view)
{
PuglInternals* impl = view->impl;
PuglOpenGLView* drawView = [PuglOpenGLView alloc];
- const NSRect rect = NSMakeRect(
- 0, 0, view->frame.width, view->frame.height);
drawView->puglview = view;
- [drawView initWithFrame:rect];
+ [drawView initWithFrame:[impl->wrapperView bounds]];
if (view->hints[PUGL_RESIZABLE]) {
[drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
} else {
M pugl/detail/mac_stub.m => pugl/detail/mac_stub.m +7 -6
@@ 1,5 1,5 @@
/*
- Copyright 2019-2020 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,25 15,26 @@
*/
/**
- @file mac_stub.m Stub graphics backend for MacOS.
+ @file mac_stub.m
+ @brief Stub graphics backend for MacOS.
*/
#include "pugl/detail/implementation.h"
#include "pugl/detail/mac.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl_stub.h"
#import <Cocoa/Cocoa.h>
@interface PuglStubView : NSView
+@end
+
+@implementation PuglStubView
{
@public
PuglView* puglview;
}
-@end
-
-@implementation PuglStubView
-
- (void) resizeWithOldSuperviewSize:(NSSize)oldSize
{
PuglWrapperView* wrapper = (PuglWrapperView*)[self superview];
A pugl/detail/stub.h => pugl/detail/stub.h +75 -0
@@ 0,0 1,75 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file stub.h
+ @brief Definition of generic stub backend functions.
+*/
+
+#ifndef PUGL_DETAIL_STUB_H
+#define PUGL_DETAIL_STUB_H
+
+#include "pugl/pugl.h"
+
+PUGL_BEGIN_DECLS
+
+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_DETAIL_STUB_H
M pugl/detail/types.h => pugl/detail/types.h +24 -19
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file types.h Shared internal type definitions.
+ @file types.h
+ @brief Shared internal type definitions.
*/
#ifndef PUGL_DETAIL_TYPES_H
@@ 36,22 37,22 @@
# define PUGL_UNUSED(name) name
#endif
-/** Platform-specific world internals. */
+/// Platform-specific world internals
typedef struct PuglWorldInternalsImpl PuglWorldInternals;
-/** Platform-specific view internals. */
+/// Platform-specific view internals
typedef struct PuglInternalsImpl PuglInternals;
-/** View hints. */
+/// View hints
typedef int PuglHints[PUGL_NUM_VIEW_HINTS];
-/** Blob of arbitrary data. */
+/// Blob of arbitrary data
typedef struct {
- void* data; //!< Dynamically allocated data
- size_t len; //!< Length of data in bytes
+ void* data; ///< Dynamically allocated data
+ size_t len; ///< Length of data in bytes
} PuglBlob;
-/** Cross-platform view definition. */
+/// Cross-platform view definition
struct PuglViewImpl {
PuglWorld* world;
const PuglBackend* backend;
@@ 62,11 63,15 @@ struct PuglViewImpl {
PuglBlob clipboard;
PuglNativeView parent;
uintptr_t transientParent;
- PuglHints hints;
PuglRect frame;
PuglEventConfigure lastConfigure;
+ PuglHints hints;
+ int defaultWidth;
+ int defaultHeight;
int minWidth;
int minHeight;
+ int maxWidth;
+ int maxHeight;
int minAspectX;
int minAspectY;
int maxAspectX;
@@ 74,7 79,7 @@ struct PuglViewImpl {
bool visible;
};
-/** Cross-platform world definition. */
+/// Cross-platform world definition
struct PuglWorldImpl {
PuglWorldInternals* impl;
PuglWorldHandle handle;
@@ 86,27 91,27 @@ struct PuglWorldImpl {
PuglLogLevel logLevel;
};
-/** Opaque surface used by graphics backend. */
+/// Opaque surface used by graphics backend
typedef void PuglSurface;
-/** Graphics backend interface. */
+/// Graphics backend interface
struct PuglBackendImpl {
- /** Get visual information from display and setup view as necessary. */
+ /// Get visual information from display and setup view as necessary
PuglStatus (*configure)(PuglView*);
- /** Create surface and drawing context. */
+ /// Create surface and drawing context
PuglStatus (*create)(PuglView*);
- /** Destroy surface and drawing context. */
+ /// Destroy surface and drawing context
PuglStatus (*destroy)(PuglView*);
- /** Enter drawing context, for drawing if expose is non-null. */
+ /// Enter drawing context, for drawing if expose is non-null
PuglStatus (*enter)(PuglView*, const PuglEventExpose*);
- /** Leave drawing context, after drawing if expose is non-null. */
+ /// Leave drawing context, after drawing if expose is non-null
PuglStatus (*leave)(PuglView*, const PuglEventExpose*);
- /** Return the puglGetContext() handle for the application, if any. */
+ /// Return the puglGetContext() handle for the application, if any
void* (*getContext)(PuglView*);
};
M pugl/detail/win.c => pugl/detail/win.c +92 -8
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,12 15,14 @@
*/
/**
- @file win.c Windows implementation.
+ @file win.c
+ @brief Windows implementation.
*/
#include "pugl/detail/win.h"
#include "pugl/detail/implementation.h"
+#include "pugl/detail/stub.h"
#include "pugl/pugl.h"
#include "pugl/pugl_stub.h"
@@ 189,6 191,8 @@ puglRealize(PuglView* view)
puglSetWindowTitle(view, view->title);
}
+ view->impl->cursor = LoadCursor(NULL, IDC_ARROW);
+
puglSetFrame(view, view->frame);
SetWindowLongPtr(impl->hwnd, GWLP_USERDATA, (LONG_PTR)view);
@@ 342,7 346,7 @@ initScrollEvent(PuglEvent* event, PuglView* view, LPARAM lParam)
event->scroll.dy = 0;
}
-/** Return the code point for buf, or the replacement character on error. */
+/// Return the code point for buf, or the replacement character on error
static uint32_t
puglDecodeUTF16(const wchar_t* buf, const int len)
{
@@ 539,6 543,11 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
}
switch (message) {
+ case WM_SETCURSOR:
+ if (LOWORD(lParam) == HTCLIENT) {
+ SetCursor(view->impl->cursor);
+ }
+ break;
case WM_SHOWWINDOW:
if (wParam) {
handleConfigure(view, &event);
@@ 577,8 586,9 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
RedrawWindow(view->impl->hwnd, NULL, NULL,
RDW_INVALIDATE|RDW_ALLCHILDREN|RDW_INTERNALPAINT);
} else if (wParam >= PUGL_USER_TIMER_MIN) {
- const PuglEventTimer ev = {PUGL_TIMER, 0, wParam - PUGL_USER_TIMER_MIN};
- puglDispatchEvent(view, (const PuglEvent*)&ev);
+ PuglEvent ev = {{PUGL_TIMER, 0}};
+ ev.timer.id = wParam - PUGL_USER_TIMER_MIN;
+ puglDispatchEvent(view, &ev);
}
break;
case WM_EXITSIZEMOVE:
@@ 591,6 601,10 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
mmi = (MINMAXINFO*)lParam;
mmi->ptMinTrackSize.x = view->minWidth;
mmi->ptMinTrackSize.y = view->minHeight;
+ if (view->maxWidth > 0 && view->maxHeight > 0) {
+ mmi->ptMaxTrackSize.x = view->maxWidth;
+ mmi->ptMaxTrackSize.y = view->maxHeight;
+ }
break;
case WM_PAINT:
GetUpdateRect(view->impl->hwnd, &rect, false);
@@ 599,7 613,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
event.expose.y = rect.top;
event.expose.width = rect.right - rect.left;
event.expose.height = rect.bottom - rect.top;
- event.expose.count = 0;
break;
case WM_ERASEBKGND:
return true;
@@ 627,7 640,6 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
event.motion.xRoot = pt.x;
event.motion.yRoot = pt.y;
event.motion.state = getModifiers();
- event.motion.isHint = false;
break;
case WM_MOUSELEAVE:
GetCursorPos(&pt);
@@ 656,10 668,16 @@ handleMessage(PuglView* view, UINT message, WPARAM wParam, LPARAM lParam)
case WM_MOUSEWHEEL:
initScrollEvent(&event, view, lParam);
event.scroll.dy = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ event.scroll.direction = (event.scroll.dy > 0
+ ? PUGL_SCROLL_UP
+ : PUGL_SCROLL_DOWN);
break;
case WM_MOUSEHWHEEL:
initScrollEvent(&event, view, lParam);
event.scroll.dx = GET_WHEEL_DELTA_WPARAM(wParam) / (float)WHEEL_DELTA;
+ event.scroll.direction = (event.scroll.dx > 0
+ ? PUGL_SCROLL_RIGHT
+ : PUGL_SCROLL_LEFT);
break;
case WM_KEYDOWN:
if (!ignoreKeyEvent(view, lParam)) {
@@ 958,6 976,14 @@ puglSetFrame(PuglView* view, const PuglRect frame)
}
PuglStatus
+puglSetDefaultSize(PuglView* const view, const int width, const int height)
+{
+ view->defaultWidth = width;
+ view->defaultHeight = height;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
puglSetMinSize(PuglView* const view, const int width, const int height)
{
view->minWidth = width;
@@ 966,6 992,14 @@ puglSetMinSize(PuglView* const view, const int width, const int height)
}
PuglStatus
+puglSetMaxSize(PuglView* const view, const int width, const int height)
+{
+ view->maxWidth = width;
+ view->maxHeight = height;
+ return PUGL_SUCCESS;
+}
+
+PuglStatus
puglSetAspectRatio(PuglView* const view,
const int minX,
const int minY,
@@ 979,6 1013,23 @@ puglSetAspectRatio(PuglView* const view,
return PUGL_SUCCESS;
}
+PuglStatus
+puglSetTransientFor(PuglView* view, PuglNativeView parent)
+{
+ if (view->parent) {
+ return PUGL_FAILURE;
+ }
+
+ view->transientParent = parent;
+
+ if (view->impl->hwnd) {
+ SetWindowLongPtr(view->impl->hwnd, GWLP_HWNDPARENT, (LONG_PTR)parent);
+ return GetLastError() == NO_ERROR ? PUGL_SUCCESS : PUGL_FAILURE;
+ }
+
+ return PUGL_SUCCESS;
+}
+
const void*
puglGetClipboard(PuglView* const view,
const char** const type,
@@ 1064,7 1115,40 @@ puglWinStubLeave(PuglView* view, const PuglEventExpose* expose)
if (expose) {
PAINTSTRUCT ps;
EndPaint(view->impl->hwnd, &ps);
- SwapBuffers(view->impl->hdc);
+ }
+
+ return PUGL_SUCCESS;
+}
+
+static const char* const cursor_ids[] = {
+ IDC_ARROW, // ARROW
+ IDC_IBEAM, // CARET
+ IDC_CROSS, // CROSSHAIR
+ IDC_HAND, // HAND
+ IDC_NO, // NO
+ IDC_SIZEWE, // LEFT_RIGHT
+ IDC_SIZENS, // UP_DOWN
+};
+
+PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor)
+{
+ PuglInternals* const impl = view->impl;
+ const unsigned index = (unsigned)cursor;
+ const unsigned count = sizeof(cursor_ids) / sizeof(cursor_ids[0]);
+
+ if (index >= count) {
+ return PUGL_BAD_PARAMETER;
+ }
+
+ const HCURSOR cur = LoadCursor(NULL, cursor_ids[index]);
+ if (!cur) {
+ return PUGL_FAILURE;
+ }
+
+ impl->cursor = cur;
+ if (impl->mouseTracked) {
+ SetCursor(cur);
}
return PUGL_SUCCESS;
M pugl/detail/win.h => pugl/detail/win.h +29 -7
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file win.h Shared definitions for Windows implementation.
+ @file win.h
+ @brief Shared definitions for Windows implementation.
*/
#include "pugl/detail/implementation.h"
@@ 34,6 35,7 @@ struct PuglInternalsImpl {
PuglWinPFD pfd;
int pfId;
HWND hwnd;
+ HCURSOR cursor;
HDC hdc;
PuglSurface* surface;
DWORD refreshRate;
@@ 87,15 89,35 @@ puglWinGetWindowExFlags(const PuglView* const view)
}
static inline PuglStatus
-puglWinCreateWindow(const PuglView* const view,
- const char* const title,
- HWND* const hwnd,
- HDC* const hdc)
+puglWinCreateWindow(PuglView* const view,
+ const char* const title,
+ HWND* const hwnd,
+ HDC* const hdc)
{
const char* className = (const char*)view->world->className;
const unsigned winFlags = puglWinGetWindowFlags(view);
const unsigned winExFlags = puglWinGetWindowExFlags(view);
+ if (view->frame.width == 0.0 && view->frame.height == 0.0) {
+ if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) {
+ return PUGL_BAD_CONFIGURATION;
+ }
+
+ RECT desktopRect;
+ GetClientRect(GetDesktopWindow(), &desktopRect);
+
+ const int screenWidth = desktopRect.right - desktopRect.left;
+ const int screenHeight = desktopRect.bottom - desktopRect.top;
+
+ view->frame.width = view->defaultWidth;
+ view->frame.height = view->defaultHeight;
+ view->frame.x = screenWidth / 2.0 - view->frame.width / 2.0;
+ view->frame.y = screenHeight / 2.0 - view->frame.height / 2.0;
+ }
+
+ // The meaning of "parent" depends on the window type (WS_CHILD)
+ PuglNativeView parent = view->parent ? view->parent : view->transientParent;
+
// Calculate total window size to accommodate requested view size
RECT wr = { (long)view->frame.x, (long)view->frame.y,
(long)view->frame.width, (long)view->frame.height };
@@ 105,7 127,7 @@ puglWinCreateWindow(const PuglView* const view,
if (!(*hwnd = CreateWindowEx(winExFlags, className, title, winFlags,
CW_USEDEFAULT, CW_USEDEFAULT,
wr.right-wr.left, wr.bottom-wr.top,
- (HWND)view->parent, NULL, NULL, NULL))) {
+ (HWND)parent, NULL, NULL, NULL))) {
return PUGL_REALIZE_FAILED;
} else if (!(*hdc = GetDC(*hwnd))) {
DestroyWindow(*hwnd);
M pugl/detail/win_cairo.c => pugl/detail/win_cairo.c +4 -4
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,13 15,14 @@
*/
/**
- @file win_cairo.c Cairo graphics backend for Windows.
+ @file win_cairo.c
+ @brief Cairo graphics backend for Windows.
*/
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/detail/win.h"
#include "pugl/pugl_cairo.h"
-#include "pugl/pugl_stub.h"
#include <cairo-win32.h>
#include <cairo.h>
@@ 152,7 153,6 @@ puglWinCairoLeave(PuglView* view, const PuglEventExpose* expose)
PAINTSTRUCT ps;
EndPaint(view->impl->hwnd, &ps);
- SwapBuffers(view->impl->hdc);
}
return PUGL_SUCCESS;
M pugl/detail/win_gl.c => pugl/detail/win_gl.c +5 -6
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,13 15,14 @@
*/
/**
- @file win_gl.c OpenGL graphics backend for Windows.
+ @file win_gl.c
+ @brief OpenGL graphics backend for Windows.
*/
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/detail/win.h"
#include "pugl/pugl_gl.h"
-#include "pugl/pugl_stub.h"
#include <windows.h>
@@ 35,7 36,6 @@
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_PIXEL_TYPE_ARB 0x2013
-#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_BLUE_BITS_ARB 0x2019
@@ 49,7 49,6 @@
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
-#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
@@ 295,7 294,7 @@ puglGetProcAddress(const char* name)
}
const PuglBackend*
-puglGlBackend()
+puglGlBackend(void)
{
static const PuglBackend backend = {puglWinGlConfigure,
puglWinGlCreate,
M pugl/detail/x11.c => pugl/detail/x11.c +181 -56
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Copyright 2013 Robin Gareus <robin@gareus.org>
Copyright 2011-2012 Ben Loftis, Harrison Consoles
@@ 17,7 17,8 @@
*/
/**
- @file x11.c X11 implementation.
+ @file x11.c
+ @brief X11 implementation.
*/
#define _POSIX_C_SOURCE 199309L
@@ 25,6 26,7 @@
#include "pugl/detail/x11.h"
#include "pugl/detail/implementation.h"
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/pugl.h"
#include "pugl/pugl_stub.h"
@@ 40,6 42,11 @@
# include <X11/extensions/syncconst.h>
#endif
+#ifdef HAVE_XCURSOR
+# include <X11/Xcursor/Xcursor.h>
+# include <X11/cursorfont.h>
+#endif
+
#include <sys/select.h>
#include <sys/time.h>
@@ 74,11 81,11 @@ static bool
puglInitXSync(PuglWorldInternals* impl)
{
#ifdef HAVE_XSYNC
- int syncMajor;
- int syncMinor;
- int errorBase;
- XSyncSystemCounter* counters;
- int numCounters;
+ int syncMajor = 0;
+ int syncMinor = 0;
+ int errorBase = 0;
+ XSyncSystemCounter* counters = NULL;
+ int numCounters = 0;
if (XSyncQueryExtension(impl->display, &impl->syncEventBase, &errorBase) &&
XSyncInitialize(impl->display, &syncMajor, &syncMinor) &&
@@ 151,7 158,13 @@ puglGetNativeWorld(PuglWorld* world)
PuglInternals*
puglInitViewInternals(void)
{
- return (PuglInternals*)calloc(1, sizeof(PuglInternals));
+ PuglInternals* impl = (PuglInternals*)calloc(1, sizeof(PuglInternals));
+
+#ifdef HAVE_XCURSOR
+ impl->cursorShape = XC_arrow;
+#endif
+
+ return impl;
}
static PuglStatus
@@ 192,23 205,40 @@ puglFindView(PuglWorld* world, const Window window)
return NULL;
}
-static XSizeHints
-getSizeHints(const PuglView* view)
+static PuglStatus
+updateSizeHints(const PuglView* view)
{
+ if (!view->impl->win) {
+ return PUGL_SUCCESS;
+ }
+
+ Display* display = view->world->impl->display;
XSizeHints sizeHints = {0};
if (!view->hints[PUGL_RESIZABLE]) {
- sizeHints.flags = PMinSize|PMaxSize;
- sizeHints.min_width = (int)view->frame.width;
- sizeHints.min_height = (int)view->frame.height;
- sizeHints.max_width = (int)view->frame.width;
- sizeHints.max_height = (int)view->frame.height;
+ sizeHints.flags = PBaseSize | PMinSize | PMaxSize;
+ sizeHints.base_width = (int)view->frame.width;
+ sizeHints.base_height = (int)view->frame.height;
+ sizeHints.min_width = (int)view->frame.width;
+ sizeHints.min_height = (int)view->frame.height;
+ sizeHints.max_width = (int)view->frame.width;
+ sizeHints.max_height = (int)view->frame.height;
} else {
+ if (view->defaultWidth || view->defaultHeight) {
+ sizeHints.flags = PBaseSize;
+ sizeHints.base_width = view->defaultWidth;
+ sizeHints.base_height = view->defaultHeight;
+ }
if (view->minWidth || view->minHeight) {
sizeHints.flags = PMinSize;
sizeHints.min_width = view->minWidth;
sizeHints.min_height = view->minHeight;
}
+ if (view->maxWidth || view->maxHeight) {
+ sizeHints.flags = PMaxSize;
+ sizeHints.max_width = view->maxWidth;
+ sizeHints.max_height = view->maxHeight;
+ }
if (view->minAspectX) {
sizeHints.flags |= PAspect;
sizeHints.min_aspect.x = view->minAspectX;
@@ 218,8 248,28 @@ getSizeHints(const PuglView* view)
}
}
- return sizeHints;
+ XSetNormalHints(display, view->impl->win, &sizeHints);
+ return PUGL_SUCCESS;
+}
+
+#ifdef HAVE_XCURSOR
+static PuglStatus
+puglDefineCursorShape(PuglView* view, unsigned shape)
+{
+ PuglInternals* const impl = view->impl;
+ PuglWorld* const world = view->world;
+ Display* const display = world->impl->display;
+ const Cursor cur = XcursorShapeLoadCursor(display, shape);
+
+ if (cur) {
+ XDefineCursor(display, impl->win, cur);
+ XFreeCursor(display, cur);
+ return PUGL_SUCCESS;
+ }
+
+ return PUGL_FAILURE;
}
+#endif
PuglStatus
puglRealize(PuglView* view)
@@ 234,6 284,18 @@ puglRealize(PuglView* view)
if (!view->backend || !view->backend->configure) {
return PUGL_BAD_BACKEND;
+ } else if (view->frame.width == 0.0 && view->frame.height == 0.0) {
+ if (view->defaultWidth == 0.0 && view->defaultHeight == 0.0) {
+ return PUGL_BAD_CONFIGURATION;
+ }
+
+ const int screenWidth = DisplayWidth(display, impl->screen);
+ const int screenHeight = DisplayHeight(display, impl->screen);
+
+ view->frame.width = view->defaultWidth;
+ view->frame.height = view->defaultHeight;
+ view->frame.x = screenWidth / 2.0 - view->frame.width / 2.0;
+ view->frame.y = screenHeight / 2.0 - view->frame.height / 2.0;
}
PuglStatus st = view->backend->configure(view);
@@ 263,8 325,7 @@ puglRealize(PuglView* view)
return st;
}
- XSizeHints sizeHints = getSizeHints(view);
- XSetNormalHints(display, win, &sizeHints);
+ updateSizeHints(view);
XClassHint classHint = { world->className, world->className };
XSetClassHint(display, win, &classHint);
@@ 293,6 354,10 @@ puglRealize(PuglView* view)
"XCreateID failed\n");
}
+#ifdef HAVE_XCURSOR
+ puglDefineCursorShape(view, impl->cursorShape);
+#endif
+
puglDispatchSimpleEvent(view, PUGL_CREATE);
return PUGL_SUCCESS;
@@ 450,10 515,10 @@ translateKey(PuglView* view, XEvent* xevent, PuglEvent* event)
static uint32_t
translateModifiers(const unsigned xstate)
{
- return (((xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0) |
- ((xstate & ControlMask) ? PUGL_MOD_CTRL : 0) |
- ((xstate & Mod1Mask) ? PUGL_MOD_ALT : 0) |
- ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0));
+ return (((xstate & ShiftMask) ? PUGL_MOD_SHIFT : 0u) |
+ ((xstate & ControlMask) ? PUGL_MOD_CTRL : 0u) |
+ ((xstate & Mod1Mask) ? PUGL_MOD_ALT : 0u) |
+ ((xstate & Mod4Mask) ? PUGL_MOD_SUPER : 0u));
}
static PuglEvent
@@ 500,22 565,23 @@ translateEvent(PuglView* view, XEvent xevent)
event.expose.y = xevent.xexpose.y;
event.expose.width = xevent.xexpose.width;
event.expose.height = xevent.xexpose.height;
- event.expose.count = xevent.xexpose.count;
break;
case MotionNotify:
event.type = PUGL_MOTION;
- event.motion.time = xevent.xmotion.time / 1e3;
+ event.motion.time = (double)xevent.xmotion.time / 1e3;
event.motion.x = xevent.xmotion.x;
event.motion.y = xevent.xmotion.y;
event.motion.xRoot = xevent.xmotion.x_root;
event.motion.yRoot = xevent.xmotion.y_root;
event.motion.state = translateModifiers(xevent.xmotion.state);
- event.motion.isHint = (xevent.xmotion.is_hint == NotifyHint);
+ if (xevent.xmotion.is_hint == NotifyHint) {
+ event.motion.flags |= PUGL_IS_HINT;
+ }
break;
case ButtonPress:
if (xevent.xbutton.button >= 4 && xevent.xbutton.button <= 7) {
event.type = PUGL_SCROLL;
- event.scroll.time = xevent.xbutton.time / 1e3;
+ event.scroll.time = (double)xevent.xbutton.time / 1e3;
event.scroll.x = xevent.xbutton.x;
event.scroll.y = xevent.xbutton.y;
event.scroll.xRoot = xevent.xbutton.x_root;
@@ 524,10 590,22 @@ translateEvent(PuglView* view, XEvent xevent)
event.scroll.dx = 0.0;
event.scroll.dy = 0.0;
switch (xevent.xbutton.button) {
- case 4: event.scroll.dy = 1.0; break;
- case 5: event.scroll.dy = -1.0; break;
- case 6: event.scroll.dx = -1.0; break;
- case 7: event.scroll.dx = 1.0; break;
+ case 4:
+ event.scroll.dy = 1.0;
+ event.scroll.direction = PUGL_SCROLL_UP;
+ break;
+ case 5:
+ event.scroll.dy = -1.0;
+ event.scroll.direction = PUGL_SCROLL_DOWN;
+ break;
+ case 6:
+ event.scroll.dx = -1.0;
+ event.scroll.direction = PUGL_SCROLL_LEFT;
+ break;
+ case 7:
+ event.scroll.dx = 1.0;
+ event.scroll.direction = PUGL_SCROLL_RIGHT;
+ break;
}
// fallthru
}
@@ 537,7 615,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.button.type = ((xevent.type == ButtonPress)
? PUGL_BUTTON_PRESS
: PUGL_BUTTON_RELEASE);
- event.button.time = xevent.xbutton.time / 1e3;
+ event.button.time = (double)xevent.xbutton.time / 1e3;
event.button.x = xevent.xbutton.x;
event.button.y = xevent.xbutton.y;
event.button.xRoot = xevent.xbutton.x_root;
@@ 551,7 629,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.type = ((xevent.type == KeyPress)
? PUGL_KEY_PRESS
: PUGL_KEY_RELEASE);
- event.key.time = xevent.xkey.time / 1e3;
+ event.key.time = (double)xevent.xkey.time / 1e3;
event.key.x = xevent.xkey.x;
event.key.y = xevent.xkey.y;
event.key.xRoot = xevent.xkey.x_root;
@@ 564,7 642,7 @@ translateEvent(PuglView* view, XEvent xevent)
event.type = ((xevent.type == EnterNotify)
? PUGL_POINTER_IN
: PUGL_POINTER_OUT);
- event.crossing.time = xevent.xcrossing.time / 1e3;
+ event.crossing.time = (double)xevent.xcrossing.time / 1e3;
event.crossing.x = xevent.xcrossing.x;
event.crossing.y = xevent.xcrossing.y;
event.crossing.xRoot = xevent.xcrossing.x_root;
@@ 581,7 659,12 @@ translateEvent(PuglView* view, XEvent xevent)
case FocusIn:
case FocusOut:
event.type = (xevent.type == FocusIn) ? PUGL_FOCUS_IN : PUGL_FOCUS_OUT;
- event.focus.grab = (xevent.xfocus.mode != NotifyNormal);
+ event.focus.mode = PUGL_CROSSING_NORMAL;
+ if (xevent.xfocus.mode == NotifyGrab) {
+ event.focus.mode = PUGL_CROSSING_GRAB;
+ } else if (xevent.xfocus.mode == NotifyUngrab) {
+ event.focus.mode = PUGL_CROSSING_UNGRAB;
+ }
break;
default:
@@ 646,7 729,8 @@ puglStartTimer(PuglView* view, uintptr_t id, double timeout)
PuglWorldInternals* w = view->world->impl;
Display* const display = w->display;
const XSyncCounter counter = w->serverTimeCounter;
- const XSyncTrigger trigger = {counter, XSyncRelative, value, 0};
+ const XSyncTestType type = XSyncPositiveTransition;
+ const XSyncTrigger trigger = {counter, XSyncRelative, value, type};
XSyncAlarmAttributes attr = {trigger, value, True, XSyncAlarmActive};
const XSyncAlarm alarm = XSyncCreateAlarm(display, 0x17, &attr);
const PuglTimer timer = {alarm, view, id};
@@ 791,7 875,6 @@ mergeExposeEvents(PuglEvent* dst, const PuglEvent* src)
dst->expose.y = MIN(dst->expose.y, src->expose.y);
dst->expose.width = max_x - dst->expose.x;
dst->expose.height = max_y - dst->expose.y;
- dst->expose.count = MIN(dst->expose.count, src->expose.count);
}
}
@@ 896,8 979,10 @@ handleTimerEvent(PuglWorld* world, XEvent 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);
+ PuglEvent event = {{PUGL_TIMER, 0}};
+ event.timer.id = world->impl->timers[i].id;
+ puglDispatchEvent(world->impl->timers[i].view,
+ (const PuglEvent*)&event);
}
}
@@ 1033,7 1118,8 @@ puglGetTime(const PuglWorld* world)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
- return ((double)ts.tv_sec + ts.tv_nsec / 1000000000.0) - world->startTime;
+ return ((double)ts.tv_sec + (double)ts.tv_nsec / 1000000000.0) -
+ world->startTime;
}
PuglStatus
@@ 1048,7 1134,7 @@ PuglStatus
puglPostRedisplayRect(PuglView* view, PuglRect rect)
{
const PuglEventExpose event = {
- PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height, 0
+ PUGL_EXPOSE, 0, rect.x, rect.y, rect.width, rect.height
};
if (view->world->impl->dispatchingEvents) {
@@ 1102,19 1188,27 @@ puglSetFrame(PuglView* view, const PuglRect frame)
}
PuglStatus
-puglSetMinSize(PuglView* const view, const int width, const int height)
+puglSetDefaultSize(PuglView* const view, const int width, const int height)
{
- Display* display = view->world->impl->display;
+ view->defaultWidth = width;
+ view->defaultHeight = height;
+ return updateSizeHints(view);
+}
+PuglStatus
+puglSetMinSize(PuglView* const view, const int width, const int height)
+{
view->minWidth = width;
view->minHeight = height;
+ return updateSizeHints(view);
+}
- if (view->impl->win) {
- XSizeHints sizeHints = getSizeHints(view);
- XSetNormalHints(display, view->impl->win, &sizeHints);
- }
-
- return PUGL_SUCCESS;
+PuglStatus
+puglSetMaxSize(PuglView* const view, const int width, const int height)
+{
+ view->minWidth = width;
+ view->minHeight = height;
+ return updateSizeHints(view);
}
PuglStatus
@@ 1124,19 1218,12 @@ puglSetAspectRatio(PuglView* const view,
const int maxX,
const int maxY)
{
- Display* display = view->world->impl->display;
-
view->minAspectX = minX;
view->minAspectY = minY;
view->maxAspectX = maxX;
view->maxAspectY = maxY;
- if (view->impl->win) {
- XSizeHints sizeHints = getSizeHints(view);
- XSetNormalHints(display, view->impl->win, &sizeHints);
- }
-
- return PUGL_SUCCESS;
+ return updateSizeHints(view);
}
PuglStatus
@@ 1202,6 1289,44 @@ puglSetClipboard(PuglView* const view,
return PUGL_SUCCESS;
}
+#ifdef HAVE_XCURSOR
+static const unsigned cursor_nums[] = {
+ XC_arrow, // ARROW
+ XC_xterm, // CARET
+ XC_crosshair, // CROSSHAIR
+ XC_hand2, // HAND
+ XC_pirate, // NO
+ XC_sb_h_double_arrow, // LEFT_RIGHT
+ XC_sb_v_double_arrow, // UP_DOWN
+};
+#endif
+
+PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor)
+{
+#ifdef HAVE_XCURSOR
+ PuglInternals* const impl = view->impl;
+ const unsigned index = (unsigned)cursor;
+ const unsigned count = sizeof(cursor_nums) / sizeof(cursor_nums[0]);
+ if (index >= count) {
+ return PUGL_BAD_PARAMETER;
+ }
+
+ const unsigned shape = cursor_nums[index];
+ if (!impl->win || impl->cursorShape == shape) {
+ return PUGL_SUCCESS;
+ }
+
+ impl->cursorShape = cursor_nums[index];
+
+ return puglDefineCursorShape(view, impl->cursorShape);
+#else
+ (void)view;
+ (void)cursor;
+ return PUGL_FAILURE;
+#endif
+}
+
const PuglBackend*
puglStubBackend(void)
{
M pugl/detail/x11.h => pugl/detail/x11.h +8 -4
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file x11.h Shared definitions for X11 implementation.
+ @file x11.h
+ @brief Shared definitions for X11 implementation.
*/
#include "pugl/detail/types.h"
@@ 43,7 44,7 @@ typedef struct {
typedef struct {
XID alarm;
PuglView* view;
- uint64_t id;
+ uintptr_t id;
} PuglTimer;
struct PuglWorldInternalsImpl {
@@ 60,13 61,16 @@ struct PuglWorldInternalsImpl {
struct PuglInternalsImpl {
Display* display;
- int screen;
XVisualInfo* vi;
Window win;
XIC xic;
PuglSurface* surface;
PuglEvent pendingConfigure;
PuglEvent pendingExpose;
+ int screen;
+#ifdef HAVE_XCURSOR
+ unsigned cursorShape;
+#endif
};
static inline PuglStatus
M pugl/detail/x11_cairo.c => pugl/detail/x11_cairo.c +3 -2
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file x11_cairo.c Cairo graphics backend for X11.
+ @file x11_cairo.c
+ @brief Cairo graphics backend for X11.
*/
#include "pugl/detail/types.h"
M pugl/detail/x11_gl.c => pugl/detail/x11_gl.c +28 -25
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,14 15,15 @@
*/
/**
- @file x11_gl.c OpenGL graphics backend for X11.
+ @file x11_gl.c
+ @brief OpenGL graphics backend for X11.
*/
+#include "pugl/detail/stub.h"
#include "pugl/detail/types.h"
#include "pugl/detail/x11.h"
#include "pugl/pugl.h"
#include "pugl/pugl_gl.h"
-#include "pugl/pugl_stub.h"
#include <GL/glx.h>
#include <X11/X.h>
@@ 114,6 115,28 @@ puglX11GlConfigure(PuglView* view)
}
static PuglStatus
+puglX11GlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+ glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
+puglX11GlLeave(PuglView* view, const PuglEventExpose* expose)
+{
+ PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
+
+ if (expose && surface->double_buffered) {
+ glXSwapBuffers(view->impl->display, view->impl->win);
+ }
+
+ glXMakeCurrent(view->impl->display, None, NULL);
+
+ return PUGL_SUCCESS;
+}
+
+static PuglStatus
puglX11GlCreate(PuglView* view)
{
PuglInternals* const impl = view->impl;
@@ 152,7 175,9 @@ puglX11GlCreate(PuglView* view)
const int swapInterval = view->hints[PUGL_SWAP_INTERVAL];
if (glXSwapIntervalEXT && swapInterval != PUGL_DONT_CARE) {
+ puglX11GlEnter(view, NULL);
glXSwapIntervalEXT(display, impl->win, swapInterval);
+ puglX11GlLeave(view, NULL);
}
glXGetConfig(impl->display,
@@ 175,28 200,6 @@ puglX11GlDestroy(PuglView* view)
return PUGL_SUCCESS;
}
-static PuglStatus
-puglX11GlEnter(PuglView* view, const PuglEventExpose* PUGL_UNUSED(expose))
-{
- PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
- glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx);
- return PUGL_SUCCESS;
-}
-
-static PuglStatus
-puglX11GlLeave(PuglView* view, const PuglEventExpose* expose)
-{
- PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface;
-
- if (expose && surface->double_buffered) {
- glXSwapBuffers(view->impl->display, view->impl->win);
- }
-
- glXMakeCurrent(view->impl->display, None, NULL);
-
- return PUGL_SUCCESS;
-}
-
PuglGlFunc
puglGetProcAddress(const char* name)
{
M pugl/gl.h => pugl/gl.h +3 -2
@@ 1,5 1,5 @@
/*
- Copyright 2012-2014 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file gl.h Portable header wrapper for gl.h.
+ @file gl.h
+ @brief Portable header wrapper for gl.h.
Unfortunately, GL includes vary across platforms so this header allows for
pure portable programs.
M pugl/glu.h => pugl/glu.h +3 -2
@@ 1,5 1,5 @@
/*
- Copyright 2012-2015 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file glu.h Portable header wrapper for glu.h.
+ @file glu.h
+ @brief Portable header wrapper for glu.h.
Unfortunately, GL includes vary across platforms so this header allows for
pure portable programs.
M pugl/pugl.h => pugl/pugl.h +115 -40
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl.h Pugl API.
+ @file pugl.h
+ @brief Pugl API.
*/
#ifndef PUGL_PUGL_H
@@ 52,6 53,12 @@
# endif
#endif
+#if defined(__GNUC__)
+# define PUGL_CONST_FUNC __attribute__((const))
+#else
+# define PUGL_CONST_FUNC
+#endif
+
#ifdef __cplusplus
# define PUGL_BEGIN_DECLS extern "C" {
# define PUGL_END_DECLS }
@@ 63,9 70,13 @@
PUGL_BEGIN_DECLS
/**
- @defgroup pugl_api Pugl
+ @defgroup pugl Pugl
A minimal portable API for embeddable GUIs.
@{
+
+ @defgroup pugl_c C API
+ Public C API.
+ @{
*/
/**
@@ 87,7 98,7 @@ typedef struct {
Event definitions.
All updates to the view happen via events, which are dispatched to the
- view's #PuglEventFunc by Pugl. Most events map directly to one from the
+ view's event function by Pugl. Most events map directly to one from the
underlying window system, but some are constructed by Pugl itself so there
is not necessarily a direct correspondence.
@@ 210,7 221,8 @@ typedef enum {
Common flags for all event types.
*/
typedef enum {
- PUGL_IS_SEND_EVENT = 1 ///< Event is synthetic
+ PUGL_IS_SEND_EVENT = 1, ///< Event is synthetic
+ PUGL_IS_HINT = 2 ///< Event is a hint (not direct user input)
} PuglEventFlag;
/**
@@ 228,6 240,22 @@ typedef enum {
} PuglCrossingMode;
/**
+ Scroll direction.
+
+ Describes the direction of a #PuglEventScroll along with whether the scroll
+ is a "smooth" scroll. The discrete directions are for devices like mouse
+ wheels with constrained axes, while a smooth scroll is for those with
+ arbitrary scroll direction freedom, like some touchpads.
+*/
+typedef enum {
+ PUGL_SCROLL_UP, ///< Scroll up
+ PUGL_SCROLL_DOWN, ///< Scroll down
+ PUGL_SCROLL_LEFT, ///< Scroll left
+ PUGL_SCROLL_RIGHT, ///< Scroll right
+ PUGL_SCROLL_SMOOTH ///< Smooth scroll in any direction
+} PuglScrollDirection;
+
+/**
Common header for all event structs.
*/
typedef struct {
@@ 322,7 350,6 @@ typedef struct {
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;
/**
@@ 342,9 369,9 @@ typedef PuglEventAny PuglEventClose;
view with the keyboard focus will receive any key press or release events.
*/
typedef struct {
- 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
+ PuglEventType type; ///< #PUGL_FOCUS_IN or #PUGL_FOCUS_OUT
+ PuglEventFlags flags; ///< Bitwise OR of #PuglEventFlag values
+ PuglCrossingMode mode; ///< Reason for focus change
} PuglEventFocus;
/**
@@ 355,12 382,12 @@ typedef struct {
as text input.
Keys are represented portably as Unicode code points, using the "natural"
- code point for the key where possible (see #PuglKey for details). The #key
+ code point for the key where possible (see #PuglKey for details). The `key`
field is the code for the pressed key, without any modifiers applied. For
- example, a press or release of the 'A' key will have #key 97 ('a')
+ example, a press or release of the 'A' key will have `key` 97 ('a')
regardless of whether shift or control are being held.
- Alternatively, the raw #keycode can be used to work directly with physical
+ Alternatively, the raw `keycode` can be used to work directly with physical
keys, but note that this value is not portable and differs between platforms
and hardware.
*/
@@ 407,7 434,7 @@ typedef struct {
This event is sent when the pointer enters or leaves the view. This can
happen for several reasons (not just the user dragging the pointer over the
- window edge), as described by the #mode field.
+ window edge), as described by the `mode` field.
*/
typedef struct {
PuglEventType type; ///< #PUGL_POINTER_IN or #PUGL_POINTER_OUT
@@ 448,30 475,29 @@ typedef struct {
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 view
} 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
- 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
+ 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
+ PuglScrollDirection direction; ///< Scroll direction
+ double dx; ///< Scroll X distance in lines
+ double dy; ///< Scroll Y distance in lines
} PuglEventScroll;
/**
@@ 494,7 520,7 @@ typedef struct {
This event is sent at the regular interval specified in the call to
puglStartTimer() that activated it.
- The #id is the application-specific ID given to puglStartTimer() which
+ The `id` is the application-specific ID given to puglStartTimer() which
distinguishes this timer from others. It should always be checked in the
event handler, even in applications that register only one timer.
*/
@@ 507,7 533,7 @@ typedef struct {
/**
View event.
- This is a union of all event types. The #type must be checked to determine
+ This is a union of all event types. The type must be checked to determine
which fields are safe to access. A pointer to PuglEvent can either be cast
to the appropriate type, or the union members used.
@@ 548,6 574,7 @@ typedef enum {
PUGL_FAILURE, ///< Non-fatal failure
PUGL_UNKNOWN_ERROR, ///< Unknown system error
PUGL_BAD_BACKEND, ///< Invalid or missing backend
+ PUGL_BAD_CONFIGURATION, ///< Invalid view configuration
PUGL_BAD_PARAMETER, ///< Invalid parameter
PUGL_BACKEND_FAILED, ///< Backend initialisation failed
PUGL_REGISTRATION_FAILED, ///< Class registration failed
@@ 594,7 621,7 @@ typedef struct PuglWorldImpl PuglWorld;
typedef void* PuglWorldHandle;
/**
- The type of a PuglWorld.
+ The type of a World.
*/
typedef enum {
PUGL_PROGRAM, ///< Top-level application
@@ 728,19 755,18 @@ puglGetTime(const PuglWorld* world);
This function is a single iteration of the main loop, and should be called
repeatedly to update all views.
- If a positive timeout is given, then events will be processed for that
- amount of time, starting from when this function was called. For purely
- event-driven programs, a timeout of -1.0 can be used to block indefinitely
- until something happens. For continuously animating programs, a timeout
- that is a reasonable fraction of the ideal frame period should be used, to
- minimise input latency by ensuring that as many input events are consumed as
- possible before drawing. Plugins should always use a timeout of 0.0 to
- avoid blocking the host.
+ If `timeout` is zero, then this function will not block. Plugins should
+ always use a timeout of zero to avoid blocking the host.
- @param world The world to update.
+ If a positive `timeout` is given, then events will be processed for that
+ amount of time, starting from when this function was called.
- @param timeout Maximum time to wait, in seconds. If zero, the call returns
- immediately, if negative, the call blocks indefinitely.
+ If a negative `timeout` is given, this function will block indefinitely
+ until an event occurs.
+
+ For continuously animating programs, a timeout that is a reasonable fraction
+ of the ideal frame period should be used, to minimise input latency by
+ ensuring that as many input events are consumed as possible before drawing.
@return #PUGL_SUCCESS if events are read, #PUGL_FAILURE if not, or an error.
*/
@@ 937,6 963,16 @@ PUGL_API PuglStatus
puglSetFrame(PuglView* view, PuglRect frame);
/**
+ Set the default size of the view.
+
+ This should be called before puglResize() to set the default size of the
+ view, which will be the initial size of the window if this is a top level
+ view.
+*/
+PUGL_API PuglStatus
+puglSetDefaultSize(PuglView* view, int width, int height);
+
+/**
Set the minimum size of the view.
If an initial minimum size is known, this should be called before
@@ 946,6 982,15 @@ PUGL_API PuglStatus
puglSetMinSize(PuglView* view, int width, int height);
/**
+ Set the maximum size of the view.
+
+ If an initial maximum size is known, this should be called before
+ puglRealize() to avoid stutter, though it can be called afterwards as well.
+*/
+PUGL_API PuglStatus
+puglSetMaxSize(PuglView* view, int width, int height);
+
+/**
Set the view aspect ratio range.
The x and y values here represent a ratio of width to height. To set a
@@ 992,6 1037,9 @@ puglSetParentWindow(PuglView* view, PuglNativeView parent);
Set this for transient children like dialogs, to have them properly
associated with their parent window. This should be called before
puglRealize().
+
+ A view can either have a parent (for embedding) or a transient parent (for
+ top-level windows like dialogs), but not both.
*/
PUGL_API PuglStatus
puglSetTransientFor(PuglView* view, PuglNativeView parent);
@@ 1091,6 1139,22 @@ puglPostRedisplayRect(PuglView* view, PuglRect rect);
*/
/**
+ A mouse cursor type.
+
+ This is a portable subset of mouse cursors that exist on X11, MacOS, and
+ Windows.
+*/
+typedef enum {
+ PUGL_CURSOR_ARROW, ///< Default pointing arrow
+ PUGL_CURSOR_CARET, ///< Caret (I-Beam) for text entry
+ PUGL_CURSOR_CROSSHAIR, ///< Cross-hair
+ PUGL_CURSOR_HAND, ///< Hand with a pointing finger
+ PUGL_CURSOR_NO, ///< Operation not allowed
+ PUGL_CURSOR_LEFT_RIGHT, ///< Left/right arrow for horizontal resize
+ PUGL_CURSOR_UP_DOWN, ///< Up/down arrow for vertical resize
+} PuglCursor;
+
+/**
Grab the keyboard input focus.
*/
PUGL_API PuglStatus
@@ 1134,6 1198,16 @@ PUGL_API const void*
puglGetClipboard(PuglView* view, const char** type, size_t* len);
/**
+ Set the mouse cursor.
+
+ This changes the system cursor that is displayed when the pointer is inside
+ the view. May fail if setting the cursor is not supported on this system,
+ for example if compiled on X11 without Xcursor support.
+ */
+PUGL_API PuglStatus
+puglSetCursor(PuglView* view, PuglCursor cursor);
+
+/**
Request user attention.
This hints to the system that the window or application requires attention
@@ 1496,6 1570,7 @@ puglLeaveContext(PuglView* view, bool drawing);
/**
@}
@}
+ @}
*/
PUGL_END_DECLS
M pugl/pugl.hpp => pugl/pugl.hpp +627 -47
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl.hpp Pugl C++ API wrapper.
+ @file pugl.hpp
+ @brief Pugl C++ API wrapper.
*/
#ifndef PUGL_PUGL_HPP
@@ 23,12 24,18 @@
#include "pugl/pugl.h"
-/**
- @defgroup puglxx C++
+#include <cassert>
+#include <chrono>
+#include <functional>
+#include <memory>
+#include <stdexcept>
+#include <type_traits>
+/**
+ @defgroup pugl_cxx C++ API
C++ API wrapper.
- @ingroup pugl_api
+ @ingroup pugl
@{
*/
@@ 37,84 44,657 @@
*/
namespace pugl {
+namespace detail {
+
+/// Free function for a C object
+template<typename T>
+using FreeFunc = void (*)(T*);
+
+/// Simple overhead-free deleter for a C object
+template<typename T, FreeFunc<T> Free>
+struct Deleter {
+ void operator()(T* ptr) { Free(ptr); }
+};
+
+/// Generic C++ wrapper for a C object
+template<class T, FreeFunc<T> Free>
+class Wrapper
+{
+public:
+ T* cobj() { return _ptr.get(); }
+ const T* cobj() const { return _ptr.get(); }
+
+protected:
+ explicit Wrapper(T* ptr)
+ : _ptr(ptr, Deleter<T, Free>{})
+ {}
+
+private:
+ std::unique_ptr<T, Deleter<T, Free>> _ptr;
+};
+
+} // namespace detail
+
+using Rect = PuglRect; ///< @copydoc PuglRect
+
/**
- A drawable region that receives events.
+ @defgroup eventsxx Events
+ @ingroup pugl_cxx
+ @copydoc events
+ @{
+*/
- This is a thin wrapper for a PuglView that contains only a pointer.
+/**
+ A strongly-typed analogue of PuglEvent.
- @ingroup puglxx
+ This is bit-for-bit identical to the corresponding PuglEvent, so events are
+ simply cast to this type to avoid any copying overhead.
+
+ @tparam t The `type` field of the corresponding PuglEvent.
+
+ @tparam Base The specific struct type of the corresponding PuglEvent.
*/
-class View {
+template<PuglEventType t, class Base>
+struct Event final : Base {
+ using BaseEvent = Base;
+
+ static constexpr const PuglEventType type = t;
+};
+
+using Mod = PuglMod; ///< @copydoc PuglMod
+using Mods = PuglMods; ///< @copydoc PuglMods
+using Key = PuglKey; ///< @copydoc PuglKey
+using EventType = PuglEventType; ///< @copydoc PuglEventType
+using EventFlag = PuglEventFlag; ///< @copydoc PuglEventFlag
+using EventFlags = PuglEventFlags; ///< @copydoc PuglEventFlags
+using CrossingMode = PuglCrossingMode; ///< @copydoc PuglCrossingMode
+
+/// @copydoc PuglEventCreate
+using CreateEvent = Event<PUGL_CREATE, PuglEventCreate>;
+
+/// @copydoc PuglEventDestroy
+using DestroyEvent = Event<PUGL_DESTROY, PuglEventDestroy>;
+
+/// @copydoc PuglEventConfigure
+using ConfigureEvent = Event<PUGL_CONFIGURE, PuglEventConfigure>;
+
+/// @copydoc PuglEventMap
+using MapEvent = Event<PUGL_MAP, PuglEventMap>;
+
+/// @copydoc PuglEventUnmap
+using UnmapEvent = Event<PUGL_UNMAP, PuglEventUnmap>;
+
+/// @copydoc PuglEventUpdate
+using UpdateEvent = Event<PUGL_UPDATE, PuglEventUpdate>;
+
+/// @copydoc PuglEventExpose
+using ExposeEvent = Event<PUGL_EXPOSE, PuglEventExpose>;
+
+/// @copydoc PuglEventClose
+using CloseEvent = Event<PUGL_CLOSE, PuglEventClose>;
+
+/// @copydoc PuglEventFocus
+using FocusInEvent = Event<PUGL_FOCUS_IN, PuglEventFocus>;
+
+/// @copydoc PuglEventFocus
+using FocusOutEvent = Event<PUGL_FOCUS_OUT, PuglEventFocus>;
+
+/// @copydoc PuglEventKey
+using KeyPressEvent = Event<PUGL_KEY_PRESS, PuglEventKey>;
+
+/// @copydoc PuglEventKey
+using KeyReleaseEvent = Event<PUGL_KEY_RELEASE, PuglEventKey>;
+
+/// @copydoc PuglEventText
+using TextEvent = Event<PUGL_TEXT, PuglEventText>;
+
+/// @copydoc PuglEventCrossing
+using PointerInEvent = Event<PUGL_POINTER_IN, PuglEventCrossing>;
+
+/// @copydoc PuglEventCrossing
+using PointerOutEvent = Event<PUGL_POINTER_OUT, PuglEventCrossing>;
+
+/// @copydoc PuglEventButton
+using ButtonPressEvent = Event<PUGL_BUTTON_PRESS, PuglEventButton>;
+
+/// @copydoc PuglEventButton
+using ButtonReleaseEvent = Event<PUGL_BUTTON_RELEASE, PuglEventButton>;
+
+/// @copydoc PuglEventMotion
+using MotionEvent = Event<PUGL_MOTION, PuglEventMotion>;
+
+/// @copydoc PuglEventScroll
+using ScrollEvent = Event<PUGL_SCROLL, PuglEventScroll>;
+
+/// @copydoc PuglEventClient
+using ClientEvent = Event<PUGL_CLIENT, PuglEventClient>;
+
+/// @copydoc PuglEventTimer
+using TimerEvent = Event<PUGL_TIMER, PuglEventTimer>;
+
+/**
+ @}
+ @defgroup statusxx Status
+ @ingroup pugl_cxx
+ @copydoc status
+ @{
+*/
+
+/// @copydoc PuglStatus
+enum class Status {
+ success, ///< @copydoc PUGL_SUCCESS
+ failure, ///< @copydoc PUGL_FAILURE
+ unknownError, ///< @copydoc PUGL_UNKNOWN_ERROR
+ badBackend, ///< @copydoc PUGL_BAD_BACKEND
+ badConfiguration, ///< @copydoc PUGL_BAD_CONFIGURATION
+ badParameter, ///< @copydoc PUGL_BAD_PARAMETER
+ backendFailed, ///< @copydoc PUGL_BACKEND_FAILED
+ registrationFailed, ///< @copydoc PUGL_REGISTRATION_FAILED
+ realizeFailed, ///< @copydoc PUGL_REALIZE_FAILED
+ setFormatFailed, ///< @copydoc PUGL_SET_FORMAT_FAILED
+ createContextFailed, ///< @copydoc PUGL_CREATE_CONTEXT_FAILED
+ unsupportedType, ///< @copydoc PUGL_UNSUPPORTED_TYPE
+};
+
+static_assert(Status(PUGL_UNSUPPORTED_TYPE) == Status::unsupportedType, "");
+
+/// @copydoc puglStrerror
+static inline const char*
+strerror(const pugl::Status status)
+{
+ return puglStrerror(static_cast<PuglStatus>(status));
+}
+
+/**
+ @}
+ @defgroup worldxx World
+ @ingroup pugl_cxx
+ @copydoc world
+ @{
+*/
+
+class World;
+
+/// @copydoc PuglWorldType
+enum class WorldType {
+ program, ///< @copydoc PUGL_PROGRAM
+ module, ///< @copydoc PUGL_MODULE
+};
+
+static_assert(WorldType(PUGL_MODULE) == WorldType::module, "");
+
+/// @copydoc PuglWorldFlag
+enum class WorldFlag {
+ threads = PUGL_WORLD_THREADS, ///< @copydoc PUGL_WORLD_THREADS
+};
+
+static_assert(WorldFlag(PUGL_WORLD_THREADS) == WorldFlag::threads, "");
+
+using WorldFlags = PuglWorldFlags; ///< @copydoc PuglWorldFlags
+
+/// @copydoc PuglLogLevel
+enum class LogLevel {
+ err = PUGL_LOG_LEVEL_ERR, ///< @copydoc PUGL_LOG_LEVEL_ERR
+ warning = PUGL_LOG_LEVEL_WARNING, ///< @copydoc PUGL_LOG_LEVEL_WARNING
+ info = PUGL_LOG_LEVEL_INFO, ///< @copydoc PUGL_LOG_LEVEL_INFO
+ debug = PUGL_LOG_LEVEL_DEBUG, ///< @copydoc PUGL_LOG_LEVEL_DEBUG
+};
+
+static_assert(LogLevel(PUGL_LOG_LEVEL_DEBUG) == LogLevel::debug, "");
+
+/// @copydoc PuglLogFunc
+using LogFunc =
+ std::function<void(World& world, LogLevel level, const char* msg)>;
+
+/**
+ A `std::chrono` compatible clock that uses Pugl time.
+*/
+class Clock
+{
public:
- View(int* pargc, char** argv)
- : _view(puglInit(pargc, argv))
+ using rep = double; ///< Time representation
+ using duration = std::chrono::duration<double>; ///< Duration in seconds
+ using time_point = std::chrono::time_point<Clock>; ///< A Pugl time point
+
+ static constexpr bool is_steady = true; ///< Steady clock flag, always true
+
+ /// Construct a clock that uses time from puglGetTime()
+ explicit Clock(World& world)
+ : _world{world}
+ {}
+
+ /// Return the current time
+ time_point now() const;
+
+private:
+ const pugl::World& _world;
+};
+
+/// @copydoc PuglWorld
+class World : public detail::Wrapper<PuglWorld, puglFreeWorld>
+{
+public:
+ explicit World(WorldType type, WorldFlags flags)
+ : Wrapper{puglNewWorld(static_cast<PuglWorldType>(type), flags)}
+ , _clock(*this)
+ {
+ if (!cobj()) {
+ throw std::runtime_error("Failed to create pugl::World");
+ }
+ }
+
+ explicit World(WorldType type)
+ : World{type, {}}
{
- puglSetHandle(_view, this);
- puglSetEventFunc(_view, _onEvent);
+ if (!cobj()) {
+ throw std::runtime_error("Failed to create pugl::World");
+ }
}
- virtual ~View() { puglDestroy(_view); }
+ /// @copydoc puglGetNativeWorld
+ void* nativeWorld() { return puglGetNativeWorld(cobj()); }
+
+ // TODO: setLogFunc
- virtual void initWindowParent(PuglNativeWindow parent) {
- puglInitWindowParent(_view, parent);
+ Status setLogLevel(const LogLevel level)
+ {
+ return static_cast<Status>(
+ puglSetLogLevel(cobj(), static_cast<PuglLogLevel>(level)));
}
- virtual void initWindowSize(int width, int height) {
- puglInitWindowSize(_view, width, height);
+ /// @copydoc puglSetClassName
+ Status setClassName(const char* const name)
+ {
+ return static_cast<Status>(puglSetClassName(cobj(), name));
}
- virtual void initWindowMinSize(int width, int height) {
- puglInitWindowMinSize(_view, width, height);
+ /// @copydoc puglGetTime
+ double time() const { return puglGetTime(cobj()); }
+
+ /// @copydoc puglUpdate
+ Status update(const double timeout)
+ {
+ return static_cast<Status>(puglUpdate(cobj(), timeout));
+ }
+
+ /// Return a clock that uses Pugl time
+ const Clock& clock() { return _clock; }
+
+private:
+ Clock _clock;
+};
+
+inline Clock::time_point
+Clock::now() const
+{
+ return time_point{duration{_world.time()}};
+}
+
+/**
+ @}
+ @defgroup viewxx View
+ @ingroup pugl_cxx
+ @copydoc view
+ @{
+*/
+
+using Backend = PuglBackend; ///< @copydoc PuglBackend
+using NativeView = PuglNativeView; ///< @copydoc PuglNativeView
+
+/// @copydoc PuglViewHint
+enum class ViewHint {
+ useCompatProfile, ///< @copydoc PUGL_USE_COMPAT_PROFILE
+ useDebugContext, ///< @copydoc PUGL_USE_DEBUG_CONTEXT
+ contextVersionMajor, ///< @copydoc PUGL_CONTEXT_VERSION_MAJOR
+ contextVersionMinor, ///< @copydoc PUGL_CONTEXT_VERSION_MINOR
+ redBits, ///< @copydoc PUGL_RED_BITS
+ greenBits, ///< @copydoc PUGL_GREEN_BITS
+ blueBits, ///< @copydoc PUGL_BLUE_BITS
+ alphaBits, ///< @copydoc PUGL_ALPHA_BITS
+ depthBits, ///< @copydoc PUGL_DEPTH_BITS
+ stencilBits, ///< @copydoc PUGL_STENCIL_BITS
+ samples, ///< @copydoc PUGL_SAMPLES
+ doubleBuffer, ///< @copydoc PUGL_DOUBLE_BUFFER
+ swapInterval, ///< @copydoc PUGL_SWAP_INTERVAL
+ resizable, ///< @copydoc PUGL_RESIZABLE
+ ignoreKeyRepeat, ///< @copydoc PUGL_IGNORE_KEY_REPEAT
+};
+
+static_assert(ViewHint(PUGL_IGNORE_KEY_REPEAT) == ViewHint::ignoreKeyRepeat,
+ "");
+
+using ViewHintValue = PuglViewHintValue; ///< @copydoc PuglViewHintValue
+
+/// @copydoc PuglCursor
+enum class Cursor {
+ arrow, ///< @copydoc PUGL_CURSOR_ARROW
+ caret, ///< @copydoc PUGL_CURSOR_CARET
+ crosshair, ///< @copydoc PUGL_CURSOR_CROSSHAIR
+ hand, ///< @copydoc PUGL_CURSOR_HAND
+ no, ///< @copydoc PUGL_CURSOR_NO
+ leftRight, ///< @copydoc PUGL_CURSOR_LEFT_RIGHT
+ upDown, ///< @copydoc PUGL_CURSOR_UP_DOWN
+};
+
+static_assert(Cursor(PUGL_CURSOR_UP_DOWN) == Cursor::upDown, "");
+
+/// @copydoc PuglView
+class View : protected detail::Wrapper<PuglView, puglFreeView>
+{
+public:
+ /**
+ @name Setup
+ Methods for creating and destroying a view.
+ @{
+ */
+
+ explicit View(World& world)
+ : Wrapper{puglNewView(world.cobj())}
+ , _world(world)
+ {
+ if (!cobj()) {
+ throw std::runtime_error("Failed to create pugl::View");
+ }
+
+ puglSetHandle(cobj(), this);
+ puglSetEventFunc(cobj(), dispatchEvent);
+ }
+
+ virtual ~View() = default;
+
+ View(const View&) = delete;
+ View& operator=(const View&) = delete;
+
+ View(View&&) = delete;
+ View&& operator=(View&&) = delete;
+
+ const pugl::World& world() const { return _world; }
+ pugl::World& world() { return _world; }
+
+ /// @copydoc puglSetViewHint
+ Status setHint(ViewHint hint, int value)
+ {
+ return static_cast<Status>(
+ puglSetViewHint(cobj(), static_cast<PuglViewHint>(hint), value));
+ }
+
+ /**
+ @}
+ @name Frame
+ Methods for working with the position and size of a view.
+ @{
+ */
+
+ /// @copydoc puglGetFrame
+ Rect frame() const { return puglGetFrame(cobj()); }
+
+ /// @copydoc puglSetFrame
+ Status setFrame(Rect frame)
+ {
+ return static_cast<Status>(puglSetFrame(cobj(), frame));
+ }
+
+ /// @copydoc puglSetDefaultSize
+ Status setDefaultSize(int width, int height)
+ {
+ return static_cast<Status>(puglSetDefaultSize(cobj(), width, height));
+ }
+
+ /// @copydoc puglSetMinSize
+ Status setMinSize(int width, int height)
+ {
+ return static_cast<Status>(puglSetMinSize(cobj(), width, height));
}
- virtual void initWindowAspectRatio(int min_x, int min_y, int max_x, int max_y) {
- puglInitWindowAspectRatio(_view, min_x, min_y, max_x, max_y);
+ /// @copydoc puglSetMaxSize
+ Status setMaxSize(int width, int height)
+ {
+ return static_cast<Status>(puglSetMaxSize(cobj(), width, height));
}
- virtual void initResizable(bool resizable) {
- puglInitResizable(_view, resizable);
+ /// @copydoc puglSetAspectRatio
+ Status setAspectRatio(int minX, int minY, int maxX, int maxY)
+ {
+ return static_cast<Status>(
+ puglSetAspectRatio(cobj(), minX, minY, maxX, maxY));
}
- virtual void initTransientFor(uintptr_t parent) {
- puglInitTransientFor(_view, parent);
+ /**
+ @}
+ @name Windows
+ Methods for working with top-level windows.
+ @{
+ */
+
+ /// @copydoc puglSetWindowTitle
+ Status setWindowTitle(const char* title)
+ {
+ return static_cast<Status>(puglSetWindowTitle(cobj(), title));
}
- virtual void initBackend(const PuglBackend* backend) {
- puglInitBackend(_view, backend);
+ /// @copydoc puglSetParentWindow
+ Status setParentWindow(NativeView parent)
+ {
+ return static_cast<Status>(puglSetParentWindow(cobj(), parent));
+ }
+
+ /// @copydoc puglSetTransientFor
+ Status setTransientFor(NativeView parent)
+ {
+ return static_cast<Status>(puglSetTransientFor(cobj(), parent));
+ }
+
+ /// @copydoc puglRealize
+ Status realize() { return static_cast<Status>(puglRealize(cobj())); }
+
+ /// @copydoc puglShowWindow
+ Status showWindow() { return static_cast<Status>(puglShowWindow(cobj())); }
+
+ /// @copydoc puglHideWindow
+ Status hideWindow() { return static_cast<Status>(puglHideWindow(cobj())); }
+
+ /// @copydoc puglGetVisible
+ bool visible() const { return puglGetVisible(cobj()); }
+
+ /// @copydoc puglGetNativeWindow
+ NativeView nativeWindow() { return puglGetNativeWindow(cobj()); }
+
+ /**
+ @}
+ @name Graphics
+ Methods for working with the graphics context and scheduling
+ redisplays.
+ @{
+ */
+
+ /// @copydoc puglGetContext
+ void* context() { return puglGetContext(cobj()); }
+
+ /// @copydoc puglPostRedisplay
+ Status postRedisplay()
+ {
+ return static_cast<Status>(puglPostRedisplay(cobj()));
}
- virtual void createWindow(const char* title) {
- puglCreateWindow(_view, title);
+ /// @copydoc puglPostRedisplayRect
+ Status postRedisplayRect(const Rect rect)
+ {
+ return static_cast<Status>(puglPostRedisplayRect(cobj(), rect));
}
- virtual void showWindow() { puglShowWindow(_view); }
- virtual void hideWindow() { puglHideWindow(_view); }
- virtual PuglNativeWindow getNativeWindow() { return puglGetNativeWindow(_view); }
+ /**
+ @}
+ @name Interaction
+ Methods for interacting with the user and window system.
+ @{
+ */
- virtual void onEvent(const PuglEvent* event) = 0;
+ /// @copydoc puglGrabFocus
+ Status grabFocus() { return static_cast<Status>(puglGrabFocus(cobj())); }
- virtual void* getContext() { return puglGetContext(_view); }
- virtual void ignoreKeyRepeat(bool ignore) { puglIgnoreKeyRepeat(_view, ignore); }
- virtual void grabFocus() { puglGrabFocus(_view); }
- virtual void requestAttention() { puglRequestAttention(_view); }
- virtual PuglStatus waitForEvent() { return puglWaitForEvent(_view); }
- virtual PuglStatus processEvents() { return puglProcessEvents(_view); }
- virtual void postRedisplay() { puglPostRedisplay(_view); }
+ /// @copydoc puglHasFocus
+ bool hasFocus() const { return puglHasFocus(cobj()); }
- PuglView* cobj() { return _view; }
+ /// @copydoc puglSetBackend
+ Status setBackend(const PuglBackend* backend)
+ {
+ return static_cast<Status>(puglSetBackend(cobj(), backend));
+ }
+
+ /// @copydoc puglSetCursor
+ Status setCursor(const Cursor cursor)
+ {
+ return static_cast<Status>(
+ puglSetCursor(cobj(), static_cast<PuglCursor>(cursor)));
+ }
+
+ /// @copydoc puglRequestAttention
+ Status requestAttention()
+ {
+ return static_cast<Status>(puglRequestAttention(cobj()));
+ }
+
+ /**
+ @}
+ @name Event Handlers
+ Methods called when events are dispatched to the view.
+ @{
+ */
+
+ virtual Status onCreate(const CreateEvent&) PUGL_CONST_FUNC;
+ virtual Status onDestroy(const DestroyEvent&) PUGL_CONST_FUNC;
+ virtual Status onConfigure(const ConfigureEvent&) PUGL_CONST_FUNC;
+ virtual Status onMap(const MapEvent&) PUGL_CONST_FUNC;
+ virtual Status onUnmap(const UnmapEvent&) PUGL_CONST_FUNC;
+ virtual Status onUpdate(const UpdateEvent&) PUGL_CONST_FUNC;
+ virtual Status onExpose(const ExposeEvent&) PUGL_CONST_FUNC;
+ virtual Status onClose(const CloseEvent&) PUGL_CONST_FUNC;
+ virtual Status onFocusIn(const FocusInEvent&) PUGL_CONST_FUNC;
+ virtual Status onFocusOut(const FocusOutEvent&) PUGL_CONST_FUNC;
+ virtual Status onKeyPress(const KeyPressEvent&) PUGL_CONST_FUNC;
+ virtual Status onKeyRelease(const KeyReleaseEvent&) PUGL_CONST_FUNC;
+ virtual Status onText(const TextEvent&) PUGL_CONST_FUNC;
+ virtual Status onPointerIn(const PointerInEvent&) PUGL_CONST_FUNC;
+ virtual Status onPointerOut(const PointerOutEvent&) PUGL_CONST_FUNC;
+ virtual Status onButtonPress(const ButtonPressEvent&) PUGL_CONST_FUNC;
+ virtual Status onButtonRelease(const ButtonReleaseEvent&) PUGL_CONST_FUNC;
+ virtual Status onMotion(const MotionEvent&) PUGL_CONST_FUNC;
+ virtual Status onScroll(const ScrollEvent&) PUGL_CONST_FUNC;
+ virtual Status onClient(const ClientEvent&) PUGL_CONST_FUNC;
+ virtual Status onTimer(const TimerEvent&) PUGL_CONST_FUNC;
+
+ /**
+ @}
+ */
+
+ PuglView* cobj() { return Wrapper::cobj(); }
+ const PuglView* cobj() const { return Wrapper::cobj(); }
private:
- static void _onEvent(PuglView* view, const PuglEvent* event) {
- ((View*)puglGetHandle(view))->onEvent(event);
+ template<class Typed, class Base>
+ static const Typed& typedEventRef(const Base& base)
+ {
+ const auto& event = static_cast<const Typed&>(base);
+ static_assert(sizeof(event) == sizeof(typename Typed::BaseEvent), "");
+ static_assert(std::is_standard_layout<Typed>::value, "");
+ assert(event.type == Typed::type);
+ return event;
}
- PuglView* _view;
+ static PuglStatus dispatchEvent(PuglView* view, const PuglEvent* event) noexcept {
+ try {
+ View* self = static_cast<View*>(puglGetHandle(view));
+
+ return self->dispatch(event);
+ } catch (...) {
+ return PUGL_UNKNOWN_ERROR;
+ }
+ }
+
+ PuglStatus dispatch(const PuglEvent* event)
+ {
+ switch (event->type) {
+ case PUGL_NOTHING:
+ return PUGL_SUCCESS;
+ case PUGL_CREATE:
+ return static_cast<PuglStatus>(
+ onCreate(typedEventRef<CreateEvent>(event->any)));
+ case PUGL_DESTROY:
+ return static_cast<PuglStatus>(
+ onDestroy(typedEventRef<DestroyEvent>(event->any)));
+ case PUGL_CONFIGURE:
+ return static_cast<PuglStatus>(onConfigure(
+ typedEventRef<ConfigureEvent>(event->configure)));
+ case PUGL_MAP:
+ return static_cast<PuglStatus>(
+ onMap(typedEventRef<MapEvent>(event->any)));
+ case PUGL_UNMAP:
+ return static_cast<PuglStatus>(
+ onUnmap(typedEventRef<UnmapEvent>(event->any)));
+ case PUGL_UPDATE:
+ return static_cast<PuglStatus>(
+ onUpdate(typedEventRef<UpdateEvent>(event->any)));
+ case PUGL_EXPOSE:
+ return static_cast<PuglStatus>(
+ onExpose(typedEventRef<ExposeEvent>(event->expose)));
+ case PUGL_CLOSE:
+ return static_cast<PuglStatus>(
+ onClose(typedEventRef<CloseEvent>(event->any)));
+ case PUGL_FOCUS_IN:
+ return static_cast<PuglStatus>(
+ onFocusIn(typedEventRef<FocusInEvent>(event->focus)));
+ case PUGL_FOCUS_OUT:
+ return static_cast<PuglStatus>(
+ onFocusOut(typedEventRef<FocusOutEvent>(event->focus)));
+ case PUGL_KEY_PRESS:
+ return static_cast<PuglStatus>(
+ onKeyPress(typedEventRef<KeyPressEvent>(event->key)));
+ case PUGL_KEY_RELEASE:
+ return static_cast<PuglStatus>(
+ onKeyRelease(typedEventRef<KeyReleaseEvent>(event->key)));
+ case PUGL_TEXT:
+ return static_cast<PuglStatus>(
+ onText(typedEventRef<TextEvent>(event->text)));
+ case PUGL_POINTER_IN:
+ return static_cast<PuglStatus>(onPointerIn(
+ typedEventRef<PointerInEvent>(event->crossing)));
+ case PUGL_POINTER_OUT:
+ return static_cast<PuglStatus>(onPointerOut(
+ typedEventRef<PointerOutEvent>(event->crossing)));
+ case PUGL_BUTTON_PRESS:
+ return static_cast<PuglStatus>(onButtonPress(
+ typedEventRef<ButtonPressEvent>(event->button)));
+ case PUGL_BUTTON_RELEASE:
+ return static_cast<PuglStatus>(onButtonRelease(
+ typedEventRef<ButtonReleaseEvent>(event->button)));
+ case PUGL_MOTION:
+ return static_cast<PuglStatus>(
+ onMotion(typedEventRef<MotionEvent>(event->motion)));
+ case PUGL_SCROLL:
+ return static_cast<PuglStatus>(
+ onScroll(typedEventRef<ScrollEvent>(event->scroll)));
+ case PUGL_CLIENT:
+ return static_cast<PuglStatus>(
+ onClient(typedEventRef<ClientEvent>(event->client)));
+ case PUGL_TIMER:
+ return static_cast<PuglStatus>(
+ onTimer(typedEventRef<TimerEvent>(event->timer)));
+ }
+
+ return PUGL_FAILURE;
+ }
+
+ World& _world;
};
-} // namespace pugl
+/**
+ @}
+*/
+
+} // namespace pugl
/**
@}
*/
-#endif /* PUGL_PUGL_HPP */
+#endif /* PUGL_PUGL_HPP */
A pugl/pugl.ipp => pugl/pugl.ipp +154 -0
@@ 0,0 1,154 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl.ipp
+ @brief Pugl C++ API wrapper implementation.
+
+ This file must be included exactly once in the application.
+*/
+
+#include "pugl/pugl.hpp"
+
+namespace pugl {
+
+Status
+View::onCreate(const CreateEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onDestroy(const DestroyEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onConfigure(const ConfigureEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onMap(const MapEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onUnmap(const UnmapEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onUpdate(const UpdateEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onExpose(const ExposeEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onClose(const CloseEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onFocusIn(const FocusInEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onFocusOut(const FocusOutEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onKeyPress(const KeyPressEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onKeyRelease(const KeyReleaseEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onText(const TextEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onPointerIn(const PointerInEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onPointerOut(const PointerOutEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onButtonPress(const ButtonPressEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onButtonRelease(const ButtonReleaseEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onMotion(const MotionEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onScroll(const ScrollEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onClient(const ClientEvent&)
+{
+ return pugl::Status::success;
+}
+
+Status
+View::onTimer(const TimerEvent&)
+{
+ return pugl::Status::success;
+}
+
+}
M pugl/pugl_cairo.h => pugl/pugl_cairo.h +4 -3
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_cairo.h Declaration of Cairo backend accessor.
+ @file pugl_cairo.h
+ @brief Declaration of Cairo backend accessor.
*/
#ifndef PUGL_PUGL_CAIRO_H
@@ 28,7 29,7 @@ PUGL_BEGIN_DECLS
/**
@defgroup cairo Cairo
Cairo graphics support.
- @ingroup pugl_api
+ @ingroup pugl_c
@{
*/
A pugl/pugl_cairo.hpp => pugl/pugl_cairo.hpp +50 -0
@@ 0,0 1,50 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_cairo.hpp
+ @brief Declaration of Cairo backend accessor for C++.
+*/
+
+#ifndef PUGL_PUGL_CAIRO_HPP
+#define PUGL_PUGL_CAIRO_HPP
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_cairo.h"
+
+namespace pugl {
+
+/**
+ @defgroup cairoxx Cairo
+ Cairo graphics support.
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc puglCairoBackend
+static inline const PuglBackend*
+cairoBackend()
+{
+ return puglCairoBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_CAIRO_HPP
M pugl/pugl_gl.h => pugl/pugl_gl.h +4 -3
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_gl.h OpenGL-specific API.
+ @file pugl_gl.h
+ @brief OpenGL-specific API.
*/
#ifndef PUGL_PUGL_GL_H
@@ 28,7 29,7 @@ PUGL_BEGIN_DECLS
/**
@defgroup gl OpenGL
OpenGL graphics support.
- @ingroup pugl_api
+ @ingroup pugl_c
@{
*/
A pugl/pugl_gl.hpp => pugl/pugl_gl.hpp +60 -0
@@ 0,0 1,60 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_gl.hpp
+ @brief OpenGL-specific C++ API.
+*/
+
+#ifndef PUGL_PUGL_GL_HPP
+#define PUGL_PUGL_GL_HPP
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_gl.h"
+
+namespace pugl {
+
+/**
+ @defgroup glxx OpenGL
+ OpenGL graphics support.
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc PuglGlFunc
+using GlFunc = PuglGlFunc;
+
+/// @copydoc puglGetProcAddress
+static inline GlFunc
+getProcAddress(const char* name)
+{
+ return puglGetProcAddress(name);
+}
+
+/// @copydoc puglGlBackend
+static inline const PuglBackend*
+glBackend()
+{
+ return puglGlBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_GL_HPP
M pugl/pugl_stub.h => pugl/pugl_stub.h +4 -47
@@ 1,5 1,5 @@
/*
- Copyright 2019 David Robillard <http://drobilla.net>
+ Copyright 2019-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 15,7 15,8 @@
*/
/**
- @file pugl_stub.h Stub backend functions and accessor declaration.
+ @file pugl_stub.h
+ @brief Stub backend functions and accessor declaration.
*/
#ifndef PUGL_PUGL_STUB_H
@@ 36,7 37,7 @@ PUGL_BEGIN_DECLS
for other backends to reuse since not all need non-trivial implementations
of every backend function.
- @ingroup pugl_api
+ @ingroup pugl_c
@{
*/
@@ 50,50 51,6 @@ 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;
-}
-
/**
@}
*/
A pugl/pugl_stub.hpp => pugl/pugl_stub.hpp +50 -0
@@ 0,0 1,50 @@
+/*
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file pugl_stub.hpp
+ @brief Declaration of Stub backend accessor for C++.
+*/
+
+#ifndef PUGL_PUGL_STUB_HPP
+#define PUGL_PUGL_STUB_HPP
+
+#include "pugl/pugl.h"
+#include "pugl/pugl_stub.h"
+
+namespace pugl {
+
+/**
+ @defgroup stubxx Stub
+ Stub graphics support.
+ @ingroup pugl_cxx
+ @{
+*/
+
+/// @copydoc puglStubBackend
+static inline const PuglBackend*
+stubBackend()
+{
+ return puglStubBackend();
+}
+
+/**
+ @}
+*/
+
+} // namespace pugl
+
+#endif // PUGL_PUGL_STUB_HPP
D pugl/pugl_stub_backend.h => pugl/pugl_stub_backend.h +0 -23
@@ 1,23 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.
-*/
-
-#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
A => +5 -0
@@ 0,0 1,5 @@
#version 330 core
#define INTER(qualifiers)
#define UBO(qualifiers) layout(std140)
A => +5 -0
@@ 0,0 1,5 @@
#version 420 core
#define INTER(qualifiers) layout(qualifiers)
#define UBO(qualifiers) layout(std140, qualifiers)
M shaders/rect.frag => shaders/rect.frag +3 -5
@@ 1,5 1,3 @@
-#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.
@@ 10,9 8,9 @@
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;
+INTER(location = 0) noperspective in vec2 f_uv;
+INTER(location = 1) noperspective in vec2 f_size;
+INTER(location = 2) noperspective in vec4 f_fillColor;
layout(location = 0) out vec4 FragColor;
M shaders/rect.vert => shaders/rect.vert +8 -7
@@ 1,18 1,19 @@
-#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;
+UBO(binding = 0) uniform UniformBufferObject
+{
+ mat4 projection;
+} ubo;
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;
+INTER(location = 0) noperspective out vec2 f_uv;
+INTER(location = 1) noperspective out vec2 f_size;
+INTER(location = 2) noperspective out vec4 f_fillColor;
void
main()
@@ 24,7 25,7 @@ main()
v_origin[0], v_origin[1], 0.0, 1.0);
// clang-format on
- mat4 MVP = u_projection * m;
+ mat4 MVP = ubo.projection * m;
f_uv = v_position * v_size;
f_size = v_size;
R pugl/pugl_cairo_backend.h => test/test_build.c +16 -5
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 14,10 14,21 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef PUGL_PUGL_CAIRO_BACKEND_H
-#define PUGL_PUGL_CAIRO_BACKEND_H
+/*
+ Tests that C headers compile without any warnings.
+*/
+
+#define PUGL_DISABLE_DEPRECATED
-#warning "This header is deprecated, use pugl/pugl_cairo.h instead."
+#include "pugl/gl.h"
+#include "pugl/glu.h"
+#include "pugl/pugl.h"
#include "pugl/pugl_cairo.h"
+#include "pugl/pugl_gl.h"
+#include "pugl/pugl_stub.h"
-#endif // PUGL_PUGL_CAIRO_BACKEND_H
+int
+main(void)
+{
+ return 0;
+}
R pugl/pugl_gl_backend.h => test/test_build.cpp +19 -6
@@ 1,5 1,5 @@
/*
- Copyright 2012-2019 David Robillard <http://drobilla.net>
+ Copyright 2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 14,10 14,23 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#ifndef PUGL_PUGL_GL_BACKEND_H
-#define PUGL_PUGL_GL_BACKEND_H
+/*
+ Tests that C++ headers compile without any warnings.
+*/
+
+#define PUGL_DISABLE_DEPRECATED
-#warning "This header is deprecated, use pugl/pugl_gl.h instead."
-#include "pugl/pugl_gl.h"
+#include "pugl/gl.h"
+#include "pugl/glu.h"
+#include "pugl/pugl.h"
+#include "pugl/pugl.hpp"
+#include "pugl/pugl.ipp"
+#include "pugl/pugl_cairo.hpp"
+#include "pugl/pugl_gl.hpp"
+#include "pugl/pugl_stub.hpp"
-#endif // PUGL_PUGL_GL_BACKEND_H
+int
+main()
+{
+ return 0;
+}
M test/test_redisplay.c => test/test_redisplay.c +18 -9
@@ 1,5 1,5 @@
/*
- Copyright 2020 David Robillard <http://drobilla.net>
+ Copyright 2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 47,13 47,13 @@ typedef enum {
typedef struct
{
- PuglTestOptions opts;
PuglWorld* world;
PuglView* view;
+ PuglTestOptions opts;
State state;
} PuglTest;
-static const PuglRect redisplayRect = {1, 2, 3, 4};
+static const PuglRect redisplayRect = {2, 4, 8, 16};
static const uintptr_t postRedisplayId = 42;
static PuglStatus
@@ 66,6 66,13 @@ onEvent(PuglView* view, const PuglEvent* event)
}
switch (event->type) {
+ case PUGL_UPDATE:
+ if (test->state == SHOULD_REDISPLAY) {
+ puglPostRedisplayRect(view, redisplayRect);
+ test->state = POSTED_REDISPLAY;
+ }
+ break;
+
case PUGL_EXPOSE:
if (test->state == START) {
test->state = EXPOSED;
@@ 80,8 87,7 @@ onEvent(PuglView* view, const PuglEvent* event)
case PUGL_CLIENT:
if (event->client.data1 == postRedisplayId) {
- puglPostRedisplayRect(view, redisplayRect);
- test->state = POSTED_REDISPLAY;
+ test->state = SHOULD_REDISPLAY;
}
break;
@@ 94,9 100,9 @@ onEvent(PuglView* view, const PuglEvent* event)
int
main(int argc, char** argv)
{
- PuglTest app = {puglParseTestOptions(&argc, &argv),
- puglNewWorld(PUGL_PROGRAM, 0),
+ PuglTest app = {puglNewWorld(PUGL_PROGRAM, 0),
NULL,
+ puglParseTestOptions(&argc, &argv),
START};
// Set up view
@@ 105,6 111,7 @@ main(int argc, char** argv)
puglSetBackend(app.view, puglStubBackend());
puglSetHandle(app.view, &app);
puglSetEventFunc(app.view, onEvent);
+ puglSetDefaultSize(app.view, 512, 512);
// Create and show window
assert(!puglRealize(app.view));
@@ 114,8 121,10 @@ main(int argc, char** argv)
}
// 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));
+ PuglEvent client_event = {{PUGL_CLIENT, 0}};
+ client_event.client.data1 = postRedisplayId;
+ client_event.client.data2 = 0;
+ assert(!puglSendEvent(app.view, &client_event));
// Loop until an expose happens in the same iteration as the redisplay
app.state = SHOULD_REDISPLAY;
M test/test_show_hide.c => test/test_show_hide.c +6 -5
@@ 1,5 1,5 @@
/*
- Copyright 2020 David Robillard <http://drobilla.net>
+ Copyright 2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 41,9 41,9 @@ typedef enum {
} State;
typedef struct {
- PuglTestOptions opts;
PuglWorld* world;
PuglView* view;
+ PuglTestOptions opts;
State state;
} PuglTest;
@@ 71,7 71,7 @@ onEvent(PuglView* view, const PuglEvent* event)
test->state = MAPPED;
break;
case PUGL_EXPOSE:
- assert(test->state == MAPPED);
+ assert(test->state == MAPPED || test->state == EXPOSED);
test->state = EXPOSED;
break;
case PUGL_UNMAP:
@@ 104,9 104,9 @@ tick(PuglWorld* world)
int
main(int argc, char** argv)
{
- PuglTest test = {puglParseTestOptions(&argc, &argv),
- puglNewWorld(PUGL_PROGRAM, 0),
+ PuglTest test = {puglNewWorld(PUGL_PROGRAM, 0),
NULL,
+ puglParseTestOptions(&argc, &argv),
START};
// Set up view
@@ 115,6 115,7 @@ main(int argc, char** argv)
puglSetBackend(test.view, puglStubBackend());
puglSetHandle(test.view, &test);
puglSetEventFunc(test.view, onEvent);
+ puglSetDefaultSize(test.view, 512, 512);
// Create initially invisible window
assert(!puglRealize(test.view));
M test/test_timer.c => test/test_timer.c +5 -4
@@ 1,5 1,5 @@
/*
- Copyright 2020 David Robillard <http://drobilla.net>
+ Copyright 2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 55,9 55,9 @@ typedef enum {
} State;
typedef struct {
- PuglTestOptions opts;
PuglWorld* world;
PuglView* view;
+ PuglTestOptions opts;
size_t numAlarms;
State state;
} PuglTest;
@@ 97,9 97,9 @@ roundPeriod(const double period)
int
main(int argc, char** argv)
{
- PuglTest app = {puglParseTestOptions(&argc, &argv),
- puglNewWorld(PUGL_PROGRAM, 0),
+ PuglTest app = {puglNewWorld(PUGL_PROGRAM, 0),
NULL,
+ puglParseTestOptions(&argc, &argv),
0,
START};
@@ 109,6 109,7 @@ main(int argc, char** argv)
puglSetBackend(app.view, puglStubBackend());
puglSetHandle(app.view, &app);
puglSetEventFunc(app.view, onEvent);
+ puglSetDefaultSize(app.view, 512, 512);
// Create and show window
assert(!puglRealize(app.view));
M test/test_update.c => test/test_update.c +5 -4
@@ 1,5 1,5 @@
/*
- Copyright 2020 David Robillard <http://drobilla.net>
+ Copyright 2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 44,9 44,9 @@ typedef enum {
} State;
typedef struct {
- PuglTestOptions opts;
PuglWorld* world;
PuglView* view;
+ PuglTestOptions opts;
State state;
} PuglTest;
@@ 90,9 90,9 @@ onEvent(PuglView* view, const PuglEvent* event)
int
main(int argc, char** argv)
{
- PuglTest app = {puglParseTestOptions(&argc, &argv),
- puglNewWorld(PUGL_PROGRAM, 0),
+ PuglTest app = {puglNewWorld(PUGL_PROGRAM, 0),
NULL,
+ puglParseTestOptions(&argc, &argv),
START};
// Set up view
@@ 101,6 101,7 @@ main(int argc, char** argv)
puglSetBackend(app.view, puglStubBackend());
puglSetHandle(app.view, &app);
puglSetEventFunc(app.view, onEvent);
+ puglSetDefaultSize(app.view, 512, 512);
// Create and show window
assert(!puglRealize(app.view));
M test/test_utils.h => test/test_utils.h +48 -12
@@ 1,5 1,5 @@
/*
- Copyright 2012-2020 David Robillard <http://drobilla.net>
+ Copyright 2012-2020 David Robillard <d@drobilla.net>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above
@@ 67,6 67,40 @@ printModifiers(const uint32_t mods)
(mods & PUGL_MOD_SUPER) ? " Super" : "");
}
+static inline const char*
+crossingModeString(const PuglCrossingMode mode)
+{
+ switch (mode) {
+ case PUGL_CROSSING_NORMAL:
+ return "normal";
+ case PUGL_CROSSING_GRAB:
+ return "grab";
+ case PUGL_CROSSING_UNGRAB:
+ return "ungrab";
+ }
+
+ return "unknown";
+}
+
+static inline const char*
+scrollDirectionString(const PuglScrollDirection direction)
+{
+ switch (direction) {
+ case PUGL_SCROLL_UP:
+ return "up";
+ case PUGL_SCROLL_DOWN:
+ return "down";
+ case PUGL_SCROLL_LEFT:
+ return "left";
+ case PUGL_SCROLL_RIGHT:
+ return "right";
+ case PUGL_SCROLL_SMOOTH:
+ return "smooth";
+ }
+
+ return "unknown";
+}
+
static inline int
printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
{
@@ 103,31 137,34 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
event->button.y) +
printModifiers(event->scroll.state));
case PUGL_SCROLL:
- return (PRINT("%sScroll %5.1f %5.1f at " PFMT " ",
+ return (PRINT("%sScroll %5.1f %5.1f (%s) at " PFMT " ",
prefix,
event->scroll.dx,
event->scroll.dy,
+ scrollDirectionString(event->scroll.direction),
event->scroll.x,
event->scroll.y) +
printModifiers(event->scroll.state));
case PUGL_POINTER_IN:
- return PRINT("%sMouse enter at " PFMT "\n",
+ return PRINT("%sMouse enter at " PFMT " (%s)\n",
prefix,
event->crossing.x,
- event->crossing.y);
+ event->crossing.y,
+ crossingModeString(event->crossing.mode));
case PUGL_POINTER_OUT:
- return PRINT("%sMouse leave at " PFMT "\n",
+ return PRINT("%sMouse leave at " PFMT " (%s)\n",
prefix,
event->crossing.x,
- event->crossing.y);
+ event->crossing.y,
+ crossingModeString(event->crossing.mode));
case PUGL_FOCUS_IN:
- return PRINT("%sFocus in%s\n",
+ return PRINT("%sFocus in (%s)\n",
prefix,
- event->focus.grab ? " (grab)" : "");
+ crossingModeString(event->crossing.mode));
case PUGL_FOCUS_OUT:
- return PRINT("%sFocus out%s\n",
+ return PRINT("%sFocus out (%s)\n",
prefix,
- event->focus.grab ? " (ungrab)" : "");
+ crossingModeString(event->crossing.mode));
case PUGL_CLIENT:
return PRINT("%sClient %" PRIXPTR " %" PRIXPTR "\n",
prefix,
@@ 173,8 210,7 @@ printEvent(const PuglEvent* event, const char* prefix, const bool verbose)
event->motion.x,
event->motion.y);
default:
- fprintf(stderr, "%sUnknown event type %u\n", prefix, event->type);
- break;
+ return PRINT("%sUnknown event type %d\n", prefix, (int)event->type);
}
}
M waflib => waflib +1 -1
@@ 1,1 1,1 @@
-Subproject commit 569d532e011bcad1ac308391e6d00b1b28789c10
+Subproject commit f773f9a6a1ace4e3ddda5db05db4fa49bd0173ac
M wscript => wscript +230 -55
@@ 3,7 3,7 @@
import os
import sys
-from waflib import Logs, Options, TaskGen
+from waflib import Build, Logs, Options, TaskGen
from waflib.extras import autowaf
# Library and package version (UNIX style major, minor, micro)
@@ 22,6 22,7 @@ out = 'build' # Build directory
def options(ctx):
ctx.load('compiler_c')
+ ctx.load('compiler_cxx')
opts = ctx.configuration_options()
opts.add_option('--target', default=None, dest='target',
@@ 41,46 42,102 @@ def options(ctx):
def configure(conf):
conf.load('compiler_c', cache=True)
+ try:
+ conf.load('compiler_cxx', cache=True)
+ except Exception:
+ pass
+
conf.load('autowaf', cache=True)
autowaf.set_c_lang(conf, 'c99')
+ if 'COMPILER_CXX' in conf.env:
+ autowaf.set_cxx_lang(conf, 'c++11')
conf.env.ALL_HEADERS = Options.options.all_headers
conf.env.TARGET_PLATFORM = Options.options.target or sys.platform
platform = conf.env.TARGET_PLATFORM
- if platform == 'darwin':
- conf.env.append_unique('CFLAGS', ['-Wno-deprecated-declarations'])
+ if Options.options.strict:
+ # Check for programs used by lint target
+ conf.find_program("flake8", var="FLAKE8", mandatory=False)
+ conf.find_program("clang-tidy", var="CLANG_TIDY", mandatory=False)
+ conf.find_program("iwyu_tool", var="IWYU_TOOL", mandatory=False)
- if conf.env.MSVC_COMPILER:
- conf.env.append_unique('CFLAGS', ['/wd4191'])
- else:
- conf.env.append_value('LINKFLAGS', ['-fvisibility=hidden'])
- 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-float-equal',
- '-Wno-format-nonliteral',
+ if Options.options.ultra_strict:
+ # All warnings enabled by autowaf, disable some we trigger
+
+ autowaf.add_compiler_flags(conf.env, '*', {
+ 'clang': [
'-Wno-padded',
'-Wno-reserved-id-macro',
'-Wno-switch-enum',
- ])
-
- conf.env.append_value('CXXFLAGS', ['-Wno-c++98-compat',
- '-Wno-c++98-compat-pedantic'])
-
+ ],
+ 'gcc': [
+ '-Wno-padded',
+ '-Wno-switch-enum',
+ ],
+ 'msvc': [
+ '/wd4061', # enumerator in switch is not explicitly handled
+ '/wd4514', # unreferenced inline function has been removed
+ '/wd4820', # padding added after construct
+ '/wd5045', # will insert Spectre mitigation for memory load
+ ],
+ })
+
+ autowaf.add_compiler_flags(conf.env, 'c', {
+ 'clang': [
+ '-Wno-bad-function-cast',
+ '-Wno-float-equal',
+ '-Wno-implicit-fallthrough',
+ ],
+ 'gcc': [
+ '-Wno-bad-function-cast',
+ '-Wno-float-equal',
+ ],
+ 'msvc': [
+ '/wd4191', # unsafe conversion from type to type
+ '/wd4706', # assignment within conditional expression
+ '/wd4710', # function not inlined
+ '/wd5045', # will insert Spectre mitigation for memory load
+ ],
+ })
+
+ autowaf.add_compiler_flags(conf.env, 'cxx', {
+ 'clang': [
+ '-Wno-documentation-unknown-command',
+ '-Wno-old-style-cast',
+ ],
+ 'gcc': [
+ '-Wno-old-style-cast',
+ ],
+ 'msvc': [
+ '/wd4355', # 'this' used in base member initializer list
+ '/wd4571', # structured exceptions (SEH) are no longer caught
+ '/wd4625', # copy constructor implicitly deleted
+ '/wd4626', # assignment operator implicitly deleted
+ '/wd5026', # move constructor implicitly deleted
+ '/wd5027', # move assignment operator implicitly deleted
+ ],
+ })
+
+ # Add some platform-specific warning suppressions
+ if conf.env.TARGET_PLATFORM == "win32":
+ autowaf.add_compiler_flags(conf.env, '*', {
+ 'gcc': ['-Wno-cast-function-type',
+ '-Wno-conversion',
+ '-Wno-format',
+ '-Wno-suggest-attribute=format'],
+ })
+ elif conf.env.TARGET_PLATFORM == 'darwin':
+ autowaf.add_compiler_flags(conf.env, '*', {
+ 'clang': ['-DGL_SILENCE_DEPRECATION',
+ '-Wno-deprecated-declarations',
+ '-Wno-direct-ivar-access'],
+ 'gcc': ['-DGL_SILENCE_DEPRECATION',
+ '-Wno-deprecated-declarations',
+ '-Wno-direct-ivar-access'],
+ })
+
+ # Check for base system libraries needed on some systems
conf.check_cc(lib='m', uselib_store='M', mandatory=False)
conf.check_cc(lib='dl', uselib_store='DL', mandatory=False)
@@ 114,6 171,11 @@ def configure(conf):
msg='Checking for function XSyncQueryExtension'):
conf.define('HAVE_XSYNC', 1)
+ if conf.check_cc(lib='Xcursor',
+ uselib_store='XCURSOR',
+ mandatory=False):
+ conf.define('HAVE_XCURSOR', 1)
+
if not Options.options.no_gl:
glx_fragment = """#include <GL/glx.h>
int main(void) { glXSwapBuffers(0, 0); return 0; }"""
@@ 138,7 200,18 @@ def configure(conf):
'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)
+ if conf.env.TARGET_PLATFORM == 'win32':
+ conf.env.PUGL_PLATFORM = 'win'
+ elif conf.env.TARGET_PLATFORM == 'darwin':
+ conf.env.PUGL_PLATFORM = 'mac'
+ else:
+ conf.env.PUGL_PLATFORM = 'x11'
+
+ autowaf.set_lib_env(conf, 'pugl', PUGL_VERSION,
+ lib='pugl_' + conf.env.PUGL_PLATFORM)
+
+ autowaf.set_lib_env(conf, 'pugl_gl', PUGL_VERSION,
+ lib='pugl_%s_gl' % conf.env.PUGL_PLATFORM)
autowaf.display_summary(
conf,
@@ 189,6 262,7 @@ def build(bld):
includedir = '${INCLUDEDIR}/pugl-%s/pugl' % PUGL_MAJOR_VERSION
bld.install_files(includedir, bld.path.ant_glob('pugl/*.h'))
bld.install_files(includedir, bld.path.ant_glob('pugl/*.hpp'))
+ bld.install_files(includedir, bld.path.ant_glob('pugl/*.ipp'))
if bld.env.ALL_HEADERS:
detaildir = os.path.join(includedir, 'detail')
bld.install_files(detaildir, bld.path.ant_glob('pugl/detail/*.h'))
@@ 208,18 282,26 @@ def build(bld):
'install_path': '${LIBDIR}',
'vnum': PUGL_VERSION})
+ flags = []
+ if not bld.env.MSVC_COMPILER:
+ flags = ['-fPIC', '-fvisibility=hidden']
+
if bld.env.BUILD_SHARED:
- bld(features = 'c cshlib',
- name = name,
- target = 'pugl_' + name,
- defines = ['PUGL_INTERNAL', 'PUGL_SHARED'],
+ bld(features = 'c cshlib',
+ name = name,
+ target = 'pugl_%s-%s' % (name, PUGL_MAJOR_VERSION),
+ defines = ['PUGL_INTERNAL', 'PUGL_SHARED'],
+ cflags = flags,
+ linkflags = flags,
**args)
if bld.env.BUILD_STATIC:
- bld(features = 'c cstlib',
- name = 'pugl_%s_static' % name,
- target = 'pugl_' + name,
- defines = ['PUGL_INTERNAL', 'PUGL_DISABLE_DEPRECATED'],
+ bld(features = 'c cstlib',
+ name = 'pugl_%s_static' % name,
+ target = 'pugl_%s-%s' % (name, PUGL_MAJOR_VERSION),
+ defines = ['PUGL_INTERNAL', 'PUGL_DISABLE_DEPRECATED'],
+ cflags = flags,
+ linkflags = flags,
**args)
def build_platform(platform, **kwargs):
@@ 278,7 360,7 @@ def build(bld):
else:
platform = 'x11'
build_platform('x11',
- uselib=['M', 'X11', 'XSYNC'],
+ uselib=['M', 'X11', 'XSYNC', 'XCURSOR'],
source=lib_source + ['pugl/detail/x11.c'])
if bld.env.HAVE_GL:
@@ 293,6 375,8 @@ def build(bld):
source=['pugl/detail/x11_cairo.c'])
def build_example(prog, source, platform, backend, **kwargs):
+ lang = 'cxx' if source[0].endswith('.cpp') else 'c'
+
use = ['pugl_%s_static' % platform,
'pugl_%s_%s_static' % (platform, backend)]
@@ 312,7 396,7 @@ def build(bld):
deps.get(platform, {}).get(k, []) +
deps.get(backend_lib, {}).get(k, []))})
- bld(features = 'c cprogram',
+ bld(features = '%s %sprogram' % (lang, lang),
source = source,
target = target,
use = use,
@@ 328,16 412,22 @@ def build(bld):
target = 'shaders/%s' % s)
if bld.env.HAVE_GL:
+ glad_cflags = [] if bld.env.MSVC_COMPILER else ['-Wno-pedantic']
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_cursor_demo', ['examples/pugl_cursor_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'])
+ build_example('pugl_shader_demo',
+ ['examples/pugl_shader_demo.c',
+ 'examples/glad/glad.c'],
+ platform, 'gl',
+ cflags=glad_cflags,
+ uselib=['DL', 'GL', 'M'])
if bld.env.HAVE_CAIRO:
build_example('pugl_cairo_demo', ['examples/pugl_cairo_demo.c'],
@@ 353,6 443,43 @@ def build(bld):
'pugl_%s_stub_static' % platform],
uselib = deps[platform]['uselib'] + ['CAIRO'])
+ # Make a hyper strict warning environment for checking API headers
+ strict_env = bld.env.derive()
+ autowaf.remove_all_warning_flags(strict_env)
+ autowaf.enable_all_warnings(strict_env)
+ autowaf.set_warnings_as_errors(strict_env)
+ autowaf.add_compiler_flags(strict_env, '*', {
+ 'clang': ['-Wno-padded'],
+ 'gcc': ['-Wno-padded'],
+ })
+ autowaf.add_compiler_flags(strict_env, 'cxx', {
+ 'clang': ['-Wno-documentation-unknown-command'],
+ })
+
+ # Check that C headers build with (almost) no warnings
+ bld(features = 'c cprogram',
+ source = 'test/test_build.c',
+ target = 'test/test_build_c',
+ install_path = '',
+ env = strict_env,
+ use = ['pugl_%s_static' % platform],
+ uselib = deps[platform]['uselib'] + ['CAIRO'])
+
+ # Check that C++ headers build with (almost) no warnings
+ bld(features = 'cxx cxxprogram',
+ source = 'test/test_build.cpp',
+ target = 'test/test_build_cpp',
+ install_path = '',
+ env = strict_env,
+ use = ['pugl_%s_static' % platform],
+ uselib = deps[platform]['uselib'] + ['CAIRO'])
+
+ if bld.env.CXX and bld.env.HAVE_GL:
+ build_example('pugl_cxx_demo', ['examples/pugl_cxx_demo.cpp'],
+ platform, 'gl',
+ defines=['PUGL_DISABLE_DEPRECATED'],
+ uselib=['GL', 'M'])
+
if bld.env.DOCS:
autowaf.build_dox(bld, 'PUGL', PUGL_VERSION, top, out)
@@ 364,25 491,73 @@ def test(tst):
check(['test/test_%s' % test])
+class LintContext(Build.BuildContext):
+ fun = cmd = 'lint'
+
+
def lint(ctx):
"checks code for style issues"
import json
import subprocess
- subprocess.call("flake8 wscript --ignore E221,W504,E251,E241",
- shell=True)
+ st = 0
- 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']
+ if "FLAKE8" in ctx.env:
+ Logs.info("Running flake8")
+ st = subprocess.call([ctx.env.FLAKE8[0],
+ "wscript",
+ "--ignore",
+ "E221,W504,E251,E241,E741"])
+ else:
+ Logs.warn("Not running flake8")
+
+ if "IWYU_TOOL" in ctx.env:
+ Logs.info("Running include-what-you-use")
+ cmd = [ctx.env.IWYU_TOOL[0], "-o", "clang", "-p", "build"]
+ output = subprocess.check_output(cmd).decode('utf-8')
+ if 'error: ' in output:
+ sys.stdout.write(output)
+ st += 1
+ else:
+ Logs.warn("Not running include-what-you-use")
+
+ if "CLANG_TIDY" in ctx.env and "clang" in ctx.env.CC[0]:
+ Logs.info("Running clang-tidy")
+ 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']
+
+ c_files = [os.path.join('build', f)
+ for f in files if f.endswith('.c')]
+
+ cpp_files = [os.path.join('build', f)
+ for f in files if f.endswith('.cpp')]
+
+ c_files = list(map(os.path.abspath, c_files))
+ cpp_files = list(map(os.path.abspath, cpp_files))
+
+ procs = []
+ for c_file in c_files:
+ cmd = [ctx.env.CLANG_TIDY[0], "--quiet", "-p=.", c_file]
+ procs += [subprocess.Popen(cmd, cwd="build")]
+
+ for cpp_file in cpp_files:
+ cmd = [ctx.env.CLANG_TIDY[0],
+ '--header-filter=".*\\.hpp"',
+ "--quiet",
+ "-p=.", cpp_file]
+ procs += [subprocess.Popen(cmd, cwd="build")]
+
+ for proc in procs:
+ stdout, stderr = proc.communicate()
+ st += proc.returncode
+ else:
+ Logs.warn("Not running clang-tidy")
- subprocess.call(['clang-tidy'] + files, cwd='build')
+ if st != 0:
+ sys.exit(st)
- try:
- subprocess.call(['iwyu_tool.py', '-o', 'clang', '-p', 'build'])
- except Exception:
- Logs.warn('Failed to call iwyu_tool.py')
# Alias .m files to be compiled like .c files, gcc will do the right thing.
@TaskGen.extension('.m')