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: {},
renderer: {},
texture: {},
surface: {}
surface: {},
cursor: {}
};
// ID counter for resource allocation
@@ -93,6 +94,9 @@ $_.receiver(function(msg) {
case 'surface':
response = handle_surface(msg);
break;
case 'cursor':
response = handle_cursor(msg);
break;
default:
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
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.
`
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] = `
:param data: TTF/OTF file data as an ArrayBuffer.
:param size: Pixel size for rendering glyphs.

View File

@@ -1199,19 +1199,6 @@ JSC_CCALL(os_make_aseprite,
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,
size_t len;
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_aseprite, 1),
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_line_prim, 5),
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);
}
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
void SDL_AudioStream_free(JSRuntime *rt, SDL_AudioStream *st) {
SDL_DestroyAudioStream(st);
}
// Class definitions for SDL types
QJSCLASS(SDL_Cursor,)
QJSCLASS(SDL_Camera,)
QJSCLASS(SDL_AudioStream,)
@@ -59,12 +53,6 @@ JSC_CCALL(input_mouse_show,
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,
return JS_NewString(js, SDL_GetKeyName(js2number(js,argv[0])));
)
@@ -97,7 +85,6 @@ JSC_CCALL(input_mousestate,
static const JSCFunctionListEntry js_input_funcs[] = {
MIST_FUNC_DEF(input, mouse_show, 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, keymod, 0),
MIST_FUNC_DEF(input, mousestate, 0),
@@ -109,7 +96,6 @@ JSValue js_input_use(JSContext *js) {
// Initialize SDL cursor class (no functions)
JSValue c_types = JS_GetPropertyStr(js, JS_GetGlobalObject(js), "c_types");
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
JS_FreeValue(js, c_types);
return mod;

View File

@@ -43,6 +43,13 @@ QJSCLASS(SDL_Texture,
QJSCLASS(SDL_Renderer,)
QJSCLASS(SDL_Window,)
void SDL_Cursor_free(JSRuntime *rt, SDL_Cursor *c)
{
SDL_DestroyCursor(c);
}
QJSCLASS(SDL_Cursor,)
// External function declarations
extern JSValue rect2js(JSContext *js, rect r);
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),
};
// 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
// Alpha mod getter/setter
@@ -1759,6 +1789,7 @@ static void video_actor_hook(JSContext *js) {
QJSCLASSPREP_FUNCS(SDL_Window)
QJSCLASSPREP_FUNCS(SDL_Renderer)
QJSCLASSPREP_FUNCS(SDL_Texture)
QJSCLASSPREP_NO_FUNCS(SDL_Cursor)
JS_FreeValue(js, c_types);
@@ -1789,6 +1820,12 @@ static void video_actor_hook(JSContext *js) {
JS_SetPropertyStr(js, endowments, "createWindowAndRenderer",
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, prosperon);
}

View File

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