├── .gitignore ├── LICENSE ├── README.md ├── doc └── README.md ├── example ├── README.md ├── example01.lua ├── example02.lua ├── example03.lua ├── example04.lua ├── example05.lua ├── example06.lua ├── example10.lua ├── screenshot01.png ├── screenshot02.png ├── screenshot03.png ├── screenshot04.png ├── screenshot05.png ├── screenshot06.png └── screenshot10.png ├── pugl-repo ├── AUTHORS ├── COPYING ├── README.md ├── include │ └── pugl │ │ ├── cairo.h │ │ ├── gl.h │ │ ├── pugl.h │ │ ├── stub.h │ │ └── vulkan.h └── src │ ├── implementation.c │ ├── implementation.h │ ├── mac.h │ ├── mac.m │ ├── mac_cairo.m │ ├── mac_cairo_gl.m │ ├── mac_gl.m │ ├── mac_stub.m │ ├── mac_vulkan.m │ ├── rect.h │ ├── stub.h │ ├── types.h │ ├── win.c │ ├── win.h │ ├── win_cairo.c │ ├── win_gl.c │ ├── win_stub.c │ ├── win_vulkan.c │ ├── x11.c │ ├── x11.h │ ├── x11_cairo.c │ ├── x11_clip.h │ ├── x11_gl.c │ ├── x11_keysym2ucs.c │ ├── x11_stub.c │ └── x11_vulkan.c ├── rockspecs ├── lpugl-0.0.3-1.rockspec ├── lpugl-scm-0.rockspec ├── lpugl_cairo-0.0.3-1.rockspec ├── lpugl_cairo-scm-0.rockspec ├── lpugl_opengl-0.0.3-1.rockspec ├── lpugl_opengl-scm-0.rockspec └── setversion.lua ├── src ├── Makefile ├── activate.sh ├── async_defines.h ├── async_util.c ├── async_util.h ├── backend.h ├── base.h ├── compat-5.3.c ├── compat-5.3.h ├── error.c ├── error.h ├── init.h ├── lpugl.c ├── lpugl.h ├── lpugl_cairo.c ├── lpugl_cairo.h ├── lpugl_compat.c ├── lpugl_opengl.c ├── lpugl_opengl.h ├── notify_capi.h ├── pugl.c ├── pugl.m ├── pugl_cairo.c ├── pugl_cairo.m ├── pugl_opengl.c ├── pugl_opengl.m ├── util.c ├── util.h ├── version.h ├── view.c ├── view.h ├── world.c └── world.h └── thirdparty ├── lua-compat-license ├── nanovg-0.1.2-2.rockspec ├── opengl-1.11-2.rockspec └── pugl-license /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.so.dSYM 3 | *.dll 4 | *.o 5 | /pugl-repo/.clang-format 6 | /pugl-repo/.clang-tidy 7 | /pugl-repo/.clant.json 8 | /pugl-repo/.editorconfig 9 | /pugl-repo/.git 10 | /pugl-repo/.gitattributes 11 | /pugl-repo/.gitignore 12 | /pugl-repo/.gitlab-ci.yml 13 | /pugl-repo/.gitmodules 14 | /pugl-repo/.includes.imp 15 | /pugl-repo/bindings/ 16 | /pugl-repo/include/.clang-tidy 17 | /pugl-repo/meson.build 18 | /pugl-repo/meson/ 19 | /pugl-repo/meson_options.txt 20 | /pugl-repo/scripts/ 21 | /pugl-repo/src/.clang-tidy 22 | /pugl-repo/doc/ 23 | /pugl-repo/examples/ 24 | /pugl-repo/pugl.pc.in 25 | /pugl-repo/pugl/pugl.hpp 26 | /pugl-repo/pugl/pugl.ipp 27 | /pugl-repo/pugl/pugl_cairo.hpp 28 | /pugl-repo/pugl/pugl_gl.hpp 29 | /pugl-repo/pugl/pugl_stub.hpp 30 | /pugl-repo/resources/ 31 | /pugl-repo/shaders/ 32 | /pugl-repo/test/ 33 | /pugl-repo/waf 34 | /pugl-repo/waflib 35 | /pugl-repo/wscript 36 | /src/sandbox.mk 37 | /src/build 38 | 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2020 Oliver Schmidt 4 | 5 | Software repository: https://github.com/osch/lua-lpugl 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy of 8 | this software and associated documentation files (the "Software"), to deal in 9 | the Software without restriction, including without limitation the rights to 10 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 11 | the Software, and to permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 19 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 20 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 21 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 22 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | 25 | (See also the THIRD-PARTY LICENSES contained in the thirdparty/ directory 26 | of the Software repository.) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lpugl 2 | [![Licence](http://img.shields.io/badge/Licence-MIT-brightgreen.svg)](LICENSE) 3 | [![Install](https://img.shields.io/badge/Install-LuaRocks-brightgreen.svg)](https://luarocks.org/modules/osch/lpugl) 4 | 5 | LPugl is a minimal Lua-API for building GUIs. It is based on [Pugl] (*PlUgin Graphics Library*), 6 | a minimal portable API for embeddable GUIs. 7 | 8 | LPugl provides only a very minimal API. See [lwtk] (*Lua Widget Toolkit*) 9 | for implementing GUI widgets on top of LPugl. 10 | 11 | Like Pugl, LPugl does only have explicit context and no static data, so it's 12 | possible to have several instances within the same program and even within 13 | the same Lua main state. Therefore LPugl is especially suited for building 14 | GUIs for plugins or components within larger applications. For example 15 | see [LDPF-Examples]: here LPugl is used for the GUI of audio processing plugins 16 | with [DPF] (*DISTRHO Plugin Framework*). 17 | 18 | #### Supported platforms: 19 | * X11 20 | * Windows 21 | * Mac OS X 22 | 23 | #### Supported rendering backends: 24 | * Cairo (requires [OOCairo]) 25 | * OpenGL 26 | 27 | Thanks to the modular architecture of Pugl, more rendering backends could be 28 | possible in the future. Different rendering backends can be combined in one 29 | application and also in the same window by embedding different view objects. 30 | 31 | #### LuaRocks modules: 32 | * **[lpugl]** - platform specific base module 33 | * **[lpugl_cairo]** - Cairo rendering backend module 34 | * **[lpugl_opengl]** - OpenGL rendering backend module 35 | 36 | 37 | #### Further reading: 38 | * [Documentation](./doc/README.md#lpugl-documentation) 39 | * [Examples](./example/README.md#lpugl-examples) 40 | 41 | ## First Example 42 | 43 | * Simple example for using the Cairo backend: 44 | 45 | ```lua 46 | local lpugl = require"lpugl_cairo" 47 | 48 | local world = lpugl.newWorld("Hello World App") 49 | local scale = world:getScreenScale() 50 | 51 | local view = world:newView 52 | { 53 | title = "Hello World Window", 54 | size = {300*scale, 100*scale}, 55 | resizable = true, 56 | 57 | eventFunc = function(view, event, ...) 58 | print(event, ...) 59 | if event == "EXPOSE" then 60 | local cairo = view:getDrawContext() 61 | local w, h = view:getSize() 62 | cairo:set_source_rgb(0.9, 0.9, 0.9) 63 | cairo:rectangle(0, 0, w, h) 64 | cairo:fill() 65 | cairo:set_source_rgb(0, 0, 0) 66 | cairo:select_font_face("sans-serif", "normal", "normal") 67 | cairo:set_font_size(24*scale) 68 | local text = "Hello World!" 69 | local ext = cairo:text_extents(text) 70 | cairo:move_to((w - ext.width)/2, (h - ext.height)/2 + ext.height) 71 | cairo:show_text(text) 72 | elseif event == "CLOSE" then 73 | view:close() 74 | end 75 | end 76 | } 77 | view:show() 78 | 79 | while world:hasViews() do 80 | world:update() 81 | end 82 | ``` 83 | 84 | [Pugl]: https://drobilla.net/software/pugl 85 | [DPF]: https://github.com/DISTRHO/DPF 86 | [LDPF-Examples]: https://github.com/LDPF/LDPF-Examples 87 | [OOCairo]: https://luarocks.org/modules/osch/oocairo 88 | [lwtk]: https://github.com/osch/lua-lwtk#lwtk---lua-widget-toolkit 89 | [lpugl]: https://luarocks.org/modules/osch/lpugl 90 | [lpugl_cairo]: https://luarocks.org/modules/osch/lpugl_cairo 91 | [lpugl_opengl]: https://luarocks.org/modules/osch/lpugl_opengl 92 | 93 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # lpugl Examples 2 | 3 | 4 | * [`example01.lua`](./example01.lua) 5 | 6 | Simple example using the Cairo rendering backend. The Cairo backend 7 | requires [OOCairo]. 8 | 9 | ![Screenshot example01](./screenshot01.png) 10 | 11 | 12 | 13 | 14 | * [`example02.lua`](./example02.lua) 15 | 16 | Simple example using the OpenGL rendering backend. This example 17 | uses [LuaGL] Lua binding (see also [enhanced LuaGL rockspec]). 18 | 19 | ![Screenshot example02](./screenshot02.png) 20 | 21 | 22 | 23 | 24 | * [`example03.lua`](./example03.lua) 25 | 26 | A mini game demonstrating smooth animations using pre-rendered cairo 27 | surfaces. 28 | 29 | ![Screenshot example03](./screenshot03.png) 30 | 31 | 32 | 33 | 34 | * [`example04.lua`](./example04.lua) 35 | 36 | Same mini game as in `example03.lua` but here the OpenGL backend is used. 37 | This example uses [lua-nanovg] as vector graphics rendering library for OpenGL. 38 | 39 | ![Screenshot example04](./screenshot04.png) 40 | 41 | 42 | 43 | 44 | * [`example05.lua`](./example05.lua) 45 | 46 | An interactive demonstration for partial redrawing using the cairo rendering backend. 47 | 48 | 49 | ![Screenshot example05](./screenshot05.png) 50 | 51 | 52 | 53 | 54 | * [`example06.lua`](./example06.lua) 55 | 56 | Partial redraw demonstration similar to `example05.lua` using the OpenGL backend with 57 | [lua-nanovg]. 58 | 59 | 60 | ![Screenshot example06](./screenshot06.png) 61 | 62 | 63 | 64 | 65 | * [`example10.lua`](./example10.lua) 66 | 67 | This examples demonstrates the usage of the [Notify C API] in a multithreading scenario. 68 | 69 | LPugl world objects implement the *Notify C API*, see [src/notify_capi.h](../src/notify_capi.h), 70 | i.e. the world object has an an associated meta table entry *_capi_notify* delivered by 71 | the C API function *notify_get_capi()* and the associated C API function *toNotifier()* returns 72 | a valid pointer for a given LPugl world object. 73 | 74 | In this example the *Notify C API* is used to notify the event loop by calling *world:awake()* 75 | each time a message is added by another thread to a [mtmsg](https://github.com/osch/lua-mtmsg) 76 | buffer object. 77 | 78 | This is done by connecting the LPugl world object as a notifier object to the mtmsg buffer 79 | object. 80 | 81 | [Notify C API]: https://github.com/lua-capis/lua-notify-capi 82 | 83 | ![Screenshot example10](./screenshot10.png) 84 | 85 | 86 | 87 | [OOCairo]: https://luarocks.org/modules/osch/oocairo 88 | [LuaGL]: https://luarocks.org/modules/blueowl04/opengl 89 | [enhanced LuaGL rockspec]: https://github.com/osch/luarocks-build-extended/blob/master/example/opengl-1.11-2.rockspec 90 | [lua-nanovg]: https://luarocks.org/modules/xavier-wang/nanovg 91 | 92 | 93 | -------------------------------------------------------------------------------- /example/example01.lua: -------------------------------------------------------------------------------- 1 | local lpugl = require"lpugl_cairo" 2 | 3 | local world = lpugl.newWorld("Hello World App") 4 | local scale = world:getScreenScale() 5 | 6 | local view = world:newView 7 | { 8 | title = "Hello World Window", 9 | size = {300*scale, 100*scale}, 10 | resizable = true, 11 | 12 | eventFunc = function(view, event, ...) 13 | print(event, ...) 14 | if event == "EXPOSE" then 15 | local cairo = view:getDrawContext() 16 | local w, h = view:getSize() 17 | cairo:set_source_rgb(0.9, 0.9, 0.9) 18 | cairo:rectangle(0, 0, w, h) 19 | cairo:fill() 20 | cairo:set_source_rgb(0, 0, 0) 21 | cairo:select_font_face("sans-serif", "normal", "normal") 22 | cairo:set_font_size(24*scale) 23 | local text = "Hello World!" 24 | local ext = cairo:text_extents(text) 25 | cairo:move_to((w - ext.width)/2, (h - ext.height)/2 + ext.height) 26 | cairo:show_text(text) 27 | elseif event == "CLOSE" then 28 | view:close() 29 | end 30 | end 31 | } 32 | view:show() 33 | 34 | while world:hasViews() do 35 | world:update() 36 | end 37 | -------------------------------------------------------------------------------- /example/example02.lua: -------------------------------------------------------------------------------- 1 | local lpugl = require"lpugl_opengl" 2 | local gl = require"luagl" 3 | local glu = require"luaglu" 4 | 5 | local world = lpugl.newWorld("OpenGL Example App") 6 | local scale = world:getScreenScale() 7 | 8 | local view = world:newView 9 | { 10 | title = "OpenGL Example Window", 11 | size = {300*scale, 100*scale}, 12 | resizable = true, 13 | 14 | eventFunc = function(view, event, ...) 15 | print(event, ...) 16 | if event == "EXPOSE" then 17 | local w, h = view:getSize() 18 | gl.Viewport(0, 0, w, h) 19 | gl.MatrixMode('PROJECTION') 20 | gl.LoadIdentity() 21 | glu.Perspective(80, w / h, 1, 5000) 22 | gl.MatrixMode('MODELVIEW') 23 | gl.Clear('COLOR_BUFFER_BIT,DEPTH_BUFFER_BIT') 24 | gl.LoadIdentity() 25 | gl.Translate(-1.5, 0, -6) 26 | gl.Begin('TRIANGLES') 27 | gl.Vertex( 0, 1, 0) 28 | gl.Vertex(-1, -1, 0) 29 | gl.Vertex( 1, -1, 0) 30 | gl.End() 31 | gl.Translate(3, 0, 0) 32 | gl.Begin('QUADS') 33 | gl.Vertex(-1, 1, 0) 34 | gl.Vertex( 1, 1, 0) 35 | gl.Vertex( 1, -1, 0) 36 | gl.Vertex(-1, -1, 0) 37 | gl.End() 38 | elseif event == "CLOSE" then 39 | view:close() 40 | end 41 | end 42 | } 43 | 44 | view:show() 45 | 46 | while world:hasViews() do 47 | world:update() 48 | end 49 | 50 | -------------------------------------------------------------------------------- /example/example03.lua: -------------------------------------------------------------------------------- 1 | local lpugl = require"lpugl_cairo" 2 | local oocairo = require"oocairo" 3 | 4 | local floor = math.floor 5 | local ceil = math.ceil 6 | local abs = math.abs 7 | local sqrt = math.sqrt 8 | 9 | local unpack = table.unpack or unpack -- for Lua 5.1 10 | 11 | math.randomseed(os.time()) 12 | 13 | local world = lpugl.newWorld("example03.lua") 14 | local scale = world:getScreenScale() 15 | 16 | local fillCache 17 | local drawBall 18 | do 19 | local function drawBallImpl(cairo, x, y, r, 20 | r1, g1, b1, 21 | r2, g2, b2) 22 | local alpha = 0.7 23 | local d = r*0.75 24 | local pattern = oocairo.pattern_create_radial(x - d, y - d, r - d, 25 | x - d, y - d, 2*r) 26 | pattern:add_color_stop_rgba(0, r1, g1, b1, alpha) 27 | pattern:add_color_stop_rgba(1, r2, g2, b2, alpha) 28 | cairo:set_source(pattern) 29 | cairo:arc(x, y, r, 0, math.pi * 2) 30 | cairo:fill(); 31 | end 32 | 33 | local gradientTable = { 34 | { 1.0, 0.8, 0.8, 35 | 1.0, 0.0, 0.0 }, 36 | 37 | { 0.7, 1.0, 0.7, 38 | 0.0, 1.0, 0.0 }, 39 | 40 | { 0.7, 0.7, 1.0, 41 | 0.0, 0.0, 1.0 }, 42 | } 43 | 44 | local cache = {} 45 | local use_cache = true 46 | local padding = 10 47 | 48 | fillCache = function(gradientIndex, r) 49 | local c2 = cache[gradientIndex]; if not c2 then c2 = {}; cache[gradientIndex] = c2 end 50 | if not c2[r] then 51 | local layoutTarget = world:getDefaultBackend():getLayoutContext():get_target() 52 | local surface = oocairo.surface_create_similar(layoutTarget, "color-alpha", 2*(r+padding), 2*(r+padding)) 53 | local cairo = oocairo.context_create(surface) 54 | drawBallImpl(cairo, r + padding, r + padding, r, 55 | unpack(gradientTable[gradientIndex])) 56 | c2[r] = surface 57 | end 58 | end 59 | 60 | local bug_workaround = true -- enlarge surface and clip to prevent strange border artefacts when 61 | -- painting the surface on non integer coordinates (bug in cairo?) 62 | 63 | drawBall = function(cairo, x, y, r, gradientIndex) 64 | if use_cache then 65 | local cached = cache[gradientIndex][r] 66 | if bug_workaround then 67 | cairo:save() 68 | cairo:rectangle(floor(x - r - padding/2), floor(y - r - padding/2), ceil(2*r+padding), ceil(2*r+padding)) 69 | cairo:clip() 70 | end 71 | cairo:set_source(cached, x - r - padding, y - r - padding) 72 | cairo:paint() 73 | if bug_workaround then 74 | cairo:restore() 75 | end 76 | else 77 | drawBallImpl(cairo, x, y, r, 78 | unpack(gradientTable[gradientIndex])) 79 | end 80 | end 81 | end 82 | 83 | local initialWidth, initialHeight = 640*scale, 480*scale 84 | 85 | local objects_draw 86 | local objects_push 87 | local objects_process 88 | do 89 | local objects = {} 90 | 91 | for i = 1, 500 do 92 | local obj = {} 93 | objects[i] = obj 94 | obj.r = math.random(floor(10*scale), floor(30*scale)) 95 | obj.x = math.random(obj.r, floor(initialWidth - 2 * obj.r)) 96 | obj.y = math.random(obj.r, floor(initialHeight - 2 * obj.r)) 97 | obj.g = math.random(1, 3) 98 | obj.dx = 5 * math.random() / 20 * scale 99 | obj.dy = 5 * math.random() / 20 * scale 100 | fillCache(obj.g, obj.r) 101 | end 102 | 103 | 104 | objects_draw = function(cairo, w, h) 105 | for i = 1, #objects do 106 | local obj = objects[i] 107 | drawBall(cairo, obj.x, obj.y, obj.r, obj.g) 108 | end 109 | end 110 | 111 | objects_push = function(bx, by) 112 | for i = 1, #objects do 113 | local obj = objects[i] 114 | local x, y = obj.x, obj.y 115 | local bdx, bdy = x - bx, y - by 116 | local bd = sqrt(bdx^2 + bdy^2)/scale 117 | local a = (bd + 0.01)^-1.5 118 | obj.dx = obj.dx + a * bdx 119 | obj.dy = obj.dy + a * bdy 120 | end 121 | end 122 | 123 | local lastT = world:getTime() 124 | 125 | objects_process = function(w, h) 126 | local currT = world:getTime() 127 | local dt = (currT - lastT) * 1000 128 | 129 | for i = 1, #objects do 130 | local obj = objects[i] 131 | local x, y = obj.x, obj.y 132 | x = x + obj.dx * dt 133 | if x + obj.r > w then 134 | x = w - obj.r 135 | obj.dx = -obj.dx 136 | elseif x - obj.r < 0 then 137 | x = obj.r 138 | obj.dx = -obj.dx 139 | end 140 | y = y + obj.dy * dt 141 | if y + obj.r > h then 142 | y = h - obj.r 143 | obj.dy = -obj.dy 144 | elseif y - obj.r < 0 then 145 | y = obj.r 146 | obj.dy = -obj.dy 147 | end 148 | obj.x, obj.y = x, y 149 | 150 | obj.dx = obj.dx * (1 - dt * 0.0001) 151 | obj.dy = obj.dy * (1 - dt * 0.0001) 152 | end 153 | lastT = currT 154 | end 155 | end 156 | 157 | 158 | local lastDisplayTime = 0 159 | 160 | local renderStartTime = nil 161 | local renderTime = 0 162 | local renderCount = 0 163 | 164 | local lastRender = 0 165 | local frameTime = 0 166 | local frameCount = 0 167 | 168 | local processTime = 0 169 | local processCount = 0 170 | 171 | 172 | local view = world:newView 173 | { 174 | title = "example03", 175 | size = {initialWidth, initialHeight}, 176 | resizable = true, 177 | 178 | eventFunc = function(view, event, ...) 179 | if event == "EXPOSE" then 180 | local startTime = world:getTime() 181 | frameTime = frameTime + startTime - lastRender 182 | frameCount = frameCount + 1 183 | lastRender = startTime 184 | 185 | local cairo = view:getDrawContext() 186 | local w, h = view:getSize() 187 | 188 | cairo:set_source_rgb(1.0, 1.0, 1.0) 189 | cairo:rectangle(0, 0, w, h) 190 | cairo:fill() 191 | 192 | objects_draw(cairo, w, h) 193 | 194 | renderStartTime = startTime 195 | world:setNextProcessTime(0) 196 | 197 | elseif event == "BUTTON_PRESS" then 198 | local bx, by, bn = ... 199 | if bn == 1 then 200 | objects_push(bx, by) 201 | end 202 | elseif event == "CLOSE" then 203 | view:close() 204 | end 205 | end 206 | } 207 | 208 | view:show() 209 | 210 | local lastP = world:getTime() 211 | 212 | local REFRESH_PERIOD = 0.015 213 | 214 | world:setProcessFunc(function() 215 | if not view:isClosed() then 216 | local startTime = world:getTime() 217 | 218 | if renderStartTime then 219 | renderTime = renderTime + (startTime - renderStartTime) 220 | renderCount = renderCount + 1 221 | lastDisplayTime = renderStartTime 222 | renderStartTime = nil 223 | end 224 | 225 | local now = world:getTime() 226 | if now >= lastDisplayTime + REFRESH_PERIOD then 227 | objects_process(view:getSize()) 228 | processTime = processTime + world:getTime() - startTime 229 | processCount = processCount + 1 230 | 231 | view:postRedisplay() 232 | world:setNextProcessTime(REFRESH_PERIOD) 233 | else 234 | world:setNextProcessTime(lastDisplayTime + REFRESH_PERIOD - now) 235 | end 236 | 237 | if startTime > lastP + 2 and renderCount > 0 and processCount > 0 then 238 | print(string.format("render: %5.1fms, process: %5.1fms, period: %5.1fms", 239 | renderTime/renderCount * 1000, 240 | processTime/processCount * 1000, 241 | frameTime/frameCount * 1000)) 242 | renderTime = 0 243 | renderCount = 0 244 | processTime = 0 245 | processCount = 0 246 | frameTime = 0 247 | frameCount = 0 248 | lastP = startTime 249 | end 250 | end 251 | 252 | end) 253 | world:setNextProcessTime(0) 254 | 255 | while world:hasViews() do 256 | world:update() 257 | end 258 | -------------------------------------------------------------------------------- /example/example04.lua: -------------------------------------------------------------------------------- 1 | local lpugl = require"lpugl_opengl" 2 | local nvg = require"nvg" 3 | local color = require"nvg.color" 4 | 5 | local floor = math.floor 6 | local ceil = math.ceil 7 | local abs = math.abs 8 | local sqrt = math.sqrt 9 | 10 | local unpack = table.unpack or unpack -- for Lua 5.1 11 | 12 | math.randomseed(os.time()) 13 | 14 | local world = lpugl.newWorld("example04.lua") 15 | local scale = world:getScreenScale() 16 | 17 | local drawBall 18 | do 19 | local function drawBallImpl(ctx, x, y, r, 20 | rgba1, 21 | rgba2) 22 | ctx:beginPath() 23 | 24 | local alpha = 0.7 25 | local d = r*0.75 26 | 27 | ctx:arc(x, y, r, 0, math.pi * 2) 28 | ctx.fillStyle = ctx:radialGradient(x - d, y - d, r - d, 2*r, rgba1, rgba2) 29 | ctx:fill() 30 | end 31 | 32 | local alpha = 0.7 33 | 34 | local gradientTable = { 35 | { color.rgba(1.0, 0.8, 0.8, alpha), 36 | color.rgba(1.0, 0.0, 0.0, alpha) }, 37 | 38 | { color.rgba(0.7, 1.0, 0.7, alpha), 39 | color.rgba(0.0, 1.0, 0.0, alpha) }, 40 | 41 | { color.rgba(0.7, 0.7, 1.0, alpha), 42 | color.rgba(0.0, 0.0, 1.0, alpha) }, 43 | } 44 | drawBall = function(ctx, x, y, r, gradientIndex) 45 | drawBallImpl(ctx, x, y, r, 46 | unpack(gradientTable[gradientIndex])) 47 | end 48 | 49 | end 50 | 51 | 52 | local initialWidth, initialHeight = 640*scale, 480*scale 53 | 54 | local objects_draw 55 | local objects_push 56 | local objects_process 57 | do 58 | local objects = {} 59 | 60 | for i = 1, 500 do 61 | local obj = {} 62 | objects[i] = obj 63 | obj.r = math.random(floor(10*scale), floor(30*scale)) 64 | obj.x = math.random(obj.r, floor(initialWidth - 2 * obj.r)) 65 | obj.y = math.random(obj.r, floor(initialHeight - 2 * obj.r)) 66 | obj.g = math.random(1, 3) 67 | obj.dx = 5 * math.random() / 20 * scale 68 | obj.dy = 5 * math.random() / 20 * scale 69 | end 70 | 71 | 72 | objects_draw = function(ctx, w, h) 73 | for i = 1, #objects do 74 | local obj = objects[i] 75 | drawBall(ctx, obj.x, obj.y, obj.r, obj.g) 76 | end 77 | end 78 | 79 | objects_push = function(bx, by) 80 | for i = 1, #objects do 81 | local obj = objects[i] 82 | local x, y = obj.x, obj.y 83 | local bdx, bdy = x - bx, y - by 84 | local bd = sqrt(bdx^2 + bdy^2)/scale 85 | local a = (bd + 0.01)^-1.5 86 | obj.dx = obj.dx + a * bdx 87 | obj.dy = obj.dy + a * bdy 88 | end 89 | end 90 | 91 | local lastT = world:getTime() 92 | 93 | objects_process = function(w, h) 94 | local currT = world:getTime() 95 | local dt = (currT - lastT) * 1000 96 | 97 | for i = 1, #objects do 98 | local obj = objects[i] 99 | local x, y = obj.x, obj.y 100 | x = x + obj.dx * dt 101 | if x + obj.r > w then 102 | x = w - obj.r 103 | obj.dx = -obj.dx 104 | elseif x - obj.r < 0 then 105 | x = obj.r 106 | obj.dx = -obj.dx 107 | end 108 | y = y + obj.dy * dt 109 | if y + obj.r > h then 110 | y = h - obj.r 111 | obj.dy = -obj.dy 112 | elseif y - obj.r < 0 then 113 | y = obj.r 114 | obj.dy = -obj.dy 115 | end 116 | obj.x, obj.y = x, y 117 | 118 | obj.dx = obj.dx * (1 - dt * 0.0001) 119 | obj.dy = obj.dy * (1 - dt * 0.0001) 120 | end 121 | lastT = currT 122 | end 123 | end 124 | 125 | local lastDisplayTime = 0 126 | 127 | local renderStartTime = nil 128 | local renderTime = 0 129 | local renderCount = 0 130 | 131 | local lastRender = 0 132 | local frameTime = 0 133 | local frameCount = 0 134 | 135 | local processTime = 0 136 | local processCount = 0 137 | 138 | local ctx = nil 139 | 140 | local view = world:newView 141 | { 142 | title = "example04", 143 | size = {initialWidth, initialHeight}, 144 | resizable = true, 145 | 146 | eventFunc = function(view, event, ...) 147 | 148 | if event == "CREATE" then 149 | ctx = nvg.new("antialias") 150 | 151 | elseif event == "EXPOSE" then 152 | local startTime = world:getTime() 153 | frameTime = frameTime + startTime - lastRender 154 | frameCount = frameCount + 1 155 | lastRender = startTime 156 | 157 | local w, h = view:getSize() 158 | ctx:beginFrame(w, h) 159 | ctx:clear("#ffffff") 160 | objects_draw(ctx, w, h) 161 | ctx:endFrame() 162 | 163 | renderStartTime = startTime 164 | world:setNextProcessTime(0) 165 | 166 | elseif event == "BUTTON_PRESS" then 167 | local bx, by, bn = ... 168 | if bn == 1 then 169 | objects_push(bx, by) 170 | end 171 | 172 | elseif event == "CLOSE" then 173 | view:close() 174 | end 175 | end 176 | } 177 | 178 | view:show() 179 | 180 | local lastP = world:getTime() 181 | 182 | local REFRESH_PERIOD = 0.015 183 | 184 | world:setProcessFunc(function() 185 | if not view:isClosed() then 186 | local startTime = world:getTime() 187 | 188 | if renderStartTime then 189 | renderTime = renderTime + (startTime - renderStartTime) 190 | renderCount = renderCount + 1 191 | lastDisplayTime = renderStartTime 192 | renderStartTime = nil 193 | end 194 | 195 | local now = world:getTime() 196 | if now >= lastDisplayTime + REFRESH_PERIOD then 197 | objects_process(view:getSize()) 198 | processTime = processTime + world:getTime() - startTime 199 | processCount = processCount + 1 200 | 201 | view:postRedisplay() 202 | world:setNextProcessTime(REFRESH_PERIOD) 203 | else 204 | world:setNextProcessTime(lastDisplayTime + REFRESH_PERIOD - now) 205 | end 206 | 207 | if startTime > lastP + 2 and renderCount > 0 and processCount > 0 then 208 | print(string.format("render: %5.1fms, process: %5.1fms, period: %5.1fms", 209 | renderTime/renderCount * 1000, 210 | processTime/processCount * 1000, 211 | frameTime/frameCount * 1000)) 212 | renderTime = 0 213 | renderCount = 0 214 | processTime = 0 215 | processCount = 0 216 | frameTime = 0 217 | frameCount = 0 218 | lastP = startTime 219 | end 220 | end 221 | 222 | end) 223 | world:setNextProcessTime(0) 224 | 225 | while world:hasViews() do 226 | world:update() 227 | end 228 | -------------------------------------------------------------------------------- /example/example10.lua: -------------------------------------------------------------------------------- 1 | --[[ 2 | This examples demonstrates the usage of the [Notify C API] in a multithreading scenario. 3 | 4 | LPugl world objects implement the *Notify C API*, see [src/notify_capi.h](../src/notify_capi.h), 5 | i.e. the world object has an an associated meta table entry *_capi_notify* delivered by 6 | the C API function *notify_get_capi()* and the associated C API function *toNotifier()* returns 7 | a valid pointer for a given LPugl world object. 8 | 9 | In this example the *Notify C API* is used to notify the event loop by calling *world:awake()* 10 | each time a message is added by another thread to a [mtmsg](https://github.com/osch/lua-mtmsg) 11 | buffer object. 12 | 13 | This is done by connecting the LPugl world object as a notifier object to the mtmsg buffer 14 | object. 15 | 16 | [Notify C API]: https://github.com/lua-capis/lua-notify-capi 17 | --]] 18 | 19 | local llthreads = require("llthreads2.ex") 20 | local mtmsg = require("mtmsg") 21 | local lpugl = require("lpugl_cairo") 22 | 23 | local world = lpugl.newWorld("example10.lua") 24 | local scale = world:getScreenScale() 25 | 26 | local threadIn = mtmsg.newbuffer() 27 | local threadOut = mtmsg.newbuffer() 28 | 29 | threadOut:notifier(world) 30 | threadOut:nonblock(true) 31 | 32 | local thread = llthreads.new(function(inId, outId) 33 | local mtmsg = require("mtmsg") 34 | local threadIn = mtmsg.buffer(inId) 35 | local threadOut = mtmsg.buffer(outId) 36 | local started = mtmsg.time() 37 | local lastReport = mtmsg.time() 38 | local paused = false 39 | threadOut:addmsg("working", 0) 40 | threadIn:nonblock(true) 41 | while true do 42 | local msg = threadIn:nextmsg() 43 | if msg == "start" then 44 | if not started or paused then 45 | if paused then 46 | started = mtmsg.time() - paused 47 | else 48 | started = mtmsg.time() 49 | end 50 | paused = false 51 | threadOut:addmsg("working", mtmsg.time() - started) 52 | lastReport = mtmsg.time() 53 | threadIn:nonblock(true) 54 | end 55 | elseif msg == "pause" then 56 | if started and not paused then 57 | paused = mtmsg.time() - started 58 | threadOut:addmsg("paused", paused) 59 | threadIn:nonblock(false) 60 | end 61 | elseif msg == "terminate" then 62 | threadOut:addmsg("terminated") 63 | return 64 | end 65 | if started and not paused then 66 | -- do some "work" here ... 67 | if mtmsg.time() - lastReport >= 1 then 68 | threadOut:addmsg("working", mtmsg.time() - started) 69 | lastReport = mtmsg.time() 70 | end 71 | if mtmsg.time() - started > 10 then 72 | -- work has finished after 10 seconds 73 | started = false 74 | threadOut:addmsg("finished") 75 | threadIn:nonblock(false) 76 | end 77 | end 78 | end 79 | end, 80 | threadIn:id(), threadOut:id()) 81 | thread:start() 82 | 83 | local threadState = "initial" 84 | local threadProgress = nil 85 | 86 | local view = world:newView 87 | { 88 | title = "example10", 89 | size = {300*scale, 100*scale}, 90 | resizable = true, 91 | 92 | eventFunc = function(view, event, ...) 93 | print("Event", event, ...) 94 | if event == "EXPOSE" then 95 | local cairo = view:getDrawContext() 96 | local w, h = view:getSize() 97 | if threadState == "working" then 98 | cairo:set_source_rgb(1.0, 0.9, 0.9) 99 | else 100 | cairo:set_source_rgb(0.9, 0.9, 0.9) 101 | end 102 | cairo:rectangle(0, 0, w, h) 103 | cairo:fill() 104 | cairo:set_source_rgb(0, 0, 0) 105 | cairo:select_font_face("sans-serif", "normal", "normal") 106 | cairo:set_font_size(16*scale) 107 | local text = "Thread is "..threadState 108 | if threadProgress then 109 | text = string.format("%s (%d%%)", text, math.floor(threadProgress/10*100)) 110 | end 111 | local ext = cairo:font_extents() 112 | local fonth = ext.ascent + ext.descent 113 | cairo:move_to((w - 180*scale)/2, (h - fonth)/2 + ext.ascent) 114 | cairo:show_text(text) 115 | elseif event == "BUTTON_PRESS" then 116 | local mx, my, button = ... 117 | if button == 1 then 118 | if threadState == "paused" or threadState == "finished" then 119 | threadIn:addmsg("start") 120 | else 121 | threadIn:addmsg("pause") 122 | end 123 | end 124 | elseif event == "CLOSE" then 125 | threadIn:addmsg("terminate") 126 | view:close() 127 | end 128 | end 129 | } 130 | view:show() 131 | 132 | local counter = 0 133 | while world:hasViews() do 134 | world:update() 135 | local msg, progress = threadOut:nextmsg() 136 | if msg then 137 | print("Received from thread", msg, progress) 138 | threadState = msg 139 | if msg == "paused" or msg == "working" then 140 | threadProgress = progress 141 | else 142 | threadProgress = nil 143 | end 144 | if not view:isClosed() then 145 | view:postRedisplay() 146 | end 147 | end 148 | print("----- Updated", counter) 149 | counter = counter + 1 150 | end 151 | 152 | -------------------------------------------------------------------------------- /example/screenshot01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot01.png -------------------------------------------------------------------------------- /example/screenshot02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot02.png -------------------------------------------------------------------------------- /example/screenshot03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot03.png -------------------------------------------------------------------------------- /example/screenshot04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot04.png -------------------------------------------------------------------------------- /example/screenshot05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot05.png -------------------------------------------------------------------------------- /example/screenshot06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot06.png -------------------------------------------------------------------------------- /example/screenshot10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/osch/lua-lpugl/d57e0671318fa046e5ffcd958bf6093888a60a5b/example/screenshot10.png -------------------------------------------------------------------------------- /pugl-repo/AUTHORS: -------------------------------------------------------------------------------- 1 | Pugl is primarily written and maintained by David Robillard 2 | with contributions from (in increasing chronological order): 3 | 4 | Ben Loftis 5 | Robin Gareus 6 | Erik Åldstedt Sund 7 | Hanspeter Portner 8 | Stefan Westerfeld 9 | Jordan Halase 10 | Oliver Schmidt 11 | Zoë Sparks 12 | Jean Pierre Cimalando 13 | Thomas Brand 14 | -------------------------------------------------------------------------------- /pugl-repo/COPYING: -------------------------------------------------------------------------------- 1 | Copyright 2011-2020 David Robillard 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | -------------------------------------------------------------------------------- /pugl-repo/README.md: -------------------------------------------------------------------------------- 1 | This is a modified copy of Pugl for [LPugl](https://github.com/osch/lua-lpugl), 2 | a Lua-API based on Pugl. 3 | 4 | For original Pugl see: 5 | * https://drobilla.net/software/pugl 6 | * https://gitlab.com/lv2/pugl 7 | * https://github.com/drobilla/pugl 8 | 9 | Pugl 10 | ==== 11 | 12 | Pugl (PlUgin Graphics Library) is a minimal portable API for GUIs which is 13 | suitable for use in plugins. It works on X11, MacOS, and Windows, and 14 | optionally supports Vulkan, OpenGL, and Cairo graphics contexts. 15 | 16 | Pugl is vaguely similar to libraries like GLUT and GLFW, but with some 17 | distinguishing features: 18 | 19 | * Minimal in scope, providing only a thin interface to isolate 20 | platform-specific details from applications. 21 | 22 | * Zero dependencies, aside from standard system libraries. 23 | 24 | * Support for embedding in native windows, for example as a plugin or 25 | component within a larger application that is not based on Pugl. 26 | 27 | * Simple and extensible event-based API that makes dispatching in application 28 | or toolkit code easy with minimal boilerplate. 29 | 30 | * Suitable not only for continuously rendering applications like games, but 31 | also event-driven applications that only draw when necessary. 32 | 33 | * Explicit context and no static data whatsoever, so that several instances 34 | can be used within a single program at once. 35 | 36 | * Small, liberally licensed Free Software implementation that is suitable for 37 | vendoring and/or static linking. Pugl can be installed as a library, or 38 | used by simply copying the headers into a project. 39 | 40 | Stability 41 | --------- 42 | 43 | Pugl is currently being developed towards a long-term stable API. For the time 44 | being, however, the API may break occasionally. Please report any relevant 45 | feedback, or file feature requests, so that we can ensure that the released API 46 | is stable for as long as possible. 47 | 48 | Documentation 49 | ------------- 50 | 51 | Pugl is a C library that includes C++ bindings. 52 | Each API is documented separately: 53 | 54 | * [C Documentation (single page)](https://lv2.gitlab.io/pugl/c/singlehtml/) 55 | * [C Documentation (paginated)](https://lv2.gitlab.io/pugl/c/html/) 56 | * [C++ Documentation (single page)](https://lv2.gitlab.io/pugl/cpp/singlehtml/) 57 | * [C++ Documentation (paginated)](https://lv2.gitlab.io/pugl/cpp/html/) 58 | 59 | The documentation can also be built from the source by configuring with `--docs`. 60 | 61 | Testing 62 | ------- 63 | 64 | There are a few unit tests included, but unfortunately manual testing is still 65 | required. The tests and example programs will be built if you pass the 66 | `--test` option when configuring: 67 | 68 | ./waf configure --test 69 | 70 | Then, after building, the unit tests can be run: 71 | 72 | ./waf 73 | ./waf test --gui-tests 74 | 75 | The `examples` directory contains several programs that serve as both manual 76 | tests and demonstrations: 77 | 78 | * `pugl_embed_demo` shows a view embedded in another, and also tests 79 | requesting attention (which happens after 5 seconds), keyboard focus 80 | (switched by pressing tab), view moving (with the arrow keys), and view 81 | resizing (with the arrow keys while shift is held). This program uses only 82 | very old OpenGL and should work on any system. 83 | 84 | * `pugl_window_demo` demonstrates multiple top-level windows. 85 | 86 | * `pugl_shader_demo` demonstrates using more modern OpenGL (version 3 or 4) 87 | where dynamic loading and shaders are required. It can also be used to test 88 | performance by passing the number of rectangles to draw on the command line. 89 | 90 | * `pugl_cairo_demo` demonstrates using Cairo on top of the native windowing 91 | system (without OpenGL), and partial redrawing. 92 | 93 | * `pugl_print_events` is a utility that prints all received events to the 94 | console in a human readable format. 95 | 96 | * `pugl_cxx_demo` is a simple cube demo that uses the C++ API. 97 | 98 | * `pugl_vulkan_demo` is a simple example of using Vulkan in C that simply 99 | clears the window. 100 | 101 | * `pugl_vulkan_cxx_demo` is a more advanced Vulkan demo in C++ that draws many 102 | animated rectangles like `pugl_shader_demo`. 103 | 104 | All example programs support several command line options to control various 105 | behaviours, see the output of `--help` for details. Please file an issue if 106 | any of these programs do not work as expected on your system. 107 | 108 | -- David Robillard 109 | -------------------------------------------------------------------------------- /pugl-repo/include/pugl/cairo.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_CAIRO_H 18 | #define PUGL_CAIRO_H 19 | 20 | #include "pugl/pugl.h" 21 | 22 | PUGL_BEGIN_DECLS 23 | 24 | /** 25 | @defgroup cairo Cairo 26 | Cairo graphics support. 27 | @ingroup pugl 28 | @{ 29 | */ 30 | 31 | /** 32 | Cairo graphics backend accessor. 33 | 34 | Pass the returned value to puglSetBackend() to draw to a view with Cairo. 35 | */ 36 | PUGL_CONST_API 37 | const PuglBackend* 38 | puglCairoBackend(void); 39 | 40 | /** 41 | Return a pointer to the native handle of the world. 42 | 43 | On X11, this returns a pointer to the Display. 44 | On OSX, this returns CGContextRef for current GraphicsPort. 45 | On Windows, this returns a handle to the calling process module. 46 | */ 47 | PUGL_API 48 | void* 49 | puglCairoBackendGetNativeWorld(PuglWorld* world); 50 | 51 | /** 52 | @} 53 | */ 54 | 55 | PUGL_END_DECLS 56 | 57 | #endif // PUGL_CAIRO_H 58 | -------------------------------------------------------------------------------- /pugl-repo/include/pugl/gl.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_GL_H 18 | #define PUGL_GL_H 19 | 20 | #include "pugl/pugl.h" 21 | 22 | // IWYU pragma: begin_exports 23 | 24 | /* Unfortunately, GL includes vary across platforms, so include them here to 25 | enable pure portable programs. */ 26 | 27 | #ifndef PUGL_NO_INCLUDE_GL_H 28 | # ifdef __APPLE__ 29 | # include 30 | # else 31 | # ifdef _WIN32 32 | # include 33 | # endif 34 | # include 35 | # endif 36 | #endif 37 | 38 | #ifndef PUGL_NO_INCLUDE_GLU_H 39 | # ifdef __APPLE__ 40 | # include 41 | # else 42 | # ifdef _WIN32 43 | # include 44 | # endif 45 | # include 46 | # endif 47 | #endif 48 | 49 | // IWYU pragma: end_exports 50 | 51 | PUGL_BEGIN_DECLS 52 | 53 | /** 54 | @defgroup gl OpenGL 55 | OpenGL graphics support. 56 | @ingroup pugl 57 | @{ 58 | */ 59 | 60 | /** 61 | OpenGL extension function. 62 | */ 63 | typedef void (*PuglGlFunc)(void); 64 | 65 | /** 66 | Return the address of an OpenGL extension function. 67 | */ 68 | PUGL_API 69 | PuglGlFunc 70 | puglGetProcAddress(const char* name); 71 | 72 | /** 73 | Enter the OpenGL context. 74 | 75 | This can be used to enter the graphics context in unusual situations, for 76 | doing things like loading textures. Note that this must not be used for 77 | drawing, which may only be done while processing an expose event. 78 | */ 79 | PUGL_API 80 | PuglStatus 81 | puglEnterContext(PuglView* view); 82 | 83 | /** 84 | Leave the OpenGL context. 85 | 86 | This must only be called after puglEnterContext(). 87 | */ 88 | PUGL_API 89 | PuglStatus 90 | puglLeaveContext(PuglView* view); 91 | 92 | /** 93 | OpenGL graphics backend. 94 | 95 | Pass the returned value to puglSetBackend() to draw to a view with OpenGL. 96 | */ 97 | PUGL_CONST_API 98 | const PuglBackend* 99 | puglGlBackend(void); 100 | 101 | /** 102 | Return a pointer to the native handle of the world. 103 | 104 | On X11, this returns a pointer to the Display. 105 | On OSX, this returns CGContextRef for current GraphicsPort. 106 | On Windows, this returns a handle to the calling process module. 107 | */ 108 | PUGL_API 109 | void* 110 | puglGlBackendGetNativeWorld(PuglWorld* world); 111 | 112 | PUGL_END_DECLS 113 | 114 | /** 115 | @} 116 | */ 117 | 118 | #endif // PUGL_GL_H 119 | -------------------------------------------------------------------------------- /pugl-repo/include/pugl/stub.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_STUB_H 18 | #define PUGL_STUB_H 19 | 20 | #include "pugl/pugl.h" 21 | 22 | PUGL_BEGIN_DECLS 23 | 24 | /** 25 | @defgroup stub Stub 26 | Native graphics support. 27 | @ingroup pugl 28 | @{ 29 | */ 30 | 31 | /** 32 | Stub graphics backend accessor. 33 | 34 | This backend just creates a simple native window without setting up any 35 | portable graphics API. 36 | */ 37 | PUGL_CONST_API 38 | const PuglBackend* 39 | puglStubBackend(void); 40 | 41 | /** 42 | @} 43 | */ 44 | 45 | PUGL_END_DECLS 46 | 47 | #endif // PUGL_STUB_H 48 | -------------------------------------------------------------------------------- /pugl-repo/include/pugl/vulkan.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | /* 18 | Note that this header includes Vulkan headers, so if you are writing a 19 | program or plugin that dynamically loads vulkan, you should first define 20 | `VK_NO_PROTOTYPES` before including it. 21 | */ 22 | 23 | #ifndef PUGL_VULKAN_H 24 | #define PUGL_VULKAN_H 25 | 26 | #include "pugl/pugl.h" 27 | 28 | #include 29 | 30 | #include 31 | 32 | PUGL_BEGIN_DECLS 33 | 34 | /** 35 | @defgroup vulkan Vulkan 36 | Vulkan graphics support. 37 | 38 | Vulkan support differs from OpenGL because almost all most configuration is 39 | done using the Vulkan API itself, rather than by setting view hints to 40 | configure the context. Pugl only provides a minimal loader for loading the 41 | Vulkan library, and a portable function to create a Vulkan surface for a 42 | view, which hides the platform-specific implementation details. 43 | 44 | @ingroup pugl 45 | @{ 46 | */ 47 | 48 | /** 49 | Dynamic Vulkan loader. 50 | 51 | This can be used to dynamically load the Vulkan library. Applications or 52 | plugins should not link against the Vulkan library, but instead use this at 53 | runtime. This ensures that things will work on as many systems as possible, 54 | and allows errors to be handled gracefully. 55 | 56 | This is not a "loader" in the sense of loading all the required Vulkan 57 | functions (which is the application's responsibility), but just a minimal 58 | implementation to portably load the Vulkan library and get the two functions 59 | that are used to load everything else. 60 | 61 | Note that this owns the loaded Vulkan library, so it must outlive all use of 62 | the Vulkan API. 63 | 64 | @see https://www.khronos.org/registry/vulkan/specs/1.0/html/chap4.html 65 | */ 66 | typedef struct PuglVulkanLoaderImpl PuglVulkanLoader; 67 | 68 | /** 69 | Create a new dynamic loader for Vulkan functions. 70 | 71 | This dynamically loads the Vulkan library and gets the load functions from 72 | it. 73 | 74 | @return A new Vulkan loader, or null on failure. 75 | */ 76 | PUGL_API 77 | PuglVulkanLoader* 78 | puglNewVulkanLoader(PuglWorld* world); 79 | 80 | /** 81 | Free a loader created with puglNewVulkanLoader(). 82 | 83 | Note that this closes the Vulkan library, so no Vulkan objects or API may be 84 | used after this is called. 85 | */ 86 | PUGL_API 87 | void 88 | puglFreeVulkanLoader(PuglVulkanLoader* loader); 89 | 90 | /** 91 | Return the `vkGetInstanceProcAddr` function. 92 | 93 | @return Null if the Vulkan library does not contain this function (which is 94 | unlikely and indicates a broken system). 95 | */ 96 | PUGL_API 97 | PFN_vkGetInstanceProcAddr 98 | puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader); 99 | 100 | /** 101 | Return the `vkGetDeviceProcAddr` function. 102 | 103 | @return Null if the Vulkan library does not contain this function (which is 104 | unlikely and indicates a broken system). 105 | */ 106 | PUGL_API 107 | PFN_vkGetDeviceProcAddr 108 | puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader); 109 | 110 | /** 111 | Return the Vulkan instance extensions required to draw to a PuglView. 112 | 113 | This simply returns static strings, it does not access Vulkan or the window 114 | system. The returned array always contains at least "VK_KHR_surface". 115 | 116 | @param[out] count The number of extensions in the returned array. 117 | @return An array of extension name strings. 118 | */ 119 | PUGL_API 120 | const char* const* 121 | puglGetInstanceExtensions(uint32_t* count); 122 | 123 | /** 124 | Create a Vulkan surface for a Pugl view. 125 | 126 | @param vkGetInstanceProcAddr Accessor for Vulkan functions. 127 | @param view The view the surface is to be displayed on. 128 | @param instance The Vulkan instance. 129 | @param allocator Vulkan allocation callbacks, may be NULL. 130 | @param[out] surface Pointed to a newly created Vulkan surface. 131 | @return `VK_SUCCESS` on success, or a Vulkan error code. 132 | */ 133 | PUGL_API 134 | VkResult 135 | puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, 136 | PuglView* view, 137 | VkInstance instance, 138 | const VkAllocationCallbacks* allocator, 139 | VkSurfaceKHR* surface); 140 | 141 | /** 142 | Vulkan graphics backend. 143 | 144 | Pass the returned value to puglSetBackend() to draw to a view with Vulkan. 145 | */ 146 | PUGL_CONST_API 147 | const PuglBackend* 148 | puglVulkanBackend(void); 149 | 150 | /** 151 | @} 152 | */ 153 | 154 | PUGL_END_DECLS 155 | 156 | #endif // PUGL_VULKAN_H 157 | -------------------------------------------------------------------------------- /pugl-repo/src/implementation.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_IMPLEMENTATION_H 18 | #define PUGL_IMPLEMENTATION_H 19 | 20 | #include "types.h" 21 | 22 | #include "pugl/pugl.h" 23 | 24 | #include 25 | #include 26 | 27 | #ifndef PUGL_API_PRIVATE 28 | # define PUGL_API_PRIVATE 29 | #endif 30 | 31 | PUGL_BEGIN_DECLS 32 | 33 | /// Set `blob` to `data` with length `len`, reallocating if necessary 34 | PUGL_API_PRIVATE void 35 | puglSetBlob(PuglBlob* dest, const void* data, size_t len); 36 | 37 | /// Reallocate and set `*dest` to `string` 38 | PUGL_API_PRIVATE void 39 | puglSetString(char** dest, const char* string); 40 | 41 | /// Allocate and initialise world internals (implemented once per platform) 42 | PUGL_API_PRIVATE PuglWorldInternals* 43 | puglInitWorldInternals(PuglWorldType type, PuglWorldFlags flags); 44 | 45 | /// Destroy and free world internals (implemented once per platform) 46 | PUGL_API_PRIVATE void 47 | puglFreeWorldInternals(PuglWorld* world); 48 | 49 | /// Allocate and initialise view internals (implemented once per platform) 50 | PUGL_API_PRIVATE PuglInternals* 51 | puglInitViewInternals(void); 52 | 53 | /// Destroy and free view internals (implemented once per platform) 54 | PUGL_API_PRIVATE void 55 | puglFreeViewInternals(PuglView* view); 56 | 57 | /// Return the Unicode code point for `buf` or the replacement character 58 | PUGL_API_PRIVATE uint32_t 59 | puglDecodeUTF8(const uint8_t* buf); 60 | 61 | /// Dispatch an event with a simple `type` to `view` 62 | PUGL_API_PRIVATE void 63 | puglDispatchSimpleEvent(PuglView* view, PuglEventType type); 64 | 65 | /// Dispatch `event` to `view` while already in the graphics context 66 | PUGL_API_PRIVATE void 67 | puglDispatchEventInContext(PuglView* view, PuglEvent* event); 68 | 69 | /// Dispatch `event` to `view`, entering graphics context if necessary 70 | PUGL_API_PRIVATE void 71 | puglDispatchEvent(PuglView* view, PuglEvent* event); 72 | 73 | /// Set internal (stored in view) clipboard contents 74 | PUGL_API_PRIVATE const void* 75 | puglGetInternalClipboard(const PuglView* view, const char** type, size_t* len); 76 | 77 | /// Set internal (stored in view) clipboard contents 78 | PUGL_API_PRIVATE PuglStatus 79 | puglSetInternalClipboard(PuglWorld* world, 80 | const char* type, 81 | const void* data, 82 | size_t len); 83 | 84 | PUGL_API_PRIVATE bool 85 | puglRectsInit(PuglRects* rects, int capacity); 86 | 87 | PUGL_API_PRIVATE bool 88 | puglRectsAppend(PuglRects* rects, PuglRect* rect); 89 | 90 | PUGL_API_PRIVATE void 91 | puglRectsFree(PuglRects* rects); 92 | 93 | PUGL_END_DECLS 94 | 95 | #endif // PUGL_IMPLEMENTATION_H 96 | -------------------------------------------------------------------------------- /pugl-repo/src/mac.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | Copyright 2017 Hanspeter Portner 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #ifndef PUGL_DETAIL_MAC_H 19 | #define PUGL_DETAIL_MAC_H 20 | 21 | #include "pugl/pugl.h" 22 | 23 | #import 24 | 25 | #include 26 | 27 | @interface PuglWrapperView : NSView 28 | 29 | - (void)dispatchExpose:(NSRect)rect rects:(const NSRect*)rects count:(int)c; 30 | - (void)setReshaped; 31 | - (void)clipboardReceived; 32 | 33 | @end 34 | 35 | @interface PuglWindow : NSWindow 36 | 37 | - (void)setPuglview:(PuglView*)view; 38 | 39 | @end 40 | 41 | struct PuglWorldInternalsImpl { 42 | NSApplication* app; 43 | double nextProcessTime; 44 | id worldProxy; 45 | NSTimer* processTimer; 46 | bool polling; 47 | }; 48 | 49 | struct PuglInternalsImpl { 50 | NSApplication* app; 51 | PuglWrapperView* wrapperView; 52 | NSView* drawView; 53 | NSCursor* cursor; 54 | PuglWindow* window; 55 | uint32_t mods; 56 | uint32_t mouseButtons; 57 | bool posRequested; 58 | bool displayed; 59 | bool mouseTracked; 60 | bool shouldCursorHidden; 61 | bool isCursorHidden; 62 | bool trySurfaceCache; 63 | }; 64 | 65 | #endif // PUGL_DETAIL_MAC_H 66 | -------------------------------------------------------------------------------- /pugl-repo/src/mac_gl.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include 18 | 19 | #include "implementation.h" 20 | #include "mac.h" 21 | #include "stub.h" 22 | 23 | #include "pugl/gl.h" 24 | 25 | #ifndef __MAC_10_10 26 | # define NSOpenGLProfileVersion4_1Core NSOpenGLProfileVersion3_2Core 27 | #endif 28 | 29 | @interface PuglOpenGLView : NSOpenGLView 30 | @end 31 | 32 | @implementation PuglOpenGLView { 33 | @public 34 | PuglView* puglview; 35 | } 36 | 37 | - (id)initWithFrame:(NSRect)frame 38 | { 39 | const bool compat = puglview->hints[PUGL_USE_COMPAT_PROFILE]; 40 | const unsigned samples = (unsigned)puglview->hints[PUGL_SAMPLES]; 41 | const int major = puglview->hints[PUGL_CONTEXT_VERSION_MAJOR]; 42 | const unsigned profile = 43 | ((compat || major < 3) ? NSOpenGLProfileVersionLegacy 44 | : (major >= 4 ? NSOpenGLProfileVersion4_1Core 45 | : NSOpenGLProfileVersion3_2Core)); 46 | 47 | // Set attributes to default if they are unset 48 | // (There is no GLX_DONT_CARE equivalent on MacOS) 49 | if (puglview->hints[PUGL_DEPTH_BITS] == PUGL_DONT_CARE) { 50 | puglview->hints[PUGL_DEPTH_BITS] = 0; 51 | } 52 | if (puglview->hints[PUGL_STENCIL_BITS] == PUGL_DONT_CARE) { 53 | puglview->hints[PUGL_STENCIL_BITS] = 0; 54 | } 55 | if (puglview->hints[PUGL_SAMPLES] == PUGL_DONT_CARE) { 56 | puglview->hints[PUGL_SAMPLES] = 1; 57 | } 58 | if (puglview->hints[PUGL_DOUBLE_BUFFER] == PUGL_DONT_CARE) { 59 | puglview->hints[PUGL_DOUBLE_BUFFER] = 1; 60 | } 61 | if (puglview->hints[PUGL_SWAP_INTERVAL] == PUGL_DONT_CARE) { 62 | puglview->hints[PUGL_SWAP_INTERVAL] = 1; 63 | } 64 | 65 | const unsigned colorSize = (unsigned)(puglview->hints[PUGL_RED_BITS] + 66 | puglview->hints[PUGL_BLUE_BITS] + 67 | puglview->hints[PUGL_GREEN_BITS] + 68 | puglview->hints[PUGL_ALPHA_BITS]); 69 | 70 | NSOpenGLPixelFormatAttribute pixelAttribs[17] = { 71 | NSOpenGLPFADoubleBuffer, 72 | NSOpenGLPFAAccelerated, 73 | NSOpenGLPFAOpenGLProfile, 74 | profile, 75 | NSOpenGLPFAColorSize, 76 | colorSize, 77 | NSOpenGLPFADepthSize, 78 | (unsigned)puglview->hints[PUGL_DEPTH_BITS], 79 | NSOpenGLPFAStencilSize, 80 | (unsigned)puglview->hints[PUGL_STENCIL_BITS], 81 | NSOpenGLPFAMultisample, 82 | samples ? 1 : 0, 83 | NSOpenGLPFASampleBuffers, 84 | samples ? 1 : 0, 85 | NSOpenGLPFASamples, 86 | samples, 87 | 0}; 88 | NSOpenGLPixelFormatAttribute* a = 89 | puglview->hints[PUGL_DOUBLE_BUFFER] ? &pixelAttribs[0] : &pixelAttribs[1]; 90 | 91 | NSOpenGLPixelFormat* pixelFormat = 92 | [[NSOpenGLPixelFormat alloc] initWithAttributes:a]; 93 | 94 | if (pixelFormat) { 95 | self = [super initWithFrame:frame pixelFormat:pixelFormat]; 96 | [pixelFormat release]; 97 | } else { 98 | self = [super initWithFrame:frame]; 99 | } 100 | 101 | [self setWantsBestResolutionOpenGLSurface:YES]; 102 | 103 | if (self) { 104 | [[self openGLContext] makeCurrentContext]; 105 | [self reshape]; 106 | } 107 | return self; 108 | } 109 | 110 | - (void)reshape 111 | { 112 | PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 113 | 114 | [super reshape]; 115 | [wrapper setReshaped]; 116 | } 117 | 118 | - (void)drawRect:(NSRect)rect 119 | { 120 | PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 121 | const NSRect* rects = NULL; 122 | NSInteger rectCount = 0; 123 | // getRectsBeingDrawn does only give one merged rectangle for MacOS >= 10.14 124 | // :-( see also 125 | // https://forum.juce.com/t/juce-coregraphics-render-with-multiple-paint-calls-not-working-on-new-mac-mojave/30905 126 | [self getRectsBeingDrawn:&rects count:&rectCount]; 127 | [wrapper dispatchExpose:rect rects:rects count:rectCount]; 128 | } 129 | 130 | - (BOOL)acceptsFirstMouse:(NSEvent*)event 131 | { 132 | return YES; 133 | } 134 | 135 | - (BOOL)shouldDelayWindowOrderingForEvent:(NSEvent*)theEvent 136 | { 137 | return puglview->transientParent && puglview->hints[PUGL_IS_POPUP]; 138 | } 139 | 140 | @end 141 | 142 | static PuglStatus 143 | puglMacGlCreate(PuglView* view) 144 | { 145 | PuglInternals* impl = view->impl; 146 | PuglOpenGLView* drawView = [PuglOpenGLView alloc]; 147 | 148 | drawView->puglview = view; 149 | [drawView initWithFrame:[impl->wrapperView bounds]]; 150 | if (view->hints[PUGL_RESIZABLE]) { 151 | [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 152 | } else { 153 | [drawView setAutoresizingMask:NSViewNotSizable]; 154 | } 155 | 156 | impl->drawView = drawView; 157 | return PUGL_SUCCESS; 158 | } 159 | 160 | static PuglStatus 161 | puglMacGlDestroy(PuglView* view) 162 | { 163 | PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; 164 | 165 | [drawView removeFromSuperview]; 166 | [drawView release]; 167 | 168 | view->impl->drawView = nil; 169 | return PUGL_SUCCESS; 170 | } 171 | 172 | static PuglStatus 173 | puglMacGlEnter(PuglView* view, 174 | const PuglEventExpose* PUGL_UNUSED(expose), 175 | PuglRects* rects) 176 | { 177 | PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; 178 | 179 | [[drawView openGLContext] makeCurrentContext]; 180 | if (rects && view->impl->trySurfaceCache) { 181 | rects->rectsCount = 0; // surface cache not supported by OpenGL backend 182 | } 183 | return PUGL_SUCCESS; 184 | } 185 | 186 | static PuglStatus 187 | puglMacGlLeave(PuglView* view, 188 | const PuglEventExpose* expose, 189 | PuglRects* PUGL_UNUSED(rects)) 190 | { 191 | PuglOpenGLView* const drawView = (PuglOpenGLView*)view->impl->drawView; 192 | 193 | if (expose) { 194 | if (view->hints[PUGL_DOUBLE_BUFFER]) { 195 | [[drawView openGLContext] flushBuffer]; 196 | } else { 197 | glFlush(); 198 | } 199 | } 200 | [NSOpenGLContext clearCurrentContext]; 201 | 202 | return PUGL_SUCCESS; 203 | } 204 | 205 | PuglGlFunc 206 | puglGetProcAddress(const char* name) 207 | { 208 | CFBundleRef framework = 209 | CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); 210 | 211 | CFStringRef symbol = CFStringCreateWithCString( 212 | kCFAllocatorDefault, name, kCFStringEncodingASCII); 213 | 214 | PuglGlFunc func = 215 | (PuglGlFunc)CFBundleGetFunctionPointerForName(framework, symbol); 216 | 217 | CFRelease(symbol); 218 | 219 | return func; 220 | } 221 | 222 | PuglStatus 223 | puglEnterContext(PuglView* view) 224 | { 225 | return view->backend->enter(view, NULL, NULL); 226 | } 227 | 228 | PuglStatus 229 | puglLeaveContext(PuglView* view) 230 | { 231 | return view->backend->leave(view, NULL, NULL); 232 | } 233 | 234 | const PuglBackend* 235 | puglGlBackend(void) 236 | { 237 | static const PuglBackend backend = {puglStubConfigure, 238 | puglMacGlCreate, 239 | puglMacGlDestroy, 240 | puglMacGlEnter, 241 | puglMacGlLeave, 242 | puglStubGetContext}; 243 | 244 | return &backend; 245 | } 246 | -------------------------------------------------------------------------------- /pugl-repo/src/mac_stub.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2019-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "implementation.h" 18 | #include "mac.h" 19 | #include "stub.h" 20 | 21 | #include "pugl/stub.h" 22 | 23 | #import 24 | 25 | @interface PuglStubView : NSView 26 | @end 27 | 28 | @implementation PuglStubView { 29 | @public 30 | PuglView* puglview; 31 | } 32 | 33 | - (void)resizeWithOldSuperviewSize:(NSSize)oldSize 34 | { 35 | PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 36 | 37 | [super resizeWithOldSuperviewSize:oldSize]; 38 | [wrapper setReshaped]; 39 | } 40 | 41 | - (void)drawRect:(NSRect)rect 42 | { 43 | PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 44 | 45 | [wrapper dispatchExpose:rect]; 46 | } 47 | 48 | @end 49 | 50 | static PuglStatus 51 | puglMacStubCreate(PuglView* view) 52 | { 53 | PuglInternals* impl = view->impl; 54 | PuglStubView* drawView = [PuglStubView alloc]; 55 | 56 | drawView->puglview = view; 57 | [drawView 58 | initWithFrame:NSMakeRect(0, 0, view->frame.width, view->frame.height)]; 59 | if (view->hints[PUGL_RESIZABLE]) { 60 | [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 61 | } else { 62 | [drawView setAutoresizingMask:NSViewNotSizable]; 63 | } 64 | 65 | impl->drawView = drawView; 66 | return PUGL_SUCCESS; 67 | } 68 | 69 | static PuglStatus 70 | puglMacStubDestroy(PuglView* view) 71 | { 72 | PuglStubView* const drawView = (PuglStubView*)view->impl->drawView; 73 | 74 | [drawView removeFromSuperview]; 75 | [drawView release]; 76 | 77 | view->impl->drawView = nil; 78 | return PUGL_SUCCESS; 79 | } 80 | 81 | const PuglBackend* 82 | puglStubBackend(void) 83 | { 84 | static const PuglBackend backend = {puglStubConfigure, 85 | puglMacStubCreate, 86 | puglMacStubDestroy, 87 | puglStubEnter, 88 | puglStubLeave, 89 | puglStubGetContext}; 90 | 91 | return &backend; 92 | } 93 | -------------------------------------------------------------------------------- /pugl-repo/src/mac_vulkan.m: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #define VK_NO_PROTOTYPES 1 18 | 19 | #include "implementation.h" 20 | #include "mac.h" 21 | #include "stub.h" 22 | #include "types.h" 23 | 24 | #include "pugl/pugl.h" 25 | #include "pugl/stub.h" 26 | #include "pugl/vulkan.h" 27 | 28 | #include 29 | #include 30 | 31 | #import 32 | #import 33 | 34 | #include 35 | 36 | #include 37 | #include 38 | 39 | @interface PuglVulkanView : NSView 40 | 41 | @end 42 | 43 | @implementation PuglVulkanView { 44 | @public 45 | PuglView* puglview; 46 | } 47 | 48 | - (id)initWithFrame:(NSRect)frame 49 | { 50 | self = [super initWithFrame:frame]; 51 | 52 | if (self) { 53 | self.wantsLayer = YES; 54 | self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawOnSetNeedsDisplay; 55 | } 56 | 57 | return self; 58 | } 59 | 60 | - (CALayer*)makeBackingLayer 61 | { 62 | CAMetalLayer* layer = [CAMetalLayer layer]; 63 | [layer setDelegate:self]; 64 | return layer; 65 | } 66 | 67 | - (void)setFrameSize:(NSSize)newSize 68 | { 69 | PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 70 | 71 | [super setFrameSize:newSize]; 72 | [wrapper setReshaped]; 73 | 74 | self.layer.frame = self.bounds; 75 | } 76 | 77 | - (void)displayLayer:(CALayer*)layer 78 | { 79 | (void)layer; 80 | PuglWrapperView* wrapper = (PuglWrapperView*)[self superview]; 81 | [wrapper dispatchExpose:[self bounds]]; 82 | } 83 | 84 | @end 85 | 86 | static PuglStatus 87 | puglMacVulkanCreate(PuglView* view) 88 | { 89 | PuglInternals* impl = view->impl; 90 | PuglVulkanView* drawView = [PuglVulkanView alloc]; 91 | const NSRect rect = NSMakeRect(0, 0, view->frame.width, view->frame.height); 92 | 93 | drawView->puglview = view; 94 | [drawView initWithFrame:rect]; 95 | if (view->hints[PUGL_RESIZABLE]) { 96 | [drawView setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable]; 97 | } else { 98 | [drawView setAutoresizingMask:NSViewNotSizable]; 99 | } 100 | 101 | impl->drawView = drawView; 102 | return PUGL_SUCCESS; 103 | } 104 | 105 | static PuglStatus 106 | puglMacVulkanDestroy(PuglView* view) 107 | { 108 | PuglVulkanView* const drawView = (PuglVulkanView*)view->impl->drawView; 109 | 110 | [drawView removeFromSuperview]; 111 | [drawView release]; 112 | 113 | view->impl->drawView = nil; 114 | return PUGL_SUCCESS; 115 | } 116 | 117 | struct PuglVulkanLoaderImpl { 118 | void* libvulkan; 119 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; 120 | PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; 121 | }; 122 | 123 | PuglVulkanLoader* 124 | puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) 125 | { 126 | PuglVulkanLoader* loader = 127 | (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); 128 | if (!loader) { 129 | return NULL; 130 | } 131 | 132 | if (!(loader->libvulkan = dlopen("libvulkan.dylib", RTLD_LAZY))) { 133 | free(loader); 134 | return NULL; 135 | } 136 | 137 | loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym( 138 | loader->libvulkan, "vkGetInstanceProcAddr"); 139 | 140 | loader->vkGetDeviceProcAddr = 141 | (PFN_vkGetDeviceProcAddr)dlsym(loader->libvulkan, "vkGetDeviceProcAddr"); 142 | 143 | return loader; 144 | } 145 | 146 | void 147 | puglFreeVulkanLoader(PuglVulkanLoader* loader) 148 | { 149 | if (loader) { 150 | dlclose(loader->libvulkan); 151 | free(loader); 152 | } 153 | } 154 | 155 | PFN_vkGetInstanceProcAddr 156 | puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) 157 | { 158 | return loader->vkGetInstanceProcAddr; 159 | } 160 | 161 | PFN_vkGetDeviceProcAddr 162 | puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) 163 | { 164 | return loader->vkGetDeviceProcAddr; 165 | } 166 | 167 | const PuglBackend* 168 | puglVulkanBackend(void) 169 | { 170 | static const PuglBackend backend = {puglStubConfigure, 171 | puglMacVulkanCreate, 172 | puglMacVulkanDestroy, 173 | puglStubEnter, 174 | puglStubLeave, 175 | puglStubGetContext}; 176 | 177 | return &backend; 178 | } 179 | 180 | const char* const* 181 | puglGetInstanceExtensions(uint32_t* const count) 182 | { 183 | static const char* const extensions[] = {"VK_KHR_surface", 184 | "VK_MVK_macos_surface"}; 185 | 186 | *count = 2; 187 | return extensions; 188 | } 189 | 190 | VkResult 191 | puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, 192 | PuglView* const view, 193 | VkInstance instance, 194 | const VkAllocationCallbacks* const allocator, 195 | VkSurfaceKHR* const surface) 196 | { 197 | PuglInternals* const impl = view->impl; 198 | 199 | PFN_vkCreateMacOSSurfaceMVK vkCreateMacOSSurfaceMVK = 200 | (PFN_vkCreateMacOSSurfaceMVK)vkGetInstanceProcAddr( 201 | instance, "vkCreateMacOSSurfaceMVK"); 202 | 203 | const VkMacOSSurfaceCreateInfoMVK info = { 204 | VK_STRUCTURE_TYPE_MACOS_SURFACE_CREATE_INFO_MVK, 205 | NULL, 206 | 0, 207 | impl->drawView, 208 | }; 209 | 210 | return vkCreateMacOSSurfaceMVK(instance, &info, allocator, surface); 211 | } 212 | -------------------------------------------------------------------------------- /pugl-repo/src/rect.h: -------------------------------------------------------------------------------- 1 | #ifndef PUGL_DETAIL_RECT_H 2 | #define PUGL_DETAIL_RECT_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "implementation.h" 9 | 10 | #include "pugl/pugl.h" 11 | 12 | static void 13 | integerRect(PuglRect* rect) 14 | { 15 | double x = floor(rect->x); 16 | double y = floor(rect->y); 17 | rect->width = ceil(rect->x + rect->width) - x; 18 | rect->height = ceil(rect->y + rect->height) - y; 19 | rect->x = x; 20 | rect->y = y; 21 | } 22 | 23 | static inline bool 24 | doesRectContain(PuglRect* r1, PuglRect* r2) 25 | { 26 | double myX0 = r1->x; 27 | double myY0 = r1->y; 28 | double myX1 = r1->x + r1->width; 29 | double myY1 = r1->y + r1->height; 30 | 31 | double otherX0 = r2->x; 32 | double otherY0 = r2->y; 33 | double otherX1 = r2->x + r2->width; 34 | double otherY1 = r2->y + r2->height; 35 | 36 | return myX0 <= otherX0 && otherX0 < myX1 && myY0 <= otherY0 && otherY0 < myY1 37 | 38 | && myX0 < otherX1 && otherX1 <= myX1 && myY0 < otherY1 && 39 | otherY1 <= myY1; 40 | } 41 | 42 | static bool 43 | addRect(PuglRects* rects, PuglRect* rect) 44 | { 45 | bool added = false; 46 | for (int i = 0; i < rects->rectsCount; ++i) { 47 | if (doesRectContain(rects->rectsList + i, rect)) { 48 | added = true; 49 | break; 50 | } 51 | } 52 | if (!added) { 53 | for (int i = 0; i < rects->rectsCount;) { 54 | if (doesRectContain(rect, rects->rectsList + i)) { 55 | if (!added) { 56 | rects->rectsList[i] = *rect; 57 | added = true; 58 | ++i; 59 | } else { 60 | memmove(rects->rectsList + i, 61 | rects->rectsList + i + 1, 62 | (rects->rectsCount - (i + 1)) * sizeof(PuglRect)); 63 | rects->rectsCount -= 1; 64 | } 65 | } else { 66 | ++i; 67 | } 68 | } 69 | } 70 | if (!added) { 71 | if (!puglRectsAppend(rects, rect)) { 72 | return false; 73 | } 74 | } 75 | return true; 76 | } 77 | 78 | #endif // PUGL_DETAIL_RECT_H 79 | -------------------------------------------------------------------------------- /pugl-repo/src/stub.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_DETAIL_STUB_H 18 | #define PUGL_DETAIL_STUB_H 19 | 20 | #include "types.h" 21 | 22 | #include "pugl/pugl.h" 23 | 24 | #include 25 | 26 | PUGL_BEGIN_DECLS 27 | 28 | static inline PuglStatus 29 | puglStubConfigure(PuglView* view) 30 | { 31 | (void)view; 32 | return PUGL_SUCCESS; 33 | } 34 | 35 | static inline PuglStatus 36 | puglStubCreate(PuglView* view) 37 | { 38 | (void)view; 39 | return PUGL_SUCCESS; 40 | } 41 | 42 | static inline PuglStatus 43 | puglStubDestroy(PuglView* view) 44 | { 45 | (void)view; 46 | return PUGL_SUCCESS; 47 | } 48 | 49 | static inline PuglStatus 50 | puglStubEnter(PuglView* view, const PuglEventExpose* expose, PuglRects* rects) 51 | { 52 | (void)view; 53 | (void)expose; 54 | (void)rects; 55 | return PUGL_SUCCESS; 56 | } 57 | 58 | static inline PuglStatus 59 | puglStubLeave(PuglView* view, const PuglEventExpose* expose, PuglRects* rects) 60 | { 61 | (void)view; 62 | (void)expose; 63 | (void)rects; 64 | return PUGL_SUCCESS; 65 | } 66 | 67 | static inline void* 68 | puglStubGetContext(PuglView* view) 69 | { 70 | (void)view; 71 | return NULL; 72 | } 73 | 74 | PUGL_END_DECLS 75 | 76 | #endif // PUGL_DETAIL_STUB_H 77 | -------------------------------------------------------------------------------- /pugl-repo/src/types.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_DETAIL_TYPES_H 18 | #define PUGL_DETAIL_TYPES_H 19 | 20 | #include "pugl/pugl.h" 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | // Unused parameter macro to suppresses warnings and make it impossible to use 27 | #if defined(__cplusplus) 28 | # define PUGL_UNUSED(name) 29 | #elif defined(__GNUC__) || defined(__clang__) 30 | # define PUGL_UNUSED(name) name##_unused __attribute__((__unused__)) 31 | #else 32 | # define PUGL_UNUSED(name) name 33 | #endif 34 | 35 | /// Platform-specific world internals 36 | typedef struct PuglWorldInternalsImpl PuglWorldInternals; 37 | 38 | /// Platform-specific view internals 39 | typedef struct PuglInternalsImpl PuglInternals; 40 | 41 | /// View hints 42 | typedef int PuglHints[PUGL_NUM_VIEW_HINTS]; 43 | 44 | /// Blob of arbitrary data 45 | typedef struct { 46 | void* data; ///< Dynamically allocated data 47 | size_t len; ///< Length of data in bytes 48 | } PuglBlob; 49 | 50 | typedef struct { 51 | PuglRect* rectsList; 52 | int rectsCapacity; 53 | int rectsCount; 54 | } PuglRects; 55 | 56 | /// Cross-platform view definition 57 | struct PuglViewImpl { 58 | PuglWorld* world; 59 | const PuglBackend* backend; 60 | PuglInternals* impl; 61 | PuglHandle handle; 62 | PuglEventFunc eventFunc; 63 | char* title; 64 | PuglBlob clipboard; 65 | PuglNativeView parent; 66 | uintptr_t transientParent; 67 | PuglRect frame; 68 | PuglRects rects; 69 | PuglRects rects2; 70 | int backgroundColor; 71 | int reqX; 72 | int reqY; 73 | int reqWidth; 74 | int reqHeight; 75 | PuglEventConfigure lastConfigure; 76 | PuglHints hints; 77 | int minWidth; 78 | int minHeight; 79 | int maxWidth; 80 | int maxHeight; 81 | int minAspectX; 82 | int minAspectY; 83 | int maxAspectX; 84 | int maxAspectY; 85 | bool created; 86 | bool configured; 87 | bool visible; 88 | }; 89 | 90 | /// Cross-platform world definition 91 | struct PuglWorldImpl { 92 | PuglWorldInternals* impl; 93 | PuglWorldHandle handle; 94 | PuglLogFunc logFunc; 95 | char* className; 96 | double startTime; 97 | PuglProcessFunc processFunc; 98 | void* processUserData; 99 | size_t numViews; 100 | PuglView** views; 101 | PuglBlob clipboard; 102 | PuglLogLevel logLevel; 103 | }; 104 | 105 | /// Opaque surface used by graphics backend 106 | typedef void PuglSurface; 107 | 108 | /// Graphics backend interface 109 | struct PuglBackendImpl { 110 | /// Get visual information from display and setup view as necessary 111 | PuglStatus (*configure)(PuglView*); 112 | 113 | /// Create surface and drawing context 114 | PuglStatus (*create)(PuglView*); 115 | 116 | /// Destroy surface and drawing context 117 | PuglStatus (*destroy)(PuglView*); 118 | 119 | /// Enter drawing context, for drawing if expose is non-null 120 | PuglStatus (*enter)(PuglView*, const PuglEventExpose*, PuglRects* rects); 121 | 122 | /// Leave drawing context, after drawing if expose is non-null 123 | PuglStatus (*leave)(PuglView*, const PuglEventExpose*, PuglRects* rects); 124 | 125 | /// Return the puglGetContext() handle for the application, if any 126 | void* (*getContext)(PuglView*); 127 | }; 128 | 129 | static inline void 130 | puglLog(PuglWorld* world, PuglLogLevel level, const char* msg) 131 | { 132 | if (level <= world->logLevel && world->logFunc) { 133 | world->logFunc(world, level, msg); 134 | } 135 | } 136 | 137 | #endif // PUGL_DETAIL_TYPES_H 138 | -------------------------------------------------------------------------------- /pugl-repo/src/win.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_DETAIL_WIN_H 18 | #define PUGL_DETAIL_WIN_H 19 | 20 | #include "implementation.h" 21 | 22 | #include 23 | 24 | #include 25 | 26 | typedef PIXELFORMATDESCRIPTOR PuglWinPFD; 27 | 28 | typedef HRESULT(WINAPI* GetDpi_F)(HMONITOR, int, UINT*, UINT*); 29 | 30 | struct PuglWorldInternalsImpl { 31 | bool initialized; 32 | wchar_t* worldClassName; 33 | wchar_t* windowClassName; 34 | wchar_t* popupClassName; 35 | GetDpi_F getDpiForMonitor; 36 | bool triedDpiForMonitor; 37 | HWND pseudoWin; 38 | double timerFrequency; 39 | double nextProcessTime; 40 | HANDLE processTimer; 41 | }; 42 | 43 | struct PuglInternalsImpl { 44 | PuglWinPFD pfd; 45 | int pfId; 46 | HWND hwnd; 47 | HCURSOR cursor; 48 | HDC hdc; 49 | HRGN updateRegion; 50 | RGNDATA* updateRegionData; 51 | DWORD updateRegionLength; 52 | PuglSurface* surface; 53 | bool flashing; 54 | bool mouseTracked; 55 | bool hasBeginPaint; 56 | bool posRequested; 57 | }; 58 | 59 | static wchar_t* 60 | puglUtf8ToWideChar(const char* const utf8) 61 | { 62 | const int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0); 63 | if (len > 0) { 64 | wchar_t* result = (wchar_t*)calloc((size_t)len, sizeof(wchar_t)); 65 | MultiByteToWideChar(CP_UTF8, 0, utf8, -1, result, len); 66 | return result; 67 | } 68 | 69 | return NULL; 70 | } 71 | 72 | static inline PuglWinPFD 73 | puglWinGetPixelFormatDescriptor(const PuglHints hints) 74 | { 75 | const int rgbBits = (hints[PUGL_RED_BITS] + // 76 | hints[PUGL_GREEN_BITS] + // 77 | hints[PUGL_BLUE_BITS]); 78 | 79 | const DWORD dwFlags = hints[PUGL_DOUBLE_BUFFER] ? PFD_DOUBLEBUFFER : 0u; 80 | 81 | PuglWinPFD pfd; 82 | ZeroMemory(&pfd, sizeof(pfd)); 83 | pfd.nSize = sizeof(pfd); 84 | pfd.nVersion = 1; 85 | pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | dwFlags; 86 | pfd.iPixelType = PFD_TYPE_RGBA; 87 | pfd.cColorBits = (BYTE)rgbBits; 88 | pfd.cRedBits = (BYTE)hints[PUGL_RED_BITS]; 89 | pfd.cGreenBits = (BYTE)hints[PUGL_GREEN_BITS]; 90 | pfd.cBlueBits = (BYTE)hints[PUGL_BLUE_BITS]; 91 | pfd.cAlphaBits = (BYTE)hints[PUGL_ALPHA_BITS]; 92 | pfd.cDepthBits = (BYTE)hints[PUGL_DEPTH_BITS]; 93 | pfd.cStencilBits = (BYTE)hints[PUGL_STENCIL_BITS]; 94 | pfd.iLayerType = PFD_MAIN_PLANE; 95 | return pfd; 96 | } 97 | 98 | static inline unsigned 99 | puglWinGetWindowFlags(const PuglView* const view) 100 | { 101 | const bool resizable = view->hints[PUGL_RESIZABLE]; 102 | const bool isPopup = view->hints[PUGL_IS_POPUP]; 103 | 104 | return (WS_CLIPCHILDREN | WS_CLIPSIBLINGS | 105 | (view->parent 106 | ? WS_CHILD 107 | // : (WS_POPUPWINDOW | WS_CAPTION | WS_MINIMIZEBOX | 108 | : ((isPopup ? (WS_POPUP) 109 | : (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | 110 | WS_MINIMIZEBOX)) | 111 | (resizable ? (WS_SIZEBOX | WS_MAXIMIZEBOX) : 0u)))); 112 | } 113 | 114 | static inline unsigned 115 | puglWinGetWindowExFlags(const PuglView* const view) 116 | { 117 | const bool isPopup = view->hints[PUGL_IS_POPUP]; 118 | return WS_EX_NOINHERITLAYOUT | WS_EX_WINDOWEDGE | 119 | ((!view->parent && !view->transientParent && !isPopup) 120 | ? WS_EX_APPWINDOW 121 | : 0u) | 122 | ((!view->parent && view->transientParent && !isPopup) 123 | ? WS_EX_TOOLWINDOW 124 | : 0u) | 125 | ((!view->parent && view->transientParent && isPopup) ? WS_EX_NOACTIVATE 126 | : 0u); 127 | } 128 | 129 | static inline PuglStatus 130 | puglWinCreateWindow(PuglView* const view, 131 | const char* const title, 132 | HWND* const hwnd, 133 | HDC* const hdc) 134 | { 135 | const wchar_t* className = view->world->impl->windowClassName; 136 | const unsigned winFlags = puglWinGetWindowFlags(view); 137 | const unsigned winExFlags = puglWinGetWindowExFlags(view); 138 | 139 | if (view->hints[PUGL_IS_POPUP]) { 140 | className = view->world->impl->popupClassName; 141 | } 142 | 143 | // The meaning of "parent" depends on the window type (WS_CHILD) 144 | HWND parent; 145 | if (view->parent) { 146 | parent = (HWND)view->parent; 147 | } else if (view->transientParent) { 148 | parent = (HWND)view->transientParent; 149 | } else { 150 | parent = HWND_DESKTOP; 151 | } 152 | 153 | // Calculate total window size to accommodate requested view size 154 | RECT wr = {(long)view->reqX, 155 | (long)view->reqY, 156 | (long)view->reqX + (long)view->reqWidth, 157 | (long)view->reqY + (long)view->reqHeight}; 158 | AdjustWindowRectEx(&wr, winFlags, FALSE, winExFlags); 159 | 160 | wchar_t* titleW = puglUtf8ToWideChar(title); 161 | 162 | // Create window and get drawing context 163 | if (!titleW) { 164 | free(titleW); 165 | return PUGL_FAILURE; 166 | } 167 | int x = CW_USEDEFAULT; 168 | int y = CW_USEDEFAULT; 169 | if (view->impl->posRequested) { 170 | x = view->reqX; 171 | y = view->reqY; 172 | view->impl->posRequested = false; 173 | } 174 | if (!(*hwnd = CreateWindowExW(winExFlags, 175 | className, 176 | titleW, 177 | winFlags, 178 | x, 179 | y, 180 | wr.right - wr.left, 181 | wr.bottom - wr.top, 182 | parent, 183 | NULL, 184 | NULL, 185 | NULL))) { 186 | free(titleW); 187 | return PUGL_REALIZE_FAILED; 188 | } else if (!(*hdc = GetDC(*hwnd))) { 189 | DestroyWindow(*hwnd); 190 | *hwnd = NULL; 191 | free(titleW); 192 | return PUGL_REALIZE_FAILED; 193 | } 194 | 195 | free(titleW); 196 | return PUGL_SUCCESS; 197 | } 198 | 199 | PUGL_API_PRIVATE PuglStatus 200 | puglWinStubConfigure(PuglView* view); 201 | 202 | PUGL_API_PRIVATE PuglStatus 203 | puglWinStubEnter(PuglView* view, const PuglEventExpose* expose, PuglRects* rects); 204 | 205 | PUGL_API_PRIVATE PuglStatus 206 | puglWinStubLeave(PuglView* view, const PuglEventExpose* expose, PuglRects* rects); 207 | 208 | #endif // PUGL_DETAIL_WIN_H 209 | -------------------------------------------------------------------------------- /pugl-repo/src/win_cairo.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stub.h" 18 | #include "types.h" 19 | #include "win.h" 20 | 21 | #include "pugl/cairo.h" 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | 28 | typedef struct { 29 | cairo_surface_t* crSurface; 30 | cairo_t* crContext; 31 | bool hasBeginPaint; 32 | } PuglWinCairoSurface; 33 | 34 | static void 35 | puglWinCairoClose(PuglView* view) 36 | { 37 | PuglInternals* const impl = view->impl; 38 | PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; 39 | 40 | if (surface) { 41 | if (surface->crContext) { 42 | cairo_destroy(surface->crContext); 43 | surface->crContext = NULL; 44 | } 45 | if (surface->crSurface) { 46 | cairo_surface_destroy(surface->crSurface); 47 | surface->crSurface = NULL; 48 | } 49 | } 50 | } 51 | 52 | static PuglStatus 53 | puglWinCairoOpen(PuglView* view) 54 | { 55 | PuglInternals* const impl = view->impl; 56 | PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; 57 | 58 | puglWinCairoClose(view); // just to be sure 59 | 60 | surface->crSurface = cairo_win32_surface_create(impl->hdc); 61 | 62 | if (surface->crSurface) { 63 | surface->crContext = cairo_create(surface->crSurface); 64 | } 65 | if (surface->crContext) { 66 | return PUGL_SUCCESS; 67 | } else { 68 | puglWinCairoClose(view); 69 | return PUGL_CREATE_CONTEXT_FAILED; 70 | } 71 | } 72 | 73 | static PuglStatus 74 | puglWinCairoCreate(PuglView* view) 75 | { 76 | PuglInternals* const impl = view->impl; 77 | 78 | impl->surface = calloc(1, sizeof(PuglWinCairoSurface)); 79 | 80 | return PUGL_SUCCESS; 81 | } 82 | 83 | static PuglStatus 84 | puglWinCairoDestroy(PuglView* view) 85 | { 86 | PuglInternals* const impl = view->impl; 87 | PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; 88 | 89 | puglWinCairoClose(view); 90 | free(surface); 91 | impl->surface = NULL; 92 | 93 | return PUGL_SUCCESS; 94 | } 95 | 96 | static PuglStatus 97 | puglWinCairoEnter(PuglView* view, 98 | const PuglEventExpose* expose, 99 | PuglRects* rects) 100 | { 101 | PuglInternals* const impl = view->impl; 102 | PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; 103 | 104 | if (!expose) { 105 | return PUGL_SUCCESS; 106 | } else { 107 | if (!surface->hasBeginPaint) { 108 | PAINTSTRUCT ps; 109 | BeginPaint(view->impl->hwnd, &ps); 110 | surface->hasBeginPaint = true; 111 | } 112 | if (!surface->crContext) { 113 | puglWinCairoOpen(view); 114 | } else { 115 | cairo_reset_clip(surface->crContext); 116 | } 117 | if (rects && rects->rectsCount > 0) { 118 | for (int i = 0; i < rects->rectsCount; ++i) { 119 | const PuglRect* r = rects->rectsList + i; 120 | cairo_rectangle(surface->crContext, r->x, r->y, r->width, r->height); 121 | } 122 | } else { 123 | cairo_rectangle(surface->crContext, 124 | expose->x, 125 | expose->y, 126 | expose->width, 127 | expose->height); 128 | } 129 | cairo_clip(surface->crContext); 130 | cairo_push_group_with_content(surface->crContext, CAIRO_CONTENT_COLOR); 131 | } 132 | return PUGL_SUCCESS; 133 | } 134 | 135 | static PuglStatus 136 | puglWinCairoLeave(PuglView* view, 137 | const PuglEventExpose* expose, 138 | PuglRects* rects) 139 | { 140 | PuglInternals* const impl = view->impl; 141 | PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; 142 | 143 | if (expose && surface->crContext) { 144 | cairo_pop_group_to_source(surface->crContext); 145 | cairo_paint(surface->crContext); 146 | if (surface->hasBeginPaint) { 147 | PAINTSTRUCT ps; 148 | EndPaint(impl->hwnd, &ps); 149 | surface->hasBeginPaint = false; 150 | } 151 | } 152 | puglWinCairoClose(view); 153 | 154 | return PUGL_SUCCESS; 155 | } 156 | 157 | static void* 158 | puglWinCairoGetContext(PuglView* view) 159 | { 160 | PuglInternals* const impl = view->impl; 161 | PuglWinCairoSurface* const surface = (PuglWinCairoSurface*)impl->surface; 162 | 163 | return surface->crContext; 164 | } 165 | 166 | const PuglBackend* 167 | puglCairoBackend() 168 | { 169 | static const PuglBackend backend = {puglWinStubConfigure, 170 | puglWinCairoCreate, 171 | puglWinCairoDestroy, 172 | puglWinCairoEnter, 173 | puglWinCairoLeave, 174 | puglWinCairoGetContext}; 175 | 176 | return &backend; 177 | } 178 | -------------------------------------------------------------------------------- /pugl-repo/src/win_stub.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stub.h" 18 | #include "types.h" 19 | #include "win.h" 20 | 21 | #include "pugl/stub.h" 22 | 23 | PuglStatus 24 | puglWinStubConfigure(PuglView* view) 25 | { 26 | PuglInternals* const impl = view->impl; 27 | PuglStatus st = PUGL_SUCCESS; 28 | 29 | if ((st = puglWinCreateWindow(view, "Pugl", &impl->hwnd, &impl->hdc))) { 30 | return st; 31 | } 32 | 33 | impl->pfd = puglWinGetPixelFormatDescriptor(view->hints); 34 | impl->pfId = ChoosePixelFormat(impl->hdc, &impl->pfd); 35 | 36 | if (!SetPixelFormat(impl->hdc, impl->pfId, &impl->pfd)) { 37 | ReleaseDC(impl->hwnd, impl->hdc); 38 | DestroyWindow(impl->hwnd); 39 | impl->hwnd = NULL; 40 | impl->hdc = NULL; 41 | return PUGL_SET_FORMAT_FAILED; 42 | } 43 | 44 | return PUGL_SUCCESS; 45 | } 46 | 47 | PuglStatus 48 | puglWinStubEnter(PuglView* view, 49 | const PuglEventExpose* expose, 50 | PuglRects* rects) 51 | { 52 | if (expose) { 53 | PAINTSTRUCT ps; 54 | BeginPaint(view->impl->hwnd, &ps); 55 | view->impl->hasBeginPaint = true; 56 | } 57 | 58 | return PUGL_SUCCESS; 59 | } 60 | 61 | PuglStatus 62 | puglWinStubLeave(PuglView* view, 63 | const PuglEventExpose* expose, 64 | PuglRects* rects) 65 | { 66 | if (expose && view->impl->hasBeginPaint) { 67 | PAINTSTRUCT ps; 68 | EndPaint(view->impl->hwnd, &ps); 69 | view->impl->hasBeginPaint = false; 70 | } 71 | 72 | return PUGL_SUCCESS; 73 | } 74 | 75 | const PuglBackend* 76 | puglStubBackend(void) 77 | { 78 | static const PuglBackend backend = {puglWinStubConfigure, 79 | puglStubCreate, 80 | puglStubDestroy, 81 | puglWinStubEnter, 82 | puglWinStubLeave, 83 | puglStubGetContext}; 84 | 85 | return &backend; 86 | } 87 | -------------------------------------------------------------------------------- /pugl-repo/src/win_vulkan.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #define VK_NO_PROTOTYPES 1 18 | 19 | #include "stub.h" 20 | #include "types.h" 21 | #include "win.h" 22 | 23 | #include "pugl/stub.h" 24 | #include "pugl/vulkan.h" 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | struct PuglVulkanLoaderImpl { 32 | HMODULE libvulkan; 33 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; 34 | PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; 35 | }; 36 | 37 | PuglVulkanLoader* 38 | puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) 39 | { 40 | PuglVulkanLoader* loader = 41 | (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); 42 | if (!loader) { 43 | return NULL; 44 | } 45 | 46 | if (!(loader->libvulkan = LoadLibrary("vulkan-1.dll"))) { 47 | free(loader); 48 | return NULL; 49 | } 50 | 51 | loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)GetProcAddress( 52 | loader->libvulkan, "vkGetInstanceProcAddr"); 53 | 54 | loader->vkGetDeviceProcAddr = (PFN_vkGetDeviceProcAddr)GetProcAddress( 55 | loader->libvulkan, "vkGetDeviceProcAddr"); 56 | 57 | return loader; 58 | } 59 | 60 | void 61 | puglFreeVulkanLoader(PuglVulkanLoader* loader) 62 | { 63 | if (loader) { 64 | FreeLibrary(loader->libvulkan); 65 | free(loader); 66 | } 67 | } 68 | 69 | PFN_vkGetInstanceProcAddr 70 | puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) 71 | { 72 | return loader->vkGetInstanceProcAddr; 73 | } 74 | 75 | PFN_vkGetDeviceProcAddr 76 | puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) 77 | { 78 | return loader->vkGetDeviceProcAddr; 79 | } 80 | 81 | const PuglBackend* 82 | puglVulkanBackend() 83 | { 84 | static const PuglBackend backend = {puglWinStubConfigure, 85 | puglStubCreate, 86 | puglStubDestroy, 87 | puglWinStubEnter, 88 | puglWinStubLeave, 89 | puglStubGetContext}; 90 | 91 | return &backend; 92 | } 93 | 94 | const char* const* 95 | puglGetInstanceExtensions(uint32_t* const count) 96 | { 97 | static const char* const extensions[] = {"VK_KHR_surface", 98 | "VK_KHR_win32_surface"}; 99 | 100 | *count = 2; 101 | return extensions; 102 | } 103 | 104 | VkResult 105 | puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, 106 | PuglView* const view, 107 | VkInstance instance, 108 | const VkAllocationCallbacks* const pAllocator, 109 | VkSurfaceKHR* const pSurface) 110 | { 111 | PuglInternals* const impl = view->impl; 112 | 113 | PFN_vkCreateWin32SurfaceKHR vkCreateWin32SurfaceKHR = 114 | (PFN_vkCreateWin32SurfaceKHR)vkGetInstanceProcAddr( 115 | instance, "vkCreateWin32SurfaceKHR"); 116 | 117 | const VkWin32SurfaceCreateInfoKHR createInfo = { 118 | VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR, 119 | NULL, 120 | 0, 121 | GetModuleHandle(NULL), 122 | impl->hwnd, 123 | }; 124 | 125 | return vkCreateWin32SurfaceKHR(instance, &createInfo, pAllocator, pSurface); 126 | } 127 | -------------------------------------------------------------------------------- /pugl-repo/src/x11.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #ifndef PUGL_DETAIL_X11_H 18 | #define PUGL_DETAIL_X11_H 19 | 20 | #include "types.h" 21 | 22 | #include "pugl/pugl.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | typedef struct { 33 | Atom CLIPBOARD; 34 | Atom TARGETS; 35 | Atom INCR; 36 | Atom UTF8_STRING; 37 | Atom WM_PROTOCOLS; 38 | Atom WM_DELETE_WINDOW; 39 | Atom PUGL_CLIENT_MSG; 40 | Atom NET_WM_NAME; 41 | Atom NET_WM_STATE; 42 | Atom NET_WM_STATE_DEMANDS_ATTENTION; 43 | } PuglX11Atoms; 44 | 45 | typedef struct PuglX11IncrTarget { 46 | Window win; 47 | Atom prop; 48 | Atom type; 49 | size_t pos; 50 | } PuglX11IncrTarget; 51 | 52 | struct PuglWorldInternalsImpl { 53 | Display* display; 54 | PuglX11Atoms atoms; 55 | XIM xim; 56 | Window pseudoWin; 57 | PuglX11IncrTarget incrTarget; 58 | unsigned long syncSerial; 59 | int syncState; 60 | double nextProcessTime; 61 | bool needsProcessing; 62 | int awake_fds[2]; 63 | bool dispatchingEvents; 64 | int shiftKeyStates; 65 | int controlKeyStates; 66 | int altKeyStates; 67 | int altgrKeyStates; 68 | int superKeyStates; 69 | }; 70 | 71 | struct PuglInternalsImpl { 72 | Display* display; 73 | XVisualInfo* vi; 74 | Colormap colormap; 75 | Window win; 76 | XIC xic; 77 | PuglSurface* surface; 78 | PuglEvent pendingConfigure; 79 | PuglEvent pendingExpose; 80 | int screen; 81 | unsigned cursorShape; 82 | int clipboardRequested; 83 | bool incrClipboardRequest; 84 | bool displayed; 85 | bool posRequested; 86 | bool hadConfigure; 87 | }; 88 | 89 | PUGL_API_PRIVATE 90 | PuglStatus 91 | puglX11StubConfigure(PuglView* view); 92 | 93 | #endif // PUGL_DETAIL_X11_H 94 | -------------------------------------------------------------------------------- /pugl-repo/src/x11_cairo.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "types.h" 18 | #include "x11.h" 19 | 20 | #include "pugl/cairo.h" 21 | #include "pugl/pugl.h" 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #include 28 | 29 | typedef struct { 30 | cairo_surface_t* crSurface; 31 | cairo_t* crContext; 32 | } PuglX11CairoSurface; 33 | 34 | static void 35 | puglX11CairoClose(PuglView* view) 36 | { 37 | PuglInternals* const impl = view->impl; 38 | PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; 39 | 40 | if (surface) { 41 | if (surface->crContext) { 42 | cairo_destroy(surface->crContext); 43 | surface->crContext = NULL; 44 | } 45 | if (surface->crSurface) { 46 | cairo_surface_destroy(surface->crSurface); 47 | surface->crSurface = NULL; 48 | } 49 | } 50 | } 51 | 52 | static PuglStatus 53 | puglX11CairoOpen(PuglView* view) 54 | { 55 | PuglInternals* const impl = view->impl; 56 | PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; 57 | 58 | puglX11CairoClose(view); // just to be sure 59 | 60 | surface->crSurface = cairo_xlib_surface_create(impl->display, 61 | impl->win, 62 | impl->vi->visual, 63 | view->frame.width, 64 | view->frame.height); 65 | 66 | if (surface->crSurface) { 67 | surface->crContext = cairo_create(surface->crSurface); 68 | } 69 | if (surface->crContext) { 70 | return PUGL_SUCCESS; 71 | } else { 72 | puglX11CairoClose(view); 73 | return PUGL_CREATE_CONTEXT_FAILED; 74 | } 75 | } 76 | 77 | static PuglStatus 78 | puglX11CairoCreate(PuglView* view) 79 | { 80 | PuglInternals* const impl = view->impl; 81 | 82 | impl->surface = calloc(1, sizeof(PuglX11CairoSurface)); 83 | 84 | return PUGL_SUCCESS; 85 | } 86 | 87 | static PuglStatus 88 | puglX11CairoDestroy(PuglView* view) 89 | { 90 | PuglInternals* const impl = view->impl; 91 | PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; 92 | 93 | puglX11CairoClose(view); 94 | free(surface); 95 | impl->surface = NULL; 96 | return PUGL_SUCCESS; 97 | } 98 | 99 | static PuglStatus 100 | puglX11CairoEnter(PuglView* view, 101 | const PuglEventExpose* expose, 102 | PuglRects* rects) 103 | { 104 | PuglInternals* const impl = view->impl; 105 | PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; 106 | if (expose) { 107 | if (!surface->crContext) { 108 | puglX11CairoOpen(view); 109 | } else { 110 | cairo_reset_clip(surface->crContext); 111 | } 112 | if (rects && rects->rectsCount > 0) { 113 | for (int i = 0; i < rects->rectsCount; ++i) { 114 | const PuglRect* r = rects->rectsList + i; 115 | cairo_rectangle(surface->crContext, r->x, r->y, r->width, r->height); 116 | } 117 | } else { 118 | cairo_rectangle(surface->crContext, 119 | expose->x, 120 | expose->y, 121 | expose->width, 122 | expose->height); 123 | } 124 | cairo_clip(surface->crContext); 125 | cairo_push_group_with_content(surface->crContext, CAIRO_CONTENT_COLOR); 126 | } 127 | return PUGL_SUCCESS; 128 | } 129 | 130 | static PuglStatus 131 | puglX11CairoLeave(PuglView* view, 132 | const PuglEventExpose* expose, 133 | PuglRects* PUGL_UNUSED(rects)) 134 | { 135 | PuglInternals* const impl = view->impl; 136 | PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; 137 | 138 | if (expose && surface->crContext) { 139 | cairo_pop_group_to_source(surface->crContext); 140 | cairo_paint(surface->crContext); 141 | } 142 | puglX11CairoClose(view); 143 | 144 | return PUGL_SUCCESS; 145 | } 146 | 147 | static void* 148 | puglX11CairoGetContext(PuglView* view) 149 | { 150 | PuglInternals* const impl = view->impl; 151 | PuglX11CairoSurface* const surface = (PuglX11CairoSurface*)impl->surface; 152 | 153 | return surface->crContext; 154 | } 155 | 156 | void* 157 | puglCairoBackendGetNativeWorld(PuglWorld* world) 158 | { 159 | return world->impl->display; 160 | } 161 | 162 | const PuglBackend* 163 | puglCairoBackend(void) 164 | { 165 | static const PuglBackend backend = {puglX11StubConfigure, 166 | puglX11CairoCreate, 167 | puglX11CairoDestroy, 168 | puglX11CairoEnter, 169 | puglX11CairoLeave, 170 | puglX11CairoGetContext}; 171 | 172 | return &backend; 173 | } 174 | -------------------------------------------------------------------------------- /pugl-repo/src/x11_clip.h: -------------------------------------------------------------------------------- 1 | static void 2 | handleSelectionRequestForOwner(Display* display, 3 | const PuglX11Atoms* const atoms, 4 | const XSelectionRequestEvent* request, 5 | PuglBlob* clipboard, 6 | PuglX11IncrTarget* incrTarget) 7 | { 8 | XSelectionEvent note = {0}; 9 | note.type = SelectionNotify; 10 | note.requestor = request->requestor; 11 | note.selection = request->selection; 12 | note.target = request->target; 13 | note.time = request->time; 14 | 15 | if (request->target == atoms->TARGETS) 16 | { 17 | Atom myTargets[] = { atoms->UTF8_STRING, XA_STRING }; 18 | static const int XSERVER_ATOM_BITSIZE = 32; // sizeof(Atom) == 64 on 64bit systems, but this value must be 32 19 | note.property = request->property; 20 | XChangeProperty(display, note.requestor, note.property, 21 | XA_ATOM, XSERVER_ATOM_BITSIZE, 0, (unsigned char*)myTargets, 22 | sizeof(myTargets)/sizeof(myTargets[0])); 23 | } 24 | else 25 | { 26 | size_t len = clipboard->len; 27 | const void* data = clipboard->data; 28 | if (data && request->selection == atoms->CLIPBOARD 29 | && ( request->target == atoms->UTF8_STRING 30 | || request->target == XA_STRING)) 31 | { 32 | note.property = request->property; 33 | 34 | if (len < 200*1000) { 35 | XChangeProperty(display, note.requestor, 36 | note.property, note.target, 8, PropModeReplace, 37 | (const uint8_t*)data, len); 38 | } else { 39 | 40 | XChangeProperty(display, note.requestor, 41 | note.property, atoms->INCR, 32, PropModeReplace, 42 | 0, 0); 43 | if (incrTarget->win) { 44 | XSelectInput(display, incrTarget->win, 0); 45 | } 46 | incrTarget->win = note.requestor; 47 | incrTarget->prop = note.property; 48 | incrTarget->type = note.target; 49 | incrTarget->pos = 0; 50 | XSelectInput(display, incrTarget->win, PropertyChangeMask); 51 | } 52 | } else { 53 | note.property = None; 54 | } 55 | } 56 | 57 | XSendEvent(display, note.requestor, True, 0, (XEvent*)¬e); 58 | } 59 | 60 | static void 61 | handlePropertyNotifyForOwner(Display* display, 62 | PuglBlob* clipboard, 63 | PuglX11IncrTarget* incrTarget) 64 | { 65 | if (incrTarget->pos < clipboard->len) { 66 | // send next part 67 | size_t len = clipboard->len - incrTarget->pos; 68 | if (len > 200*1000) len = 200*1000; 69 | XChangeProperty(display, incrTarget->win, incrTarget->prop, 70 | incrTarget->type, 8, PropModeReplace, 71 | (const uint8_t*)(clipboard->data + incrTarget->pos), len); 72 | incrTarget->pos += len; 73 | } else { 74 | // finished 75 | XChangeProperty(display, incrTarget->win, incrTarget->prop, 76 | incrTarget->type, 8, PropModeReplace, 77 | 0, 0); 78 | XSelectInput(display, incrTarget->win, 0); 79 | incrTarget->win = 0; 80 | } 81 | } 82 | 83 | static void 84 | handleSelectionNotifyForRequestor(PuglView* view, XSelectionEvent* event) 85 | { 86 | const PuglX11Atoms* const atoms = &view->world->impl->atoms; 87 | PuglInternals* const impl = view->impl; 88 | 89 | puglSetBlob(&view->clipboard, NULL, 0); 90 | impl->incrClipboardRequest = false; 91 | 92 | if (event->selection == atoms->CLIPBOARD && 93 | (event->target == atoms->UTF8_STRING || event->target == XA_STRING) && 94 | event->property == XA_PRIMARY) 95 | { 96 | uint8_t* str = NULL; 97 | Atom type = 0; 98 | int fmt = 0; 99 | unsigned long len = 0; 100 | unsigned long left = 0; 101 | XGetWindowProperty(impl->display, impl->win, XA_PRIMARY, 102 | 0, 0x1FFFFFFF, True, AnyPropertyType, 103 | &type, &fmt, &len, &left, &str); 104 | 105 | if (str && fmt == 8 && (type == atoms->UTF8_STRING || type == XA_STRING) && left == 0) { 106 | puglSetBlob(&view->clipboard, str, len); 107 | impl->clipboardRequested = 0; // finished 108 | } 109 | else if (type == atoms->INCR) { 110 | // multi part 111 | impl->incrClipboardRequest = true; 112 | } else { 113 | impl->clipboardRequested = 0; // finished 114 | } 115 | XFree(str); 116 | 117 | } 118 | else { 119 | // sender does not support format 120 | if (impl->clipboardRequested == 1) { 121 | impl->clipboardRequested = 2; // second try 122 | XConvertSelection(impl->display, 123 | atoms->CLIPBOARD, 124 | XA_STRING, 125 | XA_PRIMARY, 126 | impl->win, 127 | CurrentTime); 128 | } else { 129 | impl->clipboardRequested = 0; 130 | } 131 | } 132 | if (!impl->clipboardRequested) { 133 | // finished 134 | impl->clipboardRequested = 0; 135 | PuglEvent event; 136 | event.received.type = PUGL_DATA_RECEIVED; 137 | event.received.flags = 0; 138 | event.received.data = view->clipboard.data; 139 | event.received.len = view->clipboard.len; 140 | view->clipboard.data = NULL; 141 | view->clipboard.len = 0; 142 | puglDispatchEvent(view, &event); 143 | } 144 | 145 | } 146 | 147 | static void 148 | handlePropertyNotifyForRequestor(PuglView* view) 149 | { 150 | const PuglX11Atoms* const atoms = &view->world->impl->atoms; 151 | PuglInternals* const impl = view->impl; 152 | 153 | uint8_t* str = NULL; 154 | Atom type = 0; 155 | int fmt = 0; 156 | unsigned long len = 0; 157 | unsigned long left = 0; 158 | XGetWindowProperty(impl->display, impl->win, XA_PRIMARY, 159 | 0, 0x1FFFFFFF, True, AnyPropertyType, 160 | &type, &fmt, &len, &left, &str); 161 | 162 | if (str && fmt == 8 && (type == atoms->UTF8_STRING || type == XA_STRING) && left == 0) { 163 | if (len > 0) { 164 | void* newPtr = realloc(view->clipboard.data, view->clipboard.len + len); 165 | if (newPtr) { 166 | memcpy(newPtr + view->clipboard.len, str, len); 167 | view->clipboard.data = newPtr; 168 | view->clipboard.len += len; 169 | } 170 | } else { 171 | impl->incrClipboardRequest = false; 172 | impl->clipboardRequested = 0; 173 | PuglEvent event; 174 | event.received.type = PUGL_DATA_RECEIVED; 175 | event.received.flags = 0; 176 | event.received.data = view->clipboard.data; 177 | event.received.len = view->clipboard.len; 178 | view->clipboard.data = NULL; 179 | view->clipboard.len = 0; 180 | puglDispatchEvent(view, &event); 181 | } 182 | } 183 | XFree(str); 184 | } 185 | -------------------------------------------------------------------------------- /pugl-repo/src/x11_gl.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "stub.h" 18 | #include "types.h" 19 | #include "x11.h" 20 | 21 | #include "pugl/gl.h" 22 | #include "pugl/pugl.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | typedef struct { 33 | GLXFBConfig fb_config; 34 | GLXContext ctx; 35 | } PuglX11GlSurface; 36 | 37 | static int 38 | puglX11GlHintValue(const int value) 39 | { 40 | return value == PUGL_DONT_CARE ? (int)GLX_DONT_CARE : value; 41 | } 42 | 43 | static int 44 | puglX11GlGetAttrib(Display* const display, 45 | GLXFBConfig fb_config, 46 | const int attrib) 47 | { 48 | int value = 0; 49 | glXGetFBConfigAttrib(display, fb_config, attrib, &value); 50 | return value; 51 | } 52 | 53 | static PuglStatus 54 | puglX11GlConfigure(PuglView* view) 55 | { 56 | PuglInternals* const impl = view->impl; 57 | const int screen = impl->screen; 58 | Display* const display = impl->display; 59 | 60 | PuglX11GlSurface* const surface = 61 | (PuglX11GlSurface*)calloc(1, sizeof(PuglX11GlSurface)); 62 | impl->surface = surface; 63 | 64 | // clang-format off 65 | const int attrs[] = {GLX_X_RENDERABLE, True, 66 | GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, 67 | GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 68 | GLX_RENDER_TYPE, GLX_RGBA_BIT, 69 | GLX_SAMPLES, puglX11GlHintValue(view->hints[PUGL_SAMPLES]), 70 | GLX_RED_SIZE, puglX11GlHintValue(view->hints[PUGL_RED_BITS]), 71 | GLX_GREEN_SIZE, puglX11GlHintValue(view->hints[PUGL_GREEN_BITS]), 72 | GLX_BLUE_SIZE, puglX11GlHintValue(view->hints[PUGL_BLUE_BITS]), 73 | GLX_ALPHA_SIZE, puglX11GlHintValue(view->hints[PUGL_ALPHA_BITS]), 74 | GLX_DEPTH_SIZE, puglX11GlHintValue(view->hints[PUGL_DEPTH_BITS]), 75 | GLX_STENCIL_SIZE, puglX11GlHintValue(view->hints[PUGL_STENCIL_BITS]), 76 | GLX_DOUBLEBUFFER, puglX11GlHintValue(view->hints[PUGL_DOUBLE_BUFFER]), 77 | None}; 78 | // clang-format on 79 | 80 | int n_fbc = 0; 81 | GLXFBConfig* fbc = glXChooseFBConfig(display, screen, attrs, &n_fbc); 82 | if (n_fbc <= 0) { 83 | return PUGL_CREATE_CONTEXT_FAILED; 84 | } 85 | 86 | surface->fb_config = fbc[0]; 87 | impl->vi = glXGetVisualFromFBConfig(impl->display, fbc[0]); 88 | 89 | // clang-format off 90 | view->hints[PUGL_RED_BITS] = puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE); 91 | view->hints[PUGL_GREEN_BITS] = puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE); 92 | view->hints[PUGL_BLUE_BITS] = puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE); 93 | view->hints[PUGL_ALPHA_BITS] = puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE); 94 | view->hints[PUGL_DEPTH_BITS] = puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE); 95 | view->hints[PUGL_STENCIL_BITS] = puglX11GlGetAttrib(display, fbc[0], GLX_STENCIL_SIZE); 96 | view->hints[PUGL_SAMPLES] = puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES); 97 | view->hints[PUGL_DOUBLE_BUFFER] = puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER); 98 | // clang-format on 99 | 100 | char msg[256]; 101 | 102 | snprintf( 103 | msg, 104 | sizeof(msg), 105 | "Using visual 0x%lX: R=%d G=%d B=%d A=%d D=%d ST=%d DOUBLE=%d SAMPLES=%d\n", 106 | impl->vi->visualid, 107 | puglX11GlGetAttrib(display, fbc[0], GLX_RED_SIZE), 108 | puglX11GlGetAttrib(display, fbc[0], GLX_GREEN_SIZE), 109 | puglX11GlGetAttrib(display, fbc[0], GLX_BLUE_SIZE), 110 | puglX11GlGetAttrib(display, fbc[0], GLX_ALPHA_SIZE), 111 | puglX11GlGetAttrib(display, fbc[0], GLX_DEPTH_SIZE), 112 | puglX11GlGetAttrib(display, fbc[0], GLX_STENCIL_SIZE), 113 | puglX11GlGetAttrib(display, fbc[0], GLX_DOUBLEBUFFER), 114 | puglX11GlGetAttrib(display, fbc[0], GLX_SAMPLES)); 115 | 116 | puglLog(view->world, PUGL_LOG_LEVEL_INFO, msg); 117 | 118 | XFree(fbc); 119 | 120 | return PUGL_SUCCESS; 121 | } 122 | 123 | static PuglStatus 124 | puglX11GlEnter(PuglView* view, 125 | const PuglEventExpose* PUGL_UNUSED(expose), 126 | PuglRects* PUGL_UNUSED(rects)) 127 | { 128 | PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; 129 | glXMakeCurrent(view->impl->display, view->impl->win, surface->ctx); 130 | return PUGL_SUCCESS; 131 | } 132 | 133 | static PuglStatus 134 | puglX11GlLeave(PuglView* view, 135 | const PuglEventExpose* expose, 136 | PuglRects* PUGL_UNUSED(rects)) 137 | { 138 | if (expose && view->hints[PUGL_DOUBLE_BUFFER]) { 139 | glXSwapBuffers(view->impl->display, view->impl->win); 140 | } 141 | 142 | glXMakeCurrent(view->impl->display, None, NULL); 143 | 144 | return PUGL_SUCCESS; 145 | } 146 | 147 | static PuglStatus 148 | puglX11GlCreate(PuglView* view) 149 | { 150 | PuglInternals* const impl = view->impl; 151 | PuglX11GlSurface* const surface = (PuglX11GlSurface*)impl->surface; 152 | Display* const display = impl->display; 153 | GLXFBConfig fb_config = surface->fb_config; 154 | 155 | // clang-format off 156 | const int ctx_attrs[] = { 157 | GLX_CONTEXT_MAJOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MAJOR], 158 | GLX_CONTEXT_MINOR_VERSION_ARB, view->hints[PUGL_CONTEXT_VERSION_MINOR], 159 | GLX_CONTEXT_FLAGS_ARB, (view->hints[PUGL_USE_DEBUG_CONTEXT] 160 | ? GLX_CONTEXT_DEBUG_BIT_ARB 161 | : 0), 162 | GLX_CONTEXT_PROFILE_MASK_ARB, (view->hints[PUGL_USE_COMPAT_PROFILE] 163 | ? GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 164 | : GLX_CONTEXT_CORE_PROFILE_BIT_ARB), 165 | 0}; 166 | // clang-format on 167 | 168 | PFNGLXCREATECONTEXTATTRIBSARBPROC create_context = 169 | (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress( 170 | (const uint8_t*)"glXCreateContextAttribsARB"); 171 | 172 | PFNGLXSWAPINTERVALEXTPROC glXSwapIntervalEXT = 173 | (PFNGLXSWAPINTERVALEXTPROC)glXGetProcAddress( 174 | (const uint8_t*)"glXSwapIntervalEXT"); 175 | 176 | surface->ctx = create_context(display, fb_config, 0, True, ctx_attrs); 177 | if (!surface->ctx) { 178 | surface->ctx = 179 | glXCreateNewContext(display, fb_config, GLX_RGBA_TYPE, 0, True); 180 | } 181 | 182 | if (!surface->ctx) { 183 | return PUGL_CREATE_CONTEXT_FAILED; 184 | } 185 | 186 | const int swapInterval = view->hints[PUGL_SWAP_INTERVAL]; 187 | if (glXSwapIntervalEXT && swapInterval != PUGL_DONT_CARE) { 188 | puglX11GlEnter(view, NULL, NULL); 189 | glXSwapIntervalEXT(display, impl->win, swapInterval); 190 | puglX11GlLeave(view, NULL, NULL); 191 | } 192 | 193 | glXGetConfig(impl->display, 194 | impl->vi, 195 | GLX_DOUBLEBUFFER, 196 | &view->hints[PUGL_DOUBLE_BUFFER]); 197 | 198 | glXQueryDrawable(display, 199 | impl->win, 200 | GLX_SWAP_INTERVAL_EXT, 201 | (unsigned int*)&view->hints[PUGL_SWAP_INTERVAL]); 202 | 203 | return PUGL_SUCCESS; 204 | } 205 | 206 | static PuglStatus 207 | puglX11GlDestroy(PuglView* view) 208 | { 209 | PuglX11GlSurface* surface = (PuglX11GlSurface*)view->impl->surface; 210 | if (surface) { 211 | glXDestroyContext(view->impl->display, surface->ctx); 212 | free(surface); 213 | view->impl->surface = NULL; 214 | } 215 | return PUGL_SUCCESS; 216 | } 217 | 218 | PuglGlFunc 219 | puglGetProcAddress(const char* name) 220 | { 221 | return glXGetProcAddress((const uint8_t*)name); 222 | } 223 | 224 | PuglStatus 225 | puglEnterContext(PuglView* view) 226 | { 227 | return view->backend->enter(view, NULL, NULL); 228 | } 229 | 230 | PuglStatus 231 | puglLeaveContext(PuglView* view) 232 | { 233 | return view->backend->leave(view, NULL, NULL); 234 | } 235 | 236 | const PuglBackend* 237 | puglGlBackend(void) 238 | { 239 | static const PuglBackend backend = {puglX11GlConfigure, 240 | puglX11GlCreate, 241 | puglX11GlDestroy, 242 | puglX11GlEnter, 243 | puglX11GlLeave, 244 | puglStubGetContext}; 245 | 246 | return &backend; 247 | } 248 | -------------------------------------------------------------------------------- /pugl-repo/src/x11_stub.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #include "pugl/stub.h" 18 | 19 | #include "stub.h" 20 | #include "types.h" 21 | #include "x11.h" 22 | 23 | #include "pugl/pugl.h" 24 | 25 | #include 26 | 27 | PuglStatus 28 | puglX11StubConfigure(PuglView* view) 29 | { 30 | PuglInternals* const impl = view->impl; 31 | XVisualInfo pat = {0}; 32 | int n = 0; 33 | 34 | pat.screen = impl->screen; 35 | impl->vi = XGetVisualInfo(impl->display, VisualScreenMask, &pat, &n); 36 | 37 | view->hints[PUGL_RED_BITS] = impl->vi->bits_per_rgb; 38 | view->hints[PUGL_GREEN_BITS] = impl->vi->bits_per_rgb; 39 | view->hints[PUGL_BLUE_BITS] = impl->vi->bits_per_rgb; 40 | view->hints[PUGL_ALPHA_BITS] = 0; 41 | 42 | return PUGL_SUCCESS; 43 | } 44 | 45 | const PuglBackend* 46 | puglStubBackend(void) 47 | { 48 | static const PuglBackend backend = { 49 | puglX11StubConfigure, 50 | puglStubCreate, 51 | puglStubDestroy, 52 | puglStubEnter, 53 | puglStubLeave, 54 | puglStubGetContext, 55 | }; 56 | 57 | return &backend; 58 | } 59 | -------------------------------------------------------------------------------- /pugl-repo/src/x11_vulkan.c: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2012-2020 David Robillard 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | */ 16 | 17 | #define VK_NO_PROTOTYPES 1 18 | 19 | #include "stub.h" 20 | #include "types.h" 21 | #include "x11.h" 22 | 23 | #include "pugl/pugl.h" 24 | #include "pugl/vulkan.h" 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | #include 32 | #include 33 | 34 | struct PuglVulkanLoaderImpl { 35 | void* libvulkan; 36 | PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr; 37 | PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr; 38 | }; 39 | 40 | PuglVulkanLoader* 41 | puglNewVulkanLoader(PuglWorld* PUGL_UNUSED(world)) 42 | { 43 | PuglVulkanLoader* loader = 44 | (PuglVulkanLoader*)calloc(1, sizeof(PuglVulkanLoader)); 45 | if (!loader) { 46 | return NULL; 47 | } 48 | 49 | if (!(loader->libvulkan = dlopen("libvulkan.so", RTLD_LAZY))) { 50 | free(loader); 51 | return NULL; 52 | } 53 | 54 | loader->vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr)dlsym( 55 | loader->libvulkan, "vkGetInstanceProcAddr"); 56 | 57 | loader->vkGetDeviceProcAddr = 58 | (PFN_vkGetDeviceProcAddr)dlsym(loader->libvulkan, "vkGetDeviceProcAddr"); 59 | 60 | return loader; 61 | } 62 | 63 | void 64 | puglFreeVulkanLoader(PuglVulkanLoader* loader) 65 | { 66 | if (loader) { 67 | dlclose(loader->libvulkan); 68 | free(loader); 69 | } 70 | } 71 | 72 | PFN_vkGetInstanceProcAddr 73 | puglGetInstanceProcAddrFunc(const PuglVulkanLoader* loader) 74 | { 75 | return loader->vkGetInstanceProcAddr; 76 | } 77 | 78 | PFN_vkGetDeviceProcAddr 79 | puglGetDeviceProcAddrFunc(const PuglVulkanLoader* loader) 80 | { 81 | return loader->vkGetDeviceProcAddr; 82 | } 83 | 84 | const PuglBackend* 85 | puglVulkanBackend(void) 86 | { 87 | static const PuglBackend backend = {puglX11StubConfigure, 88 | puglStubCreate, 89 | puglStubDestroy, 90 | puglStubEnter, 91 | puglStubLeave, 92 | puglStubGetContext}; 93 | 94 | return &backend; 95 | } 96 | 97 | const char* const* 98 | puglGetInstanceExtensions(uint32_t* const count) 99 | { 100 | static const char* const extensions[] = {"VK_KHR_surface", 101 | "VK_KHR_xlib_surface"}; 102 | 103 | *count = 2; 104 | return extensions; 105 | } 106 | 107 | VkResult 108 | puglCreateSurface(PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr, 109 | PuglView* const view, 110 | VkInstance instance, 111 | const VkAllocationCallbacks* const allocator, 112 | VkSurfaceKHR* const surface) 113 | { 114 | PuglInternals* const impl = view->impl; 115 | PuglWorldInternals* world_impl = view->world->impl; 116 | 117 | PFN_vkCreateXlibSurfaceKHR vkCreateXlibSurfaceKHR = 118 | (PFN_vkCreateXlibSurfaceKHR)vkGetInstanceProcAddr(instance, 119 | "vkCreateXlibSurfaceKHR"); 120 | 121 | const VkXlibSurfaceCreateInfoKHR info = { 122 | VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR, 123 | NULL, 124 | 0, 125 | world_impl->display, 126 | impl->win, 127 | }; 128 | 129 | return vkCreateXlibSurfaceKHR(instance, &info, allocator, surface); 130 | } 131 | -------------------------------------------------------------------------------- /rockspecs/lpugl-0.0.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lpugl" 2 | version = "0.0.3-1" 3 | local versionNumber = version:gsub("^(.*)-.-$", "%1") 4 | source = { 5 | url = "https://github.com/osch/lua-lpugl/archive/v"..versionNumber..".zip", 6 | dir = "lua-lpugl-"..versionNumber, 7 | } 8 | description = { 9 | summary = "Minimal API for building GUIs", 10 | homepage = "https://github.com/osch/lua-lpugl", 11 | license = "MIT/X11", 12 | detailed = [[ 13 | LPugl is a minimal portable Lua-API for building GUIs. 14 | Currently there are two drawing backends available, 15 | see packages "lpugl_cairo" and "lpugl_opengl". 16 | Supported platforms: X11, Windows and Mac OS X. 17 | LPugl is based on Pugl, a minimal portable API for embeddable GUIs, 18 | see: https://drobilla.net/software/pugl 19 | ]], 20 | } 21 | dependencies = { 22 | "lua >= 5.1, <= 5.4", 23 | "luarocks-build-extended" 24 | } 25 | build = { 26 | type = "extended", 27 | platforms = { 28 | linux = { 29 | modules = { 30 | lpugl = { 31 | libraries = { "pthread", "X11" }, 32 | } 33 | } 34 | }, 35 | windows = { 36 | modules = { 37 | lpugl = { 38 | libraries = { "kernel32", "gdi32", "user32" }, 39 | } 40 | } 41 | }, 42 | macosx = { 43 | modules = { 44 | lpugl = { 45 | sources = { "src/pugl.m" }, 46 | variables = { 47 | LIBFLAG_EXTRAS = { "-framework", "Cocoa" } 48 | } 49 | } 50 | } 51 | }, 52 | }, 53 | modules = { 54 | lpugl = { 55 | sources = { "src/pugl.c", 56 | "src/async_util.c", 57 | "src/error.c", 58 | "src/lpugl.c", 59 | "src/lpugl_compat.c", 60 | "src/util.c", 61 | "src/view.c", 62 | "src/world.c" }, 63 | defines = { 64 | "LPUGL_VERSION="..version:gsub("^(.*)-.-$", "%1"), 65 | "LPUGL_BUILD_DATE=$(BUILD_DATE)" 66 | }, 67 | incdirs = { "pugl-repo/include", "." }, 68 | libdirs = { }, 69 | }, 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /rockspecs/lpugl-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lpugl" 2 | version = "scm-0" 3 | source = { 4 | url = "https://github.com/osch/lua-lpugl/archive/master.zip", 5 | dir = "lua-lpugl-master", 6 | } 7 | description = { 8 | summary = "Minimal API for building GUIs", 9 | homepage = "https://github.com/osch/lua-lpugl", 10 | license = "MIT/X11", 11 | detailed = [[ 12 | LPugl is a minimal portable Lua-API for building GUIs. 13 | Currently there are two drawing backends available, 14 | see packages "lpugl_cairo" and "lpugl_opengl". 15 | Supported platforms: X11, Windows and Mac OS X. 16 | LPugl is based on Pugl, a minimal portable API for embeddable GUIs, 17 | see: https://drobilla.net/software/pugl 18 | ]], 19 | } 20 | dependencies = { 21 | "lua >= 5.1, <= 5.4", 22 | "luarocks-build-extended" 23 | } 24 | build = { 25 | type = "extended", 26 | platforms = { 27 | linux = { 28 | modules = { 29 | lpugl = { 30 | libraries = { "pthread", "X11" }, 31 | } 32 | } 33 | }, 34 | windows = { 35 | modules = { 36 | lpugl = { 37 | libraries = { "kernel32", "gdi32", "user32" }, 38 | } 39 | } 40 | }, 41 | macosx = { 42 | modules = { 43 | lpugl = { 44 | sources = { "src/pugl.m" }, 45 | variables = { 46 | LIBFLAG_EXTRAS = { "-framework", "Cocoa" } 47 | } 48 | } 49 | } 50 | }, 51 | }, 52 | modules = { 53 | lpugl = { 54 | sources = { "src/pugl.c", 55 | "src/async_util.c", 56 | "src/error.c", 57 | "src/lpugl.c", 58 | "src/lpugl_compat.c", 59 | "src/util.c", 60 | "src/view.c", 61 | "src/world.c" }, 62 | defines = { 63 | "LPUGL_VERSION="..version:gsub("^(.*)-.-$", "%1"), 64 | "LPUGL_BUILD_DATE=$(BUILD_DATE)" 65 | }, 66 | incdirs = { "pugl-repo/include", "." }, 67 | libdirs = { }, 68 | }, 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /rockspecs/lpugl_cairo-0.0.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lpugl_cairo" 2 | version = "0.0.3-1" 3 | local versionNumber = version:gsub("^(.*)-.-$", "%1") 4 | source = { 5 | url = "https://github.com/osch/lua-lpugl/archive/v"..versionNumber..".zip", 6 | dir = "lua-lpugl-"..versionNumber, 7 | } 8 | description = { 9 | summary = "Cairo backend for LPugl, a minimal API for building GUIs", 10 | homepage = "https://github.com/osch/lua-lpugl", 11 | license = "MIT/X11", 12 | detailed = [[ 13 | LPugl is a minimal portable Lua-API for building GUIs. 14 | Supported platforms: X11, Windows and Mac OS X. 15 | LPugl is based on Pugl, a minimal portable API for embeddable GUIs, 16 | see: https://drobilla.net/software/pugl 17 | ]], 18 | } 19 | dependencies = { 20 | "lua >= 5.1, <= 5.4", 21 | "luarocks-build-extended", 22 | "lpugl", "oocairo" 23 | } 24 | external_dependencies = { 25 | CAIRO = { 26 | header = "cairo/cairo.h", 27 | library = "cairo", 28 | } 29 | } 30 | build = { 31 | type = "extended", 32 | platforms = { 33 | linux = { 34 | modules = { 35 | ["lpugl_cairo"] = { 36 | libraries = { "cairo", "pthread" }, 37 | } 38 | } 39 | }, 40 | windows = { 41 | modules = { 42 | ["lpugl_cairo"] = { 43 | libraries = { "cairo", "kernel32", "gdi32", "user32" }, 44 | } 45 | } 46 | }, 47 | macosx = { 48 | modules = { 49 | ["lpugl_cairo"] = { 50 | sources = { "src/pugl_cairo.m" }, 51 | libraries = { "cairo" }, 52 | variables = { 53 | LIBFLAG_EXTRAS = { 54 | "-framework", "Cocoa" 55 | } 56 | } 57 | } 58 | } 59 | }, 60 | }, 61 | modules = { 62 | ["lpugl_cairo"] = { 63 | sources = { "src/pugl_cairo.c", 64 | "src/lpugl_cairo.c", 65 | "src/lpugl_compat.c" }, 66 | defines = { 67 | "LPUGL_VERSION="..version:gsub("^(.*)-.-$", "%1"), 68 | "LPUGL_BUILD_DATE=$(BUILD_DATE)" 69 | }, 70 | incdirs = { "pugl-repo/include", ".", "$(CAIRO_INCDIR)/cairo" }, 71 | libdirs = { "$(CAIRO_LIBDIR)" }, 72 | }, 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /rockspecs/lpugl_cairo-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lpugl_cairo" 2 | version = "scm-0" 3 | source = { 4 | url = "https://github.com/osch/lua-lpugl/archive/master.zip", 5 | dir = "lua-lpugl-master", 6 | } 7 | description = { 8 | summary = "Cairo backend for LPugl, a minimal API for building GUIs", 9 | homepage = "https://github.com/osch/lua-lpugl", 10 | license = "MIT/X11", 11 | detailed = [[ 12 | LPugl is a minimal portable Lua-API for building GUIs. 13 | Supported platforms: X11, Windows and Mac OS X. 14 | LPugl is based on Pugl, a minimal portable API for embeddable GUIs, 15 | see: https://drobilla.net/software/pugl 16 | ]], 17 | } 18 | dependencies = { 19 | "lua >= 5.1, <= 5.4", 20 | "luarocks-build-extended", 21 | "lpugl", "oocairo" 22 | } 23 | external_dependencies = { 24 | CAIRO = { 25 | header = "cairo/cairo.h", 26 | library = "cairo", 27 | } 28 | } 29 | build = { 30 | type = "extended", 31 | platforms = { 32 | linux = { 33 | modules = { 34 | ["lpugl_cairo"] = { 35 | libraries = { "cairo", "pthread" }, 36 | } 37 | } 38 | }, 39 | windows = { 40 | modules = { 41 | ["lpugl_cairo"] = { 42 | libraries = { "cairo", "kernel32", "gdi32", "user32" }, 43 | } 44 | } 45 | }, 46 | macosx = { 47 | modules = { 48 | ["lpugl_cairo"] = { 49 | sources = { "src/pugl_cairo.m" }, 50 | libraries = { "cairo" }, 51 | variables = { 52 | LIBFLAG_EXTRAS = { 53 | "-framework", "Cocoa" 54 | } 55 | } 56 | } 57 | } 58 | }, 59 | }, 60 | modules = { 61 | ["lpugl_cairo"] = { 62 | sources = { "src/pugl_cairo.c", 63 | "src/lpugl_cairo.c", 64 | "src/lpugl_compat.c" }, 65 | defines = { 66 | "LPUGL_VERSION="..version:gsub("^(.*)-.-$", "%1"), 67 | "LPUGL_BUILD_DATE=$(BUILD_DATE)" 68 | }, 69 | incdirs = { "pugl-repo/include", ".", "$(CAIRO_INCDIR)/cairo" }, 70 | libdirs = { "$(CAIRO_LIBDIR)" }, 71 | }, 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /rockspecs/lpugl_opengl-0.0.3-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lpugl_opengl" 2 | version = "0.0.3-1" 3 | local versionNumber = version:gsub("^(.*)-.-$", "%1") 4 | source = { 5 | url = "https://github.com/osch/lua-lpugl/archive/v"..versionNumber..".zip", 6 | dir = "lua-lpugl-"..versionNumber, 7 | } 8 | description = { 9 | summary = "OpenGL backend for LPugl, a minimal API for building GUIs", 10 | homepage = "https://github.com/osch/lua-lpugl", 11 | license = "MIT/X11", 12 | detailed = [[ 13 | LPugl is a minimal portable Lua-API for building GUIs. 14 | Supported platforms: X11, Windows and Mac OS X. 15 | LPugl is based on Pugl, a minimal portable API for embeddable GUIs, 16 | see: https://drobilla.net/software/pugl 17 | ]], 18 | } 19 | dependencies = { 20 | "lua >= 5.1, <= 5.4", 21 | "luarocks-build-extended", 22 | "lpugl", 23 | } 24 | build = { 25 | type = "extended", 26 | 27 | external_dependencies = { 28 | GL = { header = "GL/gl.h" }, 29 | }, 30 | platforms = { 31 | linux = { 32 | modules = { 33 | ["lpugl_opengl"] = { 34 | libraries = { "GL", "pthread" }, 35 | } 36 | } 37 | }, 38 | windows = { 39 | modules = { 40 | ["lpugl_opengl"] = { 41 | libraries = { "opengl32", "kernel32", "gdi32", "user32" }, 42 | } 43 | } 44 | }, 45 | macosx = { 46 | external_dependencies = { 47 | -- for Mac OS: OpenGL headers are under "$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/OpenGL.framework/Headers" 48 | -- (e.g. /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework/Headers) 49 | -- They must be included using "OpenGL/gl.h" and cannot be found by luarocks in the file system 50 | GL = { header = false }, 51 | }, 52 | modules = { 53 | ["lpugl_opengl"] = { 54 | sources = { "src/pugl_opengl.m" }, 55 | variables = { 56 | LIBFLAG_EXTRAS = { "-framework", "Cocoa", 57 | "-framework", "OpenGL", 58 | } 59 | } 60 | } 61 | } 62 | }, 63 | }, 64 | modules = { 65 | ["lpugl_opengl"] = { 66 | sources = { "src/pugl_opengl.c", 67 | "src/lpugl_opengl.c", 68 | "src/lpugl_compat.c" }, 69 | defines = { 70 | "LPUGL_VERSION="..version:gsub("^(.*)-.-$", "%1"), 71 | "LPUGL_BUILD_DATE=$(BUILD_DATE)" 72 | }, 73 | incdirs = { "pugl-repo/include", ".", "$(GL_INCDIR)" }, 74 | libdirs = { "$(GL_LIBDIR)" }, 75 | }, 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /rockspecs/lpugl_opengl-scm-0.rockspec: -------------------------------------------------------------------------------- 1 | package = "lpugl_opengl" 2 | version = "scm-0" 3 | source = { 4 | url = "https://github.com/osch/lua-lpugl/archive/master.zip", 5 | dir = "lua-lpugl-master", 6 | } 7 | description = { 8 | summary = "OpenGL backend for LPugl, a minimal API for building GUIs", 9 | homepage = "https://github.com/osch/lua-lpugl", 10 | license = "MIT/X11", 11 | detailed = [[ 12 | LPugl is a minimal portable Lua-API for building GUIs. 13 | Supported platforms: X11, Windows and Mac OS X. 14 | LPugl is based on Pugl, a minimal portable API for embeddable GUIs, 15 | see: https://drobilla.net/software/pugl 16 | ]], 17 | } 18 | dependencies = { 19 | "lua >= 5.1, <= 5.4", 20 | "luarocks-build-extended", 21 | "lpugl", 22 | } 23 | build = { 24 | type = "extended", 25 | 26 | external_dependencies = { 27 | GL = { header = "GL/gl.h" }, 28 | }, 29 | platforms = { 30 | linux = { 31 | modules = { 32 | ["lpugl_opengl"] = { 33 | libraries = { "GL", "pthread" }, 34 | } 35 | } 36 | }, 37 | windows = { 38 | modules = { 39 | ["lpugl_opengl"] = { 40 | libraries = { "opengl32", "kernel32", "gdi32", "user32" }, 41 | } 42 | } 43 | }, 44 | macosx = { 45 | external_dependencies = { 46 | -- for Mac OS: OpenGL headers are under "$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/OpenGL.framework/Headers" 47 | -- (e.g. /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework/Headers) 48 | -- They must be included using "OpenGL/gl.h" and cannot be found by luarocks in the file system 49 | GL = { header = false }, 50 | }, 51 | modules = { 52 | ["lpugl_opengl"] = { 53 | sources = { "src/pugl_opengl.m" }, 54 | variables = { 55 | LIBFLAG_EXTRAS = { "-framework", "Cocoa", 56 | "-framework", "OpenGL", 57 | } 58 | } 59 | } 60 | } 61 | }, 62 | }, 63 | modules = { 64 | ["lpugl_opengl"] = { 65 | sources = { "src/pugl_opengl.c", 66 | "src/lpugl_opengl.c", 67 | "src/lpugl_compat.c" }, 68 | defines = { 69 | "LPUGL_VERSION="..version:gsub("^(.*)-.-$", "%1"), 70 | "LPUGL_BUILD_DATE=$(BUILD_DATE)" 71 | }, 72 | incdirs = { "pugl-repo/include", ".", "$(GL_INCDIR)" }, 73 | libdirs = { "$(GL_LIBDIR)" }, 74 | }, 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /rockspecs/setversion.lua: -------------------------------------------------------------------------------- 1 | #!/usr/bin/lua 2 | 3 | os.setlocale("C") 4 | 5 | local format = string.format 6 | local lfs = require("lfs") 7 | 8 | local version = ... 9 | assert(version:match("^%d+%.%d+%.%d+$"), format("invalid version %q", version)) 10 | 11 | for fileName in lfs.dir(".") do 12 | local p1, v, p2 = fileName:match("^(lpugl.*%-)(%d+%.%d+%.%d+)(%-%d+%.rockspec)$") 13 | if p1 then 14 | local newName = p1..version..p2 15 | print(format("%-30s -> %s", fileName, newName)) 16 | local out = {} 17 | local matched = false 18 | local inFile = io.open(fileName, "r") 19 | for line in inFile:lines() do 20 | local l1, l2 = line:match("^(%s*version%s*%=%s*\")%d+%.%d+%.%d+(%-%d+\"%s*)$") 21 | if l1 then 22 | assert(not matched) 23 | matched = true 24 | out[#out+1] = l1..version..l2 25 | else 26 | out[#out+1] = line 27 | end 28 | end 29 | out[#out+1] = "" 30 | inFile:close() 31 | assert(matched) 32 | local newFile, err = io.open(newName, "w") 33 | assert(newFile, err) 34 | newFile:write(table.concat(out, "\n")) 35 | newFile:close() 36 | if fileName ~= newName then 37 | os.remove(fileName) 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: default lpugl lpugl_cairo 2 | default: lpugl lpugl_cairo lpugl_opengl 3 | 4 | BUILD_DATE=$(shell date "+%Y-%m-%dT%H:%M:%S") 5 | 6 | X11_GCC_RUN := gcc -shared -fPIC 7 | WIN_GCC_RUN := gcc -shared -fPIC 8 | MAC_GCC_RUN := MACOSX_DEPLOYMENT_TARGET=10.8 gcc -bundle -undefined dynamic_lookup -all_load 9 | 10 | X11_PUGLC_EXT := c 11 | WIN_PUGLC_EXT := c 12 | MAC_PUGLC_EXT := m 13 | 14 | X11_SO_EXT := so 15 | WIN_SO_EXT := dll 16 | MAC_SO_EXT := so 17 | 18 | 19 | X11_COPTS := 20 | WIN_COPTS := -I/mingw64/include/lua5.1 -I/mingw64/include/cairo 21 | MAC_COPTS := -I/usr/local/opt/lua/include/lua5.4 -I/usr/local/include/cairo 22 | 23 | X11_LOPTS := -lpthread -lX11 24 | WIN_LOPTS := -lkernel32 -lgdi32 -luser32 /mingw64/lib/liblua5.1.dll.a 25 | MAC_LOPTS := -lpthread -framework Cocoa 26 | 27 | X11_LOPTS_CAIRO := -lcairo 28 | WIN_LOPTS_CAIRO := -lcairo 29 | MAC_LOPTS_CAIRO := -lcairo 30 | 31 | X11_LOPTS_OPENGL := -lGL 32 | WIN_LOPTS_OPENGL := -lopengl32 33 | MAC_LOPTS_OPENGL := -framework OpenGL 34 | 35 | GCC_RUN := 36 | PUGLC_EXT := 37 | SO_EXT := 38 | COPTS := 39 | LOPTS := 40 | LOPTS_CAIRO := 41 | LOPTS_OPENGL := 42 | 43 | PLATFORM := X11 44 | LUA_VERSION := 5.4 45 | 46 | -include sandbox.mk 47 | 48 | GCC_RUN := $(or $(GCC_RUN), $($(PLATFORM)_GCC_RUN)) 49 | PUGLC_EXT := $(or $(PUGLC_EXT), $($(PLATFORM)_PUGLC_EXT)) 50 | SO_EXT := $(or $(SO_EXT), $($(PLATFORM)_SO_EXT)) 51 | COPTS := $(or $(COPTS), $($(PLATFORM)_COPTS)) 52 | LOPTS := $(or $(LOPTS), $($(PLATFORM)_LOPTS)) 53 | LOPTS_CAIRO := $(or $(LOPTS_CAIRO), $($(PLATFORM)_LOPTS_CAIRO)) 54 | LOPTS_OPENGL := $(or $(LOPTS_OPENGL), $($(PLATFORM)_LOPTS_OPENGL)) 55 | 56 | lpugl: 57 | @mkdir -p build/lua$(LUA_VERSION) 58 | $(GCC_RUN) -g \ 59 | $(COPTS) \ 60 | -I../pugl-repo/include/ -I .. \ 61 | -o build/lua$(LUA_VERSION)/lpugl.$(SO_EXT) lpugl.c -D LPUGL_VERSION=Makefile-1 \ 62 | -DLPUGL_BUILD_DATE="$(BUILD_DATE)" \ 63 | -DPUGL_DISABLE_DEPRECATED \ 64 | async_util.c util.c world.c view.c error.c pugl.$(PUGLC_EXT) \ 65 | lpugl_compat.c \ 66 | $(LOPTS) 67 | 68 | lpugl_cairo: 69 | @mkdir -p build/lua$(LUA_VERSION) 70 | $(GCC_RUN) -g \ 71 | $(COPTS) \ 72 | -I /usr/include/cairo \ 73 | -I../pugl-repo/include/ -I .. \ 74 | -o build/lua$(LUA_VERSION)/lpugl_cairo.$(SO_EXT) lpugl_cairo.c -D LPUGL_VERSION=Makefile-1 \ 75 | -DLPUGL_BUILD_DATE="$(BUILD_DATE)" \ 76 | -DPUGL_DISABLE_DEPRECATED \ 77 | async_util.c util.c pugl_cairo.$(PUGLC_EXT) \ 78 | lpugl_compat.c \ 79 | $(LOPTS) $(LOPTS_CAIRO) 80 | 81 | lpugl_opengl: 82 | @mkdir -p build/lua$(LUA_VERSION) 83 | $(GCC_RUN) -g \ 84 | $(COPTS) \ 85 | -I../pugl-repo/include/ -I .. \ 86 | -o build/lua$(LUA_VERSION)/lpugl_opengl.$(SO_EXT) lpugl_opengl.c -D LPUGL_VERSION=Makefile-1 \ 87 | -DLPUGL_BUILD_DATE="$(BUILD_DATE)" \ 88 | -DPUGL_DISABLE_DEPRECATED \ 89 | async_util.c util.c pugl_opengl.$(PUGLC_EXT) \ 90 | lpugl_compat.c \ 91 | $(LOPTS) $(LOPTS_OPENGL) 92 | -------------------------------------------------------------------------------- /src/activate.sh: -------------------------------------------------------------------------------- 1 | # Activations script for add the lpugl module to the lua path when 2 | # building lpugl using the Makefile. 3 | # Source this into interactive shell by invoking ". activates.sh" from this directory 4 | # This is not necessary if lpugl is installed, e.g. via luarocks. 5 | 6 | ################################################### 7 | if [ -n "$MSYSTEM" ]; then 8 | ################################################### 9 | path_to_unix() 10 | { 11 | cygpath -up $1 12 | } 13 | 14 | path_to_dos() 15 | { 16 | cygpath -wp $1 17 | } 18 | 19 | so_ext=dll 20 | 21 | ################################################### 22 | else 23 | ################################################### 24 | path_to_unix() 25 | { 26 | echo $1|sed 's/;/:/g' 27 | } 28 | 29 | path_to_dos() 30 | { 31 | echo $1|sed 's/:/;/g' 32 | } 33 | 34 | so_ext=so 35 | 36 | ################################################### 37 | fi 38 | ################################################### 39 | 40 | this_dir=$(pwd) 41 | 42 | lualpugl_dir=$(cd "$this_dir"/..; pwd) 43 | 44 | if [ ! -e "$lualpugl_dir/src/activate.sh" -o ! -e "$lualpugl_dir/src/lpugl.c" ]; then 45 | 46 | echo '**** ERROR: ". activate.sh" must be invoked from "example" or "src" directory ***' 47 | 48 | else 49 | 50 | echo "Setting lua paths for: $lualpugl_dir" 51 | 52 | add_lua_path="$lualpugl_dir/src/?.lua:$lualpugl_dir/src/?/init.lua" 53 | add_lua_cpath="$lualpugl_dir/src/build" 54 | 55 | # unset LUA_PATH_5_4 LUA_CPATH_5_4 LUA_PATH_5_3 LUA_CPATH_5_3 LUA_PATH_5_2 LUA_CPATH_5_2 LUA_PATH LUA_CPATH 56 | 57 | default_version="" 58 | if which lua > /dev/null 2>&1; then 59 | default_version=$(lua -e 'v=_VERSION:gsub("^Lua ","");print(v)') 60 | fi 61 | 62 | if [ -n "$default_version" ]; then 63 | if [ "$default_version" != "5.1" ]; then 64 | echo "Setting path for lua (version=$default_version)" 65 | lua_path_vers=$(echo $default_version|sed 's/\./_/') 66 | eval "export LUA_PATH_$lua_path_vers=\"$(path_to_dos $add_lua_path:$(path_to_unix $(lua -e 'print(package.path)')))\"" 67 | eval "export LUA_CPATH_$lua_path_vers=\"$(path_to_dos $add_lua_cpath/lua$default_version/?.$so_ext:$(path_to_unix $(lua -e 'print(package.cpath)')))\"" 68 | fi 69 | fi 70 | 71 | for vers in 5.4 5.3 5.2 5.1; do 72 | lua_cmd="" 73 | if which lua$vers > /dev/null 2>&1; then 74 | lua_cmd="lua$vers" 75 | elif which lua-$vers > /dev/null 2>&1; then 76 | lua_cmd="lua-$vers" 77 | fi 78 | if [ -n "$lua_cmd" ]; then 79 | lua_version=$($lua_cmd -e 'v=_VERSION:gsub("^Lua ","");print(v)') 80 | if [ "$lua_version" != "$default_version" ]; then 81 | echo "Setting path for $lua_cmd (version=$lua_version)" 82 | if [ "$lua_version" = "5.1" ]; then 83 | export LUA_PATH=$(path_to_dos "$add_lua_path:$(path_to_unix $($lua_cmd -e 'print(package.path)'))") 84 | export LUA_CPATH=$(path_to_dos "$add_lua_cpath/lua5.1/?.$so_ext:$(path_to_unix $($lua_cmd -e 'print(package.cpath)'))") 85 | else 86 | lua_path_vers=$(echo $lua_version|sed 's/\./_/') 87 | eval "export LUA_PATH_$lua_path_vers=\"$(path_to_dos $add_lua_path:$(path_to_unix $($lua_cmd -e 'print(package.path)')))\"" 88 | eval "export LUA_CPATH_$lua_path_vers=\"$(path_to_dos $add_lua_cpath/lua$lua_version/?.$so_ext:$(path_to_unix $($lua_cmd -e 'print(package.cpath)')))\"" 89 | fi 90 | fi 91 | fi 92 | done 93 | 94 | if [ -n "$default_version" ]; then 95 | if [ "$default_version" = "5.1" ]; then 96 | echo "Setting path for lua (version=$default_version)" 97 | export LUA_PATH="$(path_to_dos $add_lua_path:$(path_to_unix $(lua -e 'print(package.path)')))" 98 | export LUA_CPATH="$(path_to_dos $add_lua_cpath/lua5.1/?.$so_ext:$(path_to_unix $(lua -e 'print(package.cpath)')))" 99 | fi 100 | fi 101 | fi 102 | 103 | unset lua_cmd this_dir lualpugl_dir add_lua_path add_lua_cpath lua_version lua_path_vers vers default_version \ 104 | path_to_unix path_to_dos so_ext 105 | 106 | -------------------------------------------------------------------------------- /src/async_defines.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_ASYNC_DEFINES_H 2 | #define LPUGL_ASYNC_DEFINES_H 3 | 4 | /* -------------------------------------------------------------------------------------------- */ 5 | 6 | #if defined(LPUGL_ASYNC_USE_WIN32) \ 7 | && ( defined(LPUGL_ASYNC_USE_STDATOMIC) \ 8 | || defined(LPUGL_ASYNC_USE_GNU)) 9 | #error "LPUGL_ASYNC: Invalid compile flag combination" 10 | #endif 11 | #if defined(LPUGL_ASYNC_USE_STDATOMIC) \ 12 | && ( defined(LPUGL_ASYNC_USE_WIN32) \ 13 | || defined(LPUGL_ASYNC_USE_GNU)) 14 | #error "LPUGL_ASYNC: Invalid compile flag combination" 15 | #endif 16 | #if defined(LPUGL_ASYNC_USE_GNU) \ 17 | && ( defined(LPUGL_ASYNC_USE_WIN32) \ 18 | || defined(LPUGL_ASYNC_USE_STDATOMIC)) 19 | #error "LPUGL_ASYNC: Invalid compile flag combination" 20 | #endif 21 | 22 | /* -------------------------------------------------------------------------------------------- */ 23 | 24 | #if !defined(LPUGL_ASYNC_USE_WIN32) \ 25 | && !defined(LPUGL_ASYNC_USE_STDATOMIC) \ 26 | && !defined(LPUGL_ASYNC_USE_GNU) 27 | 28 | #if defined(WIN32) || defined(_WIN32) 29 | #define LPUGL_ASYNC_USE_WIN32 30 | #elif defined(__GNUC__) 31 | #define LPUGL_ASYNC_USE_GNU 32 | #elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_ATOMICS__) 33 | #define LPUGL_ASYNC_USE_STDATOMIC 34 | #else 35 | #error "LPUGL_ASYNC: unknown platform" 36 | #endif 37 | #endif 38 | 39 | /* -------------------------------------------------------------------------------------------- */ 40 | 41 | #if defined(__unix__) || defined(__unix) || (defined (__APPLE__) && defined (__MACH__)) 42 | #include 43 | #endif 44 | 45 | #if !defined(LPUGL_ASYNC_USE_WINTHREAD) \ 46 | && !defined(LPUGL_ASYNC_USE_PTHREAD) \ 47 | && !defined(LPUGL_ASYNC_USE_STDTHREAD) 48 | 49 | #ifdef LPUGL_ASYNC_USE_WIN32 50 | #define LPUGL_ASYNC_USE_WINTHREAD 51 | #elif _XOPEN_VERSION >= 600 52 | #define LPUGL_ASYNC_USE_PTHREAD 53 | #elif __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) 54 | #define LPUGL_ASYNC_USE_STDTHREAD 55 | #else 56 | #define LPUGL_ASYNC_USE_PTHREAD 57 | #endif 58 | #endif 59 | 60 | /* -------------------------------------------------------------------------------------------- */ 61 | 62 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 63 | #ifndef _XOPEN_SOURCE 64 | #define _XOPEN_SOURCE 600 /* must be defined before any other include */ 65 | #endif 66 | #include 67 | #include 68 | #include 69 | #endif 70 | #if defined(LPUGL_ASYNC_USE_WIN32) || defined(LPUGL_ASYNC_USE_WINTHREAD) 71 | #include 72 | #endif 73 | #if defined(LPUGL_ASYNC_USE_STDATOMIC) 74 | #include 75 | #include 76 | #endif 77 | #if defined(LPUGL_ASYNC_USE_STDTHREAD) 78 | #include 79 | #include 80 | #endif 81 | 82 | /* -------------------------------------------------------------------------------------------- */ 83 | 84 | #if __STDC_VERSION__ >= 199901L 85 | #include 86 | #else 87 | #if !defined(__GNUC__) || defined(__STRICT_ANSI__) 88 | #define inline 89 | #endif 90 | #define bool int 91 | #define true 1 92 | #define false 0 93 | #endif 94 | 95 | /* -------------------------------------------------------------------------------------------- */ 96 | 97 | 98 | #endif /* LPUGL_ASYNC_DEFINES_H */ 99 | -------------------------------------------------------------------------------- /src/async_util.c: -------------------------------------------------------------------------------- 1 | #include "base.h" 2 | #include "async_util.h" 3 | 4 | bool async_util_abort(int rc, int line) 5 | { 6 | fprintf(stderr, 7 | "*** unexpected error in thread synchronization (rc=%d, line=%d) ***\n", 8 | rc, 9 | line); 10 | abort(); 11 | return false; 12 | } 13 | 14 | void lpugl_async_lock_init(Lock* lock) 15 | { 16 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 17 | 18 | int rc = pthread_mutexattr_init(&lock->attr); 19 | if (rc != 0) { async_util_abort(rc, __LINE__); } 20 | 21 | rc = pthread_mutexattr_settype(&lock->attr, PTHREAD_MUTEX_RECURSIVE); 22 | if (rc != 0) { async_util_abort(rc, __LINE__); } 23 | 24 | rc = pthread_mutex_init(&lock->lock, &lock->attr); 25 | if (rc != 0) { async_util_abort(rc, __LINE__); } 26 | 27 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 28 | InitializeCriticalSection(&lock->lock); 29 | 30 | #elif defined (LPUGL_ASYNC_USE_STDTHREAD) 31 | int rc = mtx_init(&lock->lock, mtx_plain | mtx_recursive); 32 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 33 | #endif 34 | } 35 | 36 | void lpugl_async_lock_destruct(Lock* lock) 37 | { 38 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 39 | pthread_mutex_destroy(&lock->lock); 40 | pthread_mutexattr_destroy(&lock->attr); 41 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 42 | DeleteCriticalSection(&lock->lock); 43 | #elif defined (LPUGL_ASYNC_USE_STDTHREAD) 44 | mtx_destroy(&lock->lock); 45 | #endif 46 | } 47 | 48 | 49 | void lpugl_async_mutex_init(Mutex* mutex) 50 | { 51 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 52 | 53 | int rc = pthread_mutexattr_init(&mutex->attr); 54 | if (rc != 0) { async_util_abort(rc, __LINE__); } 55 | 56 | rc = pthread_mutexattr_settype(&mutex->attr, PTHREAD_MUTEX_RECURSIVE); 57 | if (rc != 0) { async_util_abort(rc, __LINE__); } 58 | 59 | rc = pthread_mutex_init(&mutex->mutex, &mutex->attr); 60 | if (rc != 0) { async_util_abort(rc, __LINE__); } 61 | 62 | rc = pthread_cond_init(&mutex->condition, NULL); 63 | if (rc != 0) { async_util_abort(rc, __LINE__); } 64 | 65 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 66 | InitializeCriticalSection(&mutex->mutex); 67 | 68 | mutex->waitingCounter = 0; 69 | 70 | mutex->event = CreateEvent (NULL, /* no security */ 71 | FALSE, /* auto-reset event */ 72 | FALSE, /* non-signaled initially */ 73 | NULL); /* unnamed */ 74 | 75 | if (mutex->event == NULL) { async_util_abort(0, __LINE__); } 76 | 77 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 78 | int rc = mtx_init(&mutex->mutex, mtx_plain | mtx_recursive); 79 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 80 | 81 | rc = cnd_init(&mutex->condition); 82 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 83 | #endif 84 | } 85 | 86 | void lpugl_async_mutex_destruct(Mutex* mutex) 87 | { 88 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 89 | pthread_cond_destroy(&mutex->condition); 90 | pthread_mutex_destroy(&mutex->mutex); 91 | pthread_mutexattr_destroy(&mutex->attr); 92 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 93 | CloseHandle(mutex->event); 94 | DeleteCriticalSection(&mutex->mutex); 95 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 96 | cnd_destroy(&mutex->condition); 97 | mtx_destroy(&mutex->mutex); 98 | #endif 99 | } 100 | 101 | void lpugl_async_mutex_wait(Mutex* mutex) 102 | { 103 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 104 | 105 | int rc = pthread_cond_wait(&mutex->condition, &mutex->mutex); 106 | if (rc != 0) { async_util_abort(rc, __LINE__); } 107 | 108 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 109 | 110 | mutex->waitingCounter += 1; 111 | LeaveCriticalSection(&mutex->mutex); 112 | 113 | DWORD rc = WaitForSingleObject(mutex->event, INFINITE); 114 | 115 | EnterCriticalSection(&mutex->mutex); 116 | mutex->waitingCounter -= 1; 117 | 118 | if (rc != WAIT_OBJECT_0) { async_util_abort(rc, __LINE__); } 119 | 120 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 121 | int rc = cnd_wait(&mutex->condition, &mutex->mutex); 122 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 123 | 124 | #endif 125 | } 126 | 127 | 128 | bool lpugl_async_mutex_wait_millis(Mutex* mutex, int timeoutMillis) 129 | { 130 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 131 | struct timespec abstime; 132 | struct timeval tv; gettimeofday(&tv, NULL); 133 | 134 | abstime.tv_sec = tv.tv_sec + timeoutMillis / 1000; 135 | abstime.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * (timeoutMillis % 1000); 136 | abstime.tv_sec += abstime.tv_nsec / (1000 * 1000 * 1000); 137 | abstime.tv_nsec %= (1000 * 1000 * 1000); 138 | 139 | int rc = pthread_cond_timedwait(&mutex->condition, &mutex->mutex, &abstime); 140 | 141 | if (rc == 0 || rc == ETIMEDOUT) { 142 | return (rc == 0); 143 | } 144 | else { 145 | return async_util_abort(rc, __LINE__); 146 | } 147 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 148 | mutex->waitingCounter += 1; 149 | LeaveCriticalSection(&mutex->mutex); 150 | 151 | DWORD rc = WaitForSingleObject(mutex->event, timeoutMillis); 152 | 153 | EnterCriticalSection(&mutex->mutex); 154 | mutex->waitingCounter -= 1; 155 | 156 | if (rc == WAIT_OBJECT_0 || rc == WAIT_TIMEOUT) { 157 | return (rc == WAIT_OBJECT_0); 158 | } else { 159 | return async_util_abort(rc, __LINE__); 160 | } 161 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 162 | struct timespec abstime; 163 | struct timeval tv; gettimeofday(&tv, NULL); 164 | 165 | abstime.tv_sec = tv.tv_sec + timeoutMillis / 1000; 166 | abstime.tv_nsec = tv.tv_usec * 1000 + 1000 * 1000 * (timeoutMillis % 1000); 167 | abstime.tv_sec += abstime.tv_nsec / (1000 * 1000 * 1000); 168 | abstime.tv_nsec %= (1000 * 1000 * 1000); 169 | 170 | int rc = cnd_timedwait(&mutex->condition, &mutex->mutex, &abstime); 171 | 172 | if (rc == thrd_success || rc == thrd_timedout) { 173 | return (rc == thrd_success); 174 | } 175 | else { 176 | return async_util_abort(rc, __LINE__); 177 | } 178 | 179 | #endif 180 | } 181 | -------------------------------------------------------------------------------- /src/async_util.h: -------------------------------------------------------------------------------- 1 | #include "base.h" 2 | 3 | #ifndef LPUGL_ASYNC_UTIL_H 4 | #define LPUGL_ASYNC_UTIL_H 5 | 6 | /* -------------------------------------------------------------------------------------------- */ 7 | 8 | #define async_util_abort lpugl_async_util_abort 9 | bool async_util_abort(int rc, int line); /* function does not return */ 10 | 11 | /* -------------------------------------------------------------------------------------------- */ 12 | 13 | #if defined(LPUGL_ASYNC_USE_WIN32) 14 | typedef LONG AtomicCounter; 15 | typedef PVOID AtomicPtr; 16 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 17 | typedef atomic_int AtomicCounter; 18 | typedef atomic_intptr_t AtomicPtr; 19 | #elif defined(LPUGL_ASYNC_USE_GNU) 20 | typedef int AtomicCounter; 21 | typedef void* AtomicPtr; 22 | #endif 23 | 24 | /* -------------------------------------------------------------------------------------------- */ 25 | 26 | static inline bool atomic_set_ptr_if_equal(AtomicPtr* ptr, void* oldPtr, void* newPtr) 27 | { 28 | #if defined(LPUGL_ASYNC_USE_WIN32) 29 | return oldPtr == InterlockedCompareExchangePointer(ptr, newPtr, oldPtr); 30 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 31 | intptr_t old = (intptr_t) oldPtr; 32 | return atomic_compare_exchange_strong(ptr, &old, (intptr_t)newPtr); 33 | #elif defined(LPUGL_ASYNC_USE_GNU) 34 | return __sync_bool_compare_and_swap(ptr, oldPtr, newPtr); 35 | #endif 36 | } 37 | 38 | static inline void* atomic_get_ptr(AtomicPtr* ptr) 39 | { 40 | #if defined(LPUGL_ASYNC_USE_WIN32) 41 | return InterlockedCompareExchangePointer(ptr, 0, 0); 42 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 43 | return (void*)atomic_load(ptr); 44 | #elif defined(LPUGL_ASYNC_USE_GNU) 45 | return __sync_add_and_fetch(ptr, 0); 46 | #endif 47 | } 48 | 49 | /* -------------------------------------------------------------------------------------------- */ 50 | 51 | static inline int atomic_inc(AtomicCounter* value) 52 | { 53 | #if defined(LPUGL_ASYNC_USE_WIN32) 54 | return InterlockedIncrement(value); 55 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 56 | return atomic_fetch_add(value, 1) + 1; 57 | #elif defined(LPUGL_ASYNC_USE_GNU) 58 | return __sync_add_and_fetch(value, 1); 59 | #endif 60 | } 61 | 62 | static inline int atomic_dec(AtomicCounter* value) 63 | { 64 | #if defined(LPUGL_ASYNC_USE_WIN32) 65 | return InterlockedDecrement(value); 66 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 67 | return atomic_fetch_sub(value, 1) - 1; 68 | #elif defined(LPUGL_ASYNC_USE_GNU) 69 | return __sync_sub_and_fetch(value, 1); 70 | #endif 71 | } 72 | 73 | static inline bool atomic_set_if_equal(AtomicCounter* value, int oldValue, int newValue) 74 | { 75 | #if defined(LPUGL_ASYNC_USE_WIN32) 76 | return oldValue == InterlockedCompareExchange(value, newValue, oldValue); 77 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 78 | return atomic_compare_exchange_strong(value, &oldValue, newValue); 79 | #elif defined(LPUGL_ASYNC_USE_GNU) 80 | return __sync_bool_compare_and_swap(value, oldValue, newValue); 81 | #endif 82 | } 83 | 84 | static inline int atomic_get(AtomicCounter* value) 85 | { 86 | #if defined(LPUGL_ASYNC_USE_WIN32) 87 | return InterlockedCompareExchange(value, 0, 0); 88 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 89 | return atomic_load(value); 90 | #elif defined(LPUGL_ASYNC_USE_GNU) 91 | return __sync_add_and_fetch(value, 0); 92 | #endif 93 | } 94 | 95 | static inline int atomic_set(AtomicCounter* value, int newValue) 96 | { 97 | #if defined(LPUGL_ASYNC_USE_WIN32) 98 | return InterlockedExchange(value, newValue); 99 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 100 | return atomic_exchange(value, newValue); 101 | #elif defined(LPUGL_ASYNC_USE_GNU) 102 | int rslt = __sync_lock_test_and_set(value, newValue); 103 | __sync_synchronize(); 104 | return rslt; 105 | #endif 106 | } 107 | 108 | /* -------------------------------------------------------------------------------------------- */ 109 | 110 | 111 | typedef struct 112 | { 113 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 114 | pthread_mutexattr_t attr; 115 | pthread_mutex_t lock; 116 | 117 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 118 | CRITICAL_SECTION lock; 119 | 120 | #elif defined (LPUGL_ASYNC_USE_STDTHREAD) 121 | mtx_t lock; 122 | #endif 123 | } Lock; 124 | 125 | 126 | /* -------------------------------------------------------------------------------------------- */ 127 | 128 | #define async_lock_init lpugl_async_lock_init 129 | void async_lock_init(Lock* lock); 130 | 131 | /* -------------------------------------------------------------------------------------------- */ 132 | 133 | #define async_lock_destruct lpugl_async_lock_destruct 134 | void async_lock_destruct(Lock* lock); 135 | 136 | /* -------------------------------------------------------------------------------------------- */ 137 | 138 | static inline void async_lock_acquire(Lock* lock) 139 | { 140 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 141 | 142 | int rc = pthread_mutex_lock(&lock->lock); 143 | if (rc != 0) { async_util_abort(rc, __LINE__); } 144 | 145 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 146 | EnterCriticalSection(&lock->lock); 147 | 148 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 149 | int rc = mtx_lock(&lock->lock); 150 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 151 | #endif 152 | } 153 | 154 | /* -------------------------------------------------------------------------------------------- */ 155 | 156 | static inline void async_lock_release(Lock* lock) 157 | { 158 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 159 | 160 | int rc = pthread_mutex_unlock(&lock->lock); 161 | if (rc != 0) { async_util_abort(rc, __LINE__); } 162 | 163 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 164 | LeaveCriticalSection(&lock->lock); 165 | 166 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 167 | int rc = mtx_unlock(&lock->lock); 168 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 169 | #endif 170 | } 171 | 172 | /* -------------------------------------------------------------------------------------------- */ 173 | 174 | typedef struct 175 | { 176 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 177 | pthread_mutexattr_t attr; 178 | pthread_mutex_t mutex; 179 | pthread_cond_t condition; 180 | 181 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 182 | CRITICAL_SECTION mutex; 183 | volatile int waitingCounter; 184 | HANDLE event; 185 | 186 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 187 | mtx_t mutex; 188 | cnd_t condition; 189 | #endif 190 | } Mutex; 191 | 192 | 193 | /* -------------------------------------------------------------------------------------------- */ 194 | 195 | #define async_mutex_init lpugl_async_mutex_init 196 | void async_mutex_init(Mutex* mutex); 197 | 198 | /* -------------------------------------------------------------------------------------------- */ 199 | 200 | #define async_mutex_destruct lpugl_async_mutex_destruct 201 | void async_mutex_destruct(Mutex* mutex); 202 | 203 | /* -------------------------------------------------------------------------------------------- */ 204 | 205 | static inline void async_mutex_lock(Mutex* mutex) 206 | { 207 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 208 | 209 | int rc = pthread_mutex_lock(&mutex->mutex); 210 | if (rc != 0) { async_util_abort(rc, __LINE__); } 211 | 212 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 213 | EnterCriticalSection(&mutex->mutex); 214 | 215 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 216 | int rc = mtx_lock(&mutex->mutex); 217 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 218 | #endif 219 | } 220 | 221 | /* -------------------------------------------------------------------------------------------- */ 222 | 223 | static inline bool async_mutex_trylock(Mutex* mutex) 224 | { 225 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 226 | int rc = pthread_mutex_trylock(&mutex->mutex); 227 | if (rc == 0 || rc == EBUSY) { 228 | return (rc == 0); 229 | } else { 230 | return async_util_abort(rc, __LINE__); 231 | } 232 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 233 | return TryEnterCriticalSection(&mutex->mutex) != 0; 234 | 235 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 236 | int rc = mtx_trylock(&mutex->mutex); 237 | if (rc == thrd_success || rc == thrd_busy) { 238 | return rc == thrd_success; 239 | } else { 240 | return async_util_abort(rc, __LINE__); 241 | } 242 | #endif 243 | } 244 | 245 | /* -------------------------------------------------------------------------------------------- */ 246 | 247 | static inline void async_mutex_unlock(Mutex* mutex) 248 | { 249 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 250 | 251 | int rc = pthread_mutex_unlock(&mutex->mutex); 252 | if (rc != 0) { async_util_abort(rc, __LINE__); } 253 | 254 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 255 | LeaveCriticalSection(&mutex->mutex); 256 | 257 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 258 | int rc = mtx_unlock(&mutex->mutex); 259 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 260 | #endif 261 | } 262 | 263 | /* -------------------------------------------------------------------------------------------- */ 264 | 265 | #define async_mutex_wait lpugl_async_mutex_wait 266 | void async_mutex_wait(Mutex* mutex); 267 | 268 | /* -------------------------------------------------------------------------------------------- */ 269 | 270 | #define async_mutex_wait_millis lpugl_async_mutex_wait_millis 271 | bool async_mutex_wait_millis(Mutex* mutex, int timeoutMillis); 272 | 273 | /* -------------------------------------------------------------------------------------------- */ 274 | 275 | static inline void async_mutex_notify(Mutex* mutex) 276 | { 277 | #if defined(LPUGL_ASYNC_USE_PTHREAD) 278 | 279 | int rc = pthread_cond_signal(&mutex->condition); 280 | if (rc != 0) { async_util_abort(rc, __LINE__); } 281 | 282 | #elif defined(LPUGL_ASYNC_USE_WINTHREAD) 283 | if (mutex->waitingCounter > 0) { 284 | BOOL wasOk = SetEvent(mutex->event); 285 | if (!wasOk) { 286 | async_util_abort(GetLastError(), __LINE__); 287 | } 288 | } 289 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 290 | int rc = cnd_signal(&mutex->condition); 291 | if (rc != thrd_success) { async_util_abort(rc, __LINE__); } 292 | #endif 293 | } 294 | 295 | /* -------------------------------------------------------------------------------------------- */ 296 | 297 | #endif /* LPUGL_ASYNC_UTIL_H */ 298 | 299 | -------------------------------------------------------------------------------- /src/backend.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_BACKEND_H 2 | #define LPUGL_BACKEND_H 3 | 4 | #include "base.h" 5 | #include "pugl/pugl.h" 6 | 7 | #define LPUGL_BACKEND_MAGIC 0x123451 8 | 9 | struct LpuglWorld; 10 | 11 | typedef struct LpuglBackend { 12 | 13 | int magic; 14 | char versionId[32]; // lpugl.backend-XXX-XX.XX.XX 15 | struct LpuglWorld* world; 16 | int used; 17 | const PuglBackend* puglBackend; 18 | int (*newDrawContext)(lua_State* L, void* context); 19 | int (*finishDrawContext)(lua_State* L, int contextIdx); 20 | void (*closeBackend)(lua_State* L, int backendIdx); 21 | 22 | } LpuglBackend; 23 | 24 | #endif // LPUGL_BACKEND_H 25 | -------------------------------------------------------------------------------- /src/base.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_BASE_H 2 | #define LPUGL_BASE_H 3 | 4 | #include "init.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | /* async_defines.h must be included first */ 15 | #include "async_defines.h" 16 | 17 | #ifdef LPUGL_ASYNC_USE_WIN32 18 | #include 19 | #include 20 | #else 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | /** 30 | * dllexport 31 | * 32 | * see also http://gcc.gnu.org/wiki/Visibility 33 | */ 34 | 35 | #ifndef LPUGL_BUILDING_DLL 36 | #define LPUGL_BUILDING_DLL 1 37 | #endif 38 | 39 | #ifndef LPUGL_DLL_PUBLIC 40 | #if defined _WIN32 || defined __CYGWIN__ 41 | #if LPUGL_BUILDING_DLL 42 | #ifdef __GNUC__ 43 | #define LPUGL_DLL_PUBLIC __attribute__ ((dllexport)) 44 | #else 45 | #define LPUGL_DLL_PUBLIC __declspec(dllexport) /* Note: actually gcc seems to also supports this syntax. */ 46 | #endif 47 | #else 48 | #define LPUGL_DLL_PUBLIC 49 | #endif 50 | #else 51 | #if __GNUC__ >= 4 52 | #pragma GCC visibility push (hidden) 53 | #if LPUGL_BUILDING_DLL 54 | #define LPUGL_DLL_PUBLIC __attribute__ ((visibility ("default"))) 55 | #else 56 | #define LPUGL_DLL_PUBLIC 57 | #endif 58 | #else 59 | #define LPUGL_DLL_PUBLIC 60 | #endif 61 | #endif 62 | #endif 63 | 64 | #define COMPAT53_PREFIX lpugl_compat 65 | #include "compat-5.3.h" 66 | 67 | #endif // LPUGL_BASE_H 68 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | #include "error.h" 2 | 3 | typedef struct Error { 4 | const char* name; 5 | int details; 6 | int traceback; 7 | } Error; 8 | 9 | static void pushErrorMessage(lua_State* L, const char* name, int details) 10 | { 11 | if (details != 0) { 12 | lua_pushfstring(L, "%s: %s", name, 13 | lua_tostring(L, details)); 14 | } else { 15 | lua_pushfstring(L, "%s", name); 16 | } 17 | } 18 | 19 | /* error message details must be on top of stack */ 20 | static int throwErrorMessage(lua_State* L, const char* errorName) 21 | { 22 | int messageDetails = lua_gettop(L); 23 | pushErrorMessage(L, errorName, messageDetails); 24 | lua_remove(L, messageDetails); 25 | return lua_error(L); 26 | } 27 | 28 | static int throwError(lua_State* L, const char* errorName) 29 | { 30 | pushErrorMessage(L, errorName, 0); 31 | return lua_error(L); 32 | } 33 | 34 | 35 | int lpugl_ERROR_UNKNOWN_OBJECT_world_id(lua_State* L, lua_Integer id) 36 | { 37 | lua_pushfstring(L, "world id %d", (int)id); 38 | return throwErrorMessage(L, LPUGL_ERROR_UNKNOWN_OBJECT); 39 | } 40 | 41 | 42 | int lpugl_ERROR_OUT_OF_MEMORY(lua_State* L) 43 | { 44 | return throwError(L, LPUGL_ERROR_OUT_OF_MEMORY); 45 | } 46 | 47 | int lpugl_ERROR_OUT_OF_MEMORY_bytes(lua_State* L, size_t bytes) 48 | { 49 | #if LUA_VERSION_NUM >= 503 50 | lua_pushfstring(L, "failed to allocate %I bytes", (lua_Integer)bytes); 51 | #else 52 | lua_pushfstring(L, "failed to allocate %f bytes", (lua_Number)bytes); 53 | #endif 54 | return throwErrorMessage(L, LPUGL_ERROR_OUT_OF_MEMORY); 55 | } 56 | 57 | int lpugl_ERROR_RESTRICTED_ACCESS(lua_State* L) 58 | { 59 | return throwError(L, LPUGL_ERROR_RESTRICTED_ACCESS); 60 | } 61 | 62 | int lpugl_ERROR_FAILED_OPERATION(lua_State* L) 63 | { 64 | return throwError(L, LPUGL_ERROR_FAILED_OPERATION); 65 | } 66 | 67 | int lpugl_ERROR_FAILED_OPERATION_ex(lua_State* L, const char* message) 68 | { 69 | lua_pushstring(L, message); 70 | return throwErrorMessage(L, LPUGL_ERROR_FAILED_OPERATION); 71 | } 72 | 73 | int lpugl_ERROR_ILLEGAL_STATE(lua_State* L, const char* state) 74 | { 75 | lua_pushstring(L, state); 76 | return throwErrorMessage(L, LPUGL_ERROR_ILLEGAL_STATE); 77 | } 78 | 79 | static void publishError(lua_State* L, int module, const char* errorName) 80 | { 81 | lua_pushfstring(L, "lpugl.error.%s", errorName); 82 | lua_setfield(L, module, errorName); 83 | } 84 | 85 | int lpugl_error_init_module(lua_State* L, int errorModule) 86 | { 87 | publishError(L, errorModule, LPUGL_ERROR_ERROR_IN_EVENT_HANDLING); 88 | publishError(L, errorModule, LPUGL_ERROR_ERROR_IN_ERROR_HANDLING); 89 | publishError(L, errorModule, LPUGL_ERROR_OUT_OF_MEMORY); 90 | 91 | publishError(L, errorModule, LPUGL_ERROR_UNKNOWN_OBJECT); 92 | publishError(L, errorModule, LPUGL_ERROR_RESTRICTED_ACCESS); 93 | publishError(L, errorModule, LPUGL_ERROR_FAILED_OPERATION); 94 | publishError(L, errorModule, LPUGL_ERROR_ILLEGAL_STATE); 95 | 96 | return 0; 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/error.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_ERROR_H 2 | #define LPUGL_ERROR_H 3 | 4 | #include "util.h" 5 | 6 | #define LPUGL_ERROR_ERROR_IN_EVENT_HANDLING "error_in_event_handling" 7 | #define LPUGL_ERROR_ERROR_IN_ERROR_HANDLING "error_in_error_handling" 8 | #define LPUGL_ERROR_ERROR_IN_LOG_FUNC "error_in_log_func" 9 | #define LPUGL_ERROR_OUT_OF_MEMORY "out_of_memory" 10 | 11 | #define LPUGL_ERROR_UNKNOWN_OBJECT "unknown_object" 12 | #define LPUGL_ERROR_RESTRICTED_ACCESS "restricted_access" 13 | #define LPUGL_ERROR_FAILED_OPERATION "failed_operation" 14 | #define LPUGL_ERROR_ILLEGAL_STATE "illegal_state" 15 | 16 | #define lpugl_error(L, errorLiteral) luaL_error((L), "lpugl.error." errorLiteral) 17 | 18 | int lpugl_ERROR_UNKNOWN_OBJECT_world_id(lua_State* L, lua_Integer id); 19 | 20 | int lpugl_ERROR_OUT_OF_MEMORY(lua_State* L); 21 | int lpugl_ERROR_OUT_OF_MEMORY_bytes(lua_State* L, size_t bytes); 22 | 23 | int lpugl_ERROR_RESTRICTED_ACCESS(lua_State* L); 24 | 25 | int lpugl_ERROR_FAILED_OPERATION(lua_State* L); 26 | 27 | int lpugl_ERROR_FAILED_OPERATION_ex(lua_State* L, const char* message); 28 | 29 | int lpugl_ERROR_ILLEGAL_STATE(lua_State* L, const char* state); 30 | 31 | int lpugl_error_init_module(lua_State* L, int errorModule); 32 | 33 | 34 | #endif /* LPUGL_ERROR_H */ 35 | -------------------------------------------------------------------------------- /src/init.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_INIT_H 2 | #define LPUGL_INIT_H 3 | 4 | #if !defined(LPUGL_USE_WIN) && !defined(LPUGL_USE_X11) && !defined(LPUGL_USE_MAC) 5 | #if defined(WIN32) || defined(_WIN32) 6 | #define LPUGL_USE_WIN 7 | #elif defined(__APPLE__) && defined(__MACH__) 8 | #define LPUGL_USE_MAC 9 | #else 10 | #define LPUGL_USE_X11 11 | #endif 12 | #endif 13 | 14 | #if defined _WIN32 15 | #define PUGL_API 16 | #else 17 | #if __GNUC__ >= 4 18 | #define PUGL_API __attribute__ ((visibility ("hidden"))) 19 | #define PUGL_API_PRIVATE __attribute__ ((visibility ("hidden"))) 20 | #else 21 | #define PUGL_API 22 | #define PUGL_API_PRIVATE 23 | #endif 24 | #endif 25 | 26 | #ifndef LPUGL_UNUSED 27 | #if defined(__cplusplus) 28 | # define LPUGL_UNUSED(name) 29 | #elif defined(__GNUC__) 30 | # define LPUGL_UNUSED(name) name##_unused __attribute__((__unused__)) 31 | #else 32 | # define LPUGL_UNUSED(name) name 33 | #endif 34 | #endif 35 | 36 | #if defined(__OBJC__) && defined(LPUGL_MACOS_CLASS_SUFFIX) 37 | 38 | #define PUGL_OBJC_CLASS_NAM3(NAME,SUFFIX) NAME ## _ ## SUFFIX 39 | #define PUGL_OBJC_CLASS_NAM2(NAME,SUFFIX) PUGL_OBJC_CLASS_NAM3(NAME, SUFFIX) 40 | #define PUGL_OBJC_CLASS_NAME(NAME) PUGL_OBJC_CLASS_NAM2(NAME, LPUGL_MACOS_CLASS_SUFFIX) 41 | 42 | #define PuglCairoGLView PUGL_OBJC_CLASS_NAME(PuglCairoGLView) 43 | #define PuglCairoView PUGL_OBJC_CLASS_NAME(PuglCairoView) 44 | #define PuglOpenGLView PUGL_OBJC_CLASS_NAME(PuglOpenGLView) 45 | #define PuglStubView PUGL_OBJC_CLASS_NAME(PuglStubView) 46 | #define PuglWindow PUGL_OBJC_CLASS_NAME(PuglWindow) 47 | #define PuglWindowDelegate PUGL_OBJC_CLASS_NAME(PuglWindowDelegate) 48 | #define PuglWorldProxy PUGL_OBJC_CLASS_NAME(PuglWorldProxy) 49 | #define PuglWrapperView PUGL_OBJC_CLASS_NAME(PuglWrapperView) 50 | 51 | #endif 52 | 53 | 54 | 55 | #endif // LPUGL_INIT_H 56 | -------------------------------------------------------------------------------- /src/lpugl.c: -------------------------------------------------------------------------------- 1 | #include "lpugl.h" 2 | #include "world.h" 3 | #include "view.h" 4 | #include "error.h" 5 | #include "version.h" 6 | 7 | #define LPUGL_MODULE_NAME "lpugl" 8 | 9 | static Mutex global_mutex; 10 | static AtomicCounter initStage = 0; 11 | static bool initialized = false; 12 | static int stateCounter = 0; 13 | 14 | Mutex* lpugl_global_lock = NULL; 15 | AtomicCounter lpugl_id_counter = 0; 16 | 17 | /*static int internalError(lua_State* L, const char* text, int line) 18 | { 19 | return luaL_error(L, "Internal error: %s (%s:%d)", text, LPUGL_MODULE_NAME, line); 20 | }*/ 21 | 22 | 23 | static int Lpugl_initApplication(lua_State* L) 24 | { 25 | PuglApplicationFlags flags = 0; 26 | if (lua_gettop(L) >= 1) { 27 | luaL_checktype(L, 1, LUA_TBOOLEAN); 28 | bool withMultiThreading = lua_toboolean(L, 1); 29 | if (withMultiThreading) { 30 | flags |= PUGL_APPLICATION_THREADS; 31 | } 32 | } 33 | puglInitApplication(flags); 34 | return 0; 35 | } 36 | 37 | static int Lpugl_btest(lua_State* L) 38 | { 39 | luaL_checkinteger(L, 1); 40 | lua_Integer rslt = lua_tointeger(L, 1); 41 | int n = lua_gettop(L); 42 | for (int i = 2; i <= n; ++i) { 43 | luaL_checkinteger(L, i); 44 | rslt &= lua_tointeger(L, i); 45 | } 46 | lua_pushboolean(L, rslt); 47 | return 1; 48 | } 49 | 50 | static int Lpugl_type(lua_State* L) 51 | { 52 | luaL_checkany(L, 1); 53 | int tp = lua_type(L, 1); 54 | if (tp == LUA_TUSERDATA) { 55 | if (lua_getmetatable(L, 1)) { 56 | if (lua_getfield(L, -1, "__name") == LUA_TSTRING) { 57 | lua_pushvalue(L, -1); 58 | if (lua_gettable(L, LUA_REGISTRYINDEX) == LUA_TTABLE) { 59 | if (lua_rawequal(L, -3, -1)) { 60 | lua_pop(L, 1); 61 | return 1; 62 | } 63 | } 64 | } 65 | } 66 | } 67 | lua_pushstring(L, lua_typename(L, tp)); 68 | return 1; 69 | } 70 | 71 | static const luaL_Reg ModuleFunctions[] = 72 | { 73 | { "initApplication", Lpugl_initApplication }, 74 | { "type", Lpugl_type }, 75 | { NULL, NULL } /* sentinel */ 76 | }; 77 | 78 | static int handleClosingLuaState(lua_State* LPUGL_UNUSED(L)) 79 | { 80 | async_mutex_lock(lpugl_global_lock); 81 | stateCounter -= 1; 82 | if (stateCounter == 0) { 83 | 84 | } 85 | async_mutex_unlock(lpugl_global_lock); 86 | return 0; 87 | } 88 | 89 | 90 | LPUGL_DLL_PUBLIC int luaopen_lpugl(lua_State* L) 91 | { 92 | luaL_checkversion(L); /* does nothing if compiled for Lua 5.1 */ 93 | 94 | /* ---------------------------------------- */ 95 | 96 | if (atomic_get(&initStage) != 2) { 97 | if (atomic_set_if_equal(&initStage, 0, 1)) { 98 | async_mutex_init(&global_mutex); 99 | lpugl_global_lock = &global_mutex; 100 | atomic_set(&initStage, 2); 101 | } 102 | else { 103 | while (atomic_get(&initStage) != 2) { 104 | Mutex waitMutex; 105 | async_mutex_init(&waitMutex); 106 | async_mutex_lock(&waitMutex); 107 | async_mutex_wait_millis(&waitMutex, 1); 108 | async_mutex_destruct(&waitMutex); 109 | } 110 | } 111 | } 112 | /* ---------------------------------------- */ 113 | 114 | async_mutex_lock(lpugl_global_lock); 115 | { 116 | if (!initialized) { 117 | /* create initial id that could not accidently be mistaken with "normal" integers */ 118 | const char* ptr = LPUGL_MODULE_NAME; 119 | AtomicCounter c = 0; 120 | if (sizeof(AtomicCounter) - 1 >= 1) { 121 | size_t i; 122 | for (i = 0; i < 2 * sizeof(char*); ++i) { 123 | c ^= ((int)(((char*)&ptr)[(i + 1) % sizeof(char*)]) & 0xff) << ((i % (sizeof(AtomicCounter) - 1))*8); 124 | } 125 | lua_Number t = lpugl_current_time_seconds(); 126 | for (i = 0; i < 2 * sizeof(lua_Number); ++i) { 127 | c ^= ((int)(((char*)&t)[(i + 1) % sizeof(lua_Number)]) & 0xff) << ((i % (sizeof(AtomicCounter) - 1))*8); 128 | } 129 | } 130 | lpugl_id_counter = c; 131 | initialized = true; 132 | } 133 | 134 | 135 | /* check if initialization has been done for this lua state */ 136 | lua_pushlightuserdata(L, (void*)&initialized); /* unique void* key */ 137 | lua_rawget(L, LUA_REGISTRYINDEX); 138 | bool alreadyInitializedForThisLua = !lua_isnil(L, -1); 139 | lua_pop(L, 1); 140 | 141 | if (!alreadyInitializedForThisLua) 142 | { 143 | stateCounter += 1; 144 | 145 | lua_pushlightuserdata(L, (void*)&initialized); /* unique void* key */ 146 | lua_newuserdata(L, 1); /* sentinel for closing lua state */ 147 | lua_newtable(L); /* metatable for sentinel */ 148 | lua_pushstring(L, "__gc"); 149 | lua_pushcfunction(L, handleClosingLuaState); 150 | lua_rawset(L, -3); /* metatable.__gc = handleClosingLuaState */ 151 | lua_setmetatable(L, -2); /* sets metatable for sentinal table */ 152 | lua_rawset(L, LUA_REGISTRYINDEX); /* sets sentinel as value for unique void* in registry */ 153 | } 154 | } 155 | async_mutex_unlock(lpugl_global_lock); 156 | 157 | /* ---------------------------------------- */ 158 | 159 | int n = lua_gettop(L); 160 | 161 | int module = ++n; lua_newtable(L); 162 | int errorModule = ++n; lua_newtable(L); 163 | 164 | lua_pushvalue(L, module); 165 | luaL_setfuncs(L, ModuleFunctions, 0); 166 | lua_pop(L, 1); 167 | 168 | lua_pushliteral(L, LPUGL_VERSION_STRING); 169 | lua_setfield(L, module, "_VERSION"); 170 | 171 | { 172 | #if defined(LPUGL_ASYNC_USE_WIN32) 173 | #define LPUGL_INFO1 "WIN32" 174 | #elif defined(LPUGL_ASYNC_USE_GNU) 175 | #define LPUGL_INFO1 "GNU" 176 | #elif defined(LPUGL_ASYNC_USE_STDATOMIC) 177 | #define LPUGL_INFO1 "STD" 178 | #endif 179 | #if defined(LPUGL_ASYNC_USE_WINTHREAD) 180 | #define LPUGL_INFO2 "WIN32" 181 | #elif defined(LPUGL_ASYNC_USE_PTHREAD) 182 | #define LPUGL_INFO2 "PTHREAD" 183 | #elif defined(LPUGL_ASYNC_USE_STDTHREAD) 184 | #define LPUGL_INFO2 "STD" 185 | #endif 186 | lua_pushstring(L, LPUGL_MODULE_NAME 187 | " (Version:" LPUGL_VERSION_STRING ",Platform:" LPUGL_PLATFORM_STRING 188 | ",Atomic:" LPUGL_INFO1 ",Thread:" LPUGL_INFO2 189 | ",Date:" LPUGL_BUILD_DATE_STRING ")" ); 190 | lua_setfield(L, module, "_INFO"); 191 | } 192 | 193 | lua_pushliteral(L, LPUGL_PLATFORM_STRING); /* -> string */ 194 | lua_setfield(L, module, "platform"); /* -> */ 195 | 196 | lua_pushinteger(L, PUGL_MOD_SHIFT); /* -> integer */ 197 | lua_setfield(L, module, "MOD_SHIFT"); /* -> */ 198 | 199 | lua_pushinteger(L, PUGL_MOD_CTRL); /* -> integer */ 200 | lua_setfield(L, module, "MOD_CTRL"); /* -> */ 201 | 202 | lua_pushinteger(L, PUGL_MOD_ALT); /* -> integer */ 203 | lua_setfield(L, module, "MOD_ALT"); /* -> */ 204 | 205 | lua_pushinteger(L, PUGL_MOD_ALTGR); /* -> integer */ 206 | lua_setfield(L, module, "MOD_ALTGR"); /* -> */ 207 | 208 | lua_pushinteger(L, PUGL_MOD_SUPER); /* -> integer */ 209 | lua_setfield(L, module, "MOD_SUPER"); /* -> */ 210 | 211 | lua_pushvalue(L, errorModule); 212 | lua_setfield(L, module, "error"); 213 | 214 | lua_pushcfunction(L, Lpugl_btest); /* -> func */ 215 | lua_setfield(L, module, "btest"); /* -> */ 216 | 217 | lua_checkstack(L, LUA_MINSTACK); 218 | 219 | lpugl_world_init_module (L, module); 220 | lpugl_view_init_module (L, module); 221 | lpugl_error_init_module (L, errorModule); 222 | 223 | lua_newtable(L); /* -> meta */ 224 | lua_pushliteral(L, "lpugl"); /* -> meta, string */ 225 | lua_setfield(L, -2, "__metatable"); /* -> meta */ 226 | lua_setmetatable(L, module); /* -> */ 227 | 228 | lua_settop(L, module); 229 | return 1; 230 | } 231 | -------------------------------------------------------------------------------- /src/lpugl.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_MAIN_H 2 | #define LPUGL_MAIN_H 3 | 4 | #include "util.h" 5 | #include "async_util.h" 6 | 7 | extern Mutex* lpugl_global_lock; 8 | extern AtomicCounter lpugl_id_counter; 9 | 10 | LPUGL_DLL_PUBLIC int luaopen_lpugl(lua_State* L); 11 | 12 | #endif /* LPUGL_MAIN_H */ 13 | -------------------------------------------------------------------------------- /src/lpugl_cairo.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_CAIRO_MAIN_H 2 | #define LPUGL_CAIRO_MAIN_H 3 | 4 | #include "base.h" 5 | 6 | LPUGL_DLL_PUBLIC int luaopen_lpugl_cairo(lua_State* L); 7 | 8 | #endif // LPUGL_CAIRO_MAIN_H 9 | -------------------------------------------------------------------------------- /src/lpugl_compat.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | #include "compat-5.3.c" 3 | 4 | /* see: https://github.com/keplerproject/lua-compat-5.3 */ 5 | -------------------------------------------------------------------------------- /src/lpugl_opengl.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_OPENGL_MAIN_H 2 | #define LPUGL_OPENGL_MAIN_H 3 | 4 | #include "base.h" 5 | 6 | LPUGL_DLL_PUBLIC int luaopen_lpugl_opengl(lua_State* L); 7 | 8 | #endif // LPUGL_OPENGL_MAIN_H 9 | -------------------------------------------------------------------------------- /src/notify_capi.h: -------------------------------------------------------------------------------- 1 | #ifndef NOTIFY_CAPI_H 2 | #define NOTIFY_CAPI_H 3 | 4 | #define NOTIFY_CAPI_ID_STRING "_capi_notify" 5 | #define NOTIFY_CAPI_VERSION_MAJOR 0 6 | #define NOTIFY_CAPI_VERSION_MINOR 0 7 | #define NOTIFY_CAPI_VERSION_PATCH 2 8 | 9 | #ifndef NOTIFY_CAPI_IMPLEMENT_SET_CAPI 10 | # define NOTIFY_CAPI_IMPLEMENT_SET_CAPI 0 11 | #endif 12 | 13 | #ifndef NOTIFY_CAPI_IMPLEMENT_GET_CAPI 14 | # define NOTIFY_CAPI_IMPLEMENT_GET_CAPI 0 15 | #endif 16 | 17 | #ifdef __cplusplus 18 | 19 | extern "C" { 20 | 21 | struct notify_notifier; 22 | struct notify_capi; 23 | 24 | #else /* __cplusplus */ 25 | 26 | typedef struct notify_notifier notify_notifier; 27 | typedef struct notify_capi notify_capi; 28 | 29 | #endif /* ! __cplusplus */ 30 | 31 | /** 32 | * Type for pointer to function that may be called if an error occurs. 33 | * ehdata: void pointer that is given in notify method (see below) 34 | * msg: detailed error message 35 | * msglen: length of error message 36 | */ 37 | typedef void (*notifier_error_handler)(void* ehdata, const char* msg, size_t msglen); 38 | 39 | /** 40 | * Notify C API. 41 | */ 42 | struct notify_capi 43 | { 44 | int version_major; 45 | int version_minor; 46 | int version_patch; 47 | 48 | /** 49 | * May point to another (incompatible) version of this API implementation. 50 | * NULL if no such implementation exists. 51 | * 52 | * The usage of next_capi makes it possible to implement two or more 53 | * incompatible versions of the C API. 54 | * 55 | * An API is compatible to another API if both have the same major 56 | * version number and if the minor version number of the first API is 57 | * greater or equal than the second one's. 58 | */ 59 | void* next_capi; 60 | 61 | /** 62 | * Must return a valid pointer if the Lua object at the given stack 63 | * index is a valid notifier, otherwise must return NULL. 64 | * 65 | * The returned notifier object must be valid as long as the Lua 66 | * object at the given stack index remains valid. 67 | * To keep the notifier object beyond this call, the function 68 | * retainNotifier() should be called (see below). 69 | */ 70 | notify_notifier* (*toNotifier)(lua_State* L, int index); 71 | 72 | /** 73 | * Increase the reference counter of the notifier object. 74 | * Must be thread safe. 75 | * 76 | * This function must be called after the function toNotifier() 77 | * as long as the Lua object on the given stack index is 78 | * valid (see above). 79 | */ 80 | void (*retainNotifier)(notify_notifier* n); 81 | 82 | /** 83 | * Decrease the reference counter of the notifier object and 84 | * destructs the notifier object if no reference is left. 85 | * Must be thread safe. 86 | */ 87 | void (*releaseNotifier)(notify_notifier* n); 88 | 89 | /** 90 | * Calls the notify method of the given notifier object. 91 | * Must be thread safe. 92 | * 93 | * eh: error handling function, may be NULL 94 | * ehdata: additional data that is given to error handling function. 95 | 96 | * Returns 0 - on success otherwise an error code != 0. 97 | * 1 - if notifier is closed. The caller is expected to release the notifier. 98 | * Subsequent calls will always result this return code again. 99 | * All other error codes are implementation specific. 100 | */ 101 | int (*notify)(notify_notifier* n, notifier_error_handler eh, void* ehdata); 102 | }; 103 | 104 | #if NOTIFY_CAPI_IMPLEMENT_SET_CAPI 105 | /** 106 | * Sets the Notify C API into the metatable at the given index. 107 | * 108 | * index: index of the table that is be used as metatable for objects 109 | * that are associated to the given capi. 110 | */ 111 | static int notify_set_capi(lua_State* L, int index, const notify_capi* capi) 112 | { 113 | lua_pushlstring(L, NOTIFY_CAPI_ID_STRING, strlen(NOTIFY_CAPI_ID_STRING)); /* -> key */ 114 | void** udata = (void**) lua_newuserdata(L, sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1); /* -> key, value */ 115 | *udata = (void*)capi; 116 | strcpy((char*)(udata + 1), NOTIFY_CAPI_ID_STRING); /* -> key, value */ 117 | lua_rawset(L, (index < 0) ? (index - 2) : index); /* -> */ 118 | return 0; 119 | } 120 | #endif /* NOTIFY_CAPI_IMPLEMENT_SET_CAPI */ 121 | 122 | #if NOTIFY_CAPI_IMPLEMENT_GET_CAPI 123 | /** 124 | * Gives the associated Notify C API for the object at the given stack index. 125 | * Returns NULL, if the object at the given stack index does not have an 126 | * associated Notify C API or only has a Notify C API with incompatible version 127 | * number. If errorReason is not NULL it receives the error reason in this case: 128 | * 1 for incompatible version nummber and 2 for no associated C API at all. 129 | */ 130 | static const notify_capi* notify_get_capi(lua_State* L, int index, int* errorReason) 131 | { 132 | if (luaL_getmetafield(L, index, NOTIFY_CAPI_ID_STRING) != LUA_TNIL) /* -> _capi */ 133 | { 134 | const void** udata = (const void**) lua_touserdata(L, -1); /* -> _capi */ 135 | 136 | if ( udata 137 | && (lua_rawlen(L, -1) >= sizeof(void*) + strlen(NOTIFY_CAPI_ID_STRING) + 1) 138 | && (memcmp((char*)(udata + 1), NOTIFY_CAPI_ID_STRING, 139 | strlen(NOTIFY_CAPI_ID_STRING) + 1) == 0)) 140 | { 141 | const notify_capi* capi = (const notify_capi*) *udata; /* -> _capi */ 142 | while (capi) { 143 | if ( capi->version_major == NOTIFY_CAPI_VERSION_MAJOR 144 | && capi->version_minor >= NOTIFY_CAPI_VERSION_MINOR) 145 | { /* -> _capi */ 146 | lua_pop(L, 1); /* -> */ 147 | return capi; 148 | } 149 | capi = (const notify_capi*) capi->next_capi; 150 | } 151 | if (errorReason) { 152 | *errorReason = 1; 153 | } 154 | } else { /* -> _capi */ 155 | if (errorReason) { 156 | *errorReason = 2; 157 | } 158 | } 159 | lua_pop(L, 1); /* -> */ 160 | } else { /* -> */ 161 | if (errorReason) { 162 | *errorReason = 2; 163 | } 164 | } 165 | return NULL; 166 | } 167 | #endif /* NOTIFY_CAPI_IMPLEMENT_GET_CAPI */ 168 | 169 | #ifdef __cplusplus 170 | } /* extern "C" */ 171 | #endif 172 | 173 | #endif /* NOTIFY_CAPI_H */ 174 | -------------------------------------------------------------------------------- /src/pugl.c: -------------------------------------------------------------------------------- 1 | #include "init.h" 2 | 3 | #include "pugl-repo/src/implementation.c" 4 | 5 | #if defined(LPUGL_USE_WIN) 6 | #include "pugl-repo/src/win.c" 7 | 8 | #elif defined(LPUGL_USE_MAC) 9 | #error use pugl.m for mac 10 | 11 | #elif defined(LPUGL_USE_X11) 12 | #include "pugl-repo/src/x11.c" 13 | 14 | #else 15 | #error missing platform definition 16 | #endif 17 | -------------------------------------------------------------------------------- /src/pugl.m: -------------------------------------------------------------------------------- 1 | #include "init.h" 2 | 3 | #include "pugl-repo/src/implementation.c" 4 | 5 | #if defined(LPUGL_USE_MAC) 6 | #include "pugl-repo/src/mac.m" 7 | #else 8 | #error use pugl.c 9 | #endif 10 | -------------------------------------------------------------------------------- /src/pugl_cairo.c: -------------------------------------------------------------------------------- 1 | #include "init.h" 2 | 3 | #if defined(LPUGL_USE_WIN) 4 | #include "pugl-repo/src/win_cairo.c" 5 | #include "pugl-repo/src/win_stub.c" 6 | 7 | #elif defined(LPUGL_USE_MAC) 8 | #error use pugl_cairo.m for mac 9 | 10 | #elif defined(LPUGL_USE_X11) 11 | #include "pugl-repo/src/x11_cairo.c" 12 | #include "pugl-repo/src/x11_stub.c" 13 | 14 | #else 15 | #error missing platform definition 16 | #endif 17 | -------------------------------------------------------------------------------- /src/pugl_cairo.m: -------------------------------------------------------------------------------- 1 | #include "init.h" 2 | 3 | #if defined(LPUGL_USE_MAC) 4 | #include "pugl-repo/src/mac_cairo_gl.m" 5 | 6 | #else 7 | #error use pugl_cairo.c 8 | #endif 9 | -------------------------------------------------------------------------------- /src/pugl_opengl.c: -------------------------------------------------------------------------------- 1 | #include "init.h" 2 | 3 | #if defined(LPUGL_USE_WIN) 4 | #include "pugl-repo/src/win_gl.c" 5 | #include "pugl-repo/src/win_stub.c" 6 | 7 | #elif defined(LPUGL_USE_MAC) 8 | #error use pugl_opengl.m for mac 9 | 10 | #elif defined(LPUGL_USE_X11) 11 | #include "pugl-repo/src/x11_gl.c" 12 | #include "pugl-repo/src/x11_stub.c" 13 | 14 | #else 15 | #error missing platform definition 16 | #endif 17 | -------------------------------------------------------------------------------- /src/pugl_opengl.m: -------------------------------------------------------------------------------- 1 | #include "init.h" 2 | 3 | #if defined(LPUGL_USE_MAC) 4 | #include "pugl-repo/src/mac_gl.m" 5 | 6 | #else 7 | #error use pugl_opengl.c 8 | #endif 9 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | lua_Number lpugl_current_time_seconds() 4 | { 5 | lua_Number rslt; 6 | #ifdef LPUGL_ASYNC_USE_WIN32 7 | struct _timeb timeval; 8 | _ftime(&timeval); 9 | rslt = ((lua_Number)timeval.time) + ((lua_Number)timeval.millitm) * 0.001; 10 | #else 11 | struct timeval timeval; 12 | gettimeofday(&timeval, NULL); 13 | rslt = ((lua_Number)timeval.tv_sec) + ((lua_Number)timeval.tv_usec) * 0.000001; 14 | #endif 15 | return rslt; 16 | } 17 | 18 | 19 | bool lpugl_membuf_init(MemBuffer* b, size_t initialCapacity, lua_Number growFactor) 20 | { 21 | memset(b, 0, sizeof(MemBuffer)); 22 | b->growFactor = growFactor; 23 | if (initialCapacity > 0) { 24 | char* data = malloc(initialCapacity); 25 | if (data != NULL) { 26 | b->bufferData = data; 27 | b->bufferStart = data; 28 | b->bufferCapacity = initialCapacity; 29 | return true; 30 | } else { 31 | return false; 32 | } 33 | } else { 34 | return true; 35 | } 36 | } 37 | 38 | void lpugl_membuf_free(MemBuffer* b) 39 | { 40 | if (b->bufferData) { 41 | free(b->bufferData); 42 | b->bufferData = NULL; 43 | b->bufferStart = NULL; 44 | b->bufferLength = 0; 45 | b->bufferCapacity = 0; 46 | } 47 | } 48 | 49 | /** 50 | * 0 : ok 51 | * 1 : buffer should not grow 52 | * 2 : buffer can not grow 53 | */ 54 | int lpugl_membuf_reserve(MemBuffer* b, size_t additionalLength) 55 | { 56 | size_t newLength = b->bufferLength + additionalLength; 57 | 58 | if (b->bufferStart - b->bufferData + newLength > b->bufferCapacity) 59 | { 60 | memmove(b->bufferData, b->bufferStart, b->bufferLength); 61 | b->bufferStart = b->bufferData; 62 | 63 | if (newLength > b->bufferCapacity) { 64 | if (b->bufferData == NULL) { 65 | size_t newCapacity = 2 * (newLength); 66 | b->bufferData = malloc(newCapacity); 67 | if (b->bufferData == NULL) { 68 | return 2; 69 | } 70 | b->bufferStart = b->bufferData; 71 | b->bufferCapacity = newCapacity; 72 | } else if (b->growFactor > 0) { 73 | size_t newCapacity = b->bufferCapacity * b->growFactor; 74 | if (newCapacity < newLength) { 75 | newCapacity = newLength; 76 | } 77 | char* newData = realloc(b->bufferData, newCapacity); 78 | if (newData == NULL) { 79 | return 2; 80 | } 81 | b->bufferData = newData; 82 | b->bufferStart = newData; 83 | b->bufferCapacity = newCapacity; 84 | } else { 85 | return 1; 86 | } 87 | } 88 | } 89 | return 0; 90 | } 91 | 92 | void lpugl_util_quote_lstring(lua_State* L, const char* s, size_t len) 93 | { 94 | if (s) { 95 | luaL_Buffer tmp; 96 | luaL_buffinit(L, &tmp); 97 | luaL_addchar(&tmp, '"'); 98 | size_t i; 99 | for (i = 0; i < len; ++i) { 100 | char c = s[i]; 101 | if (c == 0) { 102 | luaL_addstring(&tmp, "\\0"); 103 | } else if (c == '"') { 104 | luaL_addstring(&tmp, "\\\""); 105 | } else if (c == '\\') { 106 | luaL_addstring(&tmp, "\\\\"); 107 | } else { 108 | luaL_addchar(&tmp, c); 109 | } 110 | } 111 | luaL_addchar(&tmp, '"'); 112 | luaL_pushresult(&tmp); 113 | } else { 114 | lua_pushstring(L, "nil"); 115 | } 116 | } 117 | 118 | void lpugl_util_quote_string(lua_State* L, const char* s) 119 | { 120 | lpugl_util_quote_lstring(L, s, (s != NULL) ? strlen(s) : 0); 121 | } 122 | 123 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_UTIL_H 2 | #define LPUGL_UTIL_H 3 | 4 | #include "base.h" 5 | 6 | lua_Number lpugl_current_time_seconds(); 7 | 8 | typedef struct MemBuffer { 9 | lua_Number growFactor; 10 | char* bufferData; 11 | char* bufferStart; 12 | size_t bufferLength; 13 | size_t bufferCapacity; 14 | } MemBuffer; 15 | 16 | bool lpugl_membuf_init(MemBuffer* b, size_t initialCapacity, lua_Number growFactor); 17 | 18 | void lpugl_membuf_free(MemBuffer* b); 19 | 20 | /** 21 | * 0 : ok 22 | * 1 : buffer should not grow 23 | * 2 : buffer can not grow 24 | */ 25 | int lpugl_membuf_reserve(MemBuffer* b, size_t additionalLength); 26 | 27 | 28 | void lpugl_util_quote_lstring(lua_State* L, const char* s, size_t len); 29 | 30 | void lpugl_util_quote_string(lua_State* L, const char* s); 31 | 32 | #endif /* LPUGL_UTIL_H */ 33 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_VERSION_H 2 | #define LPUGL_VERSION_H 3 | 4 | #include "init.h" 5 | 6 | #ifndef LPUGL_VERSION 7 | #error LPUGL_VERSION is not defined 8 | #endif 9 | 10 | #ifndef LPUGL_BUILD_DATE 11 | #error LPUGL_BUILD_DATE is not defined 12 | #endif 13 | 14 | #define LPUGL_STRINGIFY(x) #x 15 | #define LPUGL_TOSTRING(x) LPUGL_STRINGIFY(x) 16 | #define LPUGL_VERSION_STRING LPUGL_TOSTRING(LPUGL_VERSION) 17 | 18 | #define LPUGL_BUILD_DATE_STRING LPUGL_TOSTRING(LPUGL_BUILD_DATE) 19 | 20 | #if defined(LPUGL_USE_WIN) 21 | #define LPUGL_PLATFORM WIN 22 | #elif defined(LPUGL_USE_MAC) 23 | #define LPUGL_PLATFORM MAC 24 | #elif defined(LPUGL_USE_X11) 25 | #define LPUGL_PLATFORM X11 26 | #endif 27 | 28 | #define LPUGL_PLATFORM_STRING LPUGL_TOSTRING(LPUGL_PLATFORM) 29 | 30 | 31 | #endif // LPUGL_VERSION_H 32 | -------------------------------------------------------------------------------- /src/view.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_VIEW_H 2 | #define LPUGL_VIEW_H 3 | 4 | #include "base.h" 5 | 6 | #include "pugl/pugl.h" 7 | 8 | struct LpuglWorld; 9 | struct ViewUserData; 10 | 11 | /* ============================================================================================ */ 12 | 13 | // uservalue indices 14 | #define LPUGL_VIEW_UV_BACKEND -3 15 | #define LPUGL_VIEW_UV_CHILDVIEWS -2 16 | #define LPUGL_VIEW_UV_DRAWCTX -1 17 | #define LPUGL_VIEW_UV_EVENTFUNC 0 18 | 19 | /* ============================================================================================ */ 20 | 21 | int lpugl_view_init_module(lua_State* L, int module); 22 | 23 | int lpugl_view_new(lua_State* L, struct LpuglWorld* world, int initArg, int viewLookup); 24 | 25 | bool lpugl_view_close(lua_State* L, struct ViewUserData* udata, int udataIdx); 26 | 27 | 28 | #endif /* LPUGL_VIEW_H */ 29 | -------------------------------------------------------------------------------- /src/world.h: -------------------------------------------------------------------------------- 1 | #ifndef LPUGL_WORLD_H 2 | #define LPUGL_WORLD_H 3 | 4 | #include "base.h" 5 | #include "util.h" 6 | #include "async_util.h" 7 | 8 | #include "pugl/pugl.h" 9 | 10 | /* ============================================================================================ */ 11 | 12 | // uservalue indices 13 | #define LPUGL_WORLD_UV_VIEWS 1 14 | #define LPUGL_WORLD_UV_ERRFUNC 2 15 | #define LPUGL_WORLD_UV_EVENTL 3 16 | #define LPUGL_WORLD_UV_PROCFUNC 4 17 | #define LPUGL_WORLD_UV_LOGFUNC 5 18 | #define LPUGL_WORLD_UV_DEFBACKEND 6 19 | #define LPUGL_WORLD_UV_BACKENDS 7 20 | 21 | /* ============================================================================================ */ 22 | 23 | struct LpuglBackend; 24 | 25 | typedef struct LpuglWorld { 26 | Lock lock; 27 | lua_Integer id; 28 | AtomicCounter used; 29 | PuglWorld* puglWorld; 30 | struct LpuglWorld* nextWorld; 31 | int weakWorldRef; 32 | int viewCount; 33 | lua_State* eventL; 34 | bool inCallback; 35 | bool hadEvent; 36 | bool mustClosePugl; 37 | AtomicCounter awakeSent; 38 | void (*registrateBackend)(lua_State* L, int worldIdx, int backendIdx); 39 | void (*deregistrateBackend)(lua_State* L, int worldIdx, int backendIdx); 40 | } LpuglWorld; 41 | 42 | /* ============================================================================================ */ 43 | 44 | #define LPUGL_WORLD_MAGIC 0x123450 45 | 46 | typedef struct WorldUserData { 47 | int magic; 48 | char versionId[32]; // lpugl.world-XXX-XX.XX.XX 49 | LpuglWorld* world; 50 | lua_Integer id; 51 | bool restricted; 52 | } WorldUserData; 53 | 54 | /* ============================================================================================ */ 55 | 56 | int lpugl_world_errormsghandler(lua_State* L); 57 | 58 | void lpugl_world_close_pugl(LpuglWorld* world); 59 | 60 | int lpugl_world_init_module(lua_State* L, int module); 61 | 62 | /* ============================================================================================ */ 63 | 64 | #endif // LPUGL_WORLD_H 65 | 66 | -------------------------------------------------------------------------------- /thirdparty/lua-compat-license: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Kepler Project. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /thirdparty/nanovg-0.1.2-2.rockspec: -------------------------------------------------------------------------------- 1 | package = "nanovg" 2 | version = "0.1.2-2" 3 | source = { 4 | url = "https://github.com/starwing/lua-nanovg/releases/download/0.1.2/lua-nanovg-0.1.2.zip", 5 | tag = "0.1.2-0", 6 | dir = "lua-nanovg-0.1.2" 7 | } 8 | description = { 9 | summary = "Lua binding for NanoVG", 10 | homepage = "https://github.com/starwing/lua-nanovg", 11 | license = "MIT" 12 | } 13 | dependencies = { 14 | "lua >= 5.1, <= 5.4", 15 | "luarocks-build-extended" 16 | } 17 | build = { 18 | type = "extended", 19 | 20 | external_dependencies = { 21 | GL = { header = "GL/gl.h" } 22 | }, 23 | 24 | modules = { 25 | nvg = { 26 | sources = { 27 | "lua-nanovg.c", 28 | "nanovg/src/nanovg.c" 29 | }, 30 | incdirs = { "include", "$(GL_INCDIR)" }, 31 | libdirs = { "$(GL_LIBDIR)" }, 32 | } 33 | }, 34 | platforms = { 35 | linux = { 36 | modules = { 37 | nvg = { 38 | libraries = { 39 | "GL" 40 | } 41 | } 42 | } 43 | }, 44 | windows = { 45 | modules = { 46 | nvg = { 47 | libraries = { 48 | "opengl32" 49 | } 50 | } 51 | } 52 | }, 53 | macosx = { 54 | external_dependencies = { 55 | -- for Mac OS: OpenGL headers are under "$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/OpenGL.framework/Headers" 56 | -- (e.g. /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework/Headers) 57 | -- They must be included using "OpenGL/gl.h" and cannot be found by luarocks in the file system 58 | GL = { header = false }, 59 | GLU = { header = false } 60 | }, 61 | modules = { 62 | nvg = { 63 | libraries = { }, 64 | variables = { 65 | LIBFLAG_EXTRAS = { "-framework", "OpenGL" } 66 | } 67 | } 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /thirdparty/opengl-1.11-2.rockspec: -------------------------------------------------------------------------------- 1 | -- 2 | -- Modified rockspec for building LuaGL for Linux, Windows and Mac OS, 3 | -- see the original: https://luarocks.org/modules/blueowl04/opengl/1.11-1 4 | -- 5 | package = "OpenGL" 6 | version = "1.11-2" 7 | source = { 8 | url = "https://downloads.sourceforge.net/project/luagl/1.11/Docs%20and%20Sources/luagl-1.11_Sources.tar.gz", 9 | dir="luagl", 10 | md5 = "65c849dbd60007689d427cd019f95d17" 11 | } 12 | description = { 13 | summary = "Lua bindings for OpenGL", 14 | detailed = [[ 15 | LuaGL is a library that provides access to the OpenGL 1.x 16 | functionality from Lua 5.x. OpenGL is a portable software 17 | interface to graphics hardware. 18 | ]], 19 | homepage = "http://luagl.sourceforge.net/", 20 | license = "MIT/X11" 21 | } 22 | dependencies = { 23 | "lua >= 5.1", 24 | "luarocks-build-extended" 25 | } 26 | build = { 27 | type = "extended", 28 | 29 | external_dependencies = { 30 | GL = { header = "GL/gl.h" }, 31 | GLU = { header = "GL/glu.h" } 32 | }, 33 | platforms = { 34 | linux = { 35 | modules = { 36 | luagl = { 37 | libraries = { "GL" } 38 | }, 39 | luaglu = { 40 | libraries = { "GLU" } 41 | } 42 | } 43 | }, 44 | windows = { 45 | modules = { 46 | luagl = { 47 | libraries = { "opengl32" } 48 | }, 49 | luaglu = { 50 | libraries = { "glu32" } 51 | } 52 | } 53 | }, 54 | macosx = { 55 | external_dependencies = { 56 | -- for Mac OS: OpenGL headers are under "$(xcrun --sdk macosx --show-sdk-path)/System/Library/Frameworks/OpenGL.framework/Headers" 57 | -- (e.g. /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/OpenGL.framework/Headers) 58 | -- They must be included using "OpenGL/gl.h" and cannot be found by luarocks in the file system 59 | GL = { header = false }, 60 | GLU = { header = false } 61 | }, 62 | modules = { 63 | luagl = { 64 | defines = { "MacOS" }, 65 | libraries = { }, 66 | variables = { 67 | LIBFLAG_EXTRAS = { "-framework", "OpenGL" } 68 | } 69 | }, 70 | luaglu = { 71 | defines = { "MacOS" }, 72 | libraries = { }, 73 | variables = { 74 | LIBFLAG_EXTRAS = { "-framework", "OpenGL" } 75 | } 76 | } 77 | } 78 | } 79 | }, 80 | modules = { 81 | luagl = { 82 | sources = { "src/luagl.c", "src/luagl_util.c", "src/luagl_const.c" }, 83 | incdirs = { "include", "$(GL_INCDIR)" }, 84 | libdirs = { "$(GL_LIBDIR)" }, 85 | }, 86 | luaglu = { 87 | sources = { "src/luaglu.c", "src/luagl_util.c", "src/luagl_const.c" }, 88 | incdirs = { "include", "$(GLU_INCDIR)", "$(GL_INCDIR)" }, 89 | libdirs = { "$(GLU_LIBDIR)" }, 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /thirdparty/pugl-license: -------------------------------------------------------------------------------- 1 | Copyright 2011-2019 David Robillard 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted, provided that the above 5 | copyright notice and this permission notice appear in all copies. 6 | 7 | THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 10 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 12 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 13 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 | --------------------------------------------------------------------------------