From 1a045e5e8610df211caf83da5de290e6509bd125 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 22 Jun 2024 21:01:39 +0200 Subject: [PATCH] move window creation and management to Lua (#1751) * remove scaling logic from font code for the time being its been hardcoded to 1 for the non SDL Renderer basewin setup, so nothing is lost for non MacOS users. will be revisited in the future when scaling is improved with SDL3 and moved into scripts. * remove unused window_renderer argument from font functions * move window logic to lua, pass window via argument * rename window creation functions `*_create`, `*_destroy`, add real init * Set active window when processing frame * get size directly from RenWindow, get active window size from renderer * correct reverted draw calls * fix window not reappearing on restart * add simple logic to persist the core window * fix style * add renwindow documentation * make windows hidden by default again * reorder ren_update_rects execution, add comment to note future work --- data/core/docview.lua | 2 +- data/core/emptyview.lua | 1 + data/core/init.lua | 27 ++++-- data/core/nagview.lua | 2 +- docs/api/renderer.lua | 5 +- docs/api/renwindow.lua | 28 ++++++ docs/api/system.lua | 37 +++++--- src/api/api.c | 2 + src/api/api.h | 1 + src/api/renderer.c | 33 ++++--- src/api/renwindow.c | 113 ++++++++++++++++++++++ src/api/system.c | 201 +++++++++++++++++++++++++--------------- src/main.c | 38 +------- src/meson.build | 1 + src/rencache.c | 2 +- src/renderer.c | 97 +++++++++++++------ src/renderer.h | 19 ++-- 17 files changed, 424 insertions(+), 185 deletions(-) create mode 100644 docs/api/renwindow.lua create mode 100644 src/api/renwindow.c diff --git a/data/core/docview.lua b/data/core/docview.lua index b679eb6f..6ab86fc6 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -560,7 +560,7 @@ function DocView:draw_overlay() local T = config.blink_period for _, line1, col1, line2, col2 in self.doc:get_selections() do if line1 >= minline and line1 <= maxline - and system.window_has_focus() then + and system.window_has_focus(core.window) then if ime.editing then self:draw_ime_decoration(line1, col1, line2, col2) else diff --git a/data/core/emptyview.lua b/data/core/emptyview.lua index 2152da3b..5b126e24 100644 --- a/data/core/emptyview.lua +++ b/data/core/emptyview.lua @@ -1,3 +1,4 @@ +local core = require "core" local style = require "core.style" local keymap = require "core.keymap" local View = require "core.view" diff --git a/data/core/init.lua b/data/core/init.lua index ec6cebd5..49732bca 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -27,8 +27,8 @@ local function save_session() local fp = io.open(USERDIR .. PATHSEP .. "session.lua", "w") if fp then fp:write("return {recents=", common.serialize(core.recent_projects), - ", window=", common.serialize(table.pack(system.get_window_size())), - ", window_mode=", common.serialize(system.get_window_mode()), + ", window=", common.serialize(table.pack(system.get_window_size(core.window))), + ", window_mode=", common.serialize(system.get_window_mode(core.window)), ", previous_find=", common.serialize(core.previous_find), ", previous_replace=", common.serialize(core.previous_replace), "}\n") @@ -689,12 +689,16 @@ function core.init() EXEDIR = common.normalize_volume(EXEDIR) end + core.window = renwindow._restore() + if core.window == nil then + core.window = renwindow.create("") + end do local session = load_session() if session.window_mode == "normal" then - system.set_window_size(table.unpack(session.window)) + system.set_window_size(core.window, table.unpack(session.window)) elseif session.window_mode == "maximized" then - system.set_window_mode("maximized") + system.set_window_mode(core.window, "maximized") end core.recent_projects = session.recents or {} core.previous_find = session.previous_find or {} @@ -922,7 +926,10 @@ end function core.restart() - quit_with_function(function() core.restart_request = true end) + quit_with_function(function() + core.restart_request = true + core.window:_persist() + end) end @@ -1309,7 +1316,7 @@ function core.on_event(type, ...) elseif type == "touchmoved" then core.root_view:on_touch_moved(...) elseif type == "resized" then - core.window_mode = system.get_window_mode() + core.window_mode = system.get_window_mode(core.window) elseif type == "minimized" or type == "maximized" or type == "restored" then core.window_mode = type == "restored" and "normal" or type elseif type == "filedropped" then @@ -1356,7 +1363,7 @@ function core.step() core.redraw = true end - local width, height = renderer.get_size() + local width, height = core.window:get_size() -- update core.root_view.size.x, core.root_view.size.y = width, height @@ -1376,12 +1383,12 @@ function core.step() -- update window title local current_title = get_title_filename(core.active_view) if current_title ~= nil and current_title ~= core.window_title then - system.set_window_title(core.compose_window_title(current_title)) + system.set_window_title(core.window, core.compose_window_title(current_title)) core.window_title = current_title end -- draw - renderer.begin_frame() + renderer.begin_frame(core.window) core.clip_rect_stack[1] = { 0, 0, width, height } renderer.set_clip_rect(table.unpack(core.clip_rect_stack[1])) core.root_view:draw() @@ -1453,7 +1460,7 @@ function core.run() if core.restart_request or core.quit_request then break end if not did_redraw then - if system.window_has_focus() or not did_step or run_threads_full < 2 then + if system.window_has_focus(core.window) or not did_step or run_threads_full < 2 then local now = system.get_time() if not next_step then -- compute the time until the next blink local t = now - core.blink_start diff --git a/data/core/nagview.lua b/data/core/nagview.lua index 1a7fa193..b11d0218 100644 --- a/data/core/nagview.lua +++ b/data/core/nagview.lua @@ -54,7 +54,7 @@ function NagView:get_target_height() end function NagView:get_scrollable_size() - local w, h = system.get_window_size() + local w, h = system.get_window_size(core.window) if self.visible and self:get_target_height() > h then self.size.y = h return self:get_target_height() diff --git a/docs/api/renderer.lua b/docs/api/renderer.lua index e912d645..c1936219 100644 --- a/docs/api/renderer.lua +++ b/docs/api/renderer.lua @@ -116,10 +116,13 @@ function renderer.get_size() end --- ---Tell the rendering system that we want to build a new frame to render. -function renderer.begin_frame() end +--- +---@param window renwindow +function renderer.begin_frame(window) end --- ---Tell the rendering system that we finished building the frame. +--- function renderer.end_frame() end --- diff --git a/docs/api/renwindow.lua b/docs/api/renwindow.lua new file mode 100644 index 00000000..d31330e2 --- /dev/null +++ b/docs/api/renwindow.lua @@ -0,0 +1,28 @@ +---@meta + +--- +---Functionality to create and manage windows +---@class renwindow +renwindow = {} + +--- +---Create a new window +--- +--- +--- +---@param x integer? if nil will be undefined +---@param y integer? if nil will be undefined +---@param width integer? if nil or less than 1 will be calculated from display +---@param height integer? if nil or less than 1 will be calculated from display +--- +---@return renwindow +function renwindow.create(x, y, width, height) end + +--- +--- Get width and height of a window +--- +---@param renwindow +--- +---@return number width +---@return number height +function renwindow.get_size(window) end diff --git a/docs/api/system.lua b/docs/api/system.lua index 792fa623..e7f2d2e1 100644 --- a/docs/api/system.lua +++ b/docs/api/system.lua @@ -76,8 +76,9 @@ function system.set_cursor(type) end --- ---Change the window title. --- +---@param window renwindow ---@param title string -function system.set_window_title(title) end +function system.set_window_title(window, title) end ---@alias system.windowmode ---| "normal" @@ -88,14 +89,17 @@ function system.set_window_title(title) end --- ---Change the window mode. --- +---@param window renwindow ---@param mode system.windowmode -function system.set_window_mode(mode) end +function system.set_window_mode(window, mode) end --- ----Retrieve the current window mode. +---Retrieve the window mode. +--- +---@param window renwindow --- ---@return system.windowmode mode -function system.get_window_mode() end +function system.get_window_mode(window) end --- ---Toggle between bordered and borderless. @@ -118,32 +122,31 @@ function system.set_window_hit_test(title_height, controls_width, resize_border) --- ---Get the size and coordinates of the window. --- +---@param window renwindow +--- ---@return number width ---@return number height ---@return number x ---@return number y -function system.get_window_size() end +function system.get_window_size(window) end --- ---Sets the size and coordinates of the window. --- +---@param window renwindow ---@param width number ---@param height number ---@param x number ---@param y number -function system.set_window_size(width, height, x, y) end +function system.set_window_size(window, width, height, x, y) end --- ---Check if the window currently has focus. --- +---@param window renwindow +--- ---@return boolean -function system.window_has_focus() end - ---- ----Gets the mode of the window. ---- ----@return system.windowmode -function system.get_window_mode() end +function system.window_has_focus(window) end --- ---Sets the position of the IME composition window. @@ -161,7 +164,9 @@ function system.clear_ime() end --- ---Raise the main window and give it input focus. ---Note: may not always be obeyed by the users window manager. -function system.raise_window() end +-- +---@param window renwindow +function system.raise_window(window) end --- ---Opens a message box to display an error message. @@ -300,10 +305,12 @@ function system.fuzzy_match(haystack, needle, file) end --- ---Change the opacity (also known as transparency) of the window. --- +---@param window renwindow ---@param opacity number A value from 0.0 to 1.0, the lower the value ---the less visible the window will be. +--- ---@return boolean success True if the operation suceeded. -function system.set_window_opacity(opacity) end +function system.set_window_opacity(window, opacity) end --- ---Loads a lua native module using the default Lua API or lite-xl native plugin API. diff --git a/src/api/api.c b/src/api/api.c index acf6eec2..888a6e42 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -2,6 +2,7 @@ int luaopen_system(lua_State *L); int luaopen_renderer(lua_State *L); +int luaopen_renwindow(lua_State *L); int luaopen_regex(lua_State *L); int luaopen_process(lua_State *L); int luaopen_dirmonitor(lua_State* L); @@ -10,6 +11,7 @@ int luaopen_utf8extra(lua_State* L); static const luaL_Reg libs[] = { { "system", luaopen_system }, { "renderer", luaopen_renderer }, + { "renwindow", luaopen_renwindow }, { "regex", luaopen_regex }, { "process", luaopen_process }, { "dirmonitor", luaopen_dirmonitor }, diff --git a/src/api/api.h b/src/api/api.h index e27112c6..4544d606 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -9,6 +9,7 @@ #define API_TYPE_PROCESS "Process" #define API_TYPE_DIRMONITOR "Dirmonitor" #define API_TYPE_NATIVE_PLUGIN "NativePlugin" +#define API_TYPE_RENWINDOW "RenWindow" #define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key)) diff --git a/src/api/renderer.c b/src/api/renderer.c index 2a57e23a..fdd6ed72 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -1,9 +1,12 @@ #include +#include #include "api.h" #include "../renderer.h" #include "../rencache.h" #include "lua.h" +RenWindow *active_window_renderer = NULL; + // a reference index to a table that stores the fonts static int RENDERER_FONT_REF = LUA_NOREF; @@ -90,7 +93,7 @@ static int f_font_load(lua_State *L) { return ret_code; RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); - *font = ren_font_load(window_renderer, filename, size, antialiasing, hinting, style); + *font = ren_font_load(filename, size, antialiasing, hinting, style); if (!*font) return luaL_error(L, "failed to load font"); luaL_setmetatable(L, API_TYPE_FONT); @@ -136,7 +139,7 @@ static int f_font_copy(lua_State *L) { } for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); - *font = ren_font_copy(window_renderer, fonts[i], size, antialiasing, hinting, style); + *font = ren_font_copy(fonts[i], size, antialiasing, hinting, style); if (!*font) return luaL_error(L, "failed to copy font"); luaL_setmetatable(L, API_TYPE_FONT); @@ -204,7 +207,7 @@ static int f_font_get_width(lua_State *L) { size_t len; const char *text = luaL_checklstring(L, 2, &len); - lua_pushnumber(L, ren_font_group_get_width(window_renderer, fonts, text, len, NULL)); + lua_pushnumber(L, ren_font_group_get_width(fonts, text, len, NULL)); return 1; } @@ -223,7 +226,7 @@ static int f_font_get_size(lua_State *L) { static int f_font_set_size(lua_State *L) { RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); float size = luaL_checknumber(L, 2); - ren_font_group_set_size(window_renderer, fonts, size); + ren_font_group_set_size(fonts, size); return 0; } @@ -281,8 +284,9 @@ static int f_show_debug(lua_State *L) { static int f_get_size(lua_State *L) { - int w, h; - ren_get_size(window_renderer, &w, &h); + int w = 0, h = 0; + if (active_window_renderer) + ren_get_size(active_window_renderer, &w, &h); lua_pushnumber(L, w); lua_pushnumber(L, h); return 2; @@ -290,13 +294,17 @@ static int f_get_size(lua_State *L) { static int f_begin_frame(UNUSED lua_State *L) { - rencache_begin_frame(window_renderer); + assert(active_window_renderer == NULL); + active_window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + rencache_begin_frame(active_window_renderer); return 0; } static int f_end_frame(UNUSED lua_State *L) { - rencache_end_frame(window_renderer); + assert(active_window_renderer != NULL); + rencache_end_frame(active_window_renderer); + active_window_renderer = NULL; // clear the font reference table lua_newtable(L); lua_rawseti(L, LUA_REGISTRYINDEX, RENDERER_FONT_REF); @@ -312,28 +320,31 @@ static RenRect rect_to_grid(lua_Number x, lua_Number y, lua_Number w, lua_Number static int f_set_clip_rect(lua_State *L) { + assert(active_window_renderer != NULL); lua_Number x = luaL_checknumber(L, 1); lua_Number y = luaL_checknumber(L, 2); lua_Number w = luaL_checknumber(L, 3); lua_Number h = luaL_checknumber(L, 4); RenRect rect = rect_to_grid(x, y, w, h); - rencache_set_clip_rect(window_renderer, rect); + rencache_set_clip_rect(active_window_renderer, rect); return 0; } static int f_draw_rect(lua_State *L) { + assert(active_window_renderer != NULL); lua_Number x = luaL_checknumber(L, 1); lua_Number y = luaL_checknumber(L, 2); lua_Number w = luaL_checknumber(L, 3); lua_Number h = luaL_checknumber(L, 4); RenRect rect = rect_to_grid(x, y, w, h); RenColor color = checkcolor(L, 5, 255); - rencache_draw_rect(window_renderer, rect, color); + rencache_draw_rect(active_window_renderer, rect, color); return 0; } static int f_draw_text(lua_State *L) { + assert(active_window_renderer != NULL); RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); @@ -354,7 +365,7 @@ static int f_draw_text(lua_State *L) { double x = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - x = rencache_draw_text(window_renderer, fonts, text, len, x, y, color); + x = rencache_draw_text(active_window_renderer, fonts, text, len, x, y, color); lua_pushnumber(L, x); return 1; } diff --git a/src/api/renwindow.c b/src/api/renwindow.c new file mode 100644 index 00000000..84687da4 --- /dev/null +++ b/src/api/renwindow.c @@ -0,0 +1,113 @@ +#include "api.h" +#include "../renwindow.h" +#include "lua.h" +#include + +static RenWindow *persistant_window = NULL; + +static void init_window_icon(SDL_Window *window) { +#if !defined(_WIN32) && !defined(__APPLE__) + #include "../resources/icons/icon.inl" + (void) icon_rgba_len; /* unused */ + SDL_Surface *surf = SDL_CreateRGBSurfaceFrom( + icon_rgba, 64, 64, + 32, 64 * 4, + 0x000000ff, + 0x0000ff00, + 0x00ff0000, + 0xff000000); + SDL_SetWindowIcon(window, surf); + SDL_FreeSurface(surf); +#endif +} + +static int f_renwin_create(lua_State *L) { + const char *title = luaL_checkstring(L, 1); + const int x = luaL_optinteger(L, 2, SDL_WINDOWPOS_UNDEFINED); + const int y = luaL_optinteger(L, 3, SDL_WINDOWPOS_UNDEFINED); + float width = luaL_optnumber(L, 4, 0); + float height = luaL_optnumber(L, 5, 0); + + if (width < 1 || height < 1) { + SDL_DisplayMode dm; + SDL_GetCurrentDisplayMode(0, &dm); + + if (width < 1) { + width = dm.w * 0.8; + } + if (height < 1) { + height = dm.h * 0.8; + } + } + + SDL_Window *window = SDL_CreateWindow( + title, x, y, width, height, + SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN + ); + if (!window) { + return luaL_error(L, "Error creating lite-xl window: %s", SDL_GetError()); + } + init_window_icon(window); + + RenWindow **window_renderer = (RenWindow**)lua_newuserdata(L, sizeof(RenWindow*)); + luaL_setmetatable(L, API_TYPE_RENWINDOW); + + *window_renderer = ren_create(window); + + return 1; +} + +static int f_renwin_gc(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + if (window_renderer != persistant_window) + ren_destroy(window_renderer); + + return 0; +} + +static int f_renwin_get_size(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + int w, h; + ren_get_size(window_renderer, &w, &h); + lua_pushnumber(L, w); + lua_pushnumber(L, h); + return 2; +} + +static int f_renwin_persist(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + + persistant_window = window_renderer; + return 0; +} + +static int f_renwin_restore(lua_State *L) { + if (!persistant_window) { + lua_pushnil(L); + } + else { + RenWindow **window_renderer = (RenWindow**)lua_newuserdata(L, sizeof(RenWindow*)); + luaL_setmetatable(L, API_TYPE_RENWINDOW); + + *window_renderer = persistant_window; + } + + return 1; +} + +static const luaL_Reg renwindow_lib[] = { + { "create", f_renwin_create }, + { "__gc", f_renwin_gc }, + { "get_size", f_renwin_get_size }, + { "_persist", f_renwin_persist }, + { "_restore", f_renwin_restore }, + {NULL, NULL} +}; + +int luaopen_renwindow(lua_State* L) { + luaL_newmetatable(L, API_TYPE_RENWINDOW); + luaL_setfuncs(L, renwindow_lib, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); + return 1; +} diff --git a/src/api/system.c b/src/api/system.c index f599e800..f301dba5 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -74,7 +74,7 @@ static SDL_HitTestResult SDLCALL hit_test(SDL_Window *window, const SDL_Point *p const int controls_width = hit_info->controls_width; int w, h; - SDL_GetWindowSize(window_renderer->window, &w, &h); + SDL_GetWindowSize(window, &w, &h); if (pt->y < hit_info->title_height && #if RESIZE_FROM_TOP @@ -186,6 +186,7 @@ top: case SDL_WINDOWEVENT: if (e.window.event == SDL_WINDOWEVENT_RESIZED) { + RenWindow* window_renderer = ren_find_window_from_id(e.window.windowID); ren_resize_window(window_renderer); lua_pushstring(L, "resized"); /* The size below will be in points. */ @@ -222,13 +223,16 @@ top: goto top; case SDL_DROPFILE: - SDL_GetMouseState(&mx, &my); - lua_pushstring(L, "filedropped"); - lua_pushstring(L, e.drop.file); - lua_pushinteger(L, mx * window_renderer->scale_x); - lua_pushinteger(L, my * window_renderer->scale_y); - SDL_free(e.drop.file); - return 4; + { + RenWindow* window_renderer = ren_find_window_from_id(e.drop.windowID); + SDL_GetMouseState(&mx, &my); + lua_pushstring(L, "filedropped"); + lua_pushstring(L, e.drop.file); + lua_pushinteger(L, mx * window_renderer->scale_x); + lua_pushinteger(L, my * window_renderer->scale_y); + SDL_free(e.drop.file); + return 4; + } case SDL_KEYDOWN: #ifdef __APPLE__ @@ -280,36 +284,45 @@ top: #endif case SDL_MOUSEBUTTONDOWN: - if (e.button.button == 1) { SDL_CaptureMouse(1); } - lua_pushstring(L, "mousepressed"); - lua_pushstring(L, button_name(e.button.button)); - lua_pushinteger(L, e.button.x * window_renderer->scale_x); - lua_pushinteger(L, e.button.y * window_renderer->scale_y); - lua_pushinteger(L, e.button.clicks); - return 5; + { + if (e.button.button == 1) { SDL_CaptureMouse(1); } + RenWindow* window_renderer = ren_find_window_from_id(e.button.windowID); + lua_pushstring(L, "mousepressed"); + lua_pushstring(L, button_name(e.button.button)); + lua_pushinteger(L, e.button.x * window_renderer->scale_x); + lua_pushinteger(L, e.button.y * window_renderer->scale_y); + lua_pushinteger(L, e.button.clicks); + return 5; + } case SDL_MOUSEBUTTONUP: - if (e.button.button == 1) { SDL_CaptureMouse(0); } - lua_pushstring(L, "mousereleased"); - lua_pushstring(L, button_name(e.button.button)); - lua_pushinteger(L, e.button.x * window_renderer->scale_x); - lua_pushinteger(L, e.button.y * window_renderer->scale_y); - return 4; + { + if (e.button.button == 1) { SDL_CaptureMouse(0); } + RenWindow* window_renderer = ren_find_window_from_id(e.button.windowID); + lua_pushstring(L, "mousereleased"); + lua_pushstring(L, button_name(e.button.button)); + lua_pushinteger(L, e.button.x * window_renderer->scale_x); + lua_pushinteger(L, e.button.y * window_renderer->scale_y); + return 4; + } case SDL_MOUSEMOTION: - SDL_PumpEvents(); - while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) { - e.motion.x = event_plus.motion.x; - e.motion.y = event_plus.motion.y; - e.motion.xrel += event_plus.motion.xrel; - e.motion.yrel += event_plus.motion.yrel; + { + SDL_PumpEvents(); + while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) { + e.motion.x = event_plus.motion.x; + e.motion.y = event_plus.motion.y; + e.motion.xrel += event_plus.motion.xrel; + e.motion.yrel += event_plus.motion.yrel; + } + RenWindow* window_renderer = ren_find_window_from_id(e.motion.windowID); + lua_pushstring(L, "mousemoved"); + lua_pushinteger(L, e.motion.x * window_renderer->scale_x); + lua_pushinteger(L, e.motion.y * window_renderer->scale_y); + lua_pushinteger(L, e.motion.xrel * window_renderer->scale_x); + lua_pushinteger(L, e.motion.yrel * window_renderer->scale_y); + return 5; } - lua_pushstring(L, "mousemoved"); - lua_pushinteger(L, e.motion.x * window_renderer->scale_x); - lua_pushinteger(L, e.motion.y * window_renderer->scale_y); - lua_pushinteger(L, e.motion.xrel * window_renderer->scale_x); - lua_pushinteger(L, e.motion.yrel * window_renderer->scale_y); - return 5; case SDL_MOUSEWHEEL: lua_pushstring(L, "mousewheel"); @@ -324,49 +337,64 @@ top: return 3; case SDL_FINGERDOWN: - SDL_GetWindowSize(window_renderer->window, &w, &h); + { + RenWindow* window_renderer = ren_find_window_from_id(e.tfinger.windowID); + SDL_GetWindowSize(window_renderer->window, &w, &h); - lua_pushstring(L, "touchpressed"); - lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w)); - lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h)); - lua_pushinteger(L, e.tfinger.fingerId); - return 4; + lua_pushstring(L, "touchpressed"); + lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w)); + lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h)); + lua_pushinteger(L, e.tfinger.fingerId); + return 4; + } case SDL_FINGERUP: - SDL_GetWindowSize(window_renderer->window, &w, &h); + { + RenWindow* window_renderer = ren_find_window_from_id(e.tfinger.windowID); + SDL_GetWindowSize(window_renderer->window, &w, &h); - lua_pushstring(L, "touchreleased"); - lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w)); - lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h)); - lua_pushinteger(L, e.tfinger.fingerId); - return 4; + lua_pushstring(L, "touchreleased"); + lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w)); + lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h)); + lua_pushinteger(L, e.tfinger.fingerId); + return 4; + } case SDL_FINGERMOTION: - SDL_PumpEvents(); - while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) > 0) { - e.tfinger.x = event_plus.tfinger.x; - e.tfinger.y = event_plus.tfinger.y; - e.tfinger.dx += event_plus.tfinger.dx; - e.tfinger.dy += event_plus.tfinger.dy; - } - SDL_GetWindowSize(window_renderer->window, &w, &h); + { + SDL_PumpEvents(); + while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) > 0) { + e.tfinger.x = event_plus.tfinger.x; + e.tfinger.y = event_plus.tfinger.y; + e.tfinger.dx += event_plus.tfinger.dx; + e.tfinger.dy += event_plus.tfinger.dy; + } + RenWindow* window_renderer = ren_find_window_from_id(e.tfinger.windowID); + SDL_GetWindowSize(window_renderer->window, &w, &h); - lua_pushstring(L, "touchmoved"); - lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w)); - lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h)); - lua_pushinteger(L, (lua_Integer)(e.tfinger.dx * w)); - lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h)); - lua_pushinteger(L, e.tfinger.fingerId); - return 6; + lua_pushstring(L, "touchmoved"); + lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w)); + lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h)); + lua_pushinteger(L, (lua_Integer)(e.tfinger.dx * w)); + lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h)); + lua_pushinteger(L, e.tfinger.fingerId); + return 6; + } case SDL_APP_WILLENTERFOREGROUND: case SDL_APP_DIDENTERFOREGROUND: - #ifdef LITE_USE_SDL_RENDERER - rencache_invalidate(); - #else - SDL_UpdateWindowSurface(window_renderer->window); - #endif - lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground"); - return 1; + { + #ifdef LITE_USE_SDL_RENDERER + rencache_invalidate(); + #else + RenWindow** window_list; + size_t window_count = ren_get_window_list(&window_list); + while (window_count) { + SDL_UpdateWindowSurface(window_list[--window_count]->window); + } + #endif + lua_pushstring(L, e.type == SDL_APP_WILLENTERFOREGROUND ? "enteringforeground" : "enteredforeground"); + return 1; + } case SDL_APP_WILLENTERBACKGROUND: lua_pushstring(L, "enteringbackground"); return 1; @@ -428,7 +456,8 @@ static int f_set_cursor(lua_State *L) { static int f_set_window_title(lua_State *L) { - const char *title = luaL_checkstring(L, 1); + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + const char *title = luaL_checkstring(L, 2); SDL_SetWindowTitle(window_renderer->window, title); return 0; } @@ -438,7 +467,8 @@ static const char *window_opts[] = { "normal", "minimized", "maximized", "fullsc enum { WIN_NORMAL, WIN_MINIMIZED, WIN_MAXIMIZED, WIN_FULLSCREEN }; static int f_set_window_mode(lua_State *L) { - int n = luaL_checkoption(L, 1, "normal", window_opts); + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + int n = luaL_checkoption(L, 2, "normal", window_opts); SDL_SetWindowFullscreen(window_renderer->window, n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); if (n == WIN_NORMAL) { SDL_RestoreWindow(window_renderer->window); } @@ -449,26 +479,38 @@ static int f_set_window_mode(lua_State *L) { static int f_set_window_bordered(lua_State *L) { + RenWindow** window_list; + size_t window_count = ren_get_window_list(&window_list); int bordered = lua_toboolean(L, 1); - SDL_SetWindowBordered(window_renderer->window, bordered); + while (window_count) { + SDL_SetWindowBordered(window_list[--window_count]->window, bordered); + } + return 0; } static int f_set_window_hit_test(lua_State *L) { + RenWindow** window_list; + size_t window_count = ren_get_window_list(&window_list); if (lua_gettop(L) == 0) { - SDL_SetWindowHitTest(window_renderer->window, NULL, NULL); + while (window_count) { + SDL_SetWindowHitTest(window_list[--window_count]->window, NULL, NULL); + } return 0; } window_hit_info->title_height = luaL_checknumber(L, 1); window_hit_info->controls_width = luaL_checknumber(L, 2); window_hit_info->resize_border = luaL_checknumber(L, 3); - SDL_SetWindowHitTest(window_renderer->window, hit_test, window_hit_info); + while (window_count) { + SDL_SetWindowHitTest(window_list[--window_count]->window, hit_test, window_hit_info); + } return 0; } static int f_get_window_size(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); int x, y, w, h; SDL_GetWindowSize(window_renderer->window, &w, &h); SDL_GetWindowPosition(window_renderer->window, &x, &y); @@ -481,10 +523,11 @@ static int f_get_window_size(lua_State *L) { static int f_set_window_size(lua_State *L) { - double w = luaL_checknumber(L, 1); - double h = luaL_checknumber(L, 2); - double x = luaL_checknumber(L, 3); - double y = luaL_checknumber(L, 4); + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + double w = luaL_checknumber(L, 2); + double h = luaL_checknumber(L, 3); + double x = luaL_checknumber(L, 4); + double y = luaL_checknumber(L, 5); SDL_SetWindowSize(window_renderer->window, w, h); SDL_SetWindowPosition(window_renderer->window, x, y); ren_resize_window(window_renderer); @@ -493,6 +536,7 @@ static int f_set_window_size(lua_State *L) { static int f_window_has_focus(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); unsigned flags = SDL_GetWindowFlags(window_renderer->window); lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS); return 1; @@ -500,6 +544,7 @@ static int f_window_has_focus(lua_State *L) { static int f_get_window_mode(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); unsigned flags = SDL_GetWindowFlags(window_renderer->window); if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) { lua_pushstring(L, "fullscreen"); @@ -532,6 +577,7 @@ static int f_clear_ime(lua_State *L) { static int f_raise_window(lua_State *L) { + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); /* SDL_RaiseWindow should be enough but on some window managers like the one used on Gnome the window needs to first have input focus in order @@ -922,7 +968,8 @@ static int f_fuzzy_match(lua_State *L) { } static int f_set_window_opacity(lua_State *L) { - double n = luaL_checknumber(L, 1); + RenWindow *window_renderer = *(RenWindow**)luaL_checkudata(L, 1, API_TYPE_RENWINDOW); + double n = luaL_checknumber(L, 2); int r = SDL_SetWindowOpacity(window_renderer->window, n); lua_pushboolean(L, r > -1); return 1; diff --git a/src/main.c b/src/main.c index d3a19b99..74453b18 100644 --- a/src/main.c +++ b/src/main.c @@ -17,9 +17,6 @@ #include #endif - -static SDL_Window *window; - static void get_exe_filename(char *buf, int sz) { #if _WIN32 int len; @@ -56,23 +53,6 @@ static void get_exe_filename(char *buf, int sz) { #endif } - -static void init_window_icon(void) { -#if !defined(_WIN32) && !defined(__APPLE__) - #include "../resources/icons/icon.inl" - (void) icon_rgba_len; /* unused */ - SDL_Surface *surf = SDL_CreateRGBSurfaceFrom( - icon_rgba, 64, 64, - 32, 64 * 4, - 0x000000ff, - 0x0000ff00, - 0x00ff0000, - 0xff000000); - SDL_SetWindowIcon(window, surf); - SDL_FreeSurface(surf); -#endif -} - #ifdef _WIN32 #define LITE_OS_HOME "USERPROFILE" #define LITE_PATHSEP_PATTERN "\\\\" @@ -165,18 +145,9 @@ int main(int argc, char **argv) { SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software"); - SDL_DisplayMode dm; - SDL_GetCurrentDisplayMode(0, &dm); - - window = SDL_CreateWindow( - "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8, - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); - init_window_icon(); - if (!window) { - fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError()); - exit(1); + if ( ren_init() ) { + fprintf(stderr, "internal font error when starting the application\n"); } - window_renderer = ren_init(window); lua_State *L; init_lua: @@ -265,10 +236,9 @@ init_lua: goto init_lua; } - // This allows the window to be destroyed before lite-xl is done with - // reaping child processes - ren_free(window_renderer); lua_close(L); + ren_free(); + return EXIT_SUCCESS; } diff --git a/src/meson.build b/src/meson.build index abf512cb..60acf45c 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,6 +1,7 @@ lite_sources = [ 'api/api.c', 'api/renderer.c', + 'api/renwindow.c', 'api/regex.c', 'api/system.c', 'api/process.c', diff --git a/src/rencache.c b/src/rencache.c index a77f82ca..b75b824b 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -192,7 +192,7 @@ void rencache_draw_rect(RenWindow *window_renderer, RenRect rect, RenColor color double rencache_draw_text(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, double x, int y, RenColor color) { int x_offset; - double width = ren_font_group_get_width(window_renderer, fonts, text, len, &x_offset); + double width = ren_font_group_get_width(fonts, text, len, &x_offset); RenRect rect = { x + x_offset, y, (int)(width - x_offset), ren_font_group_get_height(fonts) }; if (rects_overlap(last_clip_rect, rect)) { int sz = len + 1; diff --git a/src/renderer.c b/src/renderer.c index 80b2b057..eefb1fa0 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -22,7 +22,8 @@ #define MAX_LOADABLE_GLYPHSETS (MAX_UNICODE / GLYPHSET_SIZE) #define SUBPIXEL_BITMAPS_CACHED 3 -RenWindow* window_renderer = NULL; +static RenWindow **window_list = NULL; +static size_t window_count = 0; static FT_Library library; // draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending @@ -248,7 +249,7 @@ static void font_file_close(FT_Stream stream) { } } -RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) { +RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) { RenFont *font = NULL; FT_Face face = NULL; @@ -267,8 +268,7 @@ RenFont* ren_font_load(RenWindow *window_renderer, const char* path, float size, if (FT_Open_Face(library, &(FT_Open_Args){ .flags = FT_OPEN_STREAM, .stream = &font->stream }, 0, &face)) goto failure; - const int surface_scale = renwin_get_surface(window_renderer).scale; - if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale))) + if (FT_Set_Pixel_Sizes(face, 0, (int)(size))) goto failure; strcpy(font->path, path); @@ -305,12 +305,12 @@ rwops_failure: return NULL; } -RenFont* ren_font_copy(RenWindow *window_renderer, RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style) { +RenFont* ren_font_copy(RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style) { antialiasing = antialiasing == -1 ? font->antialiasing : antialiasing; hinting = hinting == -1 ? font->hinting : hinting; style = style == -1 ? font->style : style; - return ren_font_load(window_renderer, font->path, size, antialiasing, hinting, style); + return ren_font_load(font->path, size, antialiasing, hinting, style); } const char* ren_font_get_path(RenFont *font) { @@ -344,12 +344,11 @@ float ren_font_group_get_size(RenFont **fonts) { return fonts[0]->size; } -void ren_font_group_set_size(RenWindow *window_renderer, RenFont **fonts, float size) { - const int surface_scale = renwin_get_surface(window_renderer).scale; +void ren_font_group_set_size(RenFont **fonts, float size) { for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { font_clear_glyph_cache(fonts[i]); FT_Face face = fonts[i]->face; - FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale)); + FT_Set_Pixel_Sizes(face, 0, (int)(size)); fonts[i]->size = size; #ifdef LITE_USE_SDL_RENDERER fonts[i]->scale = surface_scale; @@ -366,7 +365,7 @@ int ren_font_group_get_height(RenFont **fonts) { return fonts[0]->height; } -double ren_font_group_get_width(RenWindow *window_renderer, RenFont **fonts, const char *text, size_t len, int *x_offset) { +double ren_font_group_get_width(RenFont **fonts, const char *text, size_t len, int *x_offset) { double width = 0; const char* end = text + len; GlyphMetric* metric = NULL; GlyphSet* set = NULL; @@ -383,11 +382,10 @@ double ren_font_group_get_width(RenWindow *window_renderer, RenFont **fonts, con *x_offset = metric->bitmap_left; // TODO: should this be scaled by the surface scale? } } - const int surface_scale = renwin_get_surface(window_renderer).scale; if (!set_x_offset) { *x_offset = 0; } - return width / surface_scale; + return width; } double ren_draw_text(RenSurface *rs, RenFont **fonts, const char *text, size_t len, float x, int y, RenColor color) { @@ -519,29 +517,55 @@ void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color) { } /*************** Window Management ****************/ -RenWindow* ren_init(SDL_Window *win) { - assert(win); - int error = FT_Init_FreeType( &library ); - if ( error ) { - fprintf(stderr, "internal font error when starting the application\n"); - return NULL; +static void ren_add_window(RenWindow *window_renderer) { + window_count += 1; + window_list = realloc(window_list, window_count); + window_list[window_count-1] = window_renderer; +} + +static void ren_remove_window(RenWindow *window_renderer) { + for (size_t i = 0; i < window_count; ++i) { + if (window_list[i] == window_renderer) { + window_count -= 1; + memmove(&window_list[i], &window_list[i+1], window_count - i); + return; + } } +} + +int ren_init(void) { + int err; + + draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32, + 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + + if ((err = FT_Init_FreeType( &library ))) + return err; + + return 0; +} + +void ren_free(void) { + SDL_FreeSurface(draw_rect_surface); +} + +RenWindow* ren_create(SDL_Window *win) { + assert(win); RenWindow* window_renderer = calloc(1, sizeof(RenWindow)); window_renderer->window = win; renwin_init_surface(window_renderer); renwin_init_command_buf(window_renderer); renwin_clip_to_surface(window_renderer); - draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32, - 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); + ren_add_window(window_renderer); return window_renderer; } -void ren_free(RenWindow* window_renderer) { +void ren_destroy(RenWindow* window_renderer) { assert(window_renderer); + ren_remove_window(window_renderer); renwin_free(window_renderer); - SDL_FreeSurface(draw_rect_surface); free(window_renderer->command_buf); window_renderer->command_buf = NULL; window_renderer->command_buf_size = 0; @@ -553,14 +577,14 @@ void ren_resize_window(RenWindow *window_renderer) { renwin_update_scale(window_renderer); } - +// TODO: Does not work nicely with multiple windows void ren_update_rects(RenWindow *window_renderer, RenRect *rects, int count) { static bool initial_frame = true; + renwin_update_rects(window_renderer, rects, count); if (initial_frame) { renwin_show_window(window_renderer); initial_frame = false; } - renwin_update_rects(window_renderer, rects, count); } @@ -571,6 +595,27 @@ void ren_set_clip_rect(RenWindow *window_renderer, RenRect rect) { void ren_get_size(RenWindow *window_renderer, int *x, int *y) { RenSurface rs = renwin_get_surface(window_renderer); - *x = rs.surface->w / rs.scale; - *y = rs.surface->h / rs.scale; + *x = rs.surface->w; + *y = rs.surface->h; +} + +size_t ren_get_window_list(RenWindow ***window_list_dest) { + *window_list_dest = window_list; + return window_count; +} + +RenWindow* ren_find_window(SDL_Window *window) { + for (size_t i = 0; i < window_count; ++i) { + RenWindow* window_renderer = window_list[i]; + if (window_renderer->window == window) { + return window_renderer; + } + } + + return NULL; +} + +RenWindow* ren_find_window_from_id(uint32_t id) { + SDL_Window *window = SDL_GetWindowFromID(id); + return ren_find_window(window); } diff --git a/src/renderer.h b/src/renderer.h index 1cd6bc88..6f4fd521 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -23,31 +23,34 @@ typedef struct { SDL_Surface *surface; int scale; } RenSurface; struct RenWindow; typedef struct RenWindow RenWindow; -extern RenWindow* window_renderer; -RenFont* ren_font_load(RenWindow *window_renderer, const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style); -RenFont* ren_font_copy(RenWindow *window_renderer, RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style); +RenFont* ren_font_load(const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style); +RenFont* ren_font_copy(RenFont* font, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, int style); const char* ren_font_get_path(RenFont *font); void ren_font_free(RenFont *font); int ren_font_group_get_tab_size(RenFont **font); int ren_font_group_get_height(RenFont **font); float ren_font_group_get_size(RenFont **font); -void ren_font_group_set_size(RenWindow *window_renderer, RenFont **font, float size); +void ren_font_group_set_size(RenFont **font, float size); #ifdef LITE_USE_SDL_RENDERER void update_font_scale(RenWindow *window_renderer, RenFont **fonts); #endif void ren_font_group_set_tab_size(RenFont **font, int n); -double ren_font_group_get_width(RenWindow *window_renderer, RenFont **font, const char *text, size_t len, int *x_offset); +double ren_font_group_get_width(RenFont **font, const char *text, size_t len, int *x_offset); double ren_draw_text(RenSurface *rs, RenFont **font, const char *text, size_t len, float x, int y, RenColor color); void ren_draw_rect(RenSurface *rs, RenRect rect, RenColor color); -RenWindow* ren_init(SDL_Window *win); -void ren_free(RenWindow* window_renderer); +int ren_init(void); +void ren_free(void); +RenWindow* ren_create(SDL_Window *win); +void ren_destroy(RenWindow* window_renderer); void ren_resize_window(RenWindow *window_renderer); void ren_update_rects(RenWindow *window_renderer, RenRect *rects, int count); void ren_set_clip_rect(RenWindow *window_renderer, RenRect rect); void ren_get_size(RenWindow *window_renderer, int *x, int *y); /* Reports the size in points. */ - +size_t ren_get_window_list(RenWindow ***window_list_dest); +RenWindow* ren_find_window(SDL_Window *window); +RenWindow* ren_find_window_from_id(uint32_t id); #endif