Implement reusable cursors on macOS

master
Marius Petcu 2018-12-25 17:34:23 +02:00
parent 6c4c406211
commit 3d501ef57e
4 changed files with 123 additions and 67 deletions

View File

@ -291,6 +291,75 @@ static int is_cursor_locked(lua_State *L)
return 1;
}
static int cursor_metatable;
static void *load_custom_cursor(lua_State *L, int index)
{
#ifdef DM_PLATFORM_WINDOWS
const char *cursor_path_lua = luaL_checkstring(L, index);
return defos_load_cursor_win(cursor_path_lua);
#endif
#ifdef DM_PLATFORM_LINUX
const char *cursor_path_lua = luaL_checkstring(L, index);
return defos_load_cursor_linux(cursor_path_lua);
return 0;
// TODO: X11 support animated cursor by XRender
#endif
#ifdef DM_PLATFORM_OSX
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "hot_spot_x");
float hotSpotX = 0.0f;
if (!lua_isnil(L, -1))
{
hotSpotX = luaL_checknumber(L, -1);
}
lua_getfield(L, index, "hot_spot_y");
float hotSpotY = 0.0f;
if (!lua_isnil(L, -1))
{
hotSpotY = luaL_checknumber(L, -1);
}
lua_getfield(L, index, "image");
dmBuffer::HBuffer image = dmScript::CheckBuffer(L, -1)->m_Buffer;
void *cursor = defos_load_cursor_mac(image, hotSpotX, hotSpotY);
lua_pop(L, 3);
return cursor;
#endif
#ifdef DM_PLATFORM_HTML5
const char * cursor_url = luaL_checkstring(L, 1);
return defos_load_cursor_html5(cursor_url);
#endif
lua_pushstring(L, "Invalid argument");
lua_error(L);
return NULL;
}
static int load_cursor(lua_State *L)
{
void ** userdata = (void**)lua_newuserdata(L, sizeof(void*));
*userdata = load_custom_cursor(L, 1);
lua_rawgeti(L, LUA_REGISTRYINDEX, cursor_metatable);
lua_setmetatable(L, -2);
return 1;
}
static int gc_cursor(lua_State *L)
{
luaL_checktype(L, 1, LUA_TUSERDATA);
void **cursor = (void**)lua_touserdata(L, 1);
defos_gc_custom_cursor(*cursor);
return 0;
}
static int set_cursor(lua_State *L)
{
if (lua_isnil(L, 1))
@ -306,53 +375,16 @@ static int set_cursor(lua_State *L)
return 0;
}
#ifdef DM_PLATFORM_WINDOWS
const char *cursor_path_lua = luaL_checkstring(L, 1);
defos_set_custom_cursor_win(cursor_path_lua);
return 0;
#endif
#ifdef DM_PLATFORM_LINUX
const char *cursor_path_lua = luaL_checkstring(L,1);
defos_set_custom_cursor_linux(cursor_path_lua);
return 0;
// TODO: X11 support animated cursor by XRender
#endif
#ifdef DM_PLATFORM_OSX
luaL_checktype(L, 1, LUA_TTABLE);
lua_getfield(L, 1, "hot_spot_x");
float hotSpotX = 0.0f;
if (!lua_isnil(L, -1))
if (lua_isuserdata(L, 1))
{
hotSpotX = luaL_checknumber(L, -1);
void **cursor = (void**)lua_touserdata(L, 1);
defos_set_custom_cursor(*cursor);
return 0;
}
lua_getfield(L, 1, "hot_spot_y");
float hotSpotY = 0.0f;
if (!lua_isnil(L, -1))
{
hotSpotY = luaL_checknumber(L, -1);
}
lua_getfield(L, 1, "image");
dmBuffer::HBuffer image = dmScript::CheckBuffer(L, -1)->m_Buffer;
defos_set_custom_cursor_mac(image, hotSpotX, hotSpotY);
lua_pop(L, 3);
return 0;
#endif
#ifdef DM_PLATFORM_HTML5
const char * cursor_url = luaL_checkstring(L, 1);
defos_set_custom_cursor_html5(cursor_url);
return 0;
#endif
lua_pushstring(L, "Invalid argument");
lua_error(L);
void *custom_cursor = load_custom_cursor(L, 1);
defos_set_custom_cursor(custom_cursor);
defos_gc_custom_cursor(custom_cursor);
return 0;
}
@ -604,6 +636,7 @@ static const luaL_reg Module_methods[] =
{"get_view_size", get_view_size},
{"set_cursor", set_cursor},
{"reset_cursor", reset_cursor},
{"load_cursor", load_cursor},
{"get_displays", get_displays},
{"get_display_modes", get_display_modes},
{"get_current_display_id", get_current_display_id},
@ -635,6 +668,11 @@ static void LuaInit(lua_State *L)
lua_setfield(L, -2, "PATH_SEP");
#endif
lua_newtable(L);
lua_pushcfunction(L, gc_cursor);
lua_setfield(L, -2, "__gc");
cursor_metatable = dmScript::Ref(L, LUA_REGISTRYINDEX);
lua_pop(L, 1);
assert(top == lua_gettop(L));
}

View File

@ -53,6 +53,7 @@ void defos_init() {
// [window disableCursorRects];
// [window resetCursorRects];
default_cursor = NSCursor.arrowCursor;
[default_cursor retain];
current_cursor = default_cursor;
[current_cursor retain];
enable_mouse_tracking();
@ -60,8 +61,10 @@ void defos_init() {
void defos_final() {
disable_mouse_tracking();
[default_cursor release];
[current_cursor release];
current_cursor = nil;
default_cursor = nil;
}
void defos_update() {
@ -338,24 +341,37 @@ bool defos_is_cursor_locked() {
return is_cursor_locked;
}
void defos_set_custom_cursor_mac(dmBuffer::HBuffer buffer, float hotSpotX, float hotSpotY) {
uint8_t* bytes = NULL;
uint32_t size = 0;
if (dmBuffer::GetBytes(buffer, (void**)&bytes, &size) != dmBuffer::RESULT_OK) {
dmLogError("defos_set_custom_cursor_mac: dmBuffer::GetBytes failed");
return;
void *defos_load_cursor_mac(dmBuffer::HBuffer buffer, float hotSpotX, float hotSpotY) {
@autoreleasepool {
uint8_t* bytes = NULL;
uint32_t size = 0;
if (dmBuffer::GetBytes(buffer, (void**)&bytes, &size) != dmBuffer::RESULT_OK) {
dmLogError("defos_set_custom_cursor_mac: dmBuffer::GetBytes failed");
return NULL;
}
uint8_t* copy = (uint8_t*)malloc(size);
memcpy(copy, bytes, size);
NSData * data = [[NSData alloc] initWithBytesNoCopy:copy length:size freeWhenDone:YES];
NSImage * image = [[NSImage alloc] initWithData:data];
NSCursor * cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(hotSpotX, hotSpotY)];
[image release];
[data release];
return cursor;
}
}
uint8_t* copy = (uint8_t*)malloc(size);
memcpy(copy, bytes, size);
NSData * data = [[NSData alloc] initWithBytesNoCopy:copy length:size freeWhenDone:YES];
void defos_gc_custom_cursor(void * _cursor) {
NSCursor * cursor = (NSCursor*)_cursor;
[cursor release];
}
NSImage * image = [[NSImage alloc] initWithData:data];
NSCursor * cursor = [[NSCursor alloc] initWithImage:image hotSpot:NSMakePoint(hotSpotX, hotSpotY)];
void defos_set_custom_cursor(void * _cursor) {
NSCursor * cursor = (NSCursor*)_cursor;
[cursor retain];
[current_cursor release];
current_cursor = cursor;
[image release];
[data release];
[current_cursor set];
}
@ -376,10 +392,10 @@ static NSCursor * get_cursor(DefosCursor cursor) {
void defos_set_cursor(DefosCursor cur) {
NSCursor * cursor = get_cursor(cur);
[cursor set];
[cursor retain];
[current_cursor release];
current_cursor = cursor;
[current_cursor set];
}
void defos_reset_cursor() {

View File

@ -107,10 +107,12 @@ extern bool defos_is_cursor_clipped();
extern void defos_set_cursor_locked(bool locked);
extern bool defos_is_cursor_locked();
extern void defos_set_custom_cursor_html5(const char *url);
extern void defos_set_custom_cursor_win(const char *filename);
extern void defos_set_custom_cursor_mac(dmBuffer::HBuffer buffer, float hotSpotX, float hotSpotY);
extern void defos_set_custom_cursor_linux(const char *filename);
extern void *defos_load_cursor_html5(const char *url);
extern void *defos_load_cursor_win(const char *filename);
extern void *defos_load_cursor_mac(dmBuffer::HBuffer buffer, float hotSpotX, float hotSpotY);
extern void *defos_load_cursor_linux(const char *filename);
extern void defos_gc_custom_cursor(void *cursor);
extern void defos_set_custom_cursor(void *cursor);
extern void defos_set_cursor(DefosCursor cursor);
extern void defos_reset_cursor();

View File

@ -47,26 +47,26 @@ function init(self)
if system_name == "Linux" then
-- NOTE: since Defold cannot load resource without extension, we added .xcur here.
-- NOTE: cursor should be normal x11 cursor file that type is: image/x-xcursor
table.insert(self.cursors, extract_to_savefolder("cursor.xcur"))
table.insert(self.cursors, defos.load_cursor(extract_to_savefolder("cursor.xcur")))
end
if system_name == "Windows" then
for i, v in ipairs({"cursor_01.ani", "cursor_02.ani" }) do
-- load source file and write them to save folder, so that we can access them with fullpath, or you can use bundle_resource
table.insert(self.cursors, extract_to_savefolder(v))
table.insert(self.cursors, defos.load_cursor(extract_to_savefolder(v)))
end
end
if system_name == "Darwin" then
table.insert(self.cursors, {
table.insert(self.cursors, defos.load_cursor({
image = resource.load("/resources/cursor_mac.tiff"),
hot_spot_x = 18,
hot_spot_y = 2,
})
}))
end
if system_name == "HTML5" then
table.insert(self.cursors, cursor_url)
table.insert(self.cursors, defos.load_cursor(cursor_url))
end
defos.on_mouse_enter(function ()