760 lines
24 KiB
C
760 lines
24 KiB
C
#include "cell.h"
|
|
|
|
#include <SDL3/SDL.h>
|
|
#include <SDL3/SDL_gpu.h>
|
|
#include <SDL3/SDL_error.h>
|
|
#include <SDL3/SDL_properties.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
#include "sdl.h"
|
|
|
|
// SDL Window free function
|
|
void SDL_Window_free(JSRuntime *rt, SDL_Window *w)
|
|
{
|
|
SDL_DestroyWindow(w);
|
|
}
|
|
|
|
QJSCLASS(SDL_Window,)
|
|
|
|
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
|
|
{
|
|
SDL_DestroyCursor(c);
|
|
}
|
|
|
|
QJSCLASS(SDL_Cursor,)
|
|
|
|
// Forward declarations for blend mode helpers
|
|
static JSValue blendmode2js(JSContext *js, SDL_BlendMode mode);
|
|
static SDL_BlendMode js2blendmode(JSContext *js, JSValue v);
|
|
|
|
// Window constructor function
|
|
static JSValue js_SDL_Window_constructor(JSContext *js, JSValueConst new_target, int argc, JSValueConst *argv)
|
|
{
|
|
if (argc < 1 || !JS_IsObject(argv[0]))
|
|
return JS_ThrowTypeError(js, "Window constructor requires an object argument");
|
|
|
|
JSValue opts = argv[0];
|
|
|
|
// Get basic properties (defaults are handled in JavaScript)
|
|
const char *title = NULL;
|
|
JSValue title_val = JS_GetPropertyStr(js, opts, "title");
|
|
if (!JS_IsNull(title_val)) {
|
|
title = JS_ToCString(js, title_val);
|
|
}
|
|
JS_FreeValue(js, title_val);
|
|
|
|
if (!title) {
|
|
return JS_ThrowTypeError(js, "Window title is required");
|
|
}
|
|
|
|
int width = 640;
|
|
JSValue width_val = JS_GetPropertyStr(js, opts, "width");
|
|
if (!JS_IsNull(width_val)) {
|
|
width = js2number(js, width_val);
|
|
}
|
|
JS_FreeValue(js, width_val);
|
|
|
|
int height = 480;
|
|
JSValue height_val = JS_GetPropertyStr(js, opts, "height");
|
|
if (!JS_IsNull(height_val)) {
|
|
height = js2number(js, height_val);
|
|
}
|
|
JS_FreeValue(js, height_val);
|
|
|
|
// Create SDL properties object
|
|
SDL_PropertiesID props = SDL_CreateProperties();
|
|
|
|
// Always set basic properties
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_WIDTH_NUMBER, width);
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_HEIGHT_NUMBER, height);
|
|
SDL_SetStringProperty(props, SDL_PROP_WINDOW_CREATE_TITLE_STRING, title);
|
|
|
|
// Handle window position
|
|
JSValue x_val = JS_GetPropertyStr(js, opts, "x");
|
|
if (!JS_IsNull(x_val)) {
|
|
if (JS_IsString(x_val)) {
|
|
const char *pos = JS_ToCString(js, x_val);
|
|
if (strcmp(pos, "centered") == 0)
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_CENTERED);
|
|
else
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, SDL_WINDOWPOS_UNDEFINED);
|
|
JS_FreeCString(js, pos);
|
|
} else {
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_X_NUMBER, js2number(js, x_val));
|
|
}
|
|
}
|
|
JS_FreeValue(js, x_val);
|
|
|
|
JSValue y_val = JS_GetPropertyStr(js, opts, "y");
|
|
if (!JS_IsNull(y_val)) {
|
|
if (JS_IsString(y_val)) {
|
|
const char *pos = JS_ToCString(js, y_val);
|
|
if (strcmp(pos, "centered") == 0)
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_CENTERED);
|
|
else
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, SDL_WINDOWPOS_UNDEFINED);
|
|
JS_FreeCString(js, pos);
|
|
} else {
|
|
SDL_SetNumberProperty(props, SDL_PROP_WINDOW_CREATE_Y_NUMBER, js2number(js, y_val));
|
|
}
|
|
}
|
|
JS_FreeValue(js, y_val);
|
|
|
|
// Helper function to check and set boolean properties
|
|
#define SET_BOOL_PROP(js_name, sdl_prop) do { \
|
|
JSValue val = JS_GetPropertyStr(js, opts, js_name); \
|
|
if (!JS_IsNull(val)) { \
|
|
SDL_SetBooleanProperty(props, sdl_prop, JS_ToBool(js, val)); \
|
|
} \
|
|
JS_FreeValue(js, val); \
|
|
} while(0)
|
|
|
|
// Set all boolean properties directly on the SDL properties object
|
|
SET_BOOL_PROP("resizable", SDL_PROP_WINDOW_CREATE_RESIZABLE_BOOLEAN);
|
|
SET_BOOL_PROP("fullscreen", SDL_PROP_WINDOW_CREATE_FULLSCREEN_BOOLEAN);
|
|
SET_BOOL_PROP("hidden", SDL_PROP_WINDOW_CREATE_HIDDEN_BOOLEAN);
|
|
SET_BOOL_PROP("borderless", SDL_PROP_WINDOW_CREATE_BORDERLESS_BOOLEAN);
|
|
SET_BOOL_PROP("alwaysOnTop", SDL_PROP_WINDOW_CREATE_ALWAYS_ON_TOP_BOOLEAN);
|
|
SET_BOOL_PROP("minimized", SDL_PROP_WINDOW_CREATE_MINIMIZED_BOOLEAN);
|
|
SET_BOOL_PROP("maximized", SDL_PROP_WINDOW_CREATE_MAXIMIZED_BOOLEAN);
|
|
SET_BOOL_PROP("mouseGrabbed", SDL_PROP_WINDOW_CREATE_MOUSE_GRABBED_BOOLEAN);
|
|
SET_BOOL_PROP("highPixelDensity", SDL_PROP_WINDOW_CREATE_HIGH_PIXEL_DENSITY_BOOLEAN);
|
|
SET_BOOL_PROP("transparent", SDL_PROP_WINDOW_CREATE_TRANSPARENT_BOOLEAN);
|
|
SET_BOOL_PROP("utility", SDL_PROP_WINDOW_CREATE_UTILITY_BOOLEAN);
|
|
SET_BOOL_PROP("tooltip", SDL_PROP_WINDOW_CREATE_TOOLTIP_BOOLEAN);
|
|
SET_BOOL_PROP("popupMenu", SDL_PROP_WINDOW_CREATE_MENU_BOOLEAN);
|
|
SET_BOOL_PROP("opengl", SDL_PROP_WINDOW_CREATE_OPENGL_BOOLEAN);
|
|
SET_BOOL_PROP("vulkan", SDL_PROP_WINDOW_CREATE_VULKAN_BOOLEAN);
|
|
SET_BOOL_PROP("metal", SDL_PROP_WINDOW_CREATE_METAL_BOOLEAN);
|
|
SET_BOOL_PROP("modal", SDL_PROP_WINDOW_CREATE_MODAL_BOOLEAN);
|
|
SET_BOOL_PROP("externalGraphicsContext", SDL_PROP_WINDOW_CREATE_EXTERNAL_GRAPHICS_CONTEXT_BOOLEAN);
|
|
|
|
// Handle focusable (inverse logic)
|
|
JSValue focusable_val = JS_GetPropertyStr(js, opts, "focusable");
|
|
if (!JS_IsNull(focusable_val)) {
|
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, JS_ToBool(js, focusable_val));
|
|
}
|
|
JS_FreeValue(js, focusable_val);
|
|
|
|
// Handle notFocusable (for backwards compatibility)
|
|
JSValue not_focusable_val = JS_GetPropertyStr(js, opts, "notFocusable");
|
|
if (!JS_IsNull(not_focusable_val)) {
|
|
SDL_SetBooleanProperty(props, SDL_PROP_WINDOW_CREATE_FOCUSABLE_BOOLEAN, !JS_ToBool(js, not_focusable_val));
|
|
}
|
|
JS_FreeValue(js, not_focusable_val);
|
|
|
|
#undef SET_BOOL_PROP
|
|
|
|
// Handle parent window
|
|
JSValue parent_val = JS_GetPropertyStr(js, opts, "parent");
|
|
if (!JS_IsNull(parent_val)) {
|
|
SDL_Window *parent = js2SDL_Window(js, parent_val);
|
|
if (parent) {
|
|
SDL_SetPointerProperty(props, SDL_PROP_WINDOW_CREATE_PARENT_POINTER, parent);
|
|
}
|
|
}
|
|
JS_FreeValue(js, parent_val);
|
|
|
|
// Create window with properties
|
|
SDL_Window *window = SDL_CreateWindowWithProperties(props);
|
|
SDL_DestroyProperties(props);
|
|
|
|
// Always free the title string since we allocated it
|
|
if (title) {
|
|
JS_FreeCString(js, title);
|
|
}
|
|
|
|
if (!window) {
|
|
return JS_ThrowReferenceError(js, "Failed to create window: %s", SDL_GetError());
|
|
}
|
|
|
|
// Create the window JS object
|
|
JSValue window_obj = SDL_Window2js(js, window);
|
|
|
|
// Set additional properties that can't be set during creation
|
|
// These will be applied through the property setters
|
|
|
|
JSValue opacity_val = JS_GetPropertyStr(js, opts, "opacity");
|
|
if (!JS_IsNull(opacity_val)) {
|
|
JS_SetPropertyStr(js, window_obj, "opacity", opacity_val);
|
|
}
|
|
|
|
JSValue min_size_val = JS_GetPropertyStr(js, opts, "minimumSize");
|
|
if (!JS_IsNull(min_size_val)) {
|
|
JS_SetPropertyStr(js, window_obj, "minimumSize", min_size_val);
|
|
}
|
|
|
|
JSValue max_size_val = JS_GetPropertyStr(js, opts, "maximumSize");
|
|
if (!JS_IsNull(max_size_val)) {
|
|
JS_SetPropertyStr(js, window_obj, "maximumSize", max_size_val);
|
|
}
|
|
|
|
JSValue pos_val = JS_GetPropertyStr(js, opts, "position");
|
|
if (!JS_IsNull(pos_val)) {
|
|
JS_SetPropertyStr(js, window_obj, "position", pos_val);
|
|
}
|
|
|
|
// Handle text input
|
|
JSValue text_input = JS_GetPropertyStr(js, opts, "textInput");
|
|
if (JS_ToBool(js, text_input)) {
|
|
// SDL_StartTextInput(window);
|
|
}
|
|
JS_FreeValue(js, text_input);
|
|
|
|
printf("created window %p\n", window);
|
|
|
|
return window_obj;
|
|
}
|
|
|
|
JSC_CCALL(SDL_Window_fullscreen,
|
|
SDL_SetWindowFullscreen(js2SDL_Window(js,self), SDL_WINDOW_FULLSCREEN)
|
|
)
|
|
|
|
JSValue js_SDL_Window_keyboard_shown(JSContext *js, JSValue self, int argc, JSValue *argv) {
|
|
SDL_Window *window = js2SDL_Window(js,self);
|
|
return JS_NewBool(js,SDL_ScreenKeyboardShown(window));
|
|
}
|
|
|
|
JSValue js_window_theme(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_safe_area(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_Rect r;
|
|
SDL_GetWindowSafeArea(w, &r);
|
|
rect newr;
|
|
SDL_RectToFRect(&r, &newr);
|
|
return rect2js(js,newr);
|
|
}
|
|
|
|
JSValue js_window_bordered(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowBordered(w, JS_ToBool(js,argv[0]));
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_get_title(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
const char *title = SDL_GetWindowTitle(w);
|
|
return JS_NewString(js,title);
|
|
}
|
|
|
|
JSValue js_window_set_title(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
const char *title = JS_ToCString(js,val);
|
|
SDL_SetWindowTitle(w,title);
|
|
JS_FreeCString(js,title);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_get_size(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *win = js2SDL_Window(js,self);
|
|
int w, h;
|
|
SDL_GetWindowSize(win, &w, &h);
|
|
return vec22js(js, (vec2){w,h});
|
|
}
|
|
|
|
JSValue js_window_set_size(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
vec2 size = js2vec2(js,val);
|
|
SDL_SetWindowSize(w,size.x,size.y);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_set_icon(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
|
|
if (!SDL_SetWindowIcon(w,s))
|
|
return JS_ThrowReferenceError(js, "could not set window icon: %s", SDL_GetError());
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Position getter/setter
|
|
JSValue js_window_get_position(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
int x, y;
|
|
SDL_GetWindowPosition(w, &x, &y);
|
|
return vec22js(js, (vec2){x,y});
|
|
}
|
|
|
|
JSValue js_window_set_position(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
vec2 pos = js2vec2(js,val);
|
|
SDL_SetWindowPosition(w,pos.x,pos.y);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Mouse grab getter/setter
|
|
JSValue js_window_get_mouseGrab(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
return JS_NewBool(js, SDL_GetWindowMouseGrab(w));
|
|
}
|
|
|
|
JSValue js_window_set_mouseGrab(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowMouseGrab(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Keyboard grab getter/setter
|
|
JSValue js_window_get_keyboardGrab(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
return JS_NewBool(js, SDL_GetWindowKeyboardGrab(w));
|
|
}
|
|
|
|
JSValue js_window_set_keyboardGrab(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowKeyboardGrab(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Opacity getter/setter
|
|
JSValue js_window_get_opacity(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
return number2js(js, SDL_GetWindowOpacity(w));
|
|
}
|
|
|
|
JSValue js_window_set_opacity(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
float opacity = js2number(js,val);
|
|
SDL_SetWindowOpacity(w, opacity);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Minimum size getter/setter
|
|
JSValue js_window_get_minimumSize(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
int width, height;
|
|
SDL_GetWindowMinimumSize(w, &width, &height);
|
|
return vec22js(js, (vec2){width,height});
|
|
}
|
|
|
|
JSValue js_window_set_minimumSize(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
vec2 size = js2vec2(js,val);
|
|
SDL_SetWindowMinimumSize(w,size.x,size.y);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Maximum size getter/setter
|
|
JSValue js_window_get_maximumSize(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
int width, height;
|
|
SDL_GetWindowMaximumSize(w, &width, &height);
|
|
return vec22js(js, (vec2){width,height});
|
|
}
|
|
|
|
JSValue js_window_set_maximumSize(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
vec2 size = js2vec2(js,val);
|
|
SDL_SetWindowMaximumSize(w,size.x,size.y);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Resizable setter (read from flags)
|
|
JSValue js_window_get_resizable(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, flags & SDL_WINDOW_RESIZABLE);
|
|
}
|
|
|
|
JSValue js_window_set_resizable(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowResizable(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Bordered getter/setter
|
|
JSValue js_window_get_bordered(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, !(flags & SDL_WINDOW_BORDERLESS));
|
|
}
|
|
|
|
JSValue js_window_set_bordered(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowBordered(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Always on top getter/setter
|
|
JSValue js_window_get_alwaysOnTop(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, flags & SDL_WINDOW_ALWAYS_ON_TOP);
|
|
}
|
|
|
|
JSValue js_window_set_alwaysOnTop(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowAlwaysOnTop(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Fullscreen getter/setter
|
|
JSValue js_window_get_fullscreen(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, flags & SDL_WINDOW_FULLSCREEN);
|
|
}
|
|
|
|
JSValue js_window_set_fullscreen(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowFullscreen(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Focusable setter
|
|
JSValue js_window_get_focusable(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, !(flags & SDL_WINDOW_NOT_FOCUSABLE));
|
|
}
|
|
|
|
JSValue js_window_set_focusable(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowFocusable(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Modal setter
|
|
JSValue js_window_get_modal(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, flags & SDL_WINDOW_MODAL);
|
|
}
|
|
|
|
JSValue js_window_set_modal(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SetWindowModal(w, JS_ToBool(js,val));
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Hidden/visible state
|
|
JSValue js_window_get_visible(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, !(flags & SDL_WINDOW_HIDDEN));
|
|
}
|
|
|
|
JSValue js_window_set_visible(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
if (JS_ToBool(js,val))
|
|
SDL_ShowWindow(w);
|
|
else
|
|
SDL_HideWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Minimized state
|
|
JSValue js_window_get_minimized(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, flags & SDL_WINDOW_MINIMIZED);
|
|
}
|
|
|
|
JSValue js_window_set_minimized(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
if (JS_ToBool(js,val))
|
|
SDL_MinimizeWindow(w);
|
|
else
|
|
SDL_RestoreWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Maximized state
|
|
JSValue js_window_get_maximized(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
return JS_NewBool(js, flags & SDL_WINDOW_MAXIMIZED);
|
|
}
|
|
|
|
JSValue js_window_set_maximized(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
if (JS_ToBool(js,val))
|
|
SDL_MaximizeWindow(w);
|
|
else
|
|
SDL_RestoreWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
// Other window methods
|
|
JSValue js_window_raise(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_RaiseWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_restore(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_RestoreWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_flash(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_FlashOperation op = SDL_FLASH_BRIEFLY;
|
|
if (argc > 0 && JS_IsString(argv[0])) {
|
|
const char *operation = JS_ToCString(js,argv[0]);
|
|
if (strcmp(operation, "cancel") == 0) op = SDL_FLASH_CANCEL;
|
|
else if (strcmp(operation, "briefly") == 0) op = SDL_FLASH_BRIEFLY;
|
|
else if (strcmp(operation, "until_focused") == 0) op = SDL_FLASH_UNTIL_FOCUSED;
|
|
JS_FreeCString(js,operation);
|
|
}
|
|
SDL_FlashWindow(w, op);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_destroy(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_DestroyWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_get_id(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
return number2js(js, SDL_GetWindowID(w));
|
|
}
|
|
|
|
JSValue js_window_get_parent(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_Window *parent = SDL_GetWindowParent(w);
|
|
if (!parent) return JS_NULL;
|
|
return SDL_Window2js(js, parent);
|
|
}
|
|
|
|
JSValue js_window_set_parent(JSContext *js, JSValue self, JSValue val)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_Window *parent = NULL;
|
|
if (!JS_IsNull(val))
|
|
parent = js2SDL_Window(js,val);
|
|
SDL_SetWindowParent(w, parent);
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_get_pixelDensity(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
return number2js(js, SDL_GetWindowPixelDensity(w));
|
|
}
|
|
|
|
JSValue js_window_get_displayScale(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
return number2js(js, SDL_GetWindowDisplayScale(w));
|
|
}
|
|
|
|
JSValue js_window_get_sizeInPixels(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
int width, height;
|
|
SDL_GetWindowSizeInPixels(w, &width, &height);
|
|
return vec22js(js, (vec2){width,height});
|
|
}
|
|
|
|
// Surface related
|
|
JSValue js_window_get_surface(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_Surface *surf = SDL_GetWindowSurface(w);
|
|
if (!surf) return JS_NULL;
|
|
return SDL_Surface2js(js, surf);
|
|
}
|
|
|
|
JSValue js_window_updateSurface(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
if (!SDL_UpdateWindowSurface(w))
|
|
return JS_ThrowReferenceError(js, "Failed to update window surface: %s", SDL_GetError());
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_updateSurfaceRects(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
|
|
if (!JS_IsArray(js, argv[0]))
|
|
return JS_ThrowTypeError(js, "Expected array of rectangles");
|
|
|
|
int len = JS_ArrayLength(js, argv[0]);
|
|
SDL_Rect rects[len];
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
JSValue val = JS_GetPropertyUint32(js, argv[0], i);
|
|
rect r = js2rect(js, val);
|
|
rects[i] = (SDL_Rect){r.x, r.y, r.w, r.h};
|
|
JS_FreeValue(js, val);
|
|
}
|
|
|
|
if (!SDL_UpdateWindowSurfaceRects(w, rects, len))
|
|
return JS_ThrowReferenceError(js, "Failed to update window surface rects: %s", SDL_GetError());
|
|
return JS_NULL;
|
|
}
|
|
|
|
JSValue js_window_get_flags(JSContext *js, JSValue self)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_WindowFlags flags = SDL_GetWindowFlags(w);
|
|
|
|
JSValue ret = JS_NewObject(js);
|
|
JS_SetPropertyStr(js, ret, "fullscreen", JS_NewBool(js, flags & SDL_WINDOW_FULLSCREEN));
|
|
JS_SetPropertyStr(js, ret, "opengl", JS_NewBool(js, flags & SDL_WINDOW_OPENGL));
|
|
JS_SetPropertyStr(js, ret, "occluded", JS_NewBool(js, flags & SDL_WINDOW_OCCLUDED));
|
|
JS_SetPropertyStr(js, ret, "hidden", JS_NewBool(js, flags & SDL_WINDOW_HIDDEN));
|
|
JS_SetPropertyStr(js, ret, "borderless", JS_NewBool(js, flags & SDL_WINDOW_BORDERLESS));
|
|
JS_SetPropertyStr(js, ret, "resizable", JS_NewBool(js, flags & SDL_WINDOW_RESIZABLE));
|
|
JS_SetPropertyStr(js, ret, "minimized", JS_NewBool(js, flags & SDL_WINDOW_MINIMIZED));
|
|
JS_SetPropertyStr(js, ret, "maximized", JS_NewBool(js, flags & SDL_WINDOW_MAXIMIZED));
|
|
JS_SetPropertyStr(js, ret, "mouseGrabbed", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_GRABBED));
|
|
JS_SetPropertyStr(js, ret, "inputFocus", JS_NewBool(js, flags & SDL_WINDOW_INPUT_FOCUS));
|
|
JS_SetPropertyStr(js, ret, "mouseFocus", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_FOCUS));
|
|
JS_SetPropertyStr(js, ret, "external", JS_NewBool(js, flags & SDL_WINDOW_EXTERNAL));
|
|
JS_SetPropertyStr(js, ret, "modal", JS_NewBool(js, flags & SDL_WINDOW_MODAL));
|
|
JS_SetPropertyStr(js, ret, "highPixelDensity", JS_NewBool(js, flags & SDL_WINDOW_HIGH_PIXEL_DENSITY));
|
|
JS_SetPropertyStr(js, ret, "mouseCapture", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_CAPTURE));
|
|
JS_SetPropertyStr(js, ret, "mouseRelativeMode", JS_NewBool(js, flags & SDL_WINDOW_MOUSE_RELATIVE_MODE));
|
|
JS_SetPropertyStr(js, ret, "alwaysOnTop", JS_NewBool(js, flags & SDL_WINDOW_ALWAYS_ON_TOP));
|
|
JS_SetPropertyStr(js, ret, "utility", JS_NewBool(js, flags & SDL_WINDOW_UTILITY));
|
|
JS_SetPropertyStr(js, ret, "tooltip", JS_NewBool(js, flags & SDL_WINDOW_TOOLTIP));
|
|
JS_SetPropertyStr(js, ret, "popupMenu", JS_NewBool(js, flags & SDL_WINDOW_POPUP_MENU));
|
|
JS_SetPropertyStr(js, ret, "keyboardGrabbed", JS_NewBool(js, flags & SDL_WINDOW_KEYBOARD_GRABBED));
|
|
JS_SetPropertyStr(js, ret, "vulkan", JS_NewBool(js, flags & SDL_WINDOW_VULKAN));
|
|
JS_SetPropertyStr(js, ret, "metal", JS_NewBool(js, flags & SDL_WINDOW_METAL));
|
|
JS_SetPropertyStr(js, ret, "transparent", JS_NewBool(js, flags & SDL_WINDOW_TRANSPARENT));
|
|
JS_SetPropertyStr(js, ret, "notFocusable", JS_NewBool(js, flags & SDL_WINDOW_NOT_FOCUSABLE));
|
|
|
|
return ret;
|
|
}
|
|
|
|
JSValue js_window_sync(JSContext *js, JSValue self, int argc, JSValue *argv)
|
|
{
|
|
SDL_Window *w = js2SDL_Window(js,self);
|
|
SDL_SyncWindow(w);
|
|
return JS_NULL;
|
|
}
|
|
|
|
static const JSCFunctionListEntry js_SDL_Window_funcs[] = {
|
|
MIST_FUNC_DEF(SDL_Window, fullscreen, 0),
|
|
MIST_FUNC_DEF(SDL_Window, keyboard_shown, 0),
|
|
MIST_FUNC_DEF(window, theme, 0),
|
|
MIST_FUNC_DEF(window, safe_area, 0),
|
|
MIST_FUNC_DEF(window, set_icon, 1),
|
|
MIST_FUNC_DEF(window, raise, 0),
|
|
MIST_FUNC_DEF(window, restore, 0),
|
|
MIST_FUNC_DEF(window, flash, 1),
|
|
MIST_FUNC_DEF(window, destroy, 0),
|
|
MIST_FUNC_DEF(window, sync, 0),
|
|
CGETSET_ADD(window, title),
|
|
CGETSET_ADD(window, size),
|
|
CGETSET_ADD(window, position),
|
|
CGETSET_ADD(window, mouseGrab),
|
|
CGETSET_ADD(window, keyboardGrab),
|
|
CGETSET_ADD(window, opacity),
|
|
CGETSET_ADD(window, minimumSize),
|
|
CGETSET_ADD(window, maximumSize),
|
|
CGETSET_ADD(window, resizable),
|
|
CGETSET_ADD(window, bordered),
|
|
CGETSET_ADD(window, alwaysOnTop),
|
|
CGETSET_ADD(window, fullscreen),
|
|
CGETSET_ADD(window, focusable),
|
|
CGETSET_ADD(window, modal),
|
|
CGETSET_ADD(window, visible),
|
|
CGETSET_ADD(window, minimized),
|
|
CGETSET_ADD(window, maximized),
|
|
CGETSET_ADD(window, parent),
|
|
JS_CGETSET_DEF("id", js_window_get_id, NULL),
|
|
JS_CGETSET_DEF("pixelDensity", js_window_get_pixelDensity, NULL),
|
|
JS_CGETSET_DEF("displayScale", js_window_get_displayScale, NULL),
|
|
JS_CGETSET_DEF("sizeInPixels", js_window_get_sizeInPixels, NULL),
|
|
JS_CGETSET_DEF("flags", js_window_get_flags, NULL),
|
|
JS_CGETSET_DEF("surface", js_window_get_surface, NULL),
|
|
MIST_FUNC_DEF(window, updateSurface, 0),
|
|
MIST_FUNC_DEF(window, updateSurfaceRects, 1),
|
|
};
|
|
|
|
// Cursor creation function
|
|
JSC_CCALL(sdl_create_cursor,
|
|
SDL_Surface *surf = js2SDL_Surface(js, argv[0]);
|
|
if (!surf) return JS_ThrowReferenceError(js, "Invalid surface");
|
|
|
|
vec2 hot = {0, 0};
|
|
if (argc > 1) hot = js2vec2(js, argv[1]);
|
|
|
|
SDL_Cursor *cursor = SDL_CreateColorCursor(surf, hot.x, hot.y);
|
|
if (!cursor) return JS_ThrowReferenceError(js, "Failed to create cursor: %s", SDL_GetError());
|
|
|
|
return SDL_Cursor2js(js, cursor);
|
|
)
|
|
|
|
// Set cursor function
|
|
JSC_CCALL(sdl_set_cursor,
|
|
SDL_Cursor *cursor = js2SDL_Cursor(js, argv[0]);
|
|
|
|
if (!cursor) return JS_ThrowReferenceError(js, "Invalid cursor");
|
|
|
|
SDL_SetCursor(cursor);
|
|
)
|
|
|
|
CELL_USE_INIT(
|
|
if (!SDL_Init(SDL_INIT_VIDEO))
|
|
return JS_ThrowInternalError(js, "Unable to initialize video subsystem: %s", SDL_GetError());
|
|
|
|
JSValue ret = JS_NewObject(js);
|
|
|
|
JS_SetPropertyStr(js, ret, "window", QJSCLASSPREP_FUNCS_CTOR(SDL_Window, 1));
|
|
|
|
QJSCLASSPREP_NO_FUNCS(SDL_Cursor);
|
|
|
|
// Add cursor functions
|
|
JS_SetPropertyStr(js, ret, "createCursor", JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2));
|
|
JS_SetPropertyStr(js, ret, "setCursor", JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1));
|
|
|
|
return ret;
|
|
)
|