├── .gitignore ├── monitor.h ├── gamma_ramp.h ├── video_mode.h ├── size.h ├── scale.h ├── position.h ├── .vscode └── settings.json ├── Makefile ├── workarea.h ├── window.h ├── TODO.md ├── glfw.h ├── example.js ├── README.md ├── gamma_ramp.c ├── position.c ├── size.c ├── scale.c ├── video_mode.c ├── workarea.c ├── monitor.c ├── glfw.c └── window.c /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.o 3 | *.so 4 | -------------------------------------------------------------------------------- /monitor.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_MONITOR 2 | #define GLFW_MODULE_MONITOR 1 3 | 4 | JSClassID glfw_monitor_class_id; 5 | int glfw_monitor_init(JSContext* ctx, JSModuleDef* m); 6 | int glfw_monitor_export(JSContext* ctx, JSModuleDef* m); 7 | 8 | JSValue glfw_monitor_new_instance(JSContext* ctx, GLFWmonitor* monitor); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /gamma_ramp.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_GAMMA_RAMP 2 | #define GLFW_MODULE_GAMMA_RAMP 1 3 | 4 | JSClassID glfw_gamma_ramp_class_id; 5 | JSValue glfw_gamma_ramp_new_instance(JSContext* ctx, const GLFWgammaramp* mode); 6 | int glfw_gamma_ramp_init(JSContext* ctx, JSModuleDef* m); 7 | int glfw_gamma_ramp_export(JSContext* ctx, JSModuleDef* m); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /video_mode.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_VIDEO_MODE 2 | #define GLFW_MODULE_VIDEO_MODE 1 3 | 4 | JSClassID glfw_video_mode_class_id; 5 | JSValue glfw_video_mode_new_instance(JSContext* ctx, const GLFWvidmode* mode); 6 | int glfw_video_mode_init(JSContext* ctx, JSModuleDef* m); 7 | int glfw_video_mode_export(JSContext* ctx, JSModuleDef* m); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /size.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_SIZE 2 | #define GLFW_MODULE_SIZE 1 3 | 4 | typedef struct { 5 | int width; 6 | int height; 7 | } GLFWSize; 8 | 9 | JSClassID glfw_size_class_id; 10 | JSValue glfw_size_new_instance(JSContext* ctx, GLFWSize* size); 11 | int glfw_size_init(JSContext* ctx, JSModuleDef* m); 12 | int glfw_size_export(JSContext* ctx, JSModuleDef* m); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /scale.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_SCALE 2 | #define GLFW_MODULE_SCALE 1 3 | 4 | typedef struct { 5 | double x; 6 | double y; 7 | } GLFWScale; 8 | 9 | JSClassID glfw_scale_class_id; 10 | JSValue glfw_scale_new_instance(JSContext* ctx, GLFWScale* scale); 11 | int glfw_scale_init(JSContext* ctx, JSModuleDef* m); 12 | int glfw_scale_export(JSContext* ctx, JSModuleDef* m); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /position.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_POSITION 2 | #define GLFW_MODULE_POSITION 1 3 | 4 | typedef struct { 5 | int x; 6 | int y; 7 | } GLFWPosition; 8 | 9 | JSClassID glfw_position_class_id; 10 | JSValue glfw_position_new_instance(JSContext* ctx, GLFWPosition* position); 11 | int glfw_position_init(JSContext* ctx, JSModuleDef* m); 12 | int glfw_position_export(JSContext* ctx, JSModuleDef* m); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "__bit_reference": "c", 4 | "__functional_base": "c", 5 | "algorithm": "c", 6 | "bitset": "c", 7 | "chrono": "c", 8 | "__memory": "c", 9 | "functional": "c", 10 | "iterator": "c", 11 | "limits": "c", 12 | "memory": "c", 13 | "ratio": "c", 14 | "tuple": "c", 15 | "type_traits": "c", 16 | "vector": "c" 17 | } 18 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # TODO: 2 | # - make shared library build optional 3 | # - figure out single-binary build with `qjsc -M module.so,module ...` 4 | 5 | CFLAGS=-fPIC -DJS_SHARED_LIBRARY -I./ 6 | LIBS=-lquickjs -lglfw 7 | SRCS=$(wildcard *.c) 8 | OBJS=$(patsubst %.c,%.o,$(SRCS)) 9 | 10 | .PHONY: build 11 | build: glfw.so 12 | 13 | %.o: %.c 14 | $(CC) $(CFLAGS) -c -o $@ $< 15 | 16 | glfw.so: $(OBJS) 17 | $(CC) -shared -o $@ $^ $(LIBS) 18 | 19 | .PHONY: clean 20 | clean: 21 | rm -f *.so *.o 22 | -------------------------------------------------------------------------------- /workarea.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_WORKAREA 2 | #define GLFW_MODULE_WORKAREA 1 3 | 4 | #include "position.h" 5 | #include "size.h" 6 | 7 | typedef struct { 8 | GLFWPosition* position; 9 | GLFWSize* size; 10 | } GLFWWorkArea; 11 | 12 | JSClassID glfw_workarea_class_id; 13 | JSValue glfw_workarea_new_instance(JSContext* ctx, GLFWWorkArea* workarea); 14 | int glfw_workarea_init(JSContext* ctx, JSModuleDef* m); 15 | int glfw_workarea_export(JSContext* ctx, JSModuleDef* m); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /window.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_WINDOW 2 | #define GLFW_MODULE_WINDOW 1 3 | 4 | JSClassID glfw_window_class_id; 5 | int glfw_window_init(JSContext* ctx, JSModuleDef* m); 6 | int glfw_window_export(JSContext* ctx, JSModuleDef* m); 7 | 8 | JSValue glfw_window_new_instance(JSContext* ctx, GLFWwindow* window); 9 | JSValue glfw_window_create_window(JSContext* ctx, int width, int height, 10 | const char* title, GLFWmonitor* monitor, 11 | GLFWwindow* share); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | - API Documentation 2 | - Make Window hints an object argument to the constructor? 3 | - Figure out a good model for setting callbacks 4 | - Should it be multi-tenant or direct-mounting? 5 | ```js 6 | events.onerror = error => {} 7 | // vs 8 | on('error', error => {}) 9 | ``` 10 | 11 | # Class Methods 12 | 13 | - window 14 | - glfwSetWindowIcon 15 | - glfwSetWindowSizeLimits 16 | - glfwSetWindowAspectRatio 17 | - glfwGetWindowFrameSize 18 | - glfwGetWindowContentScale 19 | - glfwSetWindowMonitor 20 | - glfwGetWindowAttrib 21 | - glfwSetWindowAttrib 22 | - glfwSetWindowUserPointer 23 | - glfwGetWindowUserPointer 24 | - All the callbacks 25 | -------------------------------------------------------------------------------- /glfw.h: -------------------------------------------------------------------------------- 1 | #ifndef GLFW_MODULE_MAIN 2 | #define GLFW_MODULE_MAIN 1 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #define countof(x) (sizeof(x) / sizeof((x)[0])) 10 | 11 | static JSValue glfw_throw(JSContext* ctx) { 12 | const char* message; 13 | if (glfwGetError(&message) != GLFW_NO_ERROR) { 14 | JSValue error = JS_NewString(ctx, message); 15 | JS_Throw(ctx, error); 16 | } 17 | return JS_EXCEPTION; 18 | } 19 | 20 | #ifndef JS_SHARED_LIBRARY 21 | #define js_init_module js_init_module_qjsc_glfw 22 | #endif 23 | 24 | JSModuleDef* js_init_module(JSContext* ctx, const char* module_name); 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /example.js: -------------------------------------------------------------------------------- 1 | import { 2 | // Top-level functions 3 | poll, 4 | context, 5 | 6 | // Constants 7 | CONTEXT_VERSION_MAJOR, 8 | CONTEXT_VERSION_MINOR, 9 | OPENGL_PROFILE, 10 | OPENGL_CORE_PROFILE, 11 | OPENGL_FORWARD_COMPAT, 12 | RESIZABLE, 13 | SAMPLES, 14 | 15 | // Classes 16 | Window, 17 | Monitor 18 | } from 'glfw.so' 19 | 20 | Window.hint(CONTEXT_VERSION_MAJOR, 3) 21 | Window.hint(CONTEXT_VERSION_MINOR, 2) 22 | Window.hint(OPENGL_PROFILE, OPENGL_CORE_PROFILE) 23 | Window.hint(OPENGL_FORWARD_COMPAT, true) 24 | Window.hint(RESIZABLE, false) 25 | Window.hint(SAMPLES, 4) 26 | 27 | const window = new Window(800, 600, "OpenGL") 28 | context.current = window 29 | 30 | const { position, size } = window 31 | const { width, height } = size 32 | const { x, y } = position 33 | 34 | console.log(`width: ${width}, height: ${height}, x: ${x}, y: ${y}`) 35 | 36 | while (!window.shouldClose) { 37 | window.swapBuffers() 38 | poll() 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # quickjs-glfw 2 | 3 | NOTE: This is _EXPERIMENTAL_. As such, expect missing/broken features. 4 | 5 | GLFW bindings for QuickJS. Meant to be built as a static library and imported 6 | with `qjs`. 7 | 8 | If you can get it to work with `qjsc -M glfw.so ...` also, PRs are welcome. 🙂 9 | 10 | ## Build 11 | 12 | ```sh 13 | make 14 | ``` 15 | 16 | ## Usage 17 | 18 | ```js 19 | import { 20 | poll, 21 | Window, 22 | 23 | // Constants 24 | CONTEXT_VERSION_MAJOR, 25 | CONTEXT_VERSION_MINOR, 26 | OPENGL_PROFILE, 27 | OPENGL_CORE_PROFILE, 28 | OPENGL_FORWARD_COMPAT, 29 | RESIZABLE, 30 | SAMPLES, 31 | } from 'glfw.so' 32 | 33 | Window.hint(CONTEXT_VERSION_MAJOR, 3) 34 | Window.hint(CONTEXT_VERSION_MINOR, 2) 35 | Window.hint(OPENGL_PROFILE, OPENGL_CORE_PROFILE) 36 | Window.hint(OPENGL_FORWARD_COMPAT, true) 37 | Window.hint(RESIZABLE, false) 38 | Window.hint(SAMPLES, 4) 39 | 40 | const window = new Window(800, 600, "OpenGL") 41 | window.makeContextCurrent() 42 | 43 | const { width, height } = window.size 44 | const { x, y } = window.position 45 | 46 | console.log(`width: ${width}, height: ${height}, x: ${x}, y: ${y}`) 47 | 48 | while (!window.shouldClose) { 49 | window.swapBuffers() 50 | poll() 51 | } 52 | ``` 53 | 54 | --- 55 | 56 | ### Copyright (c) 2020 Stephen Belanger 57 | 58 | #### Licensed under MIT License 59 | 60 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 61 | 62 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 63 | 64 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /gamma_ramp.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | 3 | #include "gamma_ramp.h" 4 | 5 | // Constructor/Destructor 6 | JSValue glfw_gamma_ramp_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { 7 | JS_ThrowInternalError(ctx, "VideoMode can not be constructed directly"); 8 | return JS_EXCEPTION; 9 | } 10 | 11 | // Properties 12 | JSValue glfw_gamma_ramp_get_prop(JSContext* ctx, JSValueConst this_val, int magic) { 13 | GLFWgammaramp* gamma_ramp = JS_GetOpaque2(ctx, this_val, glfw_gamma_ramp_class_id); 14 | if (!gamma_ramp) return JS_EXCEPTION; 15 | 16 | int value; 17 | switch (magic) { 18 | case 0: value = (int) gamma_ramp->red; break; 19 | case 1: value = (int) gamma_ramp->green; break; 20 | case 2: value = (int) gamma_ramp->blue; break; 21 | case 3: value = gamma_ramp->size; break; 22 | } 23 | 24 | return JS_NewInt32(ctx, value); 25 | } 26 | 27 | // Initialization 28 | JSClassDef glfw_gamma_ramp_class_def = { 29 | "GammaRamp", 30 | }; 31 | 32 | const JSCFunctionListEntry glfw_gamma_ramp_proto_funcs[] = { 33 | JS_CGETSET_MAGIC_DEF("red", glfw_gamma_ramp_get_prop, NULL, 0), 34 | JS_CGETSET_MAGIC_DEF("green", glfw_gamma_ramp_get_prop, NULL, 1), 35 | JS_CGETSET_MAGIC_DEF("blue", glfw_gamma_ramp_get_prop, NULL, 2), 36 | JS_CGETSET_MAGIC_DEF("size", glfw_gamma_ramp_get_prop, NULL, 3), 37 | }; 38 | 39 | JSValue glfw_gamma_ramp_proto, glfw_gamma_ramp_class; 40 | 41 | JSValue glfw_gamma_ramp_constructor(JSContext* ctx) { 42 | JSRuntime* rt = JS_GetRuntime(ctx); 43 | 44 | if (!JS_IsRegisteredClass(rt, glfw_gamma_ramp_class_id)) { 45 | JS_NewClassID(&glfw_gamma_ramp_class_id); 46 | JS_NewClass(JS_GetRuntime(ctx), glfw_gamma_ramp_class_id, &glfw_gamma_ramp_class_def); 47 | 48 | glfw_gamma_ramp_proto = JS_NewObject(ctx); 49 | JS_SetPropertyFunctionList(ctx, glfw_gamma_ramp_proto, glfw_gamma_ramp_proto_funcs, countof(glfw_gamma_ramp_proto_funcs)); 50 | JS_SetClassProto(ctx, glfw_gamma_ramp_class_id, glfw_gamma_ramp_proto); 51 | 52 | glfw_gamma_ramp_class = JS_NewCFunction2(ctx, glfw_gamma_ramp_ctor, "GammaRamp", 2, JS_CFUNC_constructor, 0); 53 | /* set proto.constructor and ctor.prototype */ 54 | JS_SetConstructor(ctx, glfw_gamma_ramp_class, glfw_gamma_ramp_proto); 55 | } 56 | 57 | return glfw_gamma_ramp_class; 58 | } 59 | 60 | JSValue glfw_gamma_ramp_new_instance(JSContext* ctx, const GLFWgammaramp* gamma_ramp) { 61 | JSValue ctor = glfw_gamma_ramp_constructor(ctx); 62 | 63 | JSValue proto = JS_GetPropertyStr(ctx, ctor, "prototype"); 64 | if (JS_IsException(proto)) { 65 | JS_FreeValue(ctx, proto); 66 | return JS_EXCEPTION; 67 | } 68 | 69 | JSValue obj = JS_NewObjectProtoClass(ctx, proto, glfw_gamma_ramp_class_id); 70 | JS_FreeValue(ctx, proto); 71 | if (JS_IsException(proto)) { 72 | JS_FreeValue(ctx, obj); 73 | return JS_EXCEPTION; 74 | } 75 | 76 | JS_SetOpaque(obj, (void*) gamma_ramp); 77 | return obj; 78 | } 79 | 80 | int glfw_gamma_ramp_init(JSContext* ctx, JSModuleDef* m) { 81 | JS_SetModuleExport(ctx, m, "GammaRamp", glfw_gamma_ramp_constructor(ctx)); 82 | return 0; 83 | } 84 | 85 | int glfw_gamma_ramp_export(JSContext* ctx, JSModuleDef* m) { 86 | return JS_AddModuleExport(ctx, m, "GammaRamp"); 87 | } 88 | -------------------------------------------------------------------------------- /position.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | 3 | #include "position.h" 4 | 5 | // Constructor/Destructor 6 | JSValue glfw_position_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { 7 | GLFWPosition* position = js_mallocz(ctx, sizeof(*position)); 8 | if (!position) return JS_EXCEPTION; 9 | 10 | if (JS_ToInt32(ctx, &position->x, argv[0])) 11 | goto fail; 12 | 13 | if (JS_ToInt32(ctx, &position->y, argv[1])) 14 | goto fail; 15 | 16 | return glfw_position_new_instance(ctx, position); 17 | fail: 18 | js_free(ctx, position); 19 | return JS_EXCEPTION; 20 | } 21 | 22 | void glfw_position_finalizer(JSRuntime* rt, JSValue val) { 23 | GLFWPosition* position = JS_GetOpaque(val, glfw_position_class_id); 24 | js_free_rt(rt, position); 25 | } 26 | 27 | // Properties 28 | JSValue glfw_position_get_xy(JSContext* ctx, JSValueConst this_val, int magic) { 29 | GLFWPosition* position = JS_GetOpaque2(ctx, this_val, glfw_position_class_id); 30 | if (!position) return JS_EXCEPTION; 31 | return JS_NewInt32(ctx, magic == 0 ? position->x : position->y); 32 | } 33 | 34 | JSValue glfw_position_set_xy(JSContext* ctx, JSValueConst this_val, JSValue val, int magic) { 35 | GLFWPosition* position = JS_GetOpaque2(ctx, this_val, glfw_position_class_id); 36 | if (!position) return JS_EXCEPTION; 37 | 38 | int value; 39 | if (JS_ToInt32(ctx, &value, val)) 40 | return JS_EXCEPTION; 41 | 42 | if (magic == 0) 43 | position->x = value; 44 | else 45 | position->y = value; 46 | 47 | return JS_UNDEFINED; 48 | } 49 | 50 | // Initialization 51 | JSClassDef glfw_position_class_def = { 52 | "Position", 53 | .finalizer = glfw_position_finalizer, 54 | }; 55 | 56 | const JSCFunctionListEntry glfw_position_proto_funcs[] = { 57 | JS_CGETSET_MAGIC_DEF("x", glfw_position_get_xy, glfw_position_set_xy, 0), 58 | JS_CGETSET_MAGIC_DEF("y", glfw_position_get_xy, glfw_position_set_xy, 1), 59 | }; 60 | 61 | JSValue glfw_position_proto, glfw_position_class; 62 | 63 | JSValue glfw_position_constructor(JSContext* ctx) { 64 | JSRuntime* rt = JS_GetRuntime(ctx); 65 | 66 | if (!JS_IsRegisteredClass(rt, glfw_position_class_id)) { 67 | JS_NewClassID(&glfw_position_class_id); 68 | JS_NewClass(rt, glfw_position_class_id, &glfw_position_class_def); 69 | 70 | glfw_position_proto = JS_NewObject(ctx); 71 | JS_SetPropertyFunctionList(ctx, glfw_position_proto, glfw_position_proto_funcs, countof(glfw_position_proto_funcs)); 72 | JS_SetClassProto(ctx, glfw_position_class_id, glfw_position_proto); 73 | 74 | glfw_position_class = JS_NewCFunction2(ctx, glfw_position_ctor, "Position", 2, JS_CFUNC_constructor, 0); 75 | /* set proto.constructor and ctor.prototype */ 76 | JS_SetConstructor(ctx, glfw_position_class, glfw_position_proto); 77 | } 78 | 79 | return glfw_position_class; 80 | } 81 | 82 | JSValue glfw_position_new_instance(JSContext* ctx, GLFWPosition* position) { 83 | JSValue obj = JS_UNDEFINED; 84 | JSValue proto; 85 | 86 | proto = JS_GetPropertyStr(ctx, glfw_position_constructor(ctx), "prototype"); 87 | if (JS_IsException(proto)) 88 | goto fail; 89 | 90 | obj = JS_NewObjectProtoClass(ctx, proto, glfw_position_class_id); 91 | JS_FreeValue(ctx, proto); 92 | if (JS_IsException(obj)) 93 | goto fail; 94 | 95 | JS_SetOpaque(obj, position); 96 | 97 | return obj; 98 | fail: 99 | JS_FreeValue(ctx, obj); 100 | return JS_EXCEPTION; 101 | } 102 | 103 | int glfw_position_init(JSContext* ctx, JSModuleDef* m) { 104 | JS_SetModuleExport(ctx, m, "Position", glfw_position_constructor(ctx)); 105 | return 0; 106 | } 107 | 108 | int glfw_position_export(JSContext* ctx, JSModuleDef* m) { 109 | return JS_AddModuleExport(ctx, m, "Position"); 110 | } 111 | -------------------------------------------------------------------------------- /size.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | 3 | #include "size.h" 4 | 5 | // Constructor/Destructor 6 | JSValue glfw_size_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { 7 | GLFWSize* size; 8 | JSValue obj = JS_UNDEFINED; 9 | JSValue proto; 10 | 11 | size = js_mallocz(ctx, sizeof(*size)); 12 | if (!size) return JS_EXCEPTION; 13 | 14 | if (JS_ToInt32(ctx, &size->width, argv[0])) 15 | goto fail; 16 | 17 | if (JS_ToInt32(ctx, &size->height, argv[1])) 18 | goto fail; 19 | 20 | /* using new_target to get the prototype is necessary when the 21 | class is extended. */ 22 | proto = JS_GetPropertyStr(ctx, new_target, "prototype"); 23 | if (JS_IsException(proto)) 24 | goto fail; 25 | 26 | obj = JS_NewObjectProtoClass(ctx, proto, glfw_size_class_id); 27 | JS_FreeValue(ctx, proto); 28 | if (JS_IsException(obj)) 29 | goto fail; 30 | 31 | JS_SetOpaque(obj, size); 32 | return obj; 33 | fail: 34 | js_free(ctx, size); 35 | JS_FreeValue(ctx, obj); 36 | return JS_EXCEPTION; 37 | } 38 | 39 | void glfw_size_finalizer(JSRuntime* rt, JSValue val) { 40 | GLFWSize* size = JS_GetOpaque(val, glfw_size_class_id); 41 | js_free_rt(rt, size); 42 | } 43 | 44 | // Properties 45 | JSValue glfw_size_get_axis(JSContext* ctx, JSValueConst this_val, int magic) { 46 | GLFWSize* size = JS_GetOpaque2(ctx, this_val, glfw_size_class_id); 47 | if (!size) return JS_EXCEPTION; 48 | return JS_NewInt32(ctx, magic == 0 ? size->width : size->height); 49 | } 50 | 51 | JSValue glfw_size_set_axis(JSContext* ctx, JSValueConst this_val, JSValue val, int magic) { 52 | GLFWSize* size = JS_GetOpaque2(ctx, this_val, glfw_size_class_id); 53 | if (!size) return JS_EXCEPTION; 54 | 55 | int value; 56 | if (JS_ToInt32(ctx, &value, val)) 57 | return JS_EXCEPTION; 58 | 59 | if (magic == 0) 60 | size->width = value; 61 | else 62 | size->height = value; 63 | 64 | return JS_UNDEFINED; 65 | } 66 | 67 | // Initialization 68 | JSClassDef glfw_size_class_def = { 69 | "Size", 70 | .finalizer = glfw_size_finalizer, 71 | }; 72 | 73 | const JSCFunctionListEntry glfw_size_proto_funcs[] = { 74 | JS_CGETSET_MAGIC_DEF("width", glfw_size_get_axis, glfw_size_set_axis, 0), 75 | JS_CGETSET_MAGIC_DEF("height", glfw_size_get_axis, glfw_size_set_axis, 1), 76 | }; 77 | 78 | JSValue glfw_size_proto, glfw_size_class; 79 | 80 | JSValue glfw_size_constructor(JSContext* ctx) { 81 | JSRuntime* rt = JS_GetRuntime(ctx); 82 | 83 | if (!JS_IsRegisteredClass(rt, glfw_size_class_id)) { 84 | JS_NewClassID(&glfw_size_class_id); 85 | JS_NewClass(JS_GetRuntime(ctx), glfw_size_class_id, &glfw_size_class_def); 86 | 87 | glfw_size_proto = JS_NewObject(ctx); 88 | JS_SetPropertyFunctionList(ctx, glfw_size_proto, glfw_size_proto_funcs, countof(glfw_size_proto_funcs)); 89 | JS_SetClassProto(ctx, glfw_size_class_id, glfw_size_proto); 90 | 91 | glfw_size_class = JS_NewCFunction2(ctx, glfw_size_ctor, "Size", 2, JS_CFUNC_constructor, 0); 92 | /* set proto.constructor and ctor.prototype */ 93 | JS_SetConstructor(ctx, glfw_size_class, glfw_size_proto); 94 | } 95 | 96 | return glfw_size_class; 97 | } 98 | 99 | JSValue glfw_size_new_instance(JSContext* ctx, GLFWSize* size) { 100 | JSValue obj = JS_UNDEFINED; 101 | JSValue proto; 102 | 103 | proto = JS_GetPropertyStr(ctx, glfw_size_constructor(ctx), "prototype"); 104 | if (JS_IsException(proto)) 105 | goto fail; 106 | 107 | obj = JS_NewObjectProtoClass(ctx, proto, glfw_size_class_id); 108 | JS_FreeValue(ctx, proto); 109 | if (JS_IsException(obj)) 110 | goto fail; 111 | 112 | JS_SetOpaque(obj, size); 113 | 114 | return obj; 115 | fail: 116 | JS_FreeValue(ctx, obj); 117 | return JS_EXCEPTION; 118 | } 119 | 120 | int glfw_size_init(JSContext* ctx, JSModuleDef* m) { 121 | JS_SetModuleExport(ctx, m, "Size", glfw_size_constructor(ctx)); 122 | return 0; 123 | } 124 | 125 | int glfw_size_export(JSContext* ctx, JSModuleDef* m) { 126 | return JS_AddModuleExport(ctx, m, "Size"); 127 | } 128 | -------------------------------------------------------------------------------- /scale.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | 3 | #include "scale.h" 4 | 5 | // Constructor/Destructor 6 | JSValue glfw_scale_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { 7 | GLFWScale* scale; 8 | JSValue obj = JS_UNDEFINED; 9 | JSValue proto; 10 | 11 | scale = js_mallocz(ctx, sizeof(*scale)); 12 | if (!scale) return JS_EXCEPTION; 13 | 14 | if (JS_ToFloat64(ctx, &scale->x, argv[0])) 15 | goto fail; 16 | 17 | if (JS_ToFloat64(ctx, &scale->y, argv[1])) 18 | goto fail; 19 | 20 | /* using new_target to get the prototype is necessary when the 21 | class is extended. */ 22 | proto = JS_GetPropertyStr(ctx, new_target, "prototype"); 23 | if (JS_IsException(proto)) 24 | goto fail; 25 | 26 | obj = JS_NewObjectProtoClass(ctx, proto, glfw_scale_class_id); 27 | JS_FreeValue(ctx, proto); 28 | if (JS_IsException(obj)) 29 | goto fail; 30 | 31 | JS_SetOpaque(obj, scale); 32 | return obj; 33 | fail: 34 | js_free(ctx, scale); 35 | JS_FreeValue(ctx, obj); 36 | return JS_EXCEPTION; 37 | } 38 | 39 | void glfw_scale_finalizer(JSRuntime* rt, JSValue val) { 40 | GLFWScale* scale = JS_GetOpaque(val, glfw_scale_class_id); 41 | js_free_rt(rt, scale); 42 | } 43 | 44 | // Properties 45 | JSValue glfw_scale_get_axis(JSContext* ctx, JSValueConst this_val, int magic) { 46 | GLFWScale* scale = JS_GetOpaque2(ctx, this_val, glfw_scale_class_id); 47 | if (!scale) return JS_EXCEPTION; 48 | return JS_NewFloat64(ctx, magic == 0 ? scale->x : scale->y); 49 | } 50 | 51 | JSValue glfw_scale_set_axis(JSContext* ctx, JSValueConst this_val, JSValue val, int magic) { 52 | GLFWScale* scale = JS_GetOpaque2(ctx, this_val, glfw_scale_class_id); 53 | if (!scale) return JS_EXCEPTION; 54 | 55 | double value; 56 | if (JS_ToFloat64(ctx, &value, val)) 57 | return JS_EXCEPTION; 58 | 59 | if (magic == 0) 60 | scale->x = value; 61 | else 62 | scale->y = value; 63 | 64 | return JS_UNDEFINED; 65 | } 66 | 67 | // Initialization 68 | JSClassDef glfw_scale_class_def = { 69 | "Scale", 70 | .finalizer = glfw_scale_finalizer, 71 | }; 72 | 73 | const JSCFunctionListEntry glfw_scale_proto_funcs[] = { 74 | JS_CGETSET_MAGIC_DEF("x", glfw_scale_get_axis, glfw_scale_set_axis, 0), 75 | JS_CGETSET_MAGIC_DEF("y", glfw_scale_get_axis, glfw_scale_set_axis, 1), 76 | }; 77 | 78 | JSValue glfw_scale_proto, glfw_scale_class; 79 | 80 | JSValue glfw_scale_constructor(JSContext* ctx) { 81 | JSRuntime* rt = JS_GetRuntime(ctx); 82 | 83 | if (!JS_IsRegisteredClass(rt, glfw_scale_class_id)) { 84 | JS_NewClassID(&glfw_scale_class_id); 85 | JS_NewClass(JS_GetRuntime(ctx), glfw_scale_class_id, &glfw_scale_class_def); 86 | 87 | glfw_scale_proto = JS_NewObject(ctx); 88 | JS_SetPropertyFunctionList(ctx, glfw_scale_proto, glfw_scale_proto_funcs, countof(glfw_scale_proto_funcs)); 89 | JS_SetClassProto(ctx, glfw_scale_class_id, glfw_scale_proto); 90 | 91 | glfw_scale_class = JS_NewCFunction2(ctx, glfw_scale_ctor, "Scale", 2, JS_CFUNC_constructor, 0); 92 | /* set proto.constructor and ctor.prototype */ 93 | JS_SetConstructor(ctx, glfw_scale_class, glfw_scale_proto); 94 | } 95 | 96 | return glfw_scale_class; 97 | } 98 | 99 | JSValue glfw_scale_new_instance(JSContext* ctx, GLFWScale* scale) { 100 | JSValue obj = JS_UNDEFINED; 101 | JSValue proto; 102 | 103 | proto = JS_GetPropertyStr(ctx, glfw_scale_constructor(ctx), "prototype"); 104 | if (JS_IsException(proto)) 105 | goto fail; 106 | 107 | obj = JS_NewObjectProtoClass(ctx, proto, glfw_scale_class_id); 108 | JS_FreeValue(ctx, proto); 109 | if (JS_IsException(obj)) 110 | goto fail; 111 | 112 | JS_SetOpaque(obj, scale); 113 | 114 | return obj; 115 | fail: 116 | JS_FreeValue(ctx, obj); 117 | return JS_EXCEPTION; 118 | } 119 | 120 | int glfw_scale_init(JSContext* ctx, JSModuleDef* m) { 121 | JS_SetModuleExport(ctx, m, "Scale", glfw_scale_constructor(ctx)); 122 | return 0; 123 | } 124 | 125 | int glfw_scale_export(JSContext* ctx, JSModuleDef* m) { 126 | return JS_AddModuleExport(ctx, m, "Scale"); 127 | } 128 | -------------------------------------------------------------------------------- /video_mode.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | 3 | #include "video_mode.h" 4 | 5 | // Constructor/Destructor 6 | JSValue glfw_video_mode_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { 7 | JS_ThrowInternalError(ctx, "VideoMode can not be constructed directly"); 8 | return JS_EXCEPTION; 9 | } 10 | 11 | // Properties 12 | JSValue glfw_video_mode_get_int(JSContext* ctx, JSValueConst this_val, int magic) { 13 | GLFWvidmode* video_mode = JS_GetOpaque2(ctx, this_val, glfw_video_mode_class_id); 14 | if (!video_mode) return JS_EXCEPTION; 15 | 16 | int value; 17 | 18 | switch (magic) { 19 | case 0: value = video_mode->width; break; 20 | case 1: value = video_mode->height; break; 21 | case 2: value = video_mode->redBits; break; 22 | case 3: value = video_mode->greenBits; break; 23 | case 4: value = video_mode->blueBits; break; 24 | case 5: value = video_mode->refreshRate; break; 25 | } 26 | 27 | return JS_NewInt64(ctx, value); 28 | } 29 | 30 | JSValue glfw_video_mode_set_int(JSContext* ctx, JSValueConst this_val, JSValue val, int magic) { 31 | GLFWvidmode* video_mode = JS_GetOpaque2(ctx, this_val, glfw_video_mode_class_id); 32 | if (!video_mode) return JS_EXCEPTION; 33 | 34 | int value; 35 | if (JS_ToInt32(ctx, &value, val)) 36 | return JS_EXCEPTION; 37 | 38 | switch (magic) { 39 | case 0: video_mode->width = value; break; 40 | case 1: video_mode->height = value; break; 41 | case 2: video_mode->redBits = value; break; 42 | case 3: video_mode->greenBits = value; break; 43 | case 4: video_mode->blueBits = value; break; 44 | case 5: video_mode->refreshRate = value; break; 45 | } 46 | 47 | return JS_UNDEFINED; 48 | } 49 | 50 | // Initialization 51 | JSClassDef glfw_video_mode_class_def = { 52 | "VideoMode", 53 | }; 54 | 55 | const JSCFunctionListEntry glfw_video_mode_proto_funcs[] = { 56 | JS_CGETSET_MAGIC_DEF("width", glfw_video_mode_get_int, glfw_video_mode_set_int, 0), 57 | JS_CGETSET_MAGIC_DEF("height", glfw_video_mode_get_int, glfw_video_mode_set_int, 1), 58 | JS_CGETSET_MAGIC_DEF("redBits", glfw_video_mode_get_int, glfw_video_mode_set_int, 2), 59 | JS_CGETSET_MAGIC_DEF("greenBits", glfw_video_mode_get_int, glfw_video_mode_set_int, 3), 60 | JS_CGETSET_MAGIC_DEF("blueBits", glfw_video_mode_get_int, glfw_video_mode_set_int, 4), 61 | JS_CGETSET_MAGIC_DEF("refreshRate", glfw_video_mode_get_int, glfw_video_mode_set_int, 5), 62 | }; 63 | 64 | JSValue glfw_video_mode_proto, glfw_video_mode_class; 65 | 66 | JSValue glfw_video_mode_constructor(JSContext* ctx) { 67 | JSRuntime* rt = JS_GetRuntime(ctx); 68 | 69 | if (!JS_IsRegisteredClass(rt, glfw_video_mode_class_id)) { 70 | JS_NewClassID(&glfw_video_mode_class_id); 71 | JS_NewClass(JS_GetRuntime(ctx), glfw_video_mode_class_id, &glfw_video_mode_class_def); 72 | 73 | glfw_video_mode_proto = JS_NewObject(ctx); 74 | JS_SetPropertyFunctionList(ctx, glfw_video_mode_proto, glfw_video_mode_proto_funcs, countof(glfw_video_mode_proto_funcs)); 75 | JS_SetClassProto(ctx, glfw_video_mode_class_id, glfw_video_mode_proto); 76 | 77 | glfw_video_mode_class = JS_NewCFunction2(ctx, glfw_video_mode_ctor, "VideoMode", 2, JS_CFUNC_constructor, 0); 78 | /* set proto.constructor and ctor.prototype */ 79 | JS_SetConstructor(ctx, glfw_video_mode_class, glfw_video_mode_proto); 80 | } 81 | 82 | return glfw_video_mode_class; 83 | } 84 | 85 | JSValue glfw_video_mode_new_instance(JSContext* ctx, const GLFWvidmode* video_mode) { 86 | JSValue ctor = glfw_video_mode_constructor(ctx); 87 | JSValue proto = JS_GetPropertyStr(ctx, ctor, "prototype"); 88 | if (JS_IsException(proto)) { 89 | JS_FreeValue(ctx, proto); 90 | return JS_EXCEPTION; 91 | } 92 | 93 | JSValue obj = JS_NewObjectProtoClass(ctx, proto, glfw_video_mode_class_id); 94 | JS_FreeValue(ctx, proto); 95 | if (JS_IsException(proto)) { 96 | JS_FreeValue(ctx, obj); 97 | return JS_EXCEPTION; 98 | } 99 | 100 | JS_SetOpaque(obj, (void*) video_mode); 101 | return obj; 102 | } 103 | 104 | int glfw_video_mode_init(JSContext* ctx, JSModuleDef* m) { 105 | JS_SetModuleExport(ctx, m, "VideoMode", glfw_video_mode_constructor(ctx)); 106 | return 0; 107 | } 108 | 109 | int glfw_video_mode_export(JSContext* ctx, JSModuleDef* m) { 110 | return JS_AddModuleExport(ctx, m, "VideoMode"); 111 | } 112 | -------------------------------------------------------------------------------- /workarea.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | 3 | #include "workarea.h" 4 | 5 | // Constructor/Destructor 6 | JSValue glfw_workarea_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst* argv) { 7 | GLFWWorkArea* workarea = js_mallocz(ctx, sizeof(*workarea)); 8 | if (!workarea) return JS_EXCEPTION; 9 | 10 | GLFWPosition* position = js_mallocz(ctx, sizeof(*position)); 11 | if (!position) return JS_EXCEPTION; 12 | 13 | GLFWSize* size = js_mallocz(ctx, sizeof(*size)); 14 | if (!size) return JS_EXCEPTION; 15 | 16 | workarea->position = position; 17 | workarea->size = size; 18 | 19 | if (JS_ToInt32(ctx, &position->x, argv[0])) 20 | goto fail; 21 | 22 | if (JS_ToInt32(ctx, &position->y, argv[1])) 23 | goto fail; 24 | 25 | if (JS_ToInt32(ctx, &size->width, argv[2])) 26 | goto fail; 27 | 28 | if (JS_ToInt32(ctx, &size->height, argv[3])) 29 | goto fail; 30 | 31 | return glfw_workarea_new_instance(ctx, workarea); 32 | fail: 33 | js_free(ctx, workarea); 34 | return JS_EXCEPTION; 35 | } 36 | 37 | void glfw_workarea_finalizer(JSRuntime* rt, JSValue val) { 38 | GLFWWorkArea* workarea = JS_GetOpaque(val, glfw_workarea_class_id); 39 | js_free_rt(rt, workarea); 40 | } 41 | 42 | // Properties 43 | JSValue glfw_workarea_get_position_or_size(JSContext* ctx, JSValueConst this_val, int magic) { 44 | GLFWWorkArea* workarea = JS_GetOpaque2(ctx, this_val, glfw_workarea_class_id); 45 | if (!workarea) return JS_EXCEPTION; 46 | 47 | if (magic == 0) 48 | return glfw_position_new_instance(ctx, workarea->position); 49 | else 50 | return glfw_size_new_instance(ctx, workarea->size); 51 | } 52 | 53 | JSValue glfw_workarea_set_position_or_size(JSContext* ctx, JSValueConst this_val, JSValue val, int magic) { 54 | GLFWWorkArea* workarea = JS_GetOpaque2(ctx, this_val, glfw_workarea_class_id); 55 | if (!workarea) return JS_EXCEPTION; 56 | 57 | if (magic == 0) { 58 | workarea->position = JS_GetOpaque2(ctx, val, glfw_position_class_id); 59 | } else { 60 | workarea->size = JS_GetOpaque2(ctx, val, glfw_size_class_id); 61 | } 62 | 63 | return JS_UNDEFINED; 64 | } 65 | 66 | // Initialization 67 | JSClassDef glfw_workarea_class_def = { 68 | "WorkArea", 69 | .finalizer = glfw_workarea_finalizer, 70 | }; 71 | 72 | const JSCFunctionListEntry glfw_workarea_proto_funcs[] = { 73 | JS_CGETSET_MAGIC_DEF("position", glfw_workarea_get_position_or_size, glfw_workarea_set_position_or_size, 0), 74 | JS_CGETSET_MAGIC_DEF("size", glfw_workarea_get_position_or_size, glfw_workarea_set_position_or_size, 1), 75 | }; 76 | 77 | JSValue glfw_workarea_proto, glfw_workarea_class; 78 | 79 | JSValue glfw_workarea_constructor(JSContext* ctx) { 80 | JSRuntime* rt = JS_GetRuntime(ctx); 81 | 82 | if (!JS_IsRegisteredClass(rt, glfw_workarea_class_id)) { 83 | JS_NewClassID(&glfw_workarea_class_id); 84 | JS_NewClass(rt, glfw_workarea_class_id, &glfw_workarea_class_def); 85 | 86 | glfw_workarea_proto = JS_NewObject(ctx); 87 | JS_SetPropertyFunctionList(ctx, glfw_workarea_proto, glfw_workarea_proto_funcs, countof(glfw_workarea_proto_funcs)); 88 | JS_SetClassProto(ctx, glfw_workarea_class_id, glfw_workarea_proto); 89 | 90 | glfw_workarea_class = JS_NewCFunction2(ctx, glfw_workarea_ctor, "WorkArea", 2, JS_CFUNC_constructor, 0); 91 | /* set proto.constructor and ctor.prototype */ 92 | JS_SetConstructor(ctx, glfw_workarea_class, glfw_workarea_proto); 93 | } 94 | 95 | return glfw_workarea_class; 96 | } 97 | 98 | JSValue glfw_workarea_new_instance(JSContext* ctx, GLFWWorkArea* workarea) { 99 | JSValue ctor = glfw_workarea_constructor(ctx); 100 | JSValue proto = JS_GetPropertyStr(ctx, ctor, "prototype"); 101 | if (JS_IsException(proto)) { 102 | JS_FreeValue(ctx, proto); 103 | return JS_EXCEPTION; 104 | } 105 | 106 | JSValue obj = JS_NewObjectProtoClass(ctx, proto, glfw_workarea_class_id); 107 | JS_FreeValue(ctx, proto); 108 | if (JS_IsException(proto)) { 109 | JS_FreeValue(ctx, obj); 110 | return JS_EXCEPTION; 111 | } 112 | 113 | JS_SetOpaque(obj, workarea); 114 | return obj; 115 | } 116 | 117 | int glfw_workarea_init(JSContext* ctx, JSModuleDef* m) { 118 | JS_SetModuleExport(ctx, m, "WorkArea", glfw_workarea_constructor(ctx)); 119 | return 0; 120 | } 121 | 122 | int glfw_workarea_export(JSContext* ctx, JSModuleDef* m) { 123 | return JS_AddModuleExport(ctx, m, "WorkArea"); 124 | } 125 | -------------------------------------------------------------------------------- /monitor.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | #include "position.h" 3 | #include "workarea.h" 4 | #include "scale.h" 5 | #include "video_mode.h" 6 | #include "gamma_ramp.h" 7 | 8 | #include "monitor.h" 9 | 10 | // Constructor/Destructor 11 | JSValue glfw_monitor_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst *argv) { 12 | return glfw_monitor_new_instance(ctx, glfwGetPrimaryMonitor()); 13 | } 14 | 15 | // Properties 16 | JSValue glfw_monitor_get_name(JSContext *ctx, JSValueConst this_val) { 17 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 18 | if (!monitor) return JS_EXCEPTION; 19 | 20 | const char* name = glfwGetMonitorName(monitor); 21 | return JS_NewString(ctx, name); 22 | } 23 | JSValue glfw_monitor_get_position(JSContext *ctx, JSValueConst this_val) { 24 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 25 | if (!monitor) return JS_EXCEPTION; 26 | 27 | GLFWPosition* position = js_mallocz(ctx, sizeof(*position)); 28 | glfwGetMonitorPos(monitor, &position->x, &position->y); 29 | return glfw_position_new_instance(ctx, position); 30 | } 31 | JSValue glfw_monitor_get_workarea(JSContext *ctx, JSValueConst this_val) { 32 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 33 | if (!monitor) return JS_EXCEPTION; 34 | 35 | GLFWWorkArea* workarea = js_mallocz(ctx, sizeof(*workarea)); 36 | if (!workarea) return JS_EXCEPTION; 37 | 38 | GLFWPosition* position = js_mallocz(ctx, sizeof(*position)); 39 | if (!position) return JS_EXCEPTION; 40 | 41 | GLFWSize* size = js_mallocz(ctx, sizeof(*size)); 42 | if (!size) return JS_EXCEPTION; 43 | 44 | workarea->position = position; 45 | workarea->size = size; 46 | 47 | glfwGetMonitorWorkarea(monitor, &position->x, &position->y, &size->width, &size->height); 48 | return glfw_workarea_new_instance(ctx, workarea); 49 | } 50 | JSValue glfw_monitor_get_physical_size(JSContext *ctx, JSValueConst this_val) { 51 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 52 | if (!monitor) return JS_EXCEPTION; 53 | 54 | GLFWSize* size = js_mallocz(ctx, sizeof(*size)); 55 | glfwGetMonitorPhysicalSize(monitor, &size->width, &size->height); 56 | return glfw_size_new_instance(ctx, size); 57 | } 58 | JSValue glfw_monitor_get_content_scale(JSContext *ctx, JSValueConst this_val) { 59 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 60 | if (!monitor) return JS_EXCEPTION; 61 | 62 | GLFWScale* scale = js_mallocz(ctx, sizeof(*scale)); 63 | glfwGetMonitorContentScale(monitor, (float*)&scale->x, (float*)&scale->y); 64 | return glfw_scale_new_instance(ctx, scale); 65 | } 66 | JSValue glfw_monitor_get_current_video_mode(JSContext *ctx, JSValueConst this_val) { 67 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 68 | if (!monitor) return JS_EXCEPTION; 69 | 70 | const GLFWvidmode* video_mode = glfwGetVideoMode(monitor); 71 | return glfw_video_mode_new_instance(ctx, video_mode); 72 | } 73 | JSValue glfw_monitor_get_video_modes(JSContext *ctx, JSValueConst this_val) { 74 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 75 | if (!monitor) return JS_EXCEPTION; 76 | 77 | int count; 78 | const GLFWvidmode* video_modes = glfwGetVideoModes(monitor, &count); 79 | 80 | JSValue array = JS_NewArray(ctx); 81 | for (int i = 0; i < count; i++) { 82 | JSValue video_mode = glfw_video_mode_new_instance(ctx, &video_modes[i]); 83 | JS_SetPropertyInt64(ctx, array, i, video_mode); 84 | } 85 | 86 | return array; 87 | } 88 | JSValue glfw_monitor_get_gamma(JSContext *ctx, JSValueConst this_val) { 89 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 90 | if (!monitor) return JS_EXCEPTION; 91 | 92 | const GLFWgammaramp* gamma = glfwGetGammaRamp(monitor); 93 | return glfw_gamma_ramp_new_instance(ctx, gamma); 94 | } 95 | JSValue glfw_monitor_set_gamma(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 96 | GLFWmonitor* monitor = JS_GetOpaque2(ctx, this_val, glfw_monitor_class_id); 97 | if (!monitor) return JS_EXCEPTION; 98 | 99 | if (JS_IsNumber(value)) { 100 | float gamma = JS_VALUE_GET_FLOAT64(value); 101 | glfwSetGamma(monitor, gamma); 102 | return JS_UNDEFINED; 103 | } 104 | 105 | GLFWgammaramp* gamma_ramp = JS_GetOpaque2(ctx, value, glfw_gamma_ramp_class_id); 106 | if (!monitor) return JS_EXCEPTION; 107 | 108 | glfwSetGammaRamp(monitor, gamma_ramp); 109 | 110 | return JS_UNDEFINED; 111 | } 112 | 113 | // Static properties 114 | JSValue glfw_monitor_get_monitors(JSContext *ctx, JSValueConst this_val) { 115 | int count; 116 | GLFWmonitor** monitors = glfwGetMonitors(&count); 117 | 118 | JSValue array = JS_NewArray(ctx); 119 | for (int i = 0; i < count; i++) { 120 | JSValue monitor = glfw_monitor_new_instance(ctx, monitors[i]); 121 | JS_SetPropertyInt64(ctx, array, i, monitor); 122 | } 123 | 124 | return array; 125 | } 126 | 127 | // Initialization 128 | JSClassDef glfw_monitor_class_def = { 129 | "Monitor", 130 | }; 131 | 132 | const JSCFunctionListEntry glfw_monitor_proto_funcs[] = { 133 | JS_CGETSET_DEF("name", glfw_monitor_get_name, NULL), 134 | JS_CGETSET_DEF("position", glfw_monitor_get_position, NULL), 135 | JS_CGETSET_DEF("workarea", glfw_monitor_get_workarea, NULL), 136 | JS_CGETSET_DEF("physical_size", glfw_monitor_get_physical_size, NULL), 137 | JS_CGETSET_DEF("content_scale", glfw_monitor_get_content_scale, NULL), 138 | JS_CGETSET_DEF("currentVideoMode", glfw_monitor_get_current_video_mode, NULL), 139 | JS_CGETSET_DEF("videoModes", glfw_monitor_get_video_modes, NULL), 140 | JS_CGETSET_DEF("gamma", glfw_monitor_get_gamma, glfw_monitor_set_gamma), 141 | }; 142 | 143 | const JSCFunctionListEntry glfw_monitor_funcs[] = { 144 | JS_CGETSET_DEF("monitors", glfw_monitor_get_monitors, NULL), 145 | }; 146 | 147 | JSValue glfw_monitor_proto, glfw_monitor_class; 148 | 149 | JSValue glfw_monitor_constructor(JSContext* ctx) { 150 | JSRuntime* rt = JS_GetRuntime(ctx); 151 | 152 | if (!JS_IsRegisteredClass(rt, glfw_monitor_class_id)) { 153 | JS_NewClassID(&glfw_monitor_class_id); 154 | JS_NewClass(rt, glfw_monitor_class_id, &glfw_monitor_class_def); 155 | 156 | glfw_monitor_proto = JS_NewObject(ctx); 157 | JS_SetPropertyFunctionList(ctx, glfw_monitor_proto, glfw_monitor_proto_funcs, countof(glfw_monitor_proto_funcs)); 158 | JS_SetClassProto(ctx, glfw_monitor_class_id, glfw_monitor_proto); 159 | 160 | glfw_monitor_class = JS_NewCFunction2(ctx, glfw_monitor_ctor, "Monitor", 5, JS_CFUNC_constructor, 0); 161 | JS_SetPropertyFunctionList(ctx, glfw_monitor_class, glfw_monitor_funcs, countof(glfw_monitor_funcs)); 162 | JS_SetConstructor(ctx, glfw_monitor_class, glfw_monitor_proto); 163 | } 164 | 165 | return glfw_monitor_class; 166 | } 167 | 168 | JSValue glfw_monitor_new_instance(JSContext* ctx, GLFWmonitor* monitor) { 169 | JSValue ctor = glfw_monitor_constructor(ctx); 170 | JSValue proto = JS_GetPropertyStr(ctx, ctor, "prototype"); 171 | if (JS_IsException(proto)) { 172 | JS_FreeValue(ctx, proto); 173 | return JS_EXCEPTION; 174 | } 175 | 176 | JSValue obj = JS_NewObjectProtoClass(ctx, proto, glfw_monitor_class_id); 177 | JS_FreeValue(ctx, proto); 178 | if (JS_IsException(obj)) { 179 | JS_FreeValue(ctx, obj); 180 | return JS_EXCEPTION; 181 | } 182 | 183 | JS_SetOpaque(obj, monitor); 184 | return obj; 185 | } 186 | 187 | int glfw_monitor_init(JSContext* ctx, JSModuleDef* m) { 188 | JS_SetModuleExport(ctx, m, "Monitor", glfw_monitor_constructor(ctx)); 189 | return 0; 190 | } 191 | 192 | int glfw_monitor_export(JSContext* ctx, JSModuleDef* m) { 193 | return JS_AddModuleExport(ctx, m, "Monitor"); 194 | } 195 | -------------------------------------------------------------------------------- /glfw.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | #include "position.h" 3 | #include "size.h" 4 | #include "window.h" 5 | #include "monitor.h" 6 | 7 | // 8 | // Top-level 9 | // 10 | JSValue glfw_poll_events(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 11 | glfwPollEvents(); 12 | return JS_UNDEFINED; 13 | } 14 | 15 | JSValue glfw_wait_events(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 16 | if (JS_IsNumber(argv[0])) { 17 | double timeout; 18 | if (JS_ToFloat64(ctx, &timeout, argv[0])) 19 | return JS_EXCEPTION; 20 | 21 | glfwWaitEventsTimeout(timeout); 22 | } else { 23 | glfwWaitEvents(); 24 | } 25 | 26 | return JS_UNDEFINED; 27 | } 28 | 29 | JSValue glfw_post_empty_event(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 30 | if (JS_IsNumber(argv[0])) { 31 | double timeout; 32 | if (JS_ToFloat64(ctx, &timeout, argv[0])) 33 | return JS_EXCEPTION; 34 | 35 | glfwWaitEventsTimeout(timeout); 36 | } else { 37 | glfwWaitEvents(); 38 | } 39 | 40 | return JS_UNDEFINED; 41 | } 42 | 43 | // Context 44 | JSValue glfw_context_get_current(JSContext *ctx, JSValueConst this_val) { 45 | GLFWwindow* window = glfwGetCurrentContext(); 46 | if (!window) return JS_EXCEPTION; 47 | // TODO: window is not owned so finalizer should not destroy it 48 | return glfw_window_new_instance(ctx, window); 49 | } 50 | JSValue glfw_context_set_current(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 51 | GLFWwindow* window = JS_GetOpaque2(ctx, value, glfw_window_class_id); 52 | if (!window) return JS_EXCEPTION; 53 | glfwMakeContextCurrent(window); 54 | return JS_UNDEFINED; 55 | } 56 | 57 | JSValue glfw_context_swap_interval(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 58 | int interval; 59 | if (JS_ToInt32(ctx, &interval, argv[0])) 60 | return JS_EXCEPTION; 61 | 62 | glfwSwapInterval(interval); 63 | 64 | return JS_UNDEFINED; 65 | } 66 | 67 | static const JSCFunctionListEntry glfw_context_props[] = { 68 | JS_CGETSET_DEF("current", glfw_context_get_current, glfw_context_set_current), 69 | JS_CFUNC_DEF("swapInterval", 2, glfw_context_swap_interval), 70 | }; 71 | 72 | // Version 73 | JSValue glfw_version_to_string(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 74 | return JS_NewString(ctx, glfwGetVersionString()); 75 | } 76 | 77 | #define CONSTANTS(V) \ 78 | V(FOCUSED) \ 79 | V(ICONIFIED) \ 80 | V(RESIZABLE) \ 81 | V(VISIBLE) \ 82 | V(DECORATED) \ 83 | V(AUTO_ICONIFY) \ 84 | V(FLOATING) \ 85 | V(MAXIMIZED) \ 86 | V(CENTER_CURSOR) \ 87 | V(TRANSPARENT_FRAMEBUFFER) \ 88 | V(HOVERED) \ 89 | V(FOCUS_ON_SHOW) \ 90 | V(RED_BITS) \ 91 | V(GREEN_BITS) \ 92 | V(BLUE_BITS) \ 93 | V(ALPHA_BITS) \ 94 | V(DEPTH_BITS) \ 95 | V(STENCIL_BITS) \ 96 | V(ACCUM_RED_BITS) \ 97 | V(ACCUM_GREEN_BITS) \ 98 | V(ACCUM_BLUE_BITS) \ 99 | V(ACCUM_ALPHA_BITS) \ 100 | V(AUX_BUFFERS) \ 101 | V(STEREO) \ 102 | V(SAMPLES) \ 103 | V(SRGB_CAPABLE) \ 104 | V(REFRESH_RATE) \ 105 | V(DOUBLEBUFFER) \ 106 | V(CLIENT_API) \ 107 | V(CONTEXT_VERSION_MAJOR) \ 108 | V(CONTEXT_VERSION_MINOR) \ 109 | V(CONTEXT_REVISION) \ 110 | V(CONTEXT_ROBUSTNESS) \ 111 | V(OPENGL_FORWARD_COMPAT) \ 112 | V(OPENGL_DEBUG_CONTEXT) \ 113 | V(OPENGL_PROFILE) \ 114 | V(CONTEXT_RELEASE_BEHAVIOR) \ 115 | V(CONTEXT_NO_ERROR) \ 116 | V(CONTEXT_CREATION_API) \ 117 | V(SCALE_TO_MONITOR) \ 118 | V(COCOA_RETINA_FRAMEBUFFER) \ 119 | V(COCOA_FRAME_NAME) \ 120 | V(COCOA_GRAPHICS_SWITCHING) \ 121 | V(X11_CLASS_NAME) \ 122 | V(X11_INSTANCE_NAME) \ 123 | V(OPENGL_CORE_PROFILE) \ 124 | V(VERSION_MAJOR) \ 125 | V(VERSION_MINOR) \ 126 | V(VERSION_REVISION) \ 127 | V(JOYSTICK_HAT_BUTTONS) \ 128 | V(COCOA_CHDIR_RESOURCES) \ 129 | V(COCOA_MENUBAR) 130 | 131 | #define DEFINE_CONSTANT(Name) \ 132 | JS_PROP_INT32_DEF(#Name, GLFW_ ## Name, 0), 133 | 134 | const JSCFunctionListEntry glfw_exports[] = { 135 | JS_CFUNC_DEF("poll", 2, glfw_poll_events), 136 | JS_CFUNC_DEF("wait", 2, glfw_wait_events), 137 | JS_CFUNC_DEF("postEmptyEvent", 2, glfw_post_empty_event), 138 | JS_OBJECT_DEF("context", glfw_context_props, countof(glfw_context_props), JS_PROP_CONFIGURABLE), 139 | CONSTANTS(DEFINE_CONSTANT) 140 | }; 141 | 142 | #undef CONSTANTS 143 | #undef DEFINE_CONSTANT 144 | 145 | // Initialization 146 | int glfw_init(JSContext* ctx, JSModuleDef* m) { 147 | JS_SetModuleExportList(ctx, m, glfw_exports, countof(glfw_exports)); 148 | glfw_position_init(ctx, m); 149 | glfw_size_init(ctx, m); 150 | glfw_window_init(ctx, m); 151 | glfw_monitor_init(ctx, m); 152 | 153 | // TODO: lazy-load version info with a getter? 154 | int major, minor, revision; 155 | glfwGetVersion(&major, &minor, &revision); 156 | JSValue version = JS_NewObject(ctx); 157 | JS_SetPropertyStr(ctx, version, "major", JS_NewInt32(ctx, major)); 158 | JS_SetPropertyStr(ctx, version, "minor", JS_NewInt32(ctx, minor)); 159 | JS_SetPropertyStr(ctx, version, "revision", JS_NewInt32(ctx, revision)); 160 | JS_SetPropertyStr(ctx, version, "toString", JS_NewCFunction(ctx, glfw_version_to_string, "toString", 0)); 161 | JS_SetModuleExport(ctx, m, "version", version); 162 | 163 | return 0; 164 | } 165 | 166 | int glfw_export(JSContext* ctx, JSModuleDef* m) { 167 | JS_AddModuleExportList(ctx, m, glfw_exports, countof(glfw_exports)); 168 | glfw_position_export(ctx, m); 169 | glfw_size_export(ctx, m); 170 | glfw_window_export(ctx, m); 171 | glfw_monitor_export(ctx, m); 172 | 173 | JS_AddModuleExport(ctx, m, "version"); 174 | 175 | return 0; 176 | } 177 | 178 | JSModuleDef* js_init_module(JSContext* ctx, const char* module_name) { 179 | JSModuleDef* m = JS_NewCModule(ctx, module_name, glfw_init); 180 | if (!m) return NULL; 181 | glfw_export(ctx, m); 182 | 183 | // TODO: Is it possible to check errors for init and throw in module import? 184 | glfwInit(); 185 | atexit(glfwTerminate); 186 | 187 | return m; 188 | } 189 | -------------------------------------------------------------------------------- /window.c: -------------------------------------------------------------------------------- 1 | #include "glfw.h" 2 | #include "monitor.h" 3 | #include "position.h" 4 | #include "size.h" 5 | 6 | #include "window.h" 7 | 8 | // Constructor/Destructor 9 | JSValue glfw_window_ctor(JSContext* ctx, JSValueConst new_target, int argc, JSValueConst *argv) { 10 | int width, height; 11 | const char* title; 12 | GLFWmonitor* monitor = NULL; 13 | GLFWwindow* share = NULL; 14 | 15 | if (JS_ToInt32(ctx, &width, argv[0])) 16 | return JS_EXCEPTION; 17 | 18 | if (JS_ToInt32(ctx, &height, argv[1])) 19 | return JS_EXCEPTION; 20 | 21 | title = JS_ToCString(ctx, argv[2]); 22 | if (!title) return JS_EXCEPTION; 23 | 24 | // if (JS_IsObject(argv[3])) { 25 | // monitor = JS_GetOpaque2(ctx, argv[3], glfw_monitor_class_id); 26 | // } 27 | 28 | // if (JS_IsObject(argv[4])) { 29 | // share = JS_GetOpaque2(ctx, argv[4], glfw_window_class_id); 30 | // } 31 | 32 | return glfw_window_create_window(ctx, width, height, title, monitor, share); 33 | } 34 | 35 | // Instance Methods 36 | JSValue glfw_window_make_context_current(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { 37 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 38 | if (!window) return JS_EXCEPTION; 39 | glfwMakeContextCurrent(window); 40 | return JS_UNDEFINED; 41 | } 42 | JSValue glfw_window_swap_buffers(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) { 43 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 44 | if (!window) return JS_EXCEPTION; 45 | glfwSwapBuffers(window); 46 | return JS_UNDEFINED; 47 | } 48 | 49 | // Static Methods 50 | JSValue glfw_window_hint(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 51 | int key; 52 | 53 | if (JS_ToInt32(ctx, &key, argv[0])) 54 | return JS_EXCEPTION; 55 | 56 | if (JS_IsString(argv[1])) { 57 | const char* value = JS_ToCString(ctx, argv[1]); 58 | glfwWindowHintString(key, value); 59 | } else if (JS_IsBool(argv[1])) { 60 | int value = JS_VALUE_GET_BOOL(argv[1]) == 1 ? GL_TRUE : GL_FALSE; 61 | glfwWindowHint(key, value); 62 | } else if (JS_IsNumber(argv[1])) { 63 | int value; 64 | if (JS_ToInt32(ctx, &value, argv[1])) 65 | return JS_EXCEPTION; 66 | 67 | glfwWindowHint(key, value); 68 | } else { 69 | JS_ThrowTypeError(ctx, "Value must be a number or string"); 70 | return JS_EXCEPTION; 71 | } 72 | 73 | return JS_UNDEFINED; 74 | } 75 | JSValue glfw_window_default_hints(JSContext* ctx, JSValueConst this_val, int argc, JSValueConst* argv) { 76 | glfwDefaultWindowHints(); 77 | return JS_UNDEFINED; 78 | } 79 | 80 | // Properties 81 | JSValue glfw_window_get_should_close(JSContext *ctx, JSValueConst this_val) { 82 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 83 | if (!window) return JS_EXCEPTION; 84 | 85 | if (glfwWindowShouldClose(window)) 86 | return JS_TRUE; 87 | else 88 | return JS_FALSE; 89 | } 90 | JSValue glfw_window_set_should_close(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 91 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 92 | if (!window) return JS_EXCEPTION; 93 | int shouldClose = JS_VALUE_GET_BOOL(value) == 1 ? GL_TRUE : GL_FALSE; 94 | glfwSetWindowShouldClose(window, shouldClose); 95 | return JS_UNDEFINED; 96 | } 97 | JSValue glfw_window_set_title(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 98 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 99 | if (!window) return JS_EXCEPTION; 100 | 101 | if (!JS_IsString(value)) { 102 | JS_ThrowTypeError(ctx, "Title must be a string"); 103 | return JS_EXCEPTION; 104 | } 105 | 106 | const char* title = JS_ToCString(ctx, value); 107 | glfwSetWindowTitle(window, title); 108 | return JS_UNDEFINED; 109 | } 110 | JSValue glfw_window_set_position(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 111 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 112 | if (!window) return JS_EXCEPTION; 113 | 114 | GLFWPosition* position = JS_GetOpaque2(ctx, value, glfw_position_class_id); 115 | if (!position) return JS_EXCEPTION; 116 | 117 | glfwSetWindowPos(window, position->x, position->y); 118 | return JS_UNDEFINED; 119 | } 120 | JSValue glfw_window_get_position(JSContext *ctx, JSValueConst this_val) { 121 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 122 | if (!window) return JS_EXCEPTION; 123 | 124 | GLFWPosition* position = js_mallocz(ctx, sizeof(*position)); 125 | glfwGetWindowPos(window, &position->x, &position->y); 126 | return glfw_position_new_instance(ctx, position); 127 | } 128 | 129 | // TODO: magic these with position setter/getter? 130 | JSValue glfw_window_set_size(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 131 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 132 | if (!window) return JS_EXCEPTION; 133 | 134 | GLFWSize* size = JS_GetOpaque2(ctx, value, glfw_size_class_id); 135 | if (!size) return JS_EXCEPTION; 136 | 137 | glfwSetWindowSize(window, size->width, size->height); 138 | return JS_UNDEFINED; 139 | } 140 | JSValue glfw_window_get_size(JSContext *ctx, JSValueConst this_val) { 141 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 142 | if (!window) return JS_EXCEPTION; 143 | 144 | GLFWSize* size = js_mallocz(ctx, sizeof(*size)); 145 | glfwGetWindowSize(window, &size->width, &size->height); 146 | return glfw_size_new_instance(ctx, size); 147 | } 148 | JSValue glfw_window_get_framebuffer_size(JSContext *ctx, JSValueConst this_val) { 149 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 150 | if (!window) return JS_EXCEPTION; 151 | 152 | GLFWSize* size = js_mallocz(ctx, sizeof(*size)); 153 | glfwGetFramebufferSize(window, &size->width, &size->height); 154 | return glfw_size_new_instance(ctx, size); 155 | } 156 | JSValue glfw_window_set_opacity(JSContext *ctx, JSValueConst this_val, JSValueConst value) { 157 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 158 | if (!window) return JS_EXCEPTION; 159 | 160 | double opacity; 161 | if (JS_ToFloat64(ctx, &opacity, value)) 162 | return JS_EXCEPTION; 163 | 164 | glfwSetWindowOpacity(window, opacity); 165 | return JS_UNDEFINED; 166 | } 167 | JSValue glfw_window_get_opacity(JSContext *ctx, JSValueConst this_val) { 168 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 169 | if (!window) return JS_EXCEPTION; 170 | return JS_NewFloat64(ctx, glfwGetWindowOpacity(window)); 171 | } 172 | JSValue glfw_window_get_monitor(JSContext *ctx, JSValueConst this_val) { 173 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); 174 | if (!window) return JS_EXCEPTION; 175 | 176 | GLFWmonitor* monitor = glfwGetWindowMonitor(window); 177 | if (!monitor) { 178 | return JS_UNDEFINED; 179 | } 180 | 181 | return glfw_monitor_new_instance(ctx, monitor); 182 | } 183 | 184 | // Generate a few simple methods with macros...because I'm lazy. :O 185 | #define TRIGGER_FUNCTIONS(V) \ 186 | V(IconifyWindow, iconify) \ 187 | V(RestoreWindow, restore) \ 188 | V(MaximizeWindow, maximize) \ 189 | V(ShowWindow, show) \ 190 | V(HideWindow, hide) \ 191 | V(FocusWindow, focus) \ 192 | V(RequestWindowAttention, requestAttention) 193 | 194 | #define MAKE_TRIGGER_METHOD(NativeName, JSName) \ 195 | JSValue glfw_window_ ## JSName(JSContext *ctx, JSValueConst this_val, \ 196 | int argc, JSValueConst *argv) { \ 197 | GLFWwindow* window = JS_GetOpaque2(ctx, this_val, glfw_window_class_id); \ 198 | glfw ## NativeName(window); \ 199 | if (!window) return JS_EXCEPTION; \ 200 | return JS_UNDEFINED; \ 201 | } 202 | TRIGGER_FUNCTIONS(MAKE_TRIGGER_METHOD) 203 | #undef MAKE_TRIGGER_METHODS 204 | 205 | // Initialization 206 | JSClassDef glfw_window_class_def = { 207 | "Window", 208 | }; 209 | 210 | #define MAKE_TRIGGER_METHOD_ENTRY(NativeName, JSName) \ 211 | JS_CFUNC_DEF(#JSName, 0, glfw_window_ ## JSName), 212 | 213 | const JSCFunctionListEntry glfw_window_proto_funcs[] = { 214 | JS_CFUNC_DEF("makeContextCurrent", 0, glfw_window_make_context_current), 215 | JS_CFUNC_DEF("swapBuffers", 0, glfw_window_swap_buffers), 216 | JS_CGETSET_DEF("shouldClose", glfw_window_get_should_close, glfw_window_set_should_close), 217 | JS_CGETSET_DEF("title", NULL, glfw_window_set_title), 218 | JS_CGETSET_DEF("position", glfw_window_get_position, glfw_window_set_position), 219 | JS_CGETSET_DEF("size", glfw_window_get_size, glfw_window_set_size), 220 | JS_CGETSET_DEF("framebufferSize", glfw_window_get_framebuffer_size, NULL), 221 | JS_CGETSET_DEF("opacity", glfw_window_get_opacity, glfw_window_set_opacity), 222 | JS_CGETSET_DEF("monitor", glfw_window_get_monitor, NULL), 223 | TRIGGER_FUNCTIONS(MAKE_TRIGGER_METHOD_ENTRY) 224 | }; 225 | 226 | #undef MAKE_TRIGGER_METHOD_ENTRY 227 | 228 | const JSCFunctionListEntry glfw_window_funcs[] = { 229 | JS_CFUNC_DEF("hint", 0, glfw_window_hint), 230 | JS_CFUNC_DEF("defaultHints", 0, glfw_window_default_hints), 231 | }; 232 | 233 | #undef TRIGGER_FUNCTIONS 234 | 235 | JSValue glfw_window_proto, glfw_window_class; 236 | 237 | JSValue glfw_window_constructor(JSContext* ctx) { 238 | JSRuntime* rt = JS_GetRuntime(ctx); 239 | 240 | if (!JS_IsRegisteredClass(rt, glfw_window_class_id)) { 241 | JS_NewClassID(&glfw_window_class_id); 242 | JS_NewClass(rt, glfw_window_class_id, &glfw_window_class_def); 243 | 244 | glfw_window_proto = JS_NewObject(ctx); 245 | JS_SetPropertyFunctionList(ctx, glfw_window_proto, glfw_window_proto_funcs, countof(glfw_window_proto_funcs)); 246 | JS_SetClassProto(ctx, glfw_window_class_id, glfw_window_proto); 247 | 248 | glfw_window_class = JS_NewCFunction2(ctx, glfw_window_ctor, "Window", 5, JS_CFUNC_constructor, 0); 249 | JS_SetPropertyFunctionList(ctx, glfw_window_class, glfw_window_funcs, countof(glfw_window_funcs)); 250 | JS_SetConstructor(ctx, glfw_window_class, glfw_window_proto); 251 | } 252 | 253 | return glfw_window_class; 254 | } 255 | 256 | JSValue glfw_window_create_window(JSContext* ctx, int width, int height, 257 | const char* title, GLFWmonitor* monitor, 258 | GLFWwindow* share) { 259 | GLFWwindow* window = glfwCreateWindow(width, height, title, monitor, share); 260 | if (window == NULL) { 261 | glfw_throw(ctx); 262 | return JS_EXCEPTION; 263 | } 264 | 265 | return glfw_window_new_instance(ctx, window); 266 | } 267 | 268 | JSValue glfw_window_new_instance(JSContext* ctx, GLFWwindow* window) { 269 | JSValue obj = JS_UNDEFINED; 270 | JSValue proto; 271 | 272 | proto = JS_GetPropertyStr(ctx, glfw_window_class, "prototype"); 273 | if (JS_IsException(proto)) 274 | goto fail; 275 | 276 | obj = JS_NewObjectProtoClass(ctx, proto, glfw_window_class_id); 277 | JS_FreeValue(ctx, proto); 278 | if (JS_IsException(obj)) 279 | goto fail; 280 | 281 | JS_SetOpaque(obj, window); 282 | 283 | return obj; 284 | fail: 285 | JS_FreeValue(ctx, obj); 286 | return JS_EXCEPTION; 287 | } 288 | 289 | int glfw_window_init(JSContext* ctx, JSModuleDef* m) { 290 | JS_SetModuleExport(ctx, m, "Window", glfw_window_constructor(ctx)); 291 | return 0; 292 | } 293 | 294 | int glfw_window_export(JSContext* ctx, JSModuleDef* m) { 295 | return JS_AddModuleExport(ctx, m, "Window"); 296 | } 297 | --------------------------------------------------------------------------------