diff --git a/data/core/docview.lua b/data/core/docview.lua index 0fd82b83..2c624d80 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -232,7 +232,7 @@ end function DocView:on_mouse_moved(x, y, ...) DocView.super.on_mouse_moved(self, x, y, ...) - if self:scrollbar_overlaps_point(x, y) or self.dragging_scrollbar then + if self.hovered_scrollbar_track or self.dragging_scrollbar then self.cursor = "arrow" else self.cursor = "ibeam" @@ -275,8 +275,8 @@ function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2) end -function DocView:on_mouse_released(button) - DocView.super.on_mouse_released(self, button) +function DocView:on_mouse_released(...) + DocView.super.on_mouse_released(self, ...) self.mouse_selecting = nil end diff --git a/data/core/init.lua b/data/core/init.lua index 62f311c2..625fcd91 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -469,6 +469,10 @@ local style = require "core.style" -- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 14 * SCALE) -- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 14 * SCALE) -- +-- DATADIR is the location of the installed Lite XL Lua code, default color +-- schemes and fonts. +-- USERDIR is the location of the Lite XL configuration directory. +-- -- font names used by lite: -- style.font : user interface -- style.big_font : big text in welcome screen diff --git a/data/core/start.lua b/data/core/start.lua index dfddcbda..66284b95 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -34,3 +34,12 @@ table.pack = table.pack or pack or function(...) return {...} end table.unpack = table.unpack or unpack bit32 = bit32 or require "core.bit" + +-- Because AppImages change the working directory before running the executable, +-- we need to change it back to the original one. +-- https://github.com/AppImage/AppImageKit/issues/172 +-- https://github.com/AppImage/AppImageKit/pull/191 +local appimage_owd = os.getenv("OWD") +if os.getenv("APPIMAGE") and appimage_owd then + system.chdir(appimage_owd) +end diff --git a/data/core/style.lua b/data/core/style.lua index 470a0904..70ad502c 100644 --- a/data/core/style.lua +++ b/data/core/style.lua @@ -4,6 +4,7 @@ local style = {} style.padding = { x = common.round(14 * SCALE), y = common.round(7 * SCALE) } style.divider_size = common.round(1 * SCALE) style.scrollbar_size = common.round(4 * SCALE) +style.expanded_scrollbar_size = common.round(12 * SCALE) style.caret_width = common.round(2 * SCALE) style.tab_width = common.round(170 * SCALE) @@ -43,6 +44,7 @@ style.line_number2 = { common.color "#83838f" } -- With cursor style.line_highlight = { common.color "#343438" } style.scrollbar = { common.color "#414146" } style.scrollbar2 = { common.color "#4b4b52" } -- Hovered +style.scrollbar_track = { common.color "#252529" } style.nagbar = { common.color "#FF0000" } style.nagbar_text = { common.color "#FFFFFF" } style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" } diff --git a/data/core/view.lua b/data/core/view.lua index a3664a55..f1c9a949 100644 --- a/data/core/view.lua +++ b/data/core/view.lua @@ -18,6 +18,13 @@ function View:new() self.scroll = { x = 0, y = 0, to = { x = 0, y = 0 } } self.cursor = "arrow" self.scrollable = false + self.scrollbar = { + x = { thumb = 0, track = 0 }, + y = { thumb = 0, track = 0 }, + w = { thumb = 0, track = 0, to = { thumb = 0, track = 0 } }, + h = { thumb = 0, track = 0 }, + } + self.scrollbar_alpha = { value = 0, to = 0 } end function View:move_towards(t, k, dest, rate) @@ -57,29 +64,62 @@ function View:get_scrollable_size() end +function View:get_scrollbar_track_rect() + local sz = self:get_scrollable_size() + if sz <= self.size.y or sz == math.huge then + return 0, 0, 0, 0 + end + local width = style.scrollbar_size + if self.hovered_scrollbar_track or self.dragging_scrollbar then + width = style.expanded_scrollbar_size + end + return + self.position.x + self.size.x - width, + self.position.y, + width, + self.size.y +end + + function View:get_scrollbar_rect() local sz = self:get_scrollable_size() if sz <= self.size.y or sz == math.huge then return 0, 0, 0, 0 end local h = math.max(20, self.size.y * self.size.y / sz) + local width = style.scrollbar_size + if self.hovered_scrollbar_track or self.dragging_scrollbar then + width = style.expanded_scrollbar_size + end return - self.position.x + self.size.x - style.scrollbar_size, + self.position.x + self.size.x - width, self.position.y + self.scroll.y * (self.size.y - h) / (sz - self.size.y), - style.scrollbar_size, + width, h end function View:scrollbar_overlaps_point(x, y) local sx, sy, sw, sh = self:get_scrollbar_rect() - return x >= sx - sw * 3 and x < sx + sw and y >= sy and y < sy + sh + return x >= sx - style.scrollbar_size * 3 and x < sx + sw and y > sy and y <= sy + sh +end + +function View:scrollbar_track_overlaps_point(x, y) + local sx, sy, sw, sh = self:get_scrollbar_track_rect() + return x >= sx - style.scrollbar_size * 3 and x < sx + sw and y > sy and y <= sy + sh end function View:on_mouse_pressed(button, x, y, clicks) - if self:scrollbar_overlaps_point(x, y) then - self.dragging_scrollbar = true + if self:scrollbar_track_overlaps_point(x, y) then + if self:scrollbar_overlaps_point(x, y) then + self.dragging_scrollbar = true + else + local _, _, _, sh = self:get_scrollbar_rect() + local ly = (y - self.position.y) - sh / 2 + local pct = common.clamp(ly / self.size.y, 0, 100) + self.scroll.to.y = self:get_scrollable_size() * pct + end return true end end @@ -96,6 +136,7 @@ function View:on_mouse_moved(x, y, dx, dy) self.scroll.to.y = self.scroll.to.y + delta end self.hovered_scrollbar = self:scrollbar_overlaps_point(x, y) + self.hovered_scrollbar_track = self.hovered_scrollbar or self:scrollbar_track_overlaps_point(x, y) end @@ -132,10 +173,33 @@ function View:clamp_scroll_position() end +function View:update_scrollbar() + local x, y, w, h = self:get_scrollbar_rect() + self.scrollbar.w.to.thumb = w + self:move_towards(self.scrollbar.w, "thumb", self.scrollbar.w.to.thumb, 0.3) + self.scrollbar.x.thumb = x + w - self.scrollbar.w.thumb + self.scrollbar.y.thumb = y + self.scrollbar.h.thumb = h + + local x, y, w, h = self:get_scrollbar_track_rect() + self.scrollbar.w.to.track = w + self:move_towards(self.scrollbar.w, "track", self.scrollbar.w.to.track, 0.3) + self.scrollbar.x.track = x + w - self.scrollbar.w.track + self.scrollbar.y.track = y + self.scrollbar.h.track = h + + -- we use 100 for a smoother transition + self.scrollbar_alpha.to = (self.hovered_scrollbar_track or self.dragging_scrollbar) and 100 or 0 + self:move_towards(self.scrollbar_alpha, "value", self.scrollbar_alpha.to, 0.3) +end + + function View:update() self:clamp_scroll_position() self:move_towards(self.scroll, "x", self.scroll.to.x, 0.3) self:move_towards(self.scroll, "y", self.scroll.to.y, 0.3) + + self:update_scrollbar() end @@ -146,11 +210,29 @@ function View:draw_background(color) end -function View:draw_scrollbar() - local x, y, w, h = self:get_scrollbar_rect() +function View:draw_scrollbar_track() + if not (self.hovered_scrollbar_track or self.dragging_scrollbar) + and self.scrollbar_alpha.value == 0 then + return + end + local color = { table.unpack(style.scrollbar_track) } + color[4] = color[4] * self.scrollbar_alpha.value / 100 + renderer.draw_rect(self.scrollbar.x.track, self.scrollbar.y.track, + self.scrollbar.w.track, self.scrollbar.h.track, color) +end + + +function View:draw_scrollbar_thumb() local highlight = self.hovered_scrollbar or self.dragging_scrollbar local color = highlight and style.scrollbar2 or style.scrollbar - renderer.draw_rect(x, y, w, h, color) + renderer.draw_rect(self.scrollbar.x.thumb, self.scrollbar.y.thumb, + self.scrollbar.w.thumb, self.scrollbar.h.thumb, color) +end + + +function View:draw_scrollbar() + self:draw_scrollbar_track() + self:draw_scrollbar_thumb() end diff --git a/data/plugins/autocomplete.lua b/data/plugins/autocomplete.lua index b112ca29..8273f3e5 100644 --- a/data/plugins/autocomplete.lua +++ b/data/plugins/autocomplete.lua @@ -76,12 +76,21 @@ local max_symbols = config.plugins.autocomplete.max_symbols core.add_thread(function() local cache = setmetatable({}, { __mode = "k" }) + local function get_syntax_symbols(symbols, doc) + if doc.syntax then + for sym in pairs(doc.syntax.symbols) do + symbols[sym] = true + end + end + end + local function get_symbols(doc) - if doc.disable_symbols then return {} end - local i = 1 local s = {} + get_syntax_symbols(s, doc) + if doc.disable_symbols then return s end + local i = 1 local symbols_count = 0 - while i < #doc.lines do + while i <= #doc.lines do for sym in doc.lines[i]:gmatch(config.symbol_pattern) do if not s[sym] then symbols_count = symbols_count + 1 @@ -139,6 +148,7 @@ core.add_thread(function() for _, doc in ipairs(core.docs) do if not cache_is_valid(doc) then valid = false + break end end end diff --git a/data/plugins/language_python.lua b/data/plugins/language_python.lua index aa34db0f..dbb6f77c 100644 --- a/data/plugins/language_python.lua +++ b/data/plugins/language_python.lua @@ -9,6 +9,7 @@ syntax.add { patterns = { { pattern = { "#", "\n" }, type = "comment" }, { pattern = { '[ruU]?"""', '"""'; '\\' }, type = "string" }, + { pattern = { "[ruU]?'''", "'''", '\\' }, type = "string" }, { pattern = { '[ruU]?"', '"', '\\' }, type = "string" }, { pattern = { "[ruU]?'", "'", '\\' }, type = "string" }, { pattern = "0x[%da-fA-F]+", type = "number" }, diff --git a/meson.build b/meson.build index 75cd8cf3..0e5c62ac 100644 --- a/meson.build +++ b/meson.build @@ -69,30 +69,68 @@ endif if not get_option('source-only') libm = cc.find_library('m', required : false) libdl = cc.find_library('dl', required : false) - lua_fallback = ['lua', 'lua_dep'] - lua_quick_fallback = [] - if get_option('wrap_mode') == 'forcefallback' - lua_quick_fallback = lua_fallback - endif - lua_dep = dependency('lua5.4', fallback: lua_quick_fallback, required : false) - if not lua_dep.found() - lua_dep = dependency('lua', fallback: ['lua', 'lua_dep'], + default_fallback_options = ['warning_level=0', 'werror=false'] + + # Lua has no official .pc file + # so distros come up with their own names + lua_names = [ + 'lua5.4', # Debian + 'lua-5.4', # FreeBSD + 'lua', # Fedora + ] + + foreach lua : lua_names + last_lua = (lua == lua_names[-1]) + lua_dep = dependency(lua, fallback: last_lua ? ['lua', 'lua_dep'] : [], required : last_lua, version: '>= 5.4', - default_options: ['default_library=static', 'line_editing=false', 'interpreter=false'] + default_options: default_fallback_options + ['default_library=static', 'line_editing=false', 'interpreter=false'] ) - endif + if lua_dep.found() + break + endif + endforeach pcre2_dep = dependency('libpcre2-8', fallback: ['pcre2', 'libpcre2_8'], - default_options: ['default_library=static', 'grep=false', 'test=false'] + default_options: default_fallback_options + ['default_library=static', 'grep=false', 'test=false'] ) freetype_dep = dependency('freetype2', fallback: ['freetype2', 'freetype_dep'], - default_options: ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled'] + default_options: default_fallback_options + ['default_library=static', 'zlib=disabled', 'bzip2=disabled', 'png=disabled', 'harfbuzz=disabled', 'brotli=disabled'] ) + + sdl_options = ['default_library=static'] + + # we explicitly need these + sdl_options += 'use_loadso=enabled' + sdl_options += 'prefer_dlopen=true' + sdl_options += 'use_video=enabled' + sdl_options += 'use_atomic=enabled' + sdl_options += 'use_threads=enabled' + # investigate if this is truly needed + # Do not remove before https://github.com/libsdl-org/SDL/issues/5413 is released + sdl_options += 'use_events=enabled' + + # we leave this up to what the host system has + sdl_options += 'use_video_x11=auto' + sdl_options += 'use_video_wayland=auto' + + # we don't need these + sdl_options += 'use_timers=disabled' + sdl_options += 'use_sensor=disabled' + sdl_options += 'use_haptic=disabled' + sdl_options += 'use_audio=disabled' + sdl_options += 'use_cpuinfo=disabled' + sdl_options += 'use_joystick=disabled' + sdl_options += 'use_video_opengl=disabled' + sdl_options += 'use_video_openglesv2=disabled' + sdl_options += 'use_video_vulkan=disabled' + sdl_options += 'use_video_offscreen=disabled' + sdl_options += 'use_power=disabled' + sdl_dep = dependency('sdl2', fallback: ['sdl2', 'sdl2_dep'], - default_options: ['default_library=static'] + default_options: default_fallback_options + sdl_options ) lite_deps = [lua_dep, sdl_dep, freetype_dep, pcre2_dep, libm, libdl] diff --git a/resources/linux/org.lite_xl.lite_xl.appdata.xml b/resources/linux/org.lite_xl.lite_xl.appdata.xml index 49c23430..1932af90 100644 --- a/resources/linux/org.lite_xl.lite_xl.appdata.xml +++ b/resources/linux/org.lite_xl.lite_xl.appdata.xml @@ -6,6 +6,7 @@ Lite XL A lightweight text editor written in Lua + org.lite_xl.lite_xl.desktop

diff --git a/src/api/process.c b/src/api/process.c index ecbe076e..1be1286c 100644 --- a/src/api/process.c +++ b/src/api/process.c @@ -5,6 +5,7 @@ #include #include #include +#include #if _WIN32 // https://stackoverflow.com/questions/60645/overlapped-i-o-on-anonymous-pipe @@ -21,19 +22,23 @@ #define READ_BUF_SIZE 2048 +#if _WIN32 +typedef HANDLE process_handle; +#else +typedef int process_handle; +#endif + typedef struct { bool running; int returncode, deadline; long pid; #if _WIN32 PROCESS_INFORMATION process_information; - HANDLE child_pipes[3][2]; OVERLAPPED overlapped[2]; bool reading[2]; char buffer[2][READ_BUF_SIZE]; - #else - int child_pipes[3][2]; #endif + process_handle child_pipes[3][2]; } process_t; typedef enum { @@ -91,7 +96,7 @@ static bool poll_process(process_t* proc, int timeout) { #endif if (timeout) SDL_Delay(5); - } while (timeout == WAIT_INFINITE || SDL_GetTicks() - ticks < timeout); + } while (timeout == WAIT_INFINITE || (int)SDL_GetTicks() - ticks < timeout); return proc->running; } @@ -117,8 +122,9 @@ static bool signal_process(process_t* proc, signal_e sig) { } static int process_start(lua_State* L) { + int retval = 1; size_t env_len = 0, key_len, val_len; - const char *cmd[256], *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL; + const char *cmd[256] = { NULL }, *env_names[256] = { NULL }, *env_values[256] = { NULL }, *cwd = NULL; bool detach = false; int deadline = 10, new_fds[3] = { STDIN_FD, STDOUT_FD, STDERR_FD }; luaL_checktype(L, 1, LUA_TTABLE); @@ -134,7 +140,11 @@ static int process_start(lua_State* L) { lua_rawget(L, 1); cmd[i-1] = luaL_checkstring(L, -1); } - cmd[cmd_len] = NULL; + + // this should never trip + // but if it does we are in deep trouble + assert(cmd[0]); + if (arg_len > 1) { lua_getfield(L, 2, "env"); if (!lua_isnil(L, -1)) { @@ -158,8 +168,14 @@ static int process_start(lua_State* L) { lua_getfield(L, 2, "stdout"); new_fds[STDOUT_FD] = luaL_optnumber(L, -1, STDOUT_FD); lua_getfield(L, 2, "stderr"); new_fds[STDERR_FD] = luaL_optnumber(L, -1, STDERR_FD); for (int stream = STDIN_FD; stream <= STDERR_FD; ++stream) { - if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT) - return luaL_error(L, "redirect to handles, FILE* and paths are not supported"); + if (new_fds[stream] > STDERR_FD || new_fds[stream] < REDIRECT_PARENT) { + for (size_t i = 0; i < env_len; ++i) { + free((char*)env_names[i]); + free((char*)env_values[i]); + } + retval = luaL_error(L, "redirect to handles, FILE* and paths are not supported"); + goto cleanup; + } } } @@ -188,16 +204,21 @@ static int process_start(lua_State* L) { sprintf(pipeNameBuffer, "\\\\.\\Pipe\\RemoteExeAnon.%08lx.%08lx", GetCurrentProcessId(), InterlockedIncrement(&PipeSerialNumber)); self->child_pipes[i][0] = CreateNamedPipeA(pipeNameBuffer, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_TYPE_BYTE | PIPE_WAIT, 1, READ_BUF_SIZE, READ_BUF_SIZE, 0, NULL); - if (self->child_pipes[i][0] == INVALID_HANDLE_VALUE) - return luaL_error(L, "Error creating read pipe: %d.", GetLastError()); + if (self->child_pipes[i][0] == INVALID_HANDLE_VALUE) { + retval = luaL_error(L, "Error creating read pipe: %d.", GetLastError()); + goto cleanup; + } self->child_pipes[i][1] = CreateFileA(pipeNameBuffer, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (self->child_pipes[i][1] == INVALID_HANDLE_VALUE) { CloseHandle(self->child_pipes[i][0]); - return luaL_error(L, "Error creating write pipe: %d.", GetLastError()); + retval = luaL_error(L, "Error creating write pipe: %d.", GetLastError()); + goto cleanup; } if (!SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 1 : 0], HANDLE_FLAG_INHERIT, 0) || - !SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 0 : 1], HANDLE_FLAG_INHERIT, 1)) - return luaL_error(L, "Error inheriting pipes: %d.", GetLastError()); + !SetHandleInformation(self->child_pipes[i][i == STDIN_FD ? 0 : 1], HANDLE_FLAG_INHERIT, 1)) { + retval = luaL_error(L, "Error inheriting pipes: %d.", GetLastError()); + goto cleanup; + } } } break; } @@ -237,24 +258,25 @@ static int process_start(lua_State* L) { environmentBlock[offset++] = 0; if (env_len > 0) MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, environmentBlock, offset, (LPWSTR)wideEnvironmentBlock, sizeof(wideEnvironmentBlock)); - if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information)) - return luaL_error(L, "Error creating a process: %d.", GetLastError()); + if (!CreateProcess(NULL, commandLine, NULL, NULL, true, (detach ? DETACHED_PROCESS : CREATE_NO_WINDOW) | CREATE_UNICODE_ENVIRONMENT, env_len > 0 ? wideEnvironmentBlock : NULL, cwd, &siStartInfo, &self->process_information)) { + retval = luaL_error(L, "Error creating a process: %d.", GetLastError()); + goto cleanup; + } self->pid = (long)self->process_information.dwProcessId; if (detach) CloseHandle(self->process_information.hProcess); CloseHandle(self->process_information.hThread); #else for (int i = 0; i < 3; ++i) { // Make only the parents fd's non-blocking. Children should block. - if (pipe(self->child_pipes[i]) || fcntl(self->child_pipes[i][i == STDIN_FD ? 1 : 0], F_SETFL, O_NONBLOCK) == -1) - return luaL_error(L, "Error creating pipes: %s", strerror(errno)); + if (pipe(self->child_pipes[i]) || fcntl(self->child_pipes[i][i == STDIN_FD ? 1 : 0], F_SETFL, O_NONBLOCK) == -1) { + retval = luaL_error(L, "Error creating pipes: %s", strerror(errno)); + goto cleanup; + } } self->pid = (long)fork(); if (self->pid < 0) { - for (int i = 0; i < 3; ++i) { - close(self->child_pipes[i][0]); - close(self->child_pipes[i][1]); - } - return luaL_error(L, "Error running fork: %s.", strerror(errno)); + retval = luaL_error(L, "Error running fork: %s.", strerror(errno)); + goto cleanup; } else if (!self->pid) { setpgrp(); for (int stream = 0; stream < 3; ++stream) { @@ -265,23 +287,28 @@ static int process_start(lua_State* L) { dup2(self->child_pipes[new_fds[stream]][new_fds[stream] == STDIN_FD ? 0 : 1], stream); close(self->child_pipes[stream][stream == STDIN_FD ? 1 : 0]); } - int set; + size_t set; for (set = 0; set < env_len && setenv(env_names[set], env_values[set], 1) == 0; ++set); if (set == env_len && (!detach || setsid() != -1) && (!cwd || chdir(cwd) != -1)) - execvp((const char*)cmd[0], (char* const*)cmd); + execvp(cmd[0], (char** const)cmd); const char* msg = strerror(errno); - int result = write(STDERR_FD, msg, strlen(msg)+1); + size_t result = write(STDERR_FD, msg, strlen(msg)+1); _exit(result == strlen(msg)+1 ? -1 : -2); } #endif + cleanup: for (size_t i = 0; i < env_len; ++i) { free((char*)env_names[i]); free((char*)env_values[i]); } - for (int stream = 0; stream < 3; ++stream) - close_fd(&self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]); + for (int stream = 0; stream < 3; ++stream) { + process_handle* pipe = &self->child_pipes[stream][stream == STDIN_FD ? 0 : 1]; + if (*pipe) { + close_fd(pipe); + } + } self->running = true; - return 1; + return retval; } static int g_read(lua_State* L, int stream, unsigned long read_size) { diff --git a/src/api/renderer.c b/src/api/renderer.c index 0fc21707..3ab33b1a 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -1,3 +1,4 @@ +#include #include "api.h" #include "../renderer.h" #include "../rencache.h" @@ -166,14 +167,14 @@ static int f_get_size(lua_State *L) { } -static int f_begin_frame(lua_State *L) { - rencache_begin_frame(L); +static int f_begin_frame(UNUSED lua_State *L) { + rencache_begin_frame(); return 0; } -static int f_end_frame(lua_State *L) { - rencache_end_frame(L); +static int f_end_frame(UNUSED lua_State *L) { + rencache_end_frame(); return 0; } @@ -214,7 +215,7 @@ static int f_draw_text(lua_State *L) { float x = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - x = rencache_draw_text(L, fonts, text, x, y, color); + x = rencache_draw_text(fonts, text, x, y, color); lua_pushnumber(L, x); return 1; } diff --git a/src/api/system.c b/src/api/system.c index f1f9c3c7..45231d0b 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -45,7 +45,7 @@ struct HitTestInfo { }; typedef struct HitTestInfo HitTestInfo; -static HitTestInfo window_hit_info[1] = {{0, 0}}; +static HitTestInfo window_hit_info[1] = {{0, 0, 0}}; #define RESIZE_FROM_TOP 0 #define RESIZE_FROM_RIGHT 0 @@ -705,13 +705,15 @@ static int f_set_window_opacity(lua_State *L) { return 1; } +typedef void (*fptr)(void); + typedef struct lua_function_node { const char *symbol; - void *address; + fptr address; } lua_function_node; -#define P(FUNC) { "lua_" #FUNC, (void*)(lua_##FUNC) } -#define U(FUNC) { "luaL_" #FUNC, (void*)(luaL_##FUNC) } +#define P(FUNC) { "lua_" #FUNC, (fptr)(lua_##FUNC) } +#define U(FUNC) { "luaL_" #FUNC, (fptr)(luaL_##FUNC) } static void* api_require(const char* symbol) { static lua_function_node nodes[] = { P(atpanic), P(checkstack), @@ -749,9 +751,9 @@ static void* api_require(const char* symbol) { #endif }; - for (int i = 0; i < sizeof(nodes) / sizeof(lua_function_node); ++i) { + for (size_t i = 0; i < sizeof(nodes) / sizeof(lua_function_node); ++i) { if (strcmp(nodes[i].symbol, symbol) == 0) - return nodes[i].address; + return *(void**)(&nodes[i].address); } return NULL; } @@ -775,10 +777,12 @@ static int f_load_native_plugin(lua_State *L) { const char *basename = strrchr(name, '.'); basename = !basename ? name : basename + 1; snprintf(entrypoint_name, sizeof(entrypoint_name), "luaopen_lite_xl_%s", basename); - int (*ext_entrypoint) (lua_State *L, void*) = SDL_LoadFunction(library, entrypoint_name); + int (*ext_entrypoint) (lua_State *L, void* (*)(const char*)); + *(void**)(&ext_entrypoint) = SDL_LoadFunction(library, entrypoint_name); if (!ext_entrypoint) { snprintf(entrypoint_name, sizeof(entrypoint_name), "luaopen_%s", basename); - int (*entrypoint)(lua_State *L) = SDL_LoadFunction(library, entrypoint_name); + int (*entrypoint)(lua_State *L); + *(void**)(&entrypoint) = SDL_LoadFunction(library, entrypoint_name); if (!entrypoint) return luaL_error(L, "Unable to load %s: Can't find %s(lua_State *L, void *XL)", name, entrypoint_name); result = entrypoint(L); diff --git a/src/rencache.c b/src/rencache.c index 5686d984..c847ce34 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -3,6 +3,7 @@ #include #include #include +#include #include #include "rencache.h" @@ -28,7 +29,7 @@ typedef struct { RenColor color; RenFont *fonts[FONT_FALLBACK_MAX]; float text_x; - char text[0]; + char text[]; } Command; static unsigned cells_buf1[CELLS_X * CELLS_Y]; @@ -134,7 +135,7 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } -float rencache_draw_text(lua_State *L, RenFont **fonts, const char *text, float x, int y, RenColor color) +float rencache_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor color) { float width = ren_font_group_get_width(fonts, text); RenRect rect = { x, y, (int)width, ren_font_group_get_height(fonts) }; @@ -159,7 +160,7 @@ void rencache_invalidate(void) { } -void rencache_begin_frame(lua_State *L) { +void rencache_begin_frame() { /* reset all cells if the screen width/height has changed */ int w, h; ren_get_size(&w, &h); @@ -200,7 +201,7 @@ static void push_rect(RenRect r, int *count) { } -void rencache_end_frame(lua_State *L) { +void rencache_end_frame() { /* update cells from commands */ Command *cmd = NULL; RenRect cr = screen_rect; diff --git a/src/rencache.h b/src/rencache.h index 75bb5051..fedbada6 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -8,10 +8,9 @@ void rencache_show_debug(bool enable); void rencache_set_clip_rect(RenRect rect); void rencache_draw_rect(RenRect rect, RenColor color); -float rencache_draw_text(lua_State *L, RenFont **font, - const char *text, float x, int y, RenColor color); +float rencache_draw_text(RenFont **font, const char *text, float x, int y, RenColor color); void rencache_invalidate(void); -void rencache_begin_frame(lua_State *L); -void rencache_end_frame(lua_State *L); +void rencache_begin_frame(); +void rencache_end_frame(); #endif diff --git a/src/renderer.c b/src/renderer.c index c2fa233e..f5f08045 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -18,6 +18,9 @@ static RenWindow window_renderer = {0}; static FT_Library library; +// draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending +static SDL_Surface *draw_rect_surface; + static void* check_alloc(void *ptr) { if (!ptr) { fprintf(stderr, "Fatal error: memory allocation failed\n"); @@ -43,27 +46,28 @@ typedef struct RenFont { FT_Face face; GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS]; float size, space_advance, tab_advance; - short max_height, baseline, height; + unsigned short max_height, baseline, height; ERenFontAntialiasing antialiasing; ERenFontHinting hinting; unsigned char style; - char path[0]; + char path[1]; } RenFont; static const char* utf8_to_codepoint(const char *p, unsigned *dst) { + const unsigned char *up = (unsigned char*)p; unsigned res, n; switch (*p & 0xf0) { - case 0xf0 : res = *p & 0x07; n = 3; break; - case 0xe0 : res = *p & 0x0f; n = 2; break; + case 0xf0 : res = *up & 0x07; n = 3; break; + case 0xe0 : res = *up & 0x0f; n = 2; break; case 0xd0 : - case 0xc0 : res = *p & 0x1f; n = 1; break; - default : res = *p; n = 0; break; + case 0xc0 : res = *up & 0x1f; n = 1; break; + default : res = *up; n = 0; break; } while (n--) { - res = (res << 6) | (*(++p) & 0x3f); + res = (res << 6) | (*(++up) & 0x3f); } *dst = res; - return p + 1; + return (const char*)up + 1; } static int font_set_load_options(RenFont* font) { @@ -136,7 +140,7 @@ static void font_load_glyphset(RenFont* font, int idx) { if (pen_x == 0) continue; set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 24 : 8, 0, 0, 0, 0)); - unsigned char* pixels = set->surface->pixels; + uint8_t* pixels = set->surface->pixels; for (int i = 0; i < MAX_GLYPHSET; ++i) { int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET); if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option)) @@ -145,11 +149,11 @@ static void font_load_glyphset(RenFont* font, int idx) { font_set_style(&slot->outline, (64 / bitmaps_cached) * j, font->style); if (FT_Render_Glyph(slot, render_option)) continue; - for (int line = 0; line < slot->bitmap.rows; ++line) { + for (unsigned int line = 0; line < slot->bitmap.rows; ++line) { int target_offset = set->surface->pitch * line + set->metrics[i].x0 * byte_width; int source_offset = line * slot->bitmap.pitch; if (font->antialiasing == FONT_ANTIALIASING_NONE) { - for (int column = 0; column < slot->bitmap.width; ++column) { + for (unsigned int column = 0; column < slot->bitmap.width; ++column) { int current_source_offset = source_offset + (column / 8); int source_pixel = slot->bitmap.buffer[current_source_offset]; pixels[++target_offset] = ((source_pixel >> (7 - (column % 8))) & 0x1) << 7; @@ -169,6 +173,9 @@ static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int su } static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFont** fonts, unsigned int codepoint, int bitmap_index) { + if (!metric) { + return NULL; + } if (bitmap_index < 0) bitmap_index += SUBPIXEL_BITMAPS_CACHED; for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { @@ -177,7 +184,7 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo if ((*metric)->loaded || codepoint < 0xFF) return fonts[i]; } - if (!(*metric)->loaded && codepoint > 0xFF && codepoint != 0x25A1) + if (*metric && !(*metric)->loaded && codepoint > 0xFF && codepoint != 0x25A1) return font_group_get_glyph(set, metric, fonts, 0x25A1, bitmap_index); return fonts[0]; } @@ -195,7 +202,7 @@ RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antial font->face = face; font->size = size; font->height = (short)((face->height / (float)face->units_per_EM) * font->size); - font->baseline = (short)((face->bbox.yMax / (float)face->units_per_EM) * font->size); + font->baseline = (short)((face->ascender / (float)face->units_per_EM) * font->size); font->antialiasing = antialiasing; font->hinting = hinting; font->style = style; @@ -233,7 +240,11 @@ void ren_font_group_set_tab_size(RenFont **fonts, int n) { } int ren_font_group_get_tab_size(RenFont **fonts) { - return font_get_glyphset(fonts[0], '\t', 0)->metrics['\t'].xadvance / fonts[0]->space_advance; + int advance = font_get_glyphset(fonts[0], '\t', 0)->metrics['\t'].xadvance; + if (fonts[0]->space_advance) { + advance /= fonts[0]->space_advance; + } + return advance; } float ren_font_group_get_size(RenFont **fonts) { @@ -251,6 +262,8 @@ float ren_font_group_get_width(RenFont **fonts, const char *text) { unsigned int codepoint; text = utf8_to_codepoint(text, &codepoint); RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, 0); + if (!metric) + break; width += (!font || metric->xadvance) ? metric->xadvance : fonts[0]->space_advance; } const int surface_scale = renwin_surface_scale(&window_renderer); @@ -266,7 +279,7 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor y *= surface_scale; int bytes_per_pixel = surface->format->BytesPerPixel; const char* end = text + strlen(text); - unsigned char* destination_pixels = surface->pixels; + uint8_t* destination_pixels = surface->pixels; int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height; while (text < end) { @@ -274,13 +287,15 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor text = utf8_to_codepoint(text, &codepoint); GlyphSet* set = NULL; GlyphMetric* metric = NULL; RenFont* font = font_group_get_glyph(&set, &metric, fonts, codepoint, (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED)); + if (!metric) + break; int start_x = floor(pen_x) + metric->bitmap_left; int end_x = (metric->x1 - metric->x0) + start_x; int glyph_end = metric->x1, glyph_start = metric->x0; if (!metric->loaded && codepoint > 0xFF) ren_draw_rect((RenRect){ start_x + 1, y, font->space_advance - 1, ren_font_group_get_height(fonts) }, color); if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { - unsigned char* source_pixels = set->surface->pixels; + uint8_t* source_pixels = set->surface->pixels; for (int line = metric->y0; line < metric->y1; ++line) { int target_y = line + y - metric->bitmap_top + font->baseline * surface_scale; if (target_y < clip.y) @@ -294,15 +309,30 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor start_x += offset; glyph_start += offset; } - unsigned int* destination_pixel = (unsigned int*)&destination_pixels[surface->pitch * target_y + start_x * bytes_per_pixel]; - unsigned char* source_pixel = &source_pixels[line * set->surface->pitch + glyph_start * (font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 3 : 1)]; + uint32_t* destination_pixel = (uint32_t*)&(destination_pixels[surface->pitch * target_y + start_x * bytes_per_pixel]); + uint8_t* source_pixel = &source_pixels[line * set->surface->pitch + glyph_start * (font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 3 : 1)]; for (int x = glyph_start; x < glyph_end; ++x) { - unsigned int destination_color = *destination_pixel; + uint32_t destination_color = *destination_pixel; + // the standard way of doing this would be SDL_GetRGBA, but that introduces a performance regression. needs to be investigated SDL_Color dst = { (destination_color & surface->format->Rmask) >> surface->format->Rshift, (destination_color & surface->format->Gmask) >> surface->format->Gshift, (destination_color & surface->format->Bmask) >> surface->format->Bshift, (destination_color & surface->format->Amask) >> surface->format->Ashift }; - SDL_Color src = { *(font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? source_pixel++ : source_pixel), *(font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? source_pixel++ : source_pixel), *source_pixel++ }; + SDL_Color src; + + if (font->antialiasing == FONT_ANTIALIASING_SUBPIXEL) { + src.r = *(source_pixel++); + src.g = *(source_pixel++); + } + else { + src.r = *(source_pixel); + src.g = *(source_pixel); + } + + src.b = *(source_pixel++); + src.a = 0xFF; + r = (color.r * src.r * color.a + dst.r * (65025 - src.r * color.a) + 32767) / 65025; g = (color.g * src.g * color.a + dst.g * (65025 - src.g * color.a) + 32767) / 65025; b = (color.b * src.b * color.a + dst.b * (65025 - src.b * color.a) + 32767) / 65025; + // the standard way of doing this would be SDL_GetRGBA, but that introduces a performance regression. needs to be investigated *destination_pixel++ = dst.a << surface->format->Ashift | r << surface->format->Rshift | g << surface->format->Gshift | b << surface->format->Bshift; } } @@ -343,31 +373,21 @@ void ren_draw_rect(RenRect rect, RenColor color) { y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2; SDL_Surface *surface = renwin_get_surface(&window_renderer); - uint32_t *d = surface->pixels; - d += x1 + y1 * surface->w; - int dr = surface->w - (x2 - x1); + SDL_Rect dest_rect = { x1, y1, x2 - x1, y2 - y1 }; if (color.a == 0xff) { uint32_t translated = SDL_MapRGB(surface->format, color.r, color.g, color.b); - SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 }; - SDL_FillRect(surface, &rect, translated); + SDL_FillRect(surface, &dest_rect, translated); } else { - RenColor current_color; - RenColor blended_color; - for (int j = y1; j < y2; j++) { - for (int i = x1; i < x2; i++, d++) - { - SDL_GetRGB(*d, surface->format, ¤t_color.r, ¤t_color.g, ¤t_color.b); - blended_color = blend_pixel(current_color, color); - *d = SDL_MapRGB(surface->format, blended_color.r, blended_color.g, blended_color.b); - } - d += dr; - } + uint32_t *pixel = (uint32_t *)draw_rect_surface->pixels; + *pixel = SDL_MapRGBA(draw_rect_surface->format, color.r, color.g, color.b, color.a); + SDL_BlitScaled(draw_rect_surface, NULL, surface, &dest_rect); } } /*************** Window Management ****************/ void ren_free_window_resources() { renwin_free(&window_renderer); + SDL_FreeSurface(draw_rect_surface); } void ren_init(SDL_Window *win) { @@ -380,6 +400,8 @@ void ren_init(SDL_Window *win) { window_renderer.window = win; renwin_init_surface(&window_renderer); renwin_clip_to_surface(&window_renderer); + draw_rect_surface = SDL_CreateRGBSurface(0, 1, 1, 32, + 0xFF000000, 0x00FF0000, 0x0000FF00, 0x000000FF); } diff --git a/src/renderer.h b/src/renderer.h index a97706ff..89c70d5e 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -5,6 +5,12 @@ #include #include +#ifdef __GNUC__ +#define UNUSED __attribute__((__unused__)) +#else +#define UNUSED +#endif + #define FONT_FALLBACK_MAX 4 typedef struct RenFont RenFont; typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting; diff --git a/src/renwindow.c b/src/renwindow.c index f67002f8..c2aa0096 100644 --- a/src/renwindow.c +++ b/src/renwindow.c @@ -27,7 +27,7 @@ static void setup_renderer(RenWindow *ren, int w, int h) { #endif -void renwin_init_surface(RenWindow *ren) { +void renwin_init_surface(UNUSED RenWindow *ren) { #ifdef LITE_USE_SDL_RENDERER if (ren->surface) { SDL_FreeSurface(ren->surface); @@ -39,7 +39,7 @@ void renwin_init_surface(RenWindow *ren) { #endif } -int renwin_surface_scale(RenWindow *ren) { +int renwin_surface_scale(UNUSED RenWindow *ren) { #ifdef LITE_USE_SDL_RENDERER return ren->surface_scale; #else @@ -72,7 +72,7 @@ SDL_Surface *renwin_get_surface(RenWindow *ren) { #endif } -void renwin_resize_surface(RenWindow *ren) { +void renwin_resize_surface(UNUSED RenWindow *ren) { #ifdef LITE_USE_SDL_RENDERER int new_w, new_h; SDL_GL_GetDrawableSize(ren->window, &new_w, &new_h); diff --git a/subprojects/sdl2.wrap b/subprojects/sdl2.wrap index 9ef9e1d3..b1affeb5 100644 --- a/subprojects/sdl2.wrap +++ b/subprojects/sdl2.wrap @@ -1,11 +1,11 @@ [wrap-file] -directory = SDL2-2.0.18 -source_url = https://www.libsdl.org/release/SDL2-2.0.18.tar.gz -source_filename = SDL2-2.0.18.tar.gz -source_hash = 94d40cd73dbfa10bb6eadfbc28f355992bb2d6ef6761ad9d4074eff95ee5711c -patch_filename = sdl2_2.0.18-2_patch.zip -patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.0.18-2/get_patch -patch_hash = cd77f33395d3d019bb89217b9da41fc640ed8c78cbbbebc5c662155a25e2820e +directory = SDL2-2.0.20 +source_url = https://libsdl.org/release/SDL2-2.0.20.tar.gz +source_filename = SDL2-2.0.20.tar.gz +source_hash = c56aba1d7b5b0e7e999e4a7698c70b63a3394ff9704b5f6e1c57e0c16f04dd06 +patch_filename = sdl2_2.0.20-3_patch.zip +patch_url = https://wrapdb.mesonbuild.com/v2/sdl2_2.0.20-3/get_patch +patch_hash = ade644ba46cefa4f1f9e57aa23bacc5dabf762d1f90d8416a1e1e4b0b7a188c4 [provide] sdl2 = sdl2_dep