• R/O
  • HTTP
  • SSH
  • HTTPS

コミット

タグ
未設定

よく使われているワード(クリックで追加)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

Graphics library for Mercury, including OpenGL bindings, TGA image reading, and X11, Win32, and SDL2 windowing and input.


コミットメタ情報

リビジョンbde46bead99fec6dfa3bb1bb1d2a1186c2487341 (tree)
日時2023-06-27 04:12:19
作者AlaskanEmily <emily@alas...>
コミッターAlaskanEmily

ログメッセージ

Add GLFW delegate window backend.

This doesn't yet have a Java implementation, but providing one with LWJGL is
the next step for a usable Java backend.

変更サマリ

差分

--- /dev/null
+++ b/demo/saffron_glfw_demo.m
@@ -0,0 +1,310 @@
1+% Released as public domain by Transnat Games for demonstration purposes.
2+%
3+% Any copyright is dedicated to the Public Domain.
4+% https://creativecommons.org/publicdomain/zero/1.0/
5+
6+:- module saffron_glfw_demo.
7+
8+%=============================================================================%
9+:- interface.
10+%=============================================================================%
11+
12+:- use_module io.
13+
14+%-----------------------------------------------------------------------------%
15+
16+:- pred main(io.io::di, io.io::uo) is det.
17+
18+%=============================================================================%
19+:- implementation.
20+%=============================================================================%
21+
22+:- use_module bool.
23+:- use_module exception.
24+:- import_module float.
25+:- import_module int.
26+:- import_module list.
27+:- use_module math.
28+:- use_module maybe.
29+:- use_module thread.
30+:- use_module thread.mvar.
31+
32+:- use_module mmath.
33+:- use_module mmath.matrix.
34+:- use_module mmath.vector.
35+:- import_module mmath.vector.vector3.
36+:- import_module mmath.vector.vector4.
37+
38+:- use_module saffron.
39+:- use_module saffron.draw.
40+:- import_module saffron.geometry.
41+:- use_module saffron.gl.
42+:- use_module saffron.gl2.
43+:- use_module saffron.soft.
44+:- use_module saffron_window.
45+:- use_module saffron_delegate.
46+:- use_module saffron_glfw.
47+:- use_module saffron_tga.
48+
49+:- use_module timer.
50+
51+%-----------------------------------------------------------------------------%
52+
53+:- type state(Ctx, Group) ---> state(thread.mvar.mvar({Ctx, Group})).
54+
55+%-----------------------------------------------------------------------------%
56+
57+:- pred redraw(state(Ctx, Group), io.io, io.io)
58+ <= (saffron.gl.context(Ctx),
59+ saffron.draw.basic_transform(Ctx),
60+ saffron.draw.transform_stack(Ctx),
61+ saffron.draw.draw(Ctx, Group)).
62+:- mode redraw(in, di, uo) is det.
63+
64+redraw(state(MVar), !IO) :-
65+ thread.mvar.read(MVar, {Ctx, Group}, !IO),
66+ % Get the current time in order to animate the camera.
67+ timer.ms(Ticks, !IO),
68+ Seconds = float(int.unchecked_rem(Ticks, 6283185)) / 1000.0,
69+
70+ % Alternatively...
71+ % time.clock(Ticks, !IO),
72+ % (time.clocks_per_sec =< 0 -> Rate = 100 ; Rate = time.clocks_per_sec),
73+ % Seconds = float(Ticks) / float(Rate),
74+
75+ % Build the rotation matrix.
76+ % To make this more visually interesting, we also move the rotation
77+ % vector. If this seems confusing, just change RotateVector to be
78+ % `vector(U1, U1, 1.0)` or some other constant to see the rotation
79+ % normal in action.
80+ SpinT = Seconds, % / 0.23,
81+ Angle = Seconds * 0.87,
82+ RotateVector =
83+ mmath.vector.normalize(vector(math.sin(SpinT), 1.0, math.cos(SpinT))),
84+ % In general, you should always normalize vectors being used to build
85+ % transformation matrices.
86+ Matrix = mmath.matrix.rotate(Angle, RotateVector),
87+
88+ % Here we will push the ortho matrix set in main/2 and then transform
89+ % it by our rotation. Then, after drawing, we pop the transformed
90+ % matrix to restore the ortho matrix.
91+ saffron.draw.push_matrix(Ctx, !IO),
92+
93+ % In most actual programs, this would be tracked separately for the
94+ % model and camera.
95+ saffron.draw.transform(Ctx, Matrix, !IO),
96+
97+ % Draw the model. Most real programs would iterate a set of Groups.
98+ saffron.draw.draw(Ctx, Group, !IO),
99+
100+ saffron.draw.pop_matrix(Ctx, !IO).
101+
102+%-----------------------------------------------------------------------------%
103+
104+:- instance saffron_delegate.delegate(state(Ctx, Group))
105+ <= (saffron.gl.context(Ctx),
106+ saffron.draw.basic_transform(Ctx),
107+ saffron.draw.transform_stack(Ctx),
108+ saffron.draw.draw(Ctx, Group)) where [
109+ pred(saffron_delegate.key_press/4) is saffron_delegate.key_nop,
110+ pred(saffron_delegate.key_release/4) is saffron_delegate.key_nop,
111+ pred(saffron_delegate.button_press/6) is saffron_delegate.button_nop,
112+ pred(saffron_delegate.button_release/6) is saffron_delegate.button_nop,
113+ pred(saffron_delegate.mouse_motion/5) is saffron_delegate.motion_nop,
114+ pred(saffron_delegate.resize/5) is saffron_delegate.resize_nop,
115+ pred(saffron_delegate.redraw/3) is saffron_glfw_demo.redraw,
116+ pred(saffron_delegate.quit/3) is saffron_delegate.quit_nop
117+].
118+
119+%-----------------------------------------------------------------------------%
120+
121+:- pred run(Win, thread.mvar.mvar({T1, T2}), io.io, io.io)
122+ <= saffron_delegate.window(Win).
123+:- mode run(in, in, di, uo) is det.
124+
125+run(Win, MVar, !IO) :-
126+ % Although this isn't really needed here, it is good hygiene to ensure that
127+ % you measure your game and update calls, and then sleep based on the
128+ % difference of the time used and expected frame time.
129+ timer.ms(NewTicks, !IO),
130+
131+ saffron_delegate.update(Win, ShouldQuit, !IO),
132+ (
133+ ShouldQuit = bool.yes % Nothing else to do.
134+ ;
135+ ShouldQuit = bool.no,
136+ % Delay, then continue looping.
137+ % The delay is from now (EndMS) until the anticipated end of frame/tick
138+ % (NewTicks + 16). This should give us an optimistic 60 FPS.
139+ timer.ms(EndMS, !IO),
140+ SleepMS = NewTicks + 16 - EndMS,
141+ % Delay only if we didn't blow the frame budget.
142+ ( SleepMS >= 0 -> timer.sleep(SleepMS, !IO) ; true ),
143+ run(Win, MVar, !IO)
144+ ).
145+
146+%-----------------------------------------------------------------------------%
147+% Geometry for the cube model.
148+%
149+% Normally, you will want to load a model file, or perhaps just have a few
150+% primitives defined in a similar to this.
151+%
152+% 0 ----- 1
153+% \ \
154+% | \ | \
155+% | 2 ----- 3
156+% | | | |
157+% | | | |
158+% 4 -|--- 5 |
159+% \ | \ |
160+% \ \
161+% 6 ----- 7
162+%
163+% y
164+% z |
165+% \ |
166+% \|
167+% o----x
168+%
169+% Faces (in triangle strip order):
170+% 0: 0, 1, 2, 3
171+% 1: 0, 2, 4, 6
172+% 2: 3, 1, 7, 5
173+% 3: 1, 0, 5, 4
174+% 4: 2, 3, 6, 7
175+% 5: 6, 7, 4, 5
176+:- func cube_geometry = list.list(list.list(saffron.geometry.vertex3d)).
177+cube_geometry = [
178+ [
179+ vertex(Vec0, U0, U0) |[
180+ vertex(Vec1, U1, U0) |[
181+ vertex(Vec2, U0, U1) |[
182+ vertex(Vec3, U1, U1) |[
183+ ]]]]] |[
184+ [
185+ vertex(Vec0, U0, U0) |[
186+ vertex(Vec2, U1, U0) |[
187+ vertex(Vec4, U0, U1) |[
188+ vertex(Vec6, U1, U1) |[
189+ ]]]]] |[
190+ [
191+ vertex(Vec3, U0, U0) |[
192+ vertex(Vec1, U1, U0) |[
193+ vertex(Vec7, U0, U1) |[
194+ vertex(Vec5, U1, U1) |[
195+ ]]]]] |[
196+ [
197+ vertex(Vec1, U0, U0) |[
198+ vertex(Vec0, U1, U0) |[
199+ vertex(Vec5, U0, U1) |[
200+ vertex(Vec4, U1, U1) |[
201+ ]]]]] |[
202+ [
203+ vertex(Vec2, U0, U0) |[
204+ vertex(Vec3, U1, U0) |[
205+ vertex(Vec6, U0, U1) |[
206+ vertex(Vec7, U1, U1) |[
207+ ]]]]] |[
208+ [
209+ vertex(Vec6, U0, U0) |[
210+ vertex(Vec7, U1, U0) |[
211+ vertex(Vec4, U0, U1) |[
212+ vertex(Vec5, U1, U1) |[
213+ ]]]]] |[
214+ ]]]]]]] :-
215+
216+ Vec0 = vector(-1.0, 1.0, 1.0),
217+ Vec1 = vector( 1.0, 1.0, 1.0),
218+ Vec2 = vector(-1.0, 1.0, -1.0),
219+ Vec3 = vector( 1.0, 1.0, -1.0),
220+ Vec4 = vector(-1.0, -1.0, 1.0),
221+ Vec5 = vector( 1.0, -1.0, 1.0),
222+ Vec6 = vector(-1.0, -1.0, -1.0),
223+ Vec7 = vector( 1.0, -1.0, -1.0),
224+ U0 = 0.0,
225+ U1 = 1.0 .
226+
227+%-----------------------------------------------------------------------------%
228+
229+main(!IO) :-
230+ % Load the demo image
231+ saffron_tga.load_path("res/crate.tga", ImageResult, !IO),
232+
233+ % The Mercury compiler is smart enough to see that the error branch will
234+ % throw, and will unify the result outside that disjunction.
235+ (
236+ ImageResult = io.error(Err),
237+ exception.throw(exception.software_error(io.error_message(Err)))
238+ ;
239+ ImageResult = io.ok({BMP, TexW, TexH})
240+ ),
241+ W = 640,
242+ H = 480,
243+ Title = "Saffron GLFW Demo",
244+
245+ thread.mvar.init(MVar, !IO),
246+ saffron_glfw.create_window(state(MVar), W, H, Title, 2, 0, WinRes, !IO),
247+ (
248+ WinRes = io.error(Err),
249+ exception.throw(exception.software_error(io.error_message(Err)))
250+ ;
251+ WinRes = io.ok(Win)
252+ ),
253+
254+ % Using the backend-specific type is what determines what backend Glow is
255+ % using.
256+ saffron.gl2.init(Win, Ctx, !IO),
257+
258+ saffron.gl.opengl_version_string(Ctx, GLVersion, !IO),
259+ io.write_string("OpenGL Version: ", !IO),
260+ io.write_string(GLVersion, !IO),
261+ io.nl(!IO),
262+
263+ saffron.gl.shader_model_version_string(Ctx, GLSLVersion, !IO),
264+ io.write_string("GLSL Version: ", !IO),
265+ io.write_string(GLSLVersion, !IO),
266+ io.nl(!IO),
267+
268+ % Set the ortho mode
269+ AR = float(W) / float(H),
270+
271+ Matrix = mmath.matrix.ortho(-AR, AR, 1.0, -1.0, -1000.0, 1000.0),
272+ saffron.draw.set_matrix(Ctx, Matrix, !IO),
273+
274+ saffron.draw.transform(Ctx, mmath.matrix.scale(0.4, 0.4, 0.4), !IO),
275+
276+ % Upload the image to create a texture.
277+ saffron.gl.create_texture_from_bitmap(Ctx, BMP, TexW, TexH, Texture, !IO),
278+ Faces = cube_geometry,
279+
280+ % Upload geometry to buffers.
281+ % Most programs would probably want to use index buffers for a shape like
282+ % this with mostly shared geometry.
283+ list.map_foldl(
284+ saffron.geometry.create_buffer_list(Ctx),
285+ Faces, FaceBuffers,
286+ !IO),
287+
288+ % Construct the faces.
289+ % This *could* be done as one large, wound shape, but the majority of
290+ % programs will be loading meshes that will contain individual hulls/faces
291+ % that will need to be constructed in this way.
292+ list.map_foldl(
293+ saffron.geometry.create_shape2(Ctx, saffron.geometry.triangle_strip, Texture),
294+ FaceBuffers,
295+ Shapes,
296+ !IO),
297+
298+ % Construct the shape.
299+ saffron.draw.create_group_list(Ctx, Shapes, Group, !IO),
300+
301+ thread.mvar.put(MVar, {Ctx, Group}, !IO),
302+ % Run the engine.
303+ run(Win, MVar, !IO),
304+
305+ % Cleanup
306+ saffron.destroy(Ctx, Group, !IO),
307+ list.foldl(saffron.destroy(Ctx), Shapes, !IO),
308+ list.foldl(saffron.destroy(Ctx), FaceBuffers, !IO),
309+ saffron.destroy(Ctx, Texture, !IO).
310+
--- /dev/null
+++ b/demo/saffron_glfw_demo.sh
@@ -0,0 +1,2 @@
1+cd `dirname "$0"`
2+exec sh run_demo.sh saffron_glfw_demo $@
--- a/makefile
+++ b/makefile
@@ -23,7 +23,7 @@ all: $(ALL)
2323 UTILS=build_saffron_window build_saffron_delegate build_saffron_glow build_saffron_tga
2424 utils: $(UTILS)
2525
26-DEMOS=build_saffron_cube_demo build_saffron_shader_demo
26+DEMOS=build_saffron_cube_demo build_saffron_shader_demo build_saffron_glfw_demo
2727 demos: $(DEMOS)
2828
2929 MMATH_LINK_FLAGS=--search-lib-files-dir '$(ROOT)/mmath' --init-file '$(ROOT)/mmath/mmath.init'
@@ -37,6 +37,7 @@ SAFFRON_TGA_LINK_FLAGS=--search-lib-files-dir '$(ROOT)/util/tga' --init-file '$(
3737 SAFFRON_GLOW_LINK_FLAGS=--search-lib-files-dir '$(ROOT)/util/glow' --init-file '$(ROOT)/util/glow/saffron_glow.init' $(GLOW_INCLUDE_FLAGS)
3838 SAFFRON_WINDOW_LINK_FLAGS=--search-lib-files-dir '$(ROOT)/util/window' --init-file '$(ROOT)/util/window/saffron_window.init'
3939 SAFFRON_DELEGATE_LINK_FLAGS=$(SAFFRON_WINDOW_LINK_FLAGS) --search-lib-files-dir '$(ROOT)/util/delegate' --init-file '$(ROOT)/util/delegate/saffron_delegate.init'
40+SAFFRON_GLFW_LINK_FLAGS=$(SAFFRON_DELEGATE_LINK_FLAGS) --search-lib-files-dir '$(ROOT)/util/glfw' --init-file '$(ROOT)/util/glfw/saffron_glfw.init'
4041
4142 # Generated source.
4243 saffron.gl.buffer.inc: saffron.gl_gen.awk saffron.gl.buffer.csv
@@ -91,10 +92,17 @@ build_saffron_window:
9192 util/window/libsaffron_delegate.$(SO): build_saffron_delegate
9293 build_saffron_delegate: build_saffron_window
9394 cd util/delegate && $(MMC) $(SAFFRON_MMC_OPTS) $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_WINDOW_LINK_FLAGS) --make libsaffron_delegate $(STATIC_MMC_FLAGS)
94- cd util/delegate && $(MMC) $(SAFFRON_MMC_OPTS) $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_WINDOW_LINK_FLAGS) --make libsaffron_delegate $(SHARED_MMC_FLAGS)
95+ cd util/delegate && $(MMC) $(SAFFRON_MMC_OPTS) $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_WINDOW_LINK_FLAGS) --make libsaffron_delegate $(SHARED_MMC_FLAGS) -L '$(ROOT)/util/window' -lsaffron_window
9596 install util/delegate/libsaffron_delegate.$(SO) demo/
9697 install util/delegate/libsaffron_delegate.$(SO) test/
9798
99+util/glow/libsaffron_glfw.$(SO): build_saffron_glfw
100+build_saffron_glfw: build_saffron_delegate build_saffron_window build_saffron
101+ cd util/glfw && $(MMC) $(SAFFRON_MMC_OPTS) $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_DELEGATE_LINK_FLAGS) --make libsaffron_glfw $(STATIC_MMC_FLAGS)
102+ cd util/glfw && $(MMC) $(SAFFRON_MMC_OPTS) $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_DELEGATE_LINK_FLAGS) --make libsaffron_glfw $(SHARED_MMC_FLAGS) -L '$(ROOT)/util/window' -lsaffron_window -L '$(ROOT)/util/delegate' -lsaffron_delegate -L '$(ROOT)' -lsaffron -lglfw -lGL
103+ install util/glfw/libsaffron_glfw.$(SO) demo/
104+ install util/glfw/libsaffron_glfw.$(SO) test/
105+
98106 build_saffron_tga:
99107 cd util/tga && $(MMC) $(SAFFRON_MMC_OPTS) --make libsaffron_tga $(STATIC_MMC_FLAGS)
100108
@@ -118,6 +126,9 @@ build_saffron_cube_demo: build_demo_utils demo/res/crate.tga build_saffron build
118126 build_saffron_shader_demo: build_demo_utils build_saffron build_saffron_glow
119127 cd demo && $(MMC) $(SAFFRON_MMC_OPTS) -E --make saffron_shader_demo $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_GLOW_LINK_FLAGS) $(SAFFRON_WINDOW_LINK_FLAGS) -L '$(ROOT)/glow' -lglow -lX11 -lmmath -lsaffron -lsaffron_window -lsaffron_glow -lGL
120128
129+build_saffron_glfw_demo: build_demo_utils demo/res/crate.tga build_saffron build_saffron_glfw build_saffron_tga
130+ cd demo && $(MMC) $(SAFFRON_MMC_OPTS) -E --make saffron_glfw_demo $(MMATH_LINK_FLAGS) $(SAFFRON_LINK_FLAGS) $(SAFFRON_GLFW_LINK_FLAGS) $(SAFFRON_TGA_LINK_FLAGS) -lglfw -lmmath -lsaffron -lsaffron_window -lsaffron_delegate -lsaffron_glfw --link-object '$(ROOT)/util/tga/libsaffron_tga.a' -lGL
131+
121132 # Testing
122133 build_transunit:
123134 cd test/transunit && $(MMC) $(SAFFRON_MMC_OPTS) --make libtransunit $(STATIC_MMC_FLAGS)
@@ -149,6 +160,9 @@ clean_saffron_window:
149160 clean_saffron_delegate:
150161 DIR=util/delegate ; TARGET=saffron_delegate ; $(CLEAN_MERCURY)
151162
163+clean_saffron_glfw:
164+ DIR=util/glfw ; TARGET=saffron_glfw ; $(CLEAN_MERCURY)
165+
152166 clean_test_saffron_tga:
153167 DIR=test ; TARGET=test_saffron_tga ; $(CLEAN_MERCURY)
154168
@@ -158,7 +172,10 @@ clean_saffron_cube_demo:
158172 clean_saffron_shader_demo:
159173 DIR=demo ; TARGET=saffron_shader_demo ; $(CLEAN_MERCURY)
160174
161-CLEAN=clean_mmath clean_saffron clean_glow clean_saffron_glow clean_saffron_window clean_saffron_tga clean_saffron_delegate clean_glow clean_test_saffron_tga clean_saffron_cube_demo clean_saffron_cube_demo
175+clean_saffron_glfw_demo:
176+ DIR=demo ; TARGET=saffron_glfw_demo ; $(CLEAN_MERCURY)
177+
178+CLEAN=clean_mmath clean_saffron clean_glow clean_saffron_glow clean_saffron_window clean_saffron_delegate clean_saffron_glfw clean_saffron_tga clean_saffron_delegate clean_glow clean_test_saffron_tga clean_saffron_cube_demo clean_saffron_cube_demo clean_saffron_glfw_demo
162179 clean: $(CLEAN)
163180 rm -f demo/res/crate.tga demo/res/ctc24.tga demo/lib*.$(SO) # Remove the demo libraries we copied and libtimer.
164181
--- /dev/null
+++ b/util/glfw/saffron_glfw.m
@@ -0,0 +1,487 @@
1+% Copyright (C) 2023 AlaskanEmily, Transnat Games
2+%
3+% this software is provided 'as-is', without any express or implied
4+% warranty. in no event will the authors be held liable for any damages
5+% arising from the use of this software.
6+%
7+% permission is granted to anyone to use this software for any purpose,
8+% including commercial applications, and to alter it and redistribute it
9+% freely, subject to the following restrictions:
10+%
11+% 1. the origin of this software must not be misrepresented; you must not
12+% claim that you wrote the original software. if you use this software
13+% in a product, an acknowledgment in the product documentation would be
14+% appreciated but is not required.
15+% 2. altered source versions must be plainly marked as such, and must not be
16+% misrepresented as being the original software.
17+% 3. this notice may not be removed or altered from any source distribution.
18+
19+:- module saffron_glfw.
20+%=============================================================================%
21+% Implementation of saffron_delegate using GLFW3.
22+%
23+% This is currently only implemented for C, but in the future this should
24+% include using GLFW through lwjgl for Java.
25+%
26+% There isn't a real need to use this in the C backend, as the OS X
27+% implementation of saffron_delegate and the glow backend of saffron_window are
28+% generally better and require fewer dependencies. You could use it if you
29+% only want to use saffron_delegate instead of saffron_window on some
30+% platforms, but doing that will mean more dependencies and more code running
31+% on the most common platforms.
32+:- interface.
33+%=============================================================================%
34+
35+:- use_module io.
36+
37+:- use_module saffron.
38+:- use_module saffron.gl.
39+:- use_module saffron_delegate.
40+
41+%-----------------------------------------------------------------------------%
42+
43+:- inst io_res_uniq == unique(io.ok(unique) ; io.error(ground)).
44+:- mode io_res_uo == (free >> io_res_uniq).
45+
46+%-----------------------------------------------------------------------------%
47+
48+:- type window.
49+
50+%-----------------------------------------------------------------------------%
51+
52+:- instance saffron.context(window).
53+
54+%-----------------------------------------------------------------------------%
55+
56+:- instance saffron.gl.context(window).
57+
58+%-----------------------------------------------------------------------------%
59+
60+:- instance saffron_delegate.window(window).
61+
62+%-----------------------------------------------------------------------------%
63+% create_window(T, W, H, Title, GLMajor, GLMinor, Result, !IO).
64+:- pred create_window(T, int, int, string, int, int, io.res(window), io.io, io.io)
65+ <= saffron_delegate.delegate(T).
66+:- mode create_window(in, in, in, in, in, in, io_res_uo, di, uo) is det.
67+
68+%=============================================================================%
69+:- implementation.
70+%=============================================================================%
71+
72+:- use_module maybe.
73+:- use_module bool.
74+
75+:- pragma foreign_import_module("C", saffron_window).
76+:- pragma foreign_import_module("C", saffron_delegate).
77+
78+%-----------------------------------------------------------------------------%
79+
80+:- pragma foreign_decl("C",
81+ "
82+#include <GLFW/glfw3.h>
83+extern MR_Bool saffron_glfw_init_ok;
84+
85+void SaffronGLFW_KeyCallback(
86+ GLFWwindow *win,
87+ int key,
88+ int scancode,
89+ int action,
90+ int mods);
91+
92+void SaffronGLFW_MouseButtonCallback(
93+ GLFWwindow *win,
94+ int button,
95+ int action,
96+ int mods);
97+
98+void SaffronGLFW_MouseMotionCallback(
99+ GLFWwindow *win,
100+ double x,
101+ double y);
102+
103+#define SAFFRON_GLFW_GET_ERROR(OK, ERR) do{ \\
104+ const char *SAFFRON_GLFW_GET_ERROR_err; \\
105+ if(glfwGetError(&SAFFRON_GLFW_GET_ERROR_err) == 0){ \\
106+ (OK) = 1; \\
107+ (ERR) = (MR_String)""<NULL>""; \\
108+ } \\
109+ else{ \\
110+ (OK) = 0; \\
111+ const long SAFFRON_GLFW_GET_ERROR_len = \\
112+ strlen(SAFFRON_GLFW_GET_ERROR_err); \\
113+ MR_allocate_aligned_string_msg( \\
114+ ERR, \\
115+ SAFFRON_GLFW_GET_ERROR_len, \\
116+ MR_ALLOC_ID); \\
117+ memcpy((ERR), \\
118+ SAFFRON_GLFW_GET_ERROR_err, \\
119+ SAFFRON_GLFW_GET_ERROR_len + 1); \\
120+ } \\
121+}while(0)
122+
123+ ").
124+
125+%-----------------------------------------------------------------------------%
126+
127+:- pragma foreign_code("C",
128+ "
129+MR_Bool saffron_glfw_init_ok = MR_NO;
130+
131+void SaffronGLFW_KeyCallback(
132+ GLFWwindow *win,
133+ int key,
134+ int scancode,
135+ int action,
136+ int mods){
137+
138+ MR_Word delegate;
139+ MR_Word mr_key;
140+ delegate = (MR_Word)glfwGetWindowUserPointer(win);
141+
142+ /* Create the key data. */
143+ if(key >= 32 && key <= 96){
144+ mr_key = Saffron_CreateKey(key);
145+ }
146+ else{
147+ switch(key){
148+ case GLFW_KEY_ESCAPE:
149+ mr_key = SAFFRON_KEY_ESCAPE;
150+ break;
151+ case GLFW_KEY_ENTER:
152+ mr_key = SAFFRON_KEY_ENTER;
153+ break;
154+ case GLFW_KEY_TAB:
155+ mr_key = SAFFRON_KEY_TAB;
156+ break;
157+ case GLFW_KEY_BACKSPACE:
158+ mr_key = SAFFRON_KEY_BACKSPACE;
159+ break;
160+ case GLFW_KEY_DELETE:
161+ mr_key = SAFFRON_KEY_DELETE;
162+ break;
163+ case GLFW_KEY_RIGHT:
164+ mr_key = SAFFRON_KEY_RIGHT_ARROW;
165+ break;
166+ case GLFW_KEY_LEFT:
167+ mr_key = SAFFRON_KEY_LEFT_ARROW;
168+ break;
169+ case GLFW_KEY_DOWN:
170+ mr_key = SAFFRON_KEY_DOWN_ARROW;
171+ break;
172+ case GLFW_KEY_UP:
173+ mr_key = SAFFRON_KEY_UP_ARROW;
174+ break;
175+ case GLFW_KEY_LEFT_SHIFT: /* FALLTHROUGH */
176+ case GLFW_KEY_RIGHT_SHIFT:
177+ mr_key = SAFFRON_KEY_SHIFT;
178+ break;
179+ case GLFW_KEY_LEFT_CONTROL: /* FALLTHROUGH */
180+ case GLFW_KEY_RIGHT_CONTROL:
181+ mr_key = SAFFRON_KEY_CONTROL;
182+ break;
183+ default:
184+ return;
185+ }
186+ Saffron_CreateSpecialKey(mr_key, &mr_key);
187+ }
188+ switch(action){
189+ case GLFW_PRESS:
190+ Saffron_DelegateKeyPress(delegate, mr_key);
191+ break;
192+ case GLFW_RELEASE:
193+ Saffron_DelegateKeyRelease(delegate, mr_key);
194+ break;
195+ }
196+}
197+
198+void SaffronGLFW_MouseButtonCallback(
199+ GLFWwindow *win,
200+ int button,
201+ int action,
202+ int mods){
203+
204+ MR_Word delegate;
205+ MR_Word mr_button;
206+ double x, y;
207+ delegate = (MR_Word)glfwGetWindowUserPointer(win);
208+
209+ switch(button){
210+ case GLFW_MOUSE_BUTTON_LEFT:
211+ mr_button = SAFFRON_BUTTON_LEFT;
212+ break;
213+ case GLFW_MOUSE_BUTTON_RIGHT:
214+ mr_button = SAFFRON_BUTTON_RIGHT;
215+ break;
216+ case GLFW_MOUSE_BUTTON_MIDDLE:
217+ mr_button = SAFFRON_BUTTON_MIDDLE;
218+ break;
219+ default:
220+ return;
221+ }
222+ glfwGetCursorPos(win, &x, &y);
223+ switch(action){
224+ case GLFW_PRESS:
225+ Saffron_DelegateButtonPress(delegate, mr_button, x, y);
226+ break;
227+ case GLFW_RELEASE:
228+ Saffron_DelegateButtonRelease(delegate, mr_button, x, y);
229+ break;
230+ }
231+}
232+
233+void SaffronGLFW_MouseMotionCallback(
234+ GLFWwindow *win,
235+ double x,
236+ double y){
237+
238+ MR_Word delegate;
239+ delegate = (MR_Word)glfwGetWindowUserPointer(win);
240+
241+ Saffron_DelegateMouseMotion(delegate, x, y);
242+}
243+
244+ ").
245+
246+%-----------------------------------------------------------------------------%
247+
248+:- pragma foreign_type("C", window, "GLFWwindow*").
249+
250+:- type delegate == saffron_delegate.delegate_private.
251+
252+%-----------------------------------------------------------------------------%
253+
254+:- impure pred glfw_initialize is det.
255+glfw_initialize.
256+:- pragma foreign_proc("C",
257+ glfw_initialize,
258+ [will_not_call_mercury, thread_safe, will_not_throw_exception],
259+ "saffron_glfw_init_ok = glfwInit();").
260+
261+:- initialize glfw_initialize/0.
262+
263+%-----------------------------------------------------------------------------%
264+
265+:- impure pred glfw_finalize is det.
266+glfw_finalize.
267+:- pragma foreign_proc("C",
268+ glfw_finalize,
269+ [will_not_call_mercury, thread_safe, will_not_throw_exception],
270+ "glfwTerminate();").
271+
272+:- finalize glfw_finalize/0.
273+
274+%-----------------------------------------------------------------------------%
275+
276+:- func create_window_ok(window::di) = (io.res(window)::uo) is det.
277+create_window_ok(Ctx) = io.ok(Ctx).
278+:- pragma foreign_export("C",
279+ create_window_ok(di) = (uo),
280+ "SaffronGLFW_CreateWindowOK").
281+
282+%-----------------------------------------------------------------------------%
283+
284+:- func create_window_error(string::di) = (io.res(window)::io_res_uo) is det.
285+create_window_error(Str) = io.error(io.make_io_error(Str)).
286+:- pragma foreign_export("C",
287+ create_window_error(di) = (io_res_uo),
288+ "SaffronGLFW_CreateWindowError").
289+
290+%-----------------------------------------------------------------------------%
291+
292+:- pred make_current(window::in, io.io::di, io.io::uo) is det.
293+:- pragma foreign_proc("C",
294+ make_current(Win::in, IOi::di, IOo::uo),
295+ [will_not_call_mercury, promise_pure, thread_safe, will_not_throw_exception,
296+ does_not_affect_liveness, may_duplicate],
297+ "
298+ IOo = IOi;
299+ glfwMakeContextCurrent(Win);
300+ ").
301+
302+%-----------------------------------------------------------------------------%
303+
304+:- pred load_function(window, string, maybe.maybe_error(saffron.ctx_function), io.io, io.io).
305+:- mode load_function(in, in, out, di, uo) is det.
306+:- pragma foreign_proc("C",
307+ load_function(Win::in, Name::in, MaybeFunc::out, IOi::di, IOo::uo),
308+ [will_not_call_mercury, promise_pure, thread_safe, will_not_throw_exception,
309+ does_not_affect_liveness, may_duplicate],
310+ "
311+ GLFWglproc proc;
312+ MR_String err;
313+ int ok;
314+ IOo = IOi;
315+ (void)Win;
316+ proc = glfwGetProcAddress(Name);
317+ SAFFRON_GLFW_GET_ERROR(ok, err);
318+ MaybeFunc = ok ?
319+ Saffron_CreateCtxFunctionOK((MR_Word)proc) :
320+ Saffron_CreateCtxFunctionError(err);
321+ ").
322+
323+%-----------------------------------------------------------------------------%
324+
325+:- instance saffron.context(window) where [
326+ pred(saffron.make_current/3) is saffron_glfw.make_current,
327+ pred(saffron.load_function/5) is saffron_glfw.load_function
328+].
329+
330+%-----------------------------------------------------------------------------%
331+
332+:- instance saffron.gl.context(window) where [].
333+
334+%-----------------------------------------------------------------------------%
335+
336+:- pred show_window(window::in, io.io::di, io.io::uo) is det.
337+:- pragma foreign_proc("C",
338+ show_window(Win::in, IOi::di, IOo::uo),
339+ [will_not_call_mercury, promise_pure, thread_safe, will_not_throw_exception,
340+ does_not_affect_liveness, may_duplicate],
341+ "
342+ IOo = IOi;
343+ glfwShowWindow(Win);
344+ ").
345+
346+%-----------------------------------------------------------------------------%
347+
348+:- pred hide_window(window::in, io.io::di, io.io::uo) is det.
349+:- pragma foreign_proc("C",
350+ hide_window(Win::in, IOi::di, IOo::uo),
351+ [will_not_call_mercury, promise_pure, thread_safe, will_not_throw_exception,
352+ does_not_affect_liveness, may_duplicate],
353+ "
354+ IOo = IOi;
355+ glfwHideWindow(Win);
356+ ").
357+
358+%-----------------------------------------------------------------------------%
359+
360+:- pred update(window::in, bool.bool::uo, io.io::di, io.io::uo) is det.
361+:- pragma foreign_proc("C",
362+ update(Win::in, ShouldQuit::uo, IOi::di, IOo::uo),
363+ [will_not_call_mercury, promise_pure, thread_safe, will_not_throw_exception,
364+ does_not_affect_liveness, may_duplicate],
365+ "
366+ int w, h;
367+ MR_Word delegate;
368+ IOo = IOi;
369+ ShouldQuit = glfwWindowShouldClose(Win);
370+ if(!ShouldQuit){
371+ glfwMakeContextCurrent(Win);
372+ glfwGetFramebufferSize(Win, &w, &h);
373+ glViewport(0, 0, w, h);
374+ delegate = (MR_Word)glfwGetWindowUserPointer(Win);
375+ Saffron_DelegateRedraw(delegate);
376+ glfwSwapBuffers(Win);
377+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
378+ glfwPollEvents();
379+ }
380+ ").
381+
382+%-----------------------------------------------------------------------------%
383+
384+:- instance saffron_delegate.window(window) where [
385+ pred(saffron_delegate.show_window/3) is saffron_glfw.show_window,
386+ pred(saffron_delegate.hide_window/3) is saffron_glfw.hide_window,
387+ pred(saffron_delegate.update/4) is saffron_glfw.update
388+].
389+
390+%-----------------------------------------------------------------------------%
391+
392+:- type hint --->
393+ context_major ;
394+ context_minor ;
395+ doublebuffer ;
396+ red_bits ;
397+ green_bits ;
398+ blue_bits ;
399+ alpha_bits ;
400+ retina ;
401+ no_error.
402+
403+:- pragma foreign_enum("C", hint/0, [
404+ context_major - "GLFW_CONTEXT_VERSION_MAJOR",
405+ context_minor - "GLFW_CONTEXT_VERSION_MINOR",
406+ doublebuffer - "GLFW_DOUBLEBUFFER",
407+ red_bits - "GLFW_RED_BITS",
408+ green_bits - "GLFW_GREEN_BITS",
409+ blue_bits - "GLFW_BLUE_BITS",
410+ alpha_bits - "GLFW_ALPHA_BITS",
411+ retina - "GLFW_COCOA_RETINA_FRAMEBUFFER",
412+ no_error - "GLFW_CONTEXT_NO_ERROR"
413+]).
414+
415+%-----------------------------------------------------------------------------%
416+
417+:- pred window_hint(hint::in, int::in, io.io::di, io.io::uo) is det.
418+:- pred reset_window_hints(io.io::di, io.io::uo) is det.
419+
420+:- pragma foreign_proc("C",
421+ window_hint(Hint::in, I::in, IOi::di, IOo::uo),
422+ [promise_pure, will_not_call_mercury, will_not_throw_exception, thread_safe,
423+ may_duplicate, does_not_affect_liveness],
424+ "
425+ IOo = IOi;
426+ glfwWindowHint(Hint, I);
427+ ").
428+
429+:- pragma foreign_proc("C",
430+ reset_window_hints(IOi::di, IOo::uo),
431+ [promise_pure, will_not_call_mercury, will_not_throw_exception, thread_safe,
432+ may_duplicate, does_not_affect_liveness],
433+ "
434+ IOo = IOi;
435+ glfwDefaultWindowHints();
436+ ").
437+
438+%-----------------------------------------------------------------------------%
439+
440+:- pred create_window(delegate, int, int, string, io.res(window), io.io, io.io).
441+:- mode create_window(in, in, in, in, io_res_uo, di, uo) is det.
442+
443+:- pragma foreign_proc("C",
444+ create_window(D::in, W::in, H::in, Title::in, Result::io_res_uo, IOi::di, IOo::uo),
445+ [promise_pure, will_not_call_mercury, will_not_throw_exception, thread_safe,
446+ may_duplicate, does_not_affect_liveness],
447+ "
448+ GLFWwindow *window;
449+ MR_String err;
450+ int ok;
451+
452+ IOo = IOi;
453+ if(!saffron_glfw_init_ok){
454+ ok = 0;
455+ err = (MR_String)""GLFW did not initialize"";
456+ }
457+ else{
458+ // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
459+ window = glfwCreateWindow(W, H, Title, NULL, NULL);
460+ SAFFRON_GLFW_GET_ERROR(ok, err);
461+ }
462+ if(ok && window != NULL){
463+ glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
464+ glfwSetWindowUserPointer(window, (void*)D);
465+ glfwSetKeyCallback(window, SaffronGLFW_KeyCallback);
466+ glfwSetMouseButtonCallback(window, SaffronGLFW_MouseButtonCallback);
467+ glfwSetCursorPosCallback(window, SaffronGLFW_MouseMotionCallback);
468+ Result = SaffronGLFW_CreateWindowOK(window);
469+ }
470+ else{
471+ Result = SaffronGLFW_CreateWindowError(err);
472+ }
473+ ").
474+
475+%-----------------------------------------------------------------------------%
476+
477+% create_window(T, W, H, Title, GLMajor, GLMinor, Result, !IO).
478+create_window(D, W, H, Title, GLMajor, GLMinor, Result, !IO) :-
479+ reset_window_hints(!IO),
480+ window_hint(context_major, GLMajor, !IO),
481+ window_hint(context_minor, GLMinor, !IO),
482+ window_hint(retina, 1, !IO),
483+ Delegate = saffron_delegate.create_delegate_private(D),
484+ create_window(Delegate, W, H, Title, Result, !IO).
485+
486+%-----------------------------------------------------------------------------%
487+