add sdl cursor support

This commit is contained in:
2025-05-26 16:23:37 -05:00
parent a63e5c5b55
commit 2edcd89780
6 changed files with 80 additions and 41 deletions

View File

@@ -59,7 +59,8 @@ var resources = {
window: {}, window: {},
renderer: {}, renderer: {},
texture: {}, texture: {},
surface: {} surface: {},
cursor: {}
}; };
// ID counter for resource allocation // ID counter for resource allocation
@@ -93,6 +94,9 @@ $_.receiver(function(msg) {
case 'surface': case 'surface':
response = handle_surface(msg); response = handle_surface(msg);
break; break;
case 'cursor':
response = handle_cursor(msg);
break;
default: default:
response = {error: "Unknown kind: " + msg.kind}; response = {error: "Unknown kind: " + msg.kind};
} }
@@ -605,6 +609,39 @@ function handle_surface(msg) {
} }
} }
// Cursor operations
function handle_cursor(msg) {
switch (msg.op) {
case 'create':
var surf = new surface(msg.data)
var hotspot = msg.data.hotspot || [0, 0];
var cursor = prosperon.endowments.createCursor(surf, hotspot);
var cursor_id = allocate_id();
resources.cursor[cursor_id] = cursor;
return {id: cursor_id};
case 'set':
var cursor = null;
if (msg.id && resources.cursor[msg.id]) {
cursor = resources.cursor[msg.id];
}
prosperon.endowments.setCursor(cursor);
return {success: true};
case 'destroy':
if (!msg.id || !resources.cursor[msg.id]) {
return {error: "Invalid cursor id: " + msg.id};
}
delete resources.cursor[msg.id];
return {success: true};
default:
return {error: "Unknown cursor operation: " + msg.op};
}
}
// Utility function to create window and renderer // Utility function to create window and renderer
prosperon.endowments = prosperon.endowments || {}; prosperon.endowments = prosperon.endowments || {};

View File

@@ -429,11 +429,6 @@ graphics.cull_sprites[prosperon.DOC] = `
Filter an array of sprites to only those visible in the provided cameras view. Filter an array of sprites to only those visible in the provided cameras view.
` `
graphics.make_cursor[prosperon.DOC] = `
:param opts: An object with {surface, hotx, hoty} or similar.
:return: An SDL_Cursor object referencing the given surface for a custom mouse cursor.
`
graphics.make_font[prosperon.DOC] = ` graphics.make_font[prosperon.DOC] = `
:param data: TTF/OTF file data as an ArrayBuffer. :param data: TTF/OTF file data as an ArrayBuffer.
:param size: Pixel size for rendering glyphs. :param size: Pixel size for rendering glyphs.

View File

@@ -1199,19 +1199,6 @@ JSC_CCALL(os_make_aseprite,
cute_aseprite_free(ase); cute_aseprite_free(ase);
) )
// TODO: Implement this correctly
JSC_CCALL(os_make_cursor,
/*if (SDL_GetCurrentThreadID() != main_thread)
return JS_ThrowInternalError(js, "This can only be called from the root actor.");
SDL_Surface *s = js2SDL_Surface(js,argv[0]);
HMM_Vec2 hot = js2vec2(js,argv[1]);
SDL_Cursor *c = SDL_CreateColorCursor(s, hot.x, hot.y);
if (!c) return JS_ThrowReferenceError(js,"couldn't make cursor: %s", SDL_GetError());
return SDL_Cursor2js(js,c);*/
)
JSC_CCALL(os_make_font, JSC_CCALL(os_make_font,
size_t len; size_t len;
void *data = JS_GetArrayBuffer(js,&len,argv[0]); void *data = JS_GetArrayBuffer(js,&len,argv[0]);
@@ -1501,7 +1488,6 @@ static const JSCFunctionListEntry js_graphics_funcs[] = {
MIST_FUNC_DEF(os, make_gif, 1), MIST_FUNC_DEF(os, make_gif, 1),
MIST_FUNC_DEF(os, make_aseprite, 1), MIST_FUNC_DEF(os, make_aseprite, 1),
MIST_FUNC_DEF(os, cull_sprites, 2), MIST_FUNC_DEF(os, cull_sprites, 2),
MIST_FUNC_DEF(os, make_cursor, 1),
MIST_FUNC_DEF(os, make_font, 2), MIST_FUNC_DEF(os, make_font, 2),
MIST_FUNC_DEF(os, make_line_prim, 5), MIST_FUNC_DEF(os, make_line_prim, 5),
MIST_FUNC_DEF(graphics, hsl_to_rgb, 3), MIST_FUNC_DEF(graphics, hsl_to_rgb, 3),

View File

@@ -10,17 +10,11 @@ void SDL_Camera_free(JSRuntime *rt, SDL_Camera *cam)
SDL_CloseCamera(cam); SDL_CloseCamera(cam);
} }
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) { void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) {
SDL_DestroyAudioStream(st); SDL_DestroyAudioStream(st);
} }
// Class definitions for SDL types // Class definitions for SDL types
QJSCLASS(SDL_Cursor,)
QJSCLASS(SDL_Camera,) QJSCLASS(SDL_Camera,)
QJSCLASS(SDL_AudioStream,) QJSCLASS(SDL_AudioStream,)
@@ -59,12 +53,6 @@ JSC_CCALL(input_mouse_show,
SDL_HideCursor(); SDL_HideCursor();
) )
JSC_CCALL(input_cursor_set,
SDL_Cursor *c = js2SDL_Cursor(js,argv[0]);
if (!SDL_SetCursor(c))
return JS_ThrowReferenceError(js, "could not set cursor: %s", SDL_GetError());
)
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])));
) )
@@ -97,7 +85,6 @@ JSC_CCALL(input_mousestate,
static const JSCFunctionListEntry js_input_funcs[] = { static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 1), MIST_FUNC_DEF(input, mouse_show, 1),
MIST_FUNC_DEF(input, mouse_lock, 1), MIST_FUNC_DEF(input, mouse_lock, 1),
MIST_FUNC_DEF(input, cursor_set, 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, mousestate, 0),
@@ -109,7 +96,6 @@ JSValue js_input_use(JSContext *js) {
// Initialize SDL cursor class (no functions) // Initialize SDL cursor class (no functions)
JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types"); JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types");
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
JS_FreeValue(js, c_types); JS_FreeValue(js, c_types);
return mod; return mod;

View File

@@ -43,6 +43,13 @@ QJSCLASS(SDL_Texture,
QJSCLASS(SDL_Renderer,) QJSCLASS(SDL_Renderer,)
QJSCLASS(SDL_Window,) QJSCLASS(SDL_Window,)
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
QJSCLASS(SDL_Cursor,)
// External function declarations // External function declarations
extern JSValue rect2js(JSContext *js, rect r); extern JSValue rect2js(JSContext *js, rect r);
extern rect js2rect(JSContext *js, JSValue v); extern rect js2rect(JSContext *js, JSValue v);
@@ -1555,6 +1562,29 @@ static const JSCFunctionListEntry js_sdl_video_funcs[] = {
JS_PROP_INT32_DEF("BLENDMODE_MUL", SDL_BLENDMODE_MUL, JS_PROP_CONFIGURABLE), JS_PROP_INT32_DEF("BLENDMODE_MUL", SDL_BLENDMODE_MUL, JS_PROP_CONFIGURABLE),
}; };
// Cursor creation function
JSC_CCALL(sdl_create_cursor,
SDL_Surface *surf = js2SDL_Surface(js, argv[0]);
if (!surf) return JS_ThrowReferenceError(js, "Invalid surface");
HMM_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);
)
// Texture getter/setter functions // Texture getter/setter functions
// Alpha mod getter/setter // Alpha mod getter/setter
@@ -1759,6 +1789,7 @@ static void video_actor_hook(JSContext *js) {
QJSCLASSPREP_FUNCS(SDL_Window) QJSCLASSPREP_FUNCS(SDL_Window)
QJSCLASSPREP_FUNCS(SDL_Renderer) QJSCLASSPREP_FUNCS(SDL_Renderer)
QJSCLASSPREP_FUNCS(SDL_Texture) QJSCLASSPREP_FUNCS(SDL_Texture)
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
JS_FreeValue(js, c_types); JS_FreeValue(js, c_types);
@@ -1789,6 +1820,12 @@ static void video_actor_hook(JSContext *js) {
JS_SetPropertyStr(js, endowments, "createWindowAndRenderer", JS_SetPropertyStr(js, endowments, "createWindowAndRenderer",
JS_NewCFunction(js, js_sdl_createWindowAndRenderer, "createWindowAndRenderer", 4)); JS_NewCFunction(js, js_sdl_createWindowAndRenderer, "createWindowAndRenderer", 4));
// Add cursor functions
JS_SetPropertyStr(js, endowments, "createCursor",
JS_NewCFunction(js, js_sdl_create_cursor, "createCursor", 2));
JS_SetPropertyStr(js, endowments, "setCursor",
JS_NewCFunction(js, js_sdl_set_cursor, "setCursor", 1));
JS_FreeValue(js, endowments); JS_FreeValue(js, endowments);
JS_FreeValue(js, prosperon); JS_FreeValue(js, prosperon);
} }

View File

@@ -56,13 +56,11 @@ function start_drawing() {
var start_time = os.now(); var start_time = os.now();
// Load an image // Load an image
var bunny_image = null; var bunny_image = graphics.texture('tests/bunny.png')
try {
bunny_image = graphics.texture('tests/bunny.png'); send(video_actor, {kind: "cursor", op: "create", data: bunny_image.cpu}, ({id}) => {
} catch (e) { send(video_actor, {kind:"cursor", op: "set", id})
console.log("Failed to load bunny image:", e); })
console.log(e)
}
function draw_frame() { function draw_frame() {
frame++; frame++;