diff --git a/flake.nix b/flake.nix index 3ba6dab..9a0eda0 100755 --- a/flake.nix +++ b/flake.nix @@ -67,6 +67,7 @@ local-rust nightly-cargo rust-analyzer + lua54Packages.stdlib SDL2 SDL2_ttf darwin.apple_sdk.frameworks.CoreData diff --git a/liblua54.a b/liblua54.a new file mode 100755 index 0000000..b9f953e Binary files /dev/null and b/liblua54.a differ diff --git a/plugins/lua/lib.lua b/plugins/lua/lib.lua new file mode 100644 index 0000000..1cb788a --- /dev/null +++ b/plugins/lua/lib.lua @@ -0,0 +1,47 @@ +print("Hello from lua from a file!") + +local WindowOpen = false + +function render_ui_window(ctx) + if WindowOpen then + canvas = UI.push_floating(ctx, "lua canvas", 0, 0) + UI.push_parent(ctx, canvas) + window = UI.push_rect(ctx, "fullscreen window", true, true, UI.Vertical, UI.Fill, UI.Fill) + UI.push_parent(ctx, window) + if UI.button(ctx, "Click me!").clicked then + print("you clicked me!") + end + if UI.button(ctx, "I am lua").clicked then + print("you clicked me!") + end + if UI.button(ctx, "This is another button").clicked then + print("you clicked me!") + end + if UI.button(ctx, "if the names of these are the same it will seg fault").clicked then + print("you clicked me!") + end + if UI.button(ctx, "Click me! 2").clicked then + print("you clicked me!") + end + UI.pop_parent(ctx) + UI.pop_parent(ctx) + end +end + +function handle_buffer_input() + print("you inputted into a buffer") +end + +function OnInit() + print("Test lua plugin initialized") + Editor.register_key_group({ + {Editor.Key.T, "Open Test UI", ( + function () + WindowOpen = not WindowOpen + end + )}, + }) + + Editor.register_hook(Editor.Hook.OnDraw, render_ui_window) + Editor.register_hook(Editor.Hook.OnBufferInput, handle_buffer_input) +end diff --git a/src/core/core.odin b/src/core/core.odin index 1766493..e06deb1 100644 --- a/src/core/core.odin +++ b/src/core/core.odin @@ -3,6 +3,7 @@ package core import "core:runtime" import "core:fmt" import "vendor:sdl2" +import lua "vendor:lua/5.4" import "../plugin" @@ -40,8 +41,10 @@ close_window_and_free :: proc(state: ^State) { } } +LuaHookRef :: i32; State :: struct { ctx: runtime.Context, + L: ^lua.State, sdl_renderer: ^sdl2.Renderer, font_atlas: FontAtlas, @@ -71,6 +74,7 @@ State :: struct { plugin_vtable: plugin.Plugin, highlighters: map[string]plugin.OnColorBufferProc, hooks: map[plugin.Hook][dynamic]plugin.OnHookProc, + lua_hooks: map[plugin.Hook][dynamic]LuaHookRef, } add_hook :: proc(state: ^State, hook: plugin.Hook, hook_proc: plugin.OnHookProc) { @@ -81,9 +85,17 @@ add_hook :: proc(state: ^State, hook: plugin.Hook, hook_proc: plugin.OnHookProc) runtime.append(&state.hooks[hook], hook_proc); } +add_lua_hook :: proc(state: ^State, hook: plugin.Hook, hook_ref: LuaHookRef) { + if _, exists := state.lua_hooks[hook]; !exists { + state.lua_hooks[hook] = make([dynamic]LuaHookRef); + } + + runtime.append(&state.lua_hooks[hook], hook_ref); +} +LuaEditorAction :: i32; PluginEditorAction :: proc "c" (plugin: plugin.Plugin); EditorAction :: proc(state: ^State); -InputGroup :: union {PluginEditorAction, EditorAction, InputMap} +InputGroup :: union {LuaEditorAction, PluginEditorAction, EditorAction, InputMap} Action :: struct { action: InputGroup, description: string, diff --git a/src/main.odin b/src/main.odin index 0c9e177..81fc7c2 100644 --- a/src/main.odin +++ b/src/main.odin @@ -10,6 +10,7 @@ import "core:mem" import "core:slice" import "vendor:sdl2" import "vendor:sdl2/ttf" +import lua "vendor:lua/5.4" import "core" import "theme" @@ -239,9 +240,6 @@ draw :: proc(state_with_ui: ^StateWithUi) { buffer.glyph_buffer_height = math.min(256, int((state_with_ui.state.screen_height - state_with_ui.state.source_font_height*2) / state_with_ui.state.source_font_height)) + 1; buffer.glyph_buffer_width = math.min(256, int((state_with_ui.state.screen_width - state_with_ui.state.source_font_width) / state_with_ui.state.source_font_width)); - // raylib.BeginDrawing(); - // defer raylib.EndDrawing(); - render_color := theme.get_palette_color(.Background); sdl2.SetRenderDrawColor(state_with_ui.state.sdl_renderer, render_color.r, render_color.g, render_color.b, render_color.a); sdl2.RenderClear(state_with_ui.state.sdl_renderer); @@ -253,7 +251,7 @@ draw :: proc(state_with_ui: ^StateWithUi) { ui.compute_layout(state_with_ui.ui_context, { state_with_ui.state.screen_width, state_with_ui.state.screen_height }, state_with_ui.state.source_font_width, state_with_ui.state.source_font_height, state_with_ui.ui_context.root); ui.draw(state_with_ui.ui_context, state_with_ui.state, state_with_ui.state.source_font_width, state_with_ui.state.source_font_height, state_with_ui.ui_context.root); - if state_with_ui.state.current_input_map != &state_with_ui.state.input_map { + if true || state_with_ui.state.current_input_map != &state_with_ui.state.input_map { longest_description := 0; for key, action in state_with_ui.state.current_input_map.key_actions { if len(action.description) > longest_description { @@ -278,13 +276,6 @@ draw :: proc(state_with_ui: ^StateWithUi) { helper_height, .Background2 ); - //raylib.DrawRectangle( - // i32(state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width), - // i32(state_with_ui.state.screen_height - helper_height - offset_from_bottom), - // i32(longest_description*state_with_ui.state.source_font_width), - // i32(helper_height), - // theme.get_palette_raylib_color(.Background2) - //); index := 0; for key, action in state_with_ui.state.current_input_map.key_actions { @@ -295,14 +286,6 @@ draw :: proc(state_with_ui: ^StateWithUi) { state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom ); - // raylib.DrawTextEx( - // state_with_ui.state.font, - // raylib.TextFormat("%s - %s", key, action.description), - // raylib.Vector2 { f32(state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width), f32(state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom) }, - // f32(state_with_ui.state.source_font_height), - // 0, - // theme.get_palette_raylib_color(.Foreground1) - // ); index += 1; } for key, action in state_with_ui.state.current_input_map.ctrl_key_actions { @@ -312,14 +295,7 @@ draw :: proc(state_with_ui: ^StateWithUi) { state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width, state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom ); - // raylib.DrawTextEx( - // state_with_ui.state.font, - // raylib.TextFormat("-%s - %s", key, action.description), - // raylib.Vector2 { f32(state_with_ui.state.screen_width - longest_description * state_with_ui.state.source_font_width), f32(state_with_ui.state.screen_height - helper_height + index * state_with_ui.state.source_font_height - offset_from_bottom) }, - // f32(state_with_ui.state.source_font_height), - // 0, - // theme.get_palette_raylib_color(.Foreground1) - // ); + index += 1; } } @@ -327,7 +303,6 @@ draw :: proc(state_with_ui: ^StateWithUi) { sdl2.RenderPresent(state_with_ui.state.sdl_renderer); } -// TODO: need to wrap state and ui context into one structure so that it can be used in this function expose_event_watcher :: proc "c" (state: rawptr, event: ^sdl2.Event) -> i32 { if event.type == .WINDOWEVENT { state := transmute(^StateWithUi)state; @@ -409,6 +384,8 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { if action, exists := to_be_edited_map.key_actions[key]; exists { switch value in action.action { + case core.LuaEditorAction: + fmt.eprintln("Plugin attempted to register input group on existing key action (added from Lua)"); case core.PluginEditorAction: fmt.eprintln("Plugin attempted to register input group on existing key action (added from Plugin)"); case core.EditorAction: @@ -436,6 +413,8 @@ init_plugin_vtable :: proc(ui_context: ^ui.Context) -> plugin.Plugin { if action, exists := to_be_edited_map.key_actions[key]; exists { switch value in action.action { + case core.LuaEditorAction: + fmt.eprintln("Plugin attempted to register key action on existing key action (added from Lua)"); case core.PluginEditorAction: fmt.eprintln("Plugin attempted to register key action on existing key action (added from Plugin)"); case core.EditorAction: @@ -970,6 +949,7 @@ main :: proc() { plugins = make([dynamic]plugin.Interface), highlighters = make(map[string]plugin.OnColorBufferProc), hooks = make(map[plugin.Hook][dynamic]plugin.OnHookProc), + lua_hooks = make(map[plugin.Hook][dynamic]core.LuaHookRef), }; state.current_input_map = &state.input_map; @@ -1041,7 +1021,7 @@ main :: proc() { state.screen_width = int(w); state.screen_height = int(h); } - + // Done to clear the buffer sdl2.StartTextInput(); sdl2.StopTextInput(); @@ -1060,6 +1040,333 @@ main :: proc() { } } + // ********************************************************************** + L := lua.L_newstate(); + state.L = L; + lua.L_openlibs(L); + + bbb: [^]lua.L_Reg; + editor_lib := [?]lua.L_Reg { + lua.L_Reg { + "print", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + a := lua.L_checkinteger(L, 1); + + fmt.printf("LUA: print(%d)\n", a); + return i32(lua.OK); + } + }, + lua.L_Reg { + "register_hook", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + hook := lua.L_checkinteger(L, 1); + + lua.L_checktype(L, 2, i32(lua.TFUNCTION)); + lua.pushvalue(L, 2); + fn_ref := lua.L_ref(L, i32(lua.REGISTRYINDEX)); + + fmt.println("LUA: attempting to add hook:", hook, "ref", fn_ref); + core.add_lua_hook(&state, plugin.Hook(hook), fn_ref); + + return i32(lua.OK); + } + }, + lua.L_Reg { + "register_key_action", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + key := lua.L_checkinteger(L, 1); + + lua.L_checktype(L, 2, i32(lua.TFUNCTION)); + lua.pushvalue(L, 2); + fn_ref := lua.L_ref(L, i32(lua.REGISTRYINDEX)); + + desc := strings.clone(string(lua.L_checkstring(L, 3))); + + fmt.println("LUA: attempting to register:", key, "ref", fn_ref); + core.register_key_action_group(&state.input_map, plugin.Key(key), fn_ref, desc); + + return i32(lua.OK); + } + }, + lua.L_Reg { + "register_key_group", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TTABLE)); + + table_to_action :: proc(L: ^lua.State, index: i32, input_map: ^core.InputMap) { + lua.len(L, index); + key_group_len := lua.tointeger(L, -1); + lua.pop(L, 1); + + fmt.println("num groups", key_group_len); + + for i in 1..=key_group_len { + fmt.println("LUA: index", index, "i", i); + + lua.rawgeti(L, index, i); + defer lua.pop(L, 1); + + lua.rawgeti(L, -1, 1); + key:= plugin.Key(lua.tointeger(L, -1)); + lua.pop(L, 1); + + lua.rawgeti(L, -1, 2); + desc := strings.clone(string(lua.tostring(L, -1))); + lua.pop(L, 1); + + fmt.println("LUA: attempting to register:", key, desc); + + switch lua.rawgeti(L, -1, 3) { + case i32(lua.TTABLE): + if action, exists := input_map.key_actions[key]; exists { + switch value in action.action { + case core.LuaEditorAction: + fmt.eprintln("Plugin attempted to register input group on existing key action (added from Lua)"); + case core.PluginEditorAction: + fmt.eprintln("Plugin attempted to register input group on existing key action (added from Plugin)"); + case core.EditorAction: + fmt.eprintln("Plugin attempted to register input group on existing key action"); + case core.InputMap: + input_map := &(&input_map.key_actions[key]).action.(core.InputMap); + table_to_action(L, lua.gettop(L), input_map); + } + } else { + core.register_key_action(input_map, key, core.new_input_map(), desc); + table_to_action(L, lua.gettop(L), &((&input_map.key_actions[key]).action.(core.InputMap))); + } + lua.pop(L, 1); + + case i32(lua.TFUNCTION): + fn_ref := lua.L_ref(L, i32(lua.REGISTRYINDEX)); + core.register_key_action_group(input_map, key, fn_ref, desc); + + case: + lua.pop(L, 1); + } + + fmt.println("LUA: successfully registered:", key); + } + } + + table_to_action(L, 1, state.current_input_map); + + + // key := plugin.Key(lua.L_checkinteger(L, 1)); + + // lua.L_checktype(L, 2, i32(lua.TFUNCTION)); + // lua.pushvalue(L, 2); + // fn_ref := lua.L_ref(L, i32(lua.REGISTRYINDEX)); + + // desc := strings.clone(string(lua.L_checkstring(L, 3))); + + // fmt.println("LUA: attempting to register:", key, "ref", fn_ref); + + // if action, exists := state.current_input_map.key_actions[key]; exists { + // switch value in action.action { + // case core.LuaEditorAction: + // fmt.eprintln("Plugin attempted to register input group on existing key action (added from Lua)"); + // case core.PluginEditorAction: + // fmt.eprintln("Plugin attempted to register input group on existing key action (added from Plugin)"); + // case core.EditorAction: + // fmt.eprintln("Plugin attempted to register input group on existing key action"); + // case core.InputMap: + // input_map := &(&state.current_input_map.key_actions[key]).action.(core.InputMap); + // core.register_key_action_group(input_map, key, fn_ref, desc); + // } + // } else { + // core.register_key_action(state.current_input_map, key, core.new_input_map(), desc); + // } + + return i32(lua.OK); + } + }, + }; + bbb = raw_data(editor_lib[:]); + + ui_lib := [?]lua.L_Reg { + lua.L_Reg { + "push_parent", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 1); + ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1); + if ui_ctx == nil { return i32(lua.ERRRUN); } + + lua.L_checktype(L, 2, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 2); + box := transmute(^ui.Box)lua.touserdata(L, -1); + if box == nil { return i32(lua.ERRRUN); } + + ui.push_parent(ui_ctx, box); + return i32(lua.OK); + } + }, + lua.L_Reg { + "pop_parent", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 1); + ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1); + if ui_ctx == nil { return i32(lua.ERRRUN); } + + ui.pop_parent(ui_ctx); + return i32(lua.OK); + } + }, + lua.L_Reg { + "push_floating", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 1); + ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1); + if ui_ctx != nil { + label := lua.L_checkstring(L, 2); + x := int(lua.L_checkinteger(L, 3)); + y := int(lua.L_checkinteger(L, 4)); + + box := ui.push_floating(ui_ctx, string(label), {x,y}); + lua.pushlightuserdata(L, box); + return 1; + } + + return i32(lua.ERRRUN); + } + }, + lua.L_Reg { + "push_rect", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 1); + ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1); + if ui_ctx != nil { + label := lua.L_checkstring(L, 2); + background := bool(lua.toboolean(L, 3)); + border := bool(lua.toboolean(L, 4)); + axis := ui.Axis(lua.L_checkinteger(L, 5)); + + // TODO: check the other variants for extra data + semantic_width := ui.SemanticSizeKind(lua.L_checkinteger(L, 6)); + semantic_height := ui.SemanticSizeKind(lua.L_checkinteger(L, 7)); + + box := ui.push_rect(ui_ctx, string(label), background, border, axis, { {semantic_width, 0}, {semantic_height,0} }); + lua.pushlightuserdata(L, box); + return 1; + } + + return i32(lua.ERRRUN); + } + }, + lua.L_Reg { + "button", + proc "c" (L: ^lua.State) -> i32 { + context = state.ctx; + + lua.L_checktype(L, 1, i32(lua.TLIGHTUSERDATA)); + lua.pushvalue(L, 1); + ui_ctx := transmute(^ui.Context)lua.touserdata(L, -1); + if ui_ctx != nil { + label := lua.L_checkstring(L, 2); + + interaction := ui.button(ui_ctx, string(label)); + + lua.newtable(L); + { + lua.pushboolean(L, b32(interaction.clicked)); + lua.setfield(L, -2, "clicked"); + } + + return 1; + } + + return i32(lua.ERRRUN); + } + }, + }; + + lua.newtable(L); + { + lua.newtable(L); + lua.pushinteger(L, lua.Integer(plugin.Key.T)); + lua.setfield(L, -2, "T"); + + lua.pushinteger(L, lua.Integer(plugin.Key.Y)); + lua.setfield(L, -2, "Y"); + + lua.pushinteger(L, lua.Integer(plugin.Key.P)); + lua.setfield(L, -2, "P"); + + lua.pushinteger(L, lua.Integer(plugin.Key.M)); + lua.setfield(L, -2, "M"); + + lua.pushinteger(L, lua.Integer(plugin.Key.SPACE)); + lua.setfield(L, -2, "Space"); + } + lua.setfield(L, -2, "Key"); + + { + lua.newtable(L); + lua.pushinteger(L, lua.Integer(plugin.Hook.BufferInput)); + lua.setfield(L, -2, "OnBufferInput"); + lua.pushinteger(L, lua.Integer(plugin.Hook.Draw)); + lua.setfield(L, -2, "OnDraw"); + } + lua.setfield(L, -2, "Hook"); + + lua.L_setfuncs(L, bbb, 0); + lua.setglobal(L, "Editor"); + + lua.newtable(L); + { + lua.pushinteger(L, lua.Integer(ui.Axis.Horizontal)); + lua.setfield(L, -2, "Horizontal"); + lua.pushinteger(L, lua.Integer(ui.Axis.Vertical)); + lua.setfield(L, -2, "Vertical"); + lua.pushinteger(L, lua.Integer(ui.SemanticSizeKind.Fill)); + lua.setfield(L, -2, "Fill"); + + lua.L_setfuncs(L, raw_data(&ui_lib), 0); + lua.setglobal(L, "UI"); + } + + if lua.L_dofile(L, "plugins/lua/lib.lua") == i32(lua.OK) { + lua.pop(L, lua.gettop(L)); + } else { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln(err); + } + + // Initialize Lua Plugins + { + lua.getglobal(L, "OnInit"); + if lua.pcall(L, 0, 0, 0) == i32(lua.OK) { + lua.pop(L, lua.gettop(L)); + } else { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln("failed to initialize plugin (OnInit):", err); + } + } + // ********************************************************************** + control_key_pressed: bool; for !state.should_close { { @@ -1176,6 +1483,19 @@ main :: proc() { state.window.draw(state.plugin_vtable, state.window.user_data); } + for hook_ref in state.lua_hooks[plugin.Hook.Draw] { + lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(hook_ref)); + lua.pushlightuserdata(state.L, &ui_context); + if lua.pcall(state.L, 1, 0, 0) != i32(lua.OK) { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln(err); + } else { + lua.pop(L, lua.gettop(L)); + } + } + { ui_context.last_mouse_left_down = ui_context.mouse_left_down; ui_context.last_mouse_right_down = ui_context.mouse_right_down; @@ -1216,23 +1536,44 @@ main :: proc() { if control_key_pressed { if action, exists := state.current_input_map.ctrl_key_actions[key]; exists { switch value in action.action { + case core.LuaEditorAction: + fmt.println("trying to call lua registered function:", value); + lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(value)); + if lua.pcall(state.L, 0, 0, 0) != i32(lua.OK) { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln(err); + } else { + lua.pop(L, lua.gettop(L)); + } case core.PluginEditorAction: - value(state.plugin_vtable); + value(state.plugin_vtable); case core.EditorAction: - value(&state); + value(&state); case core.InputMap: - state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) + state.current_input_map = &(&state.current_input_map.ctrl_key_actions[key]).action.(core.InputMap) } } } else { if action, exists := state.current_input_map.key_actions[key]; exists { switch value in action.action { + case core.LuaEditorAction: + lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(value)); + if lua.pcall(state.L, 0, 0, 0) != i32(lua.OK) { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln(err); + } else { + lua.pop(L, lua.gettop(L)); + } case core.PluginEditorAction: - value(state.plugin_vtable); + value(state.plugin_vtable); case core.EditorAction: - value(&state); + value(&state); case core.InputMap: - state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) + state.current_input_map = &(&state.current_input_map.key_actions[key]).action.(core.InputMap) } } } @@ -1272,6 +1613,17 @@ main :: proc() { for hook_proc in state.hooks[plugin.Hook.BufferInput] { hook_proc(state.plugin_vtable, buffer); } + for hook_ref in state.lua_hooks[plugin.Hook.BufferInput] { + lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(hook_ref)); + if lua.pcall(state.L, 0, 0, 0) != i32(lua.OK) { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln(err); + } else { + lua.pop(L, lua.gettop(L)); + } + } } case .ENTER: { append(&buffer.input_buffer, '\n'); @@ -1291,6 +1643,17 @@ main :: proc() { for hook_proc in state.hooks[plugin.Hook.BufferInput] { hook_proc(state.plugin_vtable, buffer); } + for hook_ref in state.lua_hooks[plugin.Hook.BufferInput] { + lua.rawgeti(state.L, lua.REGISTRYINDEX, lua.Integer(hook_ref)); + if lua.pcall(state.L, 0, 0, 0) != i32(lua.OK) { + err := lua.tostring(L, lua.gettop(L)); + lua.pop(L, lua.gettop(L)); + + fmt.eprintln(err); + } else { + lua.pop(L, lua.gettop(L)); + } + } } } } @@ -1337,6 +1700,7 @@ main :: proc() { plugin.on_exit(); } } + lua.close(L); sdl2.Quit(); } diff --git a/src/plugin/plugin.odin b/src/plugin/plugin.odin index a9786f1..8c2b85f 100644 --- a/src/plugin/plugin.odin +++ b/src/plugin/plugin.odin @@ -166,6 +166,7 @@ Plugin :: struct { Hook :: enum { BufferInput = 0, + Draw = 1, } Key :: enum {