move keyboard and mouse functions that are main thread only to sdl_video
Some checks failed
Build and Deploy / build-macos (push) Failing after 7s
Build and Deploy / build-windows (CLANG64) (push) Has been cancelled
Build and Deploy / build-linux (push) Has been cancelled
Build and Deploy / package-dist (push) Has been cancelled
Build and Deploy / deploy-itch (push) Has been cancelled
Build and Deploy / deploy-gitea (push) Has been cancelled

This commit is contained in:
2025-05-27 01:30:50 -05:00
parent e8fb50659d
commit bf74a3c7d4
3 changed files with 440 additions and 33 deletions

View File

@@ -97,6 +97,12 @@ $_.receiver(function(msg) {
case 'cursor': case 'cursor':
response = handle_cursor(msg); response = handle_cursor(msg);
break; break;
case 'mouse':
response = handle_mouse(msg);
break;
case 'keyboard':
response = handle_keyboard(msg);
break;
default: default:
response = {error: "Unknown kind: " + msg.kind}; response = {error: "Unknown kind: " + msg.kind};
} }
@@ -635,6 +641,190 @@ function handle_cursor(msg) {
// Utility function to create window and renderer // Utility function to create window and renderer
prosperon.endowments = prosperon.endowments || {}; prosperon.endowments = prosperon.endowments || {};
// Mouse operations
function handle_mouse(msg) {
var mouse = prosperon.endowments.mouse;
switch (msg.op) {
case 'show':
if (msg.data === undefined) return {error: "Missing show parameter"};
mouse.show(msg.data);
return {success: true};
case 'capture':
if (msg.data === undefined) return {error: "Missing capture parameter"};
mouse.capture(msg.data);
return {success: true};
case 'get_state':
return {data: mouse.get_state()};
case 'get_global_state':
return {data: mouse.get_global_state()};
case 'get_relative_state':
return {data: mouse.get_relative_state()};
case 'warp_global':
if (!msg.data) return {error: "Missing position"};
mouse.warp_global(msg.data);
return {success: true};
case 'warp_in_window':
if (!msg.data || !msg.data.window_id || !msg.data.pos)
return {error: "Missing window_id or position"};
var window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
mouse.warp_in_window(window, msg.data.pos);
return {success: true};
case 'cursor_visible':
return {data: mouse.cursor_visible()};
case 'get_cursor':
var cursor = mouse.get_cursor();
if (!cursor) return {data: null};
// Find or create cursor ID
for (var id in resources.cursor) {
if (resources.cursor[id] === cursor) {
return {data: id};
}
}
// Not tracked, add it
var cursor_id = allocate_id();
resources.cursor[cursor_id] = cursor;
return {data: cursor_id};
case 'get_default_cursor':
var cursor = mouse.get_default_cursor();
if (!cursor) return {data: null};
// Find or create cursor ID
for (var id in resources.cursor) {
if (resources.cursor[id] === cursor) {
return {data: id};
}
}
// Not tracked, add it
var cursor_id = allocate_id();
resources.cursor[cursor_id] = cursor;
return {data: cursor_id};
case 'create_system_cursor':
if (msg.data === undefined) return {error: "Missing cursor type"};
var cursor = mouse.create_system_cursor(msg.data);
var cursor_id = allocate_id();
resources.cursor[cursor_id] = cursor;
return {id: cursor_id};
case 'get_focus':
var window = mouse.get_focus();
if (!window) return {data: null};
// Find window ID
for (var id in resources.window) {
if (resources.window[id] === window) {
return {data: id};
}
}
// Not tracked, add it
var win_id = allocate_id();
resources.window[win_id] = window;
return {data: win_id};
default:
return {error: "Unknown mouse operation: " + msg.op};
}
}
// Keyboard operations
function handle_keyboard(msg) {
var keyboard = prosperon.endowments.keyboard;
switch (msg.op) {
case 'get_state':
return {data: keyboard.get_state()};
case 'get_focus':
var window = keyboard.get_focus();
if (!window) return {data: null};
// Find window ID
for (var id in resources.window) {
if (resources.window[id] === window) {
return {data: id};
}
}
// Not tracked, add it
var win_id = allocate_id();
resources.window[win_id] = window;
return {data: win_id};
case 'start_text_input':
var window = null;
if (msg.data && msg.data.window_id) {
window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
}
keyboard.start_text_input(window);
return {success: true};
case 'stop_text_input':
var window = null;
if (msg.data && msg.data.window_id) {
window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
}
keyboard.stop_text_input(window);
return {success: true};
case 'text_input_active':
var window = null;
if (msg.data && msg.data.window_id) {
window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
}
return {data: keyboard.text_input_active(window)};
case 'get_text_input_area':
var window = null;
if (msg.data && msg.data.window_id) {
window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
}
return {data: keyboard.get_text_input_area(window)};
case 'set_text_input_area':
if (!msg.data || !msg.data.rect) return {error: "Missing rect"};
var window = null;
if (msg.data.window_id) {
window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
}
keyboard.set_text_input_area(msg.data.rect, msg.data.cursor || 0, window);
return {success: true};
case 'clear_composition':
var window = null;
if (msg.data && msg.data.window_id) {
window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
}
keyboard.clear_composition(window);
return {success: true};
case 'screen_keyboard_shown':
if (!msg.data || !msg.data.window_id) return {error: "Missing window_id"};
var window = resources.window[msg.data.window_id];
if (!window) return {error: "Invalid window id"};
return {data: keyboard.screen_keyboard_shown(window)};
case 'reset':
keyboard.reset();
return {success: true};
default:
return {error: "Unknown keyboard operation: " + msg.op};
}
}
// Export resource info for debugging // Export resource info for debugging
prosperon.sdl_video = { prosperon.sdl_video = {
resources: resources, resources: resources,

View File

@@ -48,15 +48,7 @@ static JSValue js_keymod(JSContext *js)
} }
// INPUT FUNCTIONS // INPUT FUNCTIONS
JSC_CCALL(input_mouse_lock, SDL_CaptureMouse(JS_ToBool(js,argv[0]))) // Thread-safe keyboard functions remain here
JSC_CCALL(input_mouse_show,
if (JS_ToBool(js,argv[0]))
SDL_ShowCursor();
else
SDL_HideCursor();
)
JSC_CCALL(input_keyname, JSC_CCALL(input_keyname,
return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0]))); return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0])));
) )
@@ -65,27 +57,6 @@ JSC_CCALL(input_keymod,
return js_keymod(js); return js_keymod(js);
) )
JSC_CCALL(input_mousestate,
float x,y;
SDL_MouseButtonFlags flags = SDL_GetMouseState(&x,&y);
JSValue m = JS_NewObject(js);
JS_SetPropertyStr(js,m,"x", number2js(js,x));
JS_SetPropertyStr(js,m,"y", number2js(js,y));
if (flags & SDL_BUTTON_LMASK)
JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_MMASK)
JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_RMASK)
JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X1MASK)
JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X2MASK)
JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1));
return m;
)
// watch events // watch events
extern char **event_watchers; extern char **event_watchers;
extern SDL_Mutex *event_watchers_mutex; extern SDL_Mutex *event_watchers_mutex;
@@ -139,11 +110,8 @@ JSC_CCALL(input_unwatch,
) )
static const JSCFunctionListEntry js_input_funcs[] = { static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 1),
MIST_FUNC_DEF(input, mouse_lock, 1),
MIST_FUNC_DEF(input, keyname, 1), MIST_FUNC_DEF(input, keyname, 1),
MIST_FUNC_DEF(input, keymod, 0), MIST_FUNC_DEF(input, keymod, 0),
MIST_FUNC_DEF(input, mousestate, 0),
MIST_FUNC_DEF(input, watch, 1), MIST_FUNC_DEF(input, watch, 1),
MIST_FUNC_DEF(input, unwatch, 1), MIST_FUNC_DEF(input, unwatch, 1),
}; };

View File

@@ -1780,6 +1780,245 @@ JSC_CCALL(sdl_createWindowAndRenderer,
return ret; return ret;
) )
// Mouse functions that must run on main thread
JSC_CCALL(mouse_show,
if (JS_ToBool(js,argv[0]))
SDL_ShowCursor();
else
SDL_HideCursor();
)
JSC_CCALL(mouse_capture,
SDL_CaptureMouse(JS_ToBool(js,argv[0]));
)
JSC_CCALL(mouse_get_state,
float x,y;
SDL_MouseButtonFlags flags = SDL_GetMouseState(&x,&y);
JSValue m = JS_NewObject(js);
JS_SetPropertyStr(js,m,"x", number2js(js,x));
JS_SetPropertyStr(js,m,"y", number2js(js,y));
if (flags & SDL_BUTTON_LMASK)
JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_MMASK)
JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_RMASK)
JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X1MASK)
JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X2MASK)
JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1));
return m;
)
JSC_CCALL(mouse_get_global_state,
float x,y;
SDL_MouseButtonFlags flags = SDL_GetGlobalMouseState(&x,&y);
JSValue m = JS_NewObject(js);
JS_SetPropertyStr(js,m,"x", number2js(js,x));
JS_SetPropertyStr(js,m,"y", number2js(js,y));
if (flags & SDL_BUTTON_LMASK)
JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_MMASK)
JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_RMASK)
JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X1MASK)
JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X2MASK)
JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1));
return m;
)
JSC_CCALL(mouse_get_relative_state,
float x,y;
SDL_MouseButtonFlags flags = SDL_GetRelativeMouseState(&x,&y);
JSValue m = JS_NewObject(js);
JS_SetPropertyStr(js,m,"x", number2js(js,x));
JS_SetPropertyStr(js,m,"y", number2js(js,y));
if (flags & SDL_BUTTON_LMASK)
JS_SetPropertyStr(js, m, "left", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_MMASK)
JS_SetPropertyStr(js, m, "middle", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_RMASK)
JS_SetPropertyStr(js, m, "right", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X1MASK)
JS_SetPropertyStr(js, m, "x1", JS_NewBool(js, 1));
if (flags & SDL_BUTTON_X2MASK)
JS_SetPropertyStr(js, m, "x2", JS_NewBool(js, 1));
return m;
)
JSC_CCALL(mouse_warp_global,
HMM_Vec2 pos = js2vec2(js, argv[0]);
SDL_WarpMouseGlobal(pos.x, pos.y);
)
JSC_CCALL(mouse_warp_in_window,
SDL_Window *window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
HMM_Vec2 pos = js2vec2(js, argv[1]);
SDL_WarpMouseInWindow(window, pos.x, pos.y);
)
JSC_CCALL(mouse_cursor_visible,
return JS_NewBool(js, SDL_CursorVisible());
)
JSC_CCALL(mouse_get_cursor,
SDL_Cursor *cursor = SDL_GetCursor();
if (!cursor) return JS_NULL;
return SDL_Cursor2js(js, cursor);
)
JSC_CCALL(mouse_get_default_cursor,
SDL_Cursor *cursor = SDL_GetDefaultCursor();
if (!cursor) return JS_NULL;
return SDL_Cursor2js(js, cursor);
)
JSC_CCALL(mouse_create_system_cursor,
int id = js2number(js, argv[0]);
SDL_Cursor *cursor = SDL_CreateSystemCursor(id);
if (!cursor) return JS_ThrowReferenceError(js, "Failed to create system cursor: %s", SDL_GetError());
return SDL_Cursor2js(js, cursor);
)
JSC_CCALL(mouse_get_focus,
SDL_Window *window = SDL_GetMouseFocus();
if (!window) return JS_NULL;
return SDL_Window2js(js, window);
)
static const JSCFunctionListEntry js_mouse_funcs[] = {
MIST_FUNC_DEF(mouse, show, 1),
MIST_FUNC_DEF(mouse, capture, 1),
MIST_FUNC_DEF(mouse, get_state, 0),
MIST_FUNC_DEF(mouse, get_global_state, 0),
MIST_FUNC_DEF(mouse, get_relative_state, 0),
MIST_FUNC_DEF(mouse, warp_global, 1),
MIST_FUNC_DEF(mouse, warp_in_window, 2),
MIST_FUNC_DEF(mouse, cursor_visible, 0),
MIST_FUNC_DEF(mouse, get_cursor, 0),
MIST_FUNC_DEF(mouse, get_default_cursor, 0),
MIST_FUNC_DEF(mouse, create_system_cursor, 1),
MIST_FUNC_DEF(mouse, get_focus, 0),
};
// Keyboard functions that must run on main thread
JSC_CCALL(keyboard_get_state,
int numkeys;
const bool *keystate = SDL_GetKeyboardState(&numkeys);
JSValue arr = JS_NewArray(js);
for (int i = 0; i < numkeys; i++) {
JS_SetPropertyUint32(js, arr, i, JS_NewBool(js, keystate[i]));
}
return arr;
)
JSC_CCALL(keyboard_get_focus,
SDL_Window *window = SDL_GetKeyboardFocus();
if (!window) return JS_NULL;
return SDL_Window2js(js, window);
)
JSC_CCALL(keyboard_start_text_input,
SDL_Window *window = NULL;
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0])) {
window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
}
SDL_StartTextInput(window);
)
JSC_CCALL(keyboard_stop_text_input,
SDL_Window *window = NULL;
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0])) {
window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
}
SDL_StopTextInput(window);
)
JSC_CCALL(keyboard_text_input_active,
SDL_Window *window = NULL;
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0])) {
window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
}
return JS_NewBool(js, SDL_TextInputActive(window));
)
JSC_CCALL(keyboard_get_text_input_area,
SDL_Window *window = NULL;
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0])) {
window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
}
SDL_Rect rect;
int cursor;
if (!SDL_GetTextInputArea(window, &rect, &cursor))
return JS_ThrowReferenceError(js, "Failed to get text input area: %s", SDL_GetError());
JSValue obj = JS_NewObject(js);
SDL_FRect frect;
SDL_RectToFRect(&rect, &frect);
JS_SetPropertyStr(js, obj, "rect", rect2js(js, frect));
JS_SetPropertyStr(js, obj, "cursor", JS_NewInt32(js, cursor));
return obj;
)
JSC_CCALL(keyboard_set_text_input_area,
SDL_Window *window = NULL;
rect r = js2rect(js, argv[0]);
int cursor = argc > 1 ? js2number(js, argv[1]) : 0;
if (argc > 2 && !JS_IsNull(argv[2]) && !JS_IsUndefined(argv[2])) {
window = js2SDL_Window(js, argv[2]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
}
if (!SDL_SetTextInputArea(window, &r, cursor))
return JS_ThrowReferenceError(js, "Failed to set text input area: %s", SDL_GetError());
)
JSC_CCALL(keyboard_clear_composition,
SDL_Window *window = NULL;
if (argc > 0 && !JS_IsNull(argv[0]) && !JS_IsUndefined(argv[0])) {
window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
}
if (!SDL_ClearComposition(window))
return JS_ThrowReferenceError(js, "Failed to clear composition: %s", SDL_GetError());
)
JSC_CCALL(keyboard_screen_keyboard_shown,
SDL_Window *window = js2SDL_Window(js, argv[0]);
if (!window) return JS_ThrowReferenceError(js, "Invalid window");
return JS_NewBool(js, SDL_ScreenKeyboardShown(window));
)
JSC_CCALL(keyboard_reset,
SDL_ResetKeyboard();
)
static const JSCFunctionListEntry js_keyboard_funcs[] = {
MIST_FUNC_DEF(keyboard, get_state, 0),
MIST_FUNC_DEF(keyboard, get_focus, 0),
MIST_FUNC_DEF(keyboard, start_text_input, 1),
MIST_FUNC_DEF(keyboard, stop_text_input, 1),
MIST_FUNC_DEF(keyboard, text_input_active, 1),
MIST_FUNC_DEF(keyboard, get_text_input_area, 1),
MIST_FUNC_DEF(keyboard, set_text_input_area, 3),
MIST_FUNC_DEF(keyboard, clear_composition, 1),
MIST_FUNC_DEF(keyboard, screen_keyboard_shown, 1),
MIST_FUNC_DEF(keyboard, reset, 0),
};
// Hook function to set up endowments for the video actor // Hook function to set up endowments for the video actor
static void video_actor_hook(JSContext *js) { static void video_actor_hook(JSContext *js) {
// Get prosperon object // Get prosperon object
@@ -1827,6 +2066,16 @@ static void video_actor_hook(JSContext *js) {
JS_SetPropertyStr(js, endowments, "setCursor", JS_SetPropertyStr(js, endowments, "setCursor",
JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1)); JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1));
// Create mouse module
JSValue mouse_mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, mouse_mod, js_mouse_funcs, countof(js_mouse_funcs));
JS_SetPropertyStr(js, endowments, "mouse", mouse_mod);
// Create keyboard module
JSValue keyboard_mod = JS_NewObject(js);
JS_SetPropertyFunctionList(js, keyboard_mod, js_keyboard_funcs, countof(js_keyboard_funcs));
JS_SetPropertyStr(js, endowments, "keyboard", keyboard_mod);
JS_FreeValue(js, endowments); JS_FreeValue(js, endowments);
JS_FreeValue(js, prosperon); JS_FreeValue(js, prosperon);
} }