From a920e5b0e652196fbcf236a78959a59ab287c3ee Mon Sep 17 00:00:00 2001 From: Guldoman Date: Tue, 31 Aug 2021 23:16:02 +0200 Subject: [PATCH 001/165] Avoid setting a new file as dirty if it is empty --- data/core/doc/init.lua | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 8b6ef5e0..73f15b79 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -117,7 +117,11 @@ end function Doc:is_dirty() - return self.clean_change_id ~= self:get_change_id() or self.new_file + if self.new_file then + return #self.lines > 1 or #self.lines[1] > 1 + else + return self.clean_change_id ~= self:get_change_id() + end end From 3cc4cd1adab62411f5666f71c1df13d45c4c1557 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 05:09:26 -0700 Subject: [PATCH 002/165] Fix error when opening root directory --- data/core/common.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/core/common.lua b/data/core/common.lua index 3093a36d..9f3102bb 100644 --- a/data/core/common.lua +++ b/data/core/common.lua @@ -276,6 +276,7 @@ end function common.normalize_path(filename) + if not filename then return end if PATHSEP == '\\' then filename = filename:gsub('[/\\]', '\\') local drive, rem = filename:match('^([a-zA-Z])(:.*)') @@ -290,7 +291,8 @@ function common.normalize_path(filename) table.insert(accu, part) end end - return table.concat(accu, PATHSEP) + local npath = table.concat(accu, PATHSEP) + return npath == "" and PATHSEP or npath end From 368ffca40ab2746f7eb4896bbfa63a428e8cce1f Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 05:10:00 -0700 Subject: [PATCH 003/165] Fix macOS minimum system version in info.plist --- resources/macos/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/macos/Info.plist b/resources/macos/Info.plist index 70d49a02..d465479c 100644 --- a/resources/macos/Info.plist +++ b/resources/macos/Info.plist @@ -14,7 +14,7 @@ APPL NSHighResolutionCapable - MinimumOSVersion10.13 + LSMinimumSystemVersion10.11 NSDocumentsFolderUsageDescriptionTo access, edit and index your projects. NSDesktopFolderUsageDescriptionTo access, edit and index your projects. NSDownloadsFolderUsageDescriptionTo access, edit and index your projects. From 28e8a98ffc27d0f0c7342e5250ea19ba02d575ee Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 05:21:45 -0700 Subject: [PATCH 004/165] Fix error in change-project-folder command --- data/core/commands/core.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/core/commands/core.lua b/data/core/commands/core.lua index 7ac36976..215ff654 100644 --- a/data/core/commands/core.lua +++ b/data/core/commands/core.lua @@ -148,7 +148,9 @@ command.add(nil, { ["core:change-project-folder"] = function() local dirname = common.dirname(core.project_dir) - core.command_view:set_text(common.home_encode(dirname) .. PATHSEP) + if dirname then + core.command_view:set_text(common.home_encode(dirname) .. PATHSEP) + end core.command_view:enter("Change Project Folder", function(text, item) text = system.absolute_path(common.home_expand(item and item.text or text)) if text == core.project_dir then return end From afaf0a718d634fb786285370fa4b2f005f17c9f6 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Sun, 22 Aug 2021 19:29:21 +0800 Subject: [PATCH 005/165] improve logview The logview is now less cluttered. The filename and stack trace (if any) is hidden by default. The user can click on the log entry to expand it. --- data/core/logview.lua | 146 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 131 insertions(+), 15 deletions(-) diff --git a/data/core/logview.lua b/data/core/logview.lua index b731df3c..b81e8efe 100644 --- a/data/core/logview.lua +++ b/data/core/logview.lua @@ -1,8 +1,37 @@ local core = require "core" +local common = require "core.common" local style = require "core.style" local View = require "core.view" +local function lines(text) + if text == "" then return 0 end + local l = 1 + for _ in string.gmatch(text, "\n") do + l = l + 1 + end + return l +end + + +local item_height_result = {} + + +local function get_item_height(item) + local h = item_height_result[item] + if not h then + h = {} + local l = 1 + lines(item.text) + lines(item.info or "") + h.normal = style.font:get_height() + style.padding.y + h.expanded = l * style.font:get_height() + style.padding.y + h.current = h.normal + h.target = h.current + item_height_result[item] = h + end + return h +end + + local LogView = View:extend() LogView.context = "session" @@ -10,6 +39,7 @@ LogView.context = "session" function LogView:new() LogView.super.new(self) self.last_item = core.log_items[#core.log_items] + self.expanding = {} self.scrollable = true self.yoffset = 0 end @@ -20,6 +50,55 @@ function LogView:get_name() end +local function is_expanded(item) + local item_height = get_item_height(item) + return item_height.target == item_height.expanded +end + + +function LogView:expand_item(item) + item = get_item_height(item) + item.target = item.target == item.expanded and item.normal or item.expanded + table.insert(self.expanding, item) +end + + +function LogView:each_item() + local x, y = self:get_content_offset() + y = y + style.padding.y + self.yoffset + return coroutine.wrap(function() + for i = #core.log_items, 1, -1 do + local item = core.log_items[i] + local h = get_item_height(item).current + coroutine.yield(i, item, x, y, self.size.x, h) + y = y + h + end + end) +end + + +function LogView:on_mouse_moved(px, py, ...) + LogView.super.on_mouse_moved(self, px, py, ...) + local hovered = false + for _, item, x, y, w, h in self:each_item() do + if px >= x and py >= y and px < x + w and py < y + h then + hovered = true + self.hovered_item = item + break + end + end + if not hovered then self.hovered_item = nil end +end + + +function LogView:on_mouse_pressed(button, mx, my, clicks) + if LogView.super.on_mouse_pressed(self, button, mx, my, clicks) then return end + if self.hovered_item then + self:expand_item(self.hovered_item) + end +end + + function LogView:update() local item = core.log_items[#core.log_items] if self.last_item ~= item then @@ -28,6 +107,14 @@ function LogView:update() self.yoffset = -(style.font:get_height() + style.padding.y) end + local expanding = self.expanding[1] + if expanding then + self:move_towards(expanding, "current", expanding.target) + if expanding.current == expanding.target then + table.remove(self.expanding, 1) + end + end + self:move_towards("yoffset", 0) LogView.super.update(self) @@ -46,28 +133,57 @@ local function draw_text_multiline(font, text, x, y, color) end +local function draw_text_elipsis(font, color, text, x, y, w, h, elipsis_style) + elipsis_style = elipsis_style or "end" + local c = font:get_width("_") + local approxc = math.floor(w / c) + if #text > approxc then + if elipsis_style == "end" then + text = string.sub(text, 1, approxc - 3) .. "..." + elseif elipsis_style == "middle" then + local mid = math.floor(#text / 2) + text = string.sub(text, 1, mid - 3) .. "..." .. string.sub(text, mid) + end + end + return common.draw_text(font, color, text, "left", x, y, w, h) +end + + function LogView:draw() self:draw_background(style.background) - local ox, oy = self:get_content_offset() local th = style.font:get_height() - local y = oy + style.padding.y + self.yoffset - - for i = #core.log_items, 1, -1 do - local x = ox + style.padding.x - local item = core.log_items[i] - local time = os.date(nil, item.time) - x = renderer.draw_text(style.font, time, x, y, style.dim) + local lh = th + style.padding.y -- for one line + for _, item, x, y, w, h in self:each_item() do + core.push_clip_rect(x, y, w, h) x = x + style.padding.x - local subx = x - x, y = draw_text_multiline(style.font, item.text, x, y, style.text) - renderer.draw_text(style.font, " at " .. item.at, x, y, style.dim) - y = y + th - if item.info then - subx, y = draw_text_multiline(style.font, item.info, subx, y, style.dim) + + local time = os.date(nil, item.time) + x = common.draw_text(style.font, style.dim, time, "left", x, y, w, lh) + x = x + style.padding.x + + x = common.draw_text(style.code_font, style.dim, is_expanded(item) and "-" or "+", "left", x, y, w, lh) + x = x + style.padding.x + w = w - (x - self:get_content_offset()) + + if is_expanded(item) then + y = y + common.round(style.padding.y / 2) + draw_text_multiline(style.font, item.text, x, y, style.text) + y = y + th + + local at = "at " .. common.home_encode(item.at) + draw_text_elipsis(style.font, style.dim, at, x, y, w, lh, "middle") + y = y + th + + if item.info then + draw_text_multiline(style.font, item.info, x, y, style.dim) + end + else + draw_text_elipsis(style.font, style.text, item.text, x, y, w, lh) y = y + th end - y = y + style.padding.y + + core.pop_clip_rect() end end From 622b162225689424740a92c6390fff494eabd08c Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Sun, 22 Aug 2021 19:32:45 +0800 Subject: [PATCH 006/165] add core.get_log() --- data/core/init.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/data/core/init.lua b/data/core/init.lua index f6034a06..702e31fd 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -881,6 +881,23 @@ function core.error(...) end +function core.get_log(i) + if i == nil then + local r = {} + for _, item in ipairs(core.log_items) do + table.insert(r, core.get_log(item)) + end + return table.concat(r, "\n") + end + local item = type(i) == "number" and core.log_items[i] or i + local text = string.format("[%s] %s at %s", os.date(nil, item.time), item.text, item.at) + if item.info then + text = string.format("%s\n%s\n", text, item.info) + end + return text +end + + function core.try(fn, ...) local err local ok, res = xpcall(fn, function(msg) From e25ea1196a539f56b88b84bc3fd442001251bb65 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Sun, 22 Aug 2021 19:33:28 +0800 Subject: [PATCH 007/165] add context menu options for logview --- data/plugins/contextmenu.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/data/plugins/contextmenu.lua b/data/plugins/contextmenu.lua index b84d114d..dc95567f 100644 --- a/data/plugins/contextmenu.lua +++ b/data/plugins/contextmenu.lua @@ -42,6 +42,24 @@ keymap.add { ["menu"] = "context:show" } +local function copy_log() + local item = core.active_view.hovered_item + if item then + system.set_clipboard(core.get_log(item)) + end +end + +local function open_as_doc() + local doc = core.open_doc("logs.txt") + core.root_view:open_doc(doc) + doc:insert(1, 1, core.get_log()) +end + +menu:register("core.logview", { + { text = "Copy entry", command = copy_log }, + { text = "Open as file", command = open_as_doc } +}) + if require("plugins.scale") then menu:register("core.docview", { { text = "Font +", command = "scale:increase" }, From 14565b5226523614719f5a6e77fbf0c1e6ad7e23 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Tue, 24 Aug 2021 11:32:20 +0800 Subject: [PATCH 008/165] more changes to logview - remove draw_text_elipsis - remove clip rect operations - fix text drawing when expanded - simplify code --- data/core/logview.lua | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/data/core/logview.lua b/data/core/logview.lua index b81e8efe..f79ff3e3 100644 --- a/data/core/logview.lua +++ b/data/core/logview.lua @@ -123,29 +123,12 @@ end local function draw_text_multiline(font, text, x, y, color) local th = font:get_height() - local resx, resy = x, y + local resx = x for line in text:gmatch("[^\n]+") do - resy = y resx = renderer.draw_text(style.font, line, x, y, color) y = y + th end - return resx, resy -end - - -local function draw_text_elipsis(font, color, text, x, y, w, h, elipsis_style) - elipsis_style = elipsis_style or "end" - local c = font:get_width("_") - local approxc = math.floor(w / c) - if #text > approxc then - if elipsis_style == "end" then - text = string.sub(text, 1, approxc - 3) .. "..." - elseif elipsis_style == "middle" then - local mid = math.floor(#text / 2) - text = string.sub(text, 1, mid - 3) .. "..." .. string.sub(text, mid) - end - end - return common.draw_text(font, color, text, "left", x, y, w, h) + return resx, y end @@ -155,7 +138,6 @@ function LogView:draw() local th = style.font:get_height() local lh = th + style.padding.y -- for one line for _, item, x, y, w, h in self:each_item() do - core.push_clip_rect(x, y, w, h) x = x + style.padding.x local time = os.date(nil, item.time) @@ -168,22 +150,21 @@ function LogView:draw() if is_expanded(item) then y = y + common.round(style.padding.y / 2) - draw_text_multiline(style.font, item.text, x, y, style.text) - y = y + th + _, y = draw_text_multiline(style.font, item.text, x, y, style.text) local at = "at " .. common.home_encode(item.at) - draw_text_elipsis(style.font, style.dim, at, x, y, w, lh, "middle") - y = y + th + _, y = common.draw_text(style.font, style.dim, at, "left", x, y, w, lh) if item.info then - draw_text_multiline(style.font, item.info, x, y, style.dim) + _, y = draw_text_multiline(style.font, item.info, x, y, style.dim) end else - draw_text_elipsis(style.font, style.text, item.text, x, y, w, lh) - y = y + th + local line, has_newline = string.match(item.text, "([^\n]+)(\n?)") + if has_newline ~= "" then + line = line .. " ..." + end + _, y = common.draw_text(style.font, style.text, line, "left", x, y, w, lh) end - - core.pop_clip_rect() end end From 30d3751632cfa237e81d5f7b4c1ddffe2d630f84 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Tue, 24 Aug 2021 12:02:22 +0800 Subject: [PATCH 009/165] remove unused variable --- data/core/logview.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/logview.lua b/data/core/logview.lua index f79ff3e3..1ea0e43e 100644 --- a/data/core/logview.lua +++ b/data/core/logview.lua @@ -137,7 +137,7 @@ function LogView:draw() local th = style.font:get_height() local lh = th + style.padding.y -- for one line - for _, item, x, y, w, h in self:each_item() do + for _, item, x, y, w in self:each_item() do x = x + style.padding.x local time = os.date(nil, item.time) From 97493a1a4eca756e81404bc053de90a4591fb4d9 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Mon, 2 Aug 2021 11:37:00 +0800 Subject: [PATCH 010/165] add doc:get_selection_text() --- data/core/doc/init.lua | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index d05d9d45..ae948479 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -124,9 +124,21 @@ function Doc:get_selection(sort) return line1, col1, line2, col2, sort end +function Doc:get_selection_text(limit) + limit = limit or math.huge + local result = {} + for idx, line1, col1, line2, col2 in self:get_selections() do + if idx > limit then break end + if line1 ~= line2 or col1 ~= col2 then + local text = self:get_text(line1, col1, line2, col2) + if text ~= "" then result[#result + 1] = text end + end + end + return table.concat(result, "\n") +end + function Doc:has_selection() - local line1, col1, line2, col2 = self:get_selection(false) - return line1 ~= line2 or col1 ~= col2 + local line1, col1, line2, col2 = self:get_selection(false) return line1 ~= line2 or col1 ~= col2 end function Doc:sanitize_selection() From fb907c9bf4ffbcb84f9b7b1b1e010cf66ac88fd8 Mon Sep 17 00:00:00 2001 From: Takase <20792268+takase1121@users.noreply.github.com> Date: Tue, 17 Aug 2021 20:16:56 +0800 Subject: [PATCH 011/165] increase code readibility --- data/core/doc/init.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index ae948479..4a231295 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -138,7 +138,8 @@ function Doc:get_selection_text(limit) end function Doc:has_selection() - local line1, col1, line2, col2 = self:get_selection(false) return line1 ~= line2 or col1 ~= col2 + local line1, col1, line2, col2 = self:get_selection(false) + return line1 ~= line2 or col1 ~= col2 end function Doc:sanitize_selection() From fc4c7a29eee4cb0c775454ce700f32f49fd936f4 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Mon, 23 Aug 2021 19:10:13 +0200 Subject: [PATCH 012/165] use dependency fallbacks, use system reproc if available --- lib/font_renderer/meson.build | 6 +----- meson.build | 13 ++----------- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/lib/font_renderer/meson.build b/lib/font_renderer/meson.build index 7724d584..d596e152 100644 --- a/lib/font_renderer/meson.build +++ b/lib/font_renderer/meson.build @@ -1,10 +1,6 @@ freetype_dep = dependency('freetype2') -libagg_dep = dependency('libagg', required: false) -if not libagg_dep.found() - libagg_subproject = subproject('libagg') - libagg_dep = libagg_subproject.get_variable('libagg_dep') -endif +libagg_dep = dependency('libagg', fallback: ['libagg', 'libagg_dep']) font_renderer_sources = [ 'agg_font_freetype.cpp', diff --git a/meson.build b/meson.build index 5dee12ec..20067d43 100644 --- a/meson.build +++ b/meson.build @@ -51,24 +51,15 @@ endif #=============================================================================== libm = cc.find_library('m', required : false) libdl = cc.find_library('dl', required : false) -lua_dep = dependency('lua5.2', required : false) +lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep']) pcre2_dep = dependency('libpcre2-8') sdl_dep = dependency('sdl2', method: 'config-tool') - -if not lua_dep.found() - lua_subproject = subproject('lua', - default_options: ['shared=false', 'use_readline=false', 'app=false'] - ) - lua_dep = lua_subproject.get_variable('lua_dep') -endif - -reproc_subproject = subproject('reproc', +reproc_dep = dependency('reproc', fallback: ['reproc', 'reproc_dep'], default_options: [ 'default_library=static', 'multithreaded=false', 'reproc-cpp=false', 'examples=false' ] ) -reproc_dep = reproc_subproject.get_variable('reproc_dep') lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl] From e23b6176f4921a9f086cb94ee81b79a73ed414f2 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 24 Aug 2021 19:04:48 +0200 Subject: [PATCH 013/165] Fix lua subproject options removed by error --- meson.build | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 20067d43..0c5bc865 100644 --- a/meson.build +++ b/meson.build @@ -51,7 +51,9 @@ endif #=============================================================================== libm = cc.find_library('m', required : false) libdl = cc.find_library('dl', required : false) -lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep']) +lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep'], + default_options: ['shared=false', 'use_readline=false', 'app=false'] +) pcre2_dep = dependency('libpcre2-8') sdl_dep = dependency('sdl2', method: 'config-tool') reproc_dep = dependency('reproc', fallback: ['reproc', 'reproc_dep'], From 7f338fc993b2edc940268e884e638e5d2072ace8 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 27 Aug 2021 19:03:41 +0200 Subject: [PATCH 014/165] Allow tabs to always be visible --- data/core/config.lua | 1 + data/core/rootview.lua | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/data/core/config.lua b/data/core/config.lua index 401ac0af..caecdfcd 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -12,6 +12,7 @@ config.non_word_chars = " \t\n/\\()\"':,.;<>~!@#$%^&*|+=[]{}`?-" config.undo_merge_timeout = 0.3 config.max_undos = 10000 config.max_tabs = 10 +config.always_show_tabs = false config.highlight_current_line = true config.line_height = 1.2 config.indent_size = 2 diff --git a/data/core/rootview.lua b/data/core/rootview.lua index f2499e68..9d017268 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -261,7 +261,7 @@ end function Node:get_tab_overlapping_point(px, py) - if #self.views == 1 then return nil end + if not self:should_show_tabs() then return nil end local tabs_number = self:get_visible_tabs_number() local x1, y1, w, h = self:get_tab_rect(self.tab_offset) local x2, y2 = self:get_tab_rect(self.tab_offset + tabs_number) @@ -271,6 +271,16 @@ function Node:get_tab_overlapping_point(px, py) end +function Node:should_show_tabs() + if self.locked then return false end + if #self.views > 1 then return true + elseif config.always_show_tabs then + return not self.views[1]:is(EmptyView) + end + return false +end + + local function close_button_location(x, w) local cw = style.icon_font:get_width("C") local pad = style.padding.y @@ -414,7 +424,7 @@ end function Node:update_layout() if self.type == "leaf" then local av = self.active_view - if #self.views > 1 then + if self:should_show_tabs() then local _, _, _, th = self:get_tab_rect(1) av.position.x, av.position.y = self.position.x, self.position.y + th av.size.x, av.size.y = self.size.x, self.size.y - th @@ -569,7 +579,7 @@ end function Node:draw() if self.type == "leaf" then - if #self.views > 1 then + if self:should_show_tabs() then self:draw_tabs() end local pos, size = self.active_view.position, self.active_view.size From d3bd35b577b3d65f5a0704566219e4cecf1c9cc3 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sun, 29 Aug 2021 02:23:47 +0200 Subject: [PATCH 015/165] Use plain `string:find` when matching plugin directories --- data/core/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/init.lua b/data/core/init.lua index 702e31fd..5b1cca26 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -692,7 +692,7 @@ function core.load_plugins() if is_lua_file then if not version_match then core.log_quiet("Version mismatch for plugin %q from %s", basename, plugin_dir) - local list = refused_list[plugin_dir:find(USERDIR) == 1 and 'userdir' or 'datadir'].plugins + local list = refused_list[plugin_dir:find(USERDIR, 1, true) == 1 and 'userdir' or 'datadir'].plugins table.insert(list, filename) end if version_match and config.plugins[basename] ~= false then From e93bbc559c5dda25a2aab870a49b76f64124e681 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sun, 29 Aug 2021 04:02:53 +0200 Subject: [PATCH 016/165] Fix crash in project search when project has no files --- data/plugins/projectsearch.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/plugins/projectsearch.lua b/data/plugins/projectsearch.lua index 45967697..dda3a2d0 100644 --- a/data/plugins/projectsearch.lua +++ b/data/plugins/projectsearch.lua @@ -171,7 +171,7 @@ function ResultsView:draw() local ox, oy = self:get_content_offset() local x, y = ox + style.padding.x, oy + style.padding.y local files_number = core.project_files_number() - local per = files_number and self.last_file_idx / files_number or 1 + local per = common.clamp(files_number and self.last_file_idx / files_number or 1, 0, 1) local text if self.searching then if files_number then From ea795aa411ef814c5db3fb0d70619184f0e520cc Mon Sep 17 00:00:00 2001 From: boppyt <71049646+boppyt@users.noreply.github.com> Date: Sat, 28 Aug 2021 22:37:02 -0700 Subject: [PATCH 017/165] build script: add .git fallback --- build-packages.sh | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/build-packages.sh b/build-packages.sh index f30b1112..039e56f8 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -10,7 +10,16 @@ copy_directory_from_repo () { fi local dirname="$1" local destdir="$2" - git archive "$use_branch" "$dirname" --format=tar | tar xf - -C "$destdir" "${tar_options[@]}" + _archive "$use_branch" "$dirname" "$destdir" "${tar_options[@]}" +} + +_archive () { + if [[ -d ".git" ]]; then + git archive "$1" "$2" --format=tar | tar xf - -C "$3" "$4" + else + echo ".git not found, falling back to UNIX operation" + cp -r "$2" "$3" + fi } # Check if build directory is ok to be used to build. @@ -20,10 +29,14 @@ build_dir_is_usable () { echo "invalid build directory, no path allowed: \"$build\"" return 1 fi - git ls-files --error-unmatch "$build" &> /dev/null - if [ $? == 0 ]; then - echo "invalid path, \"$build\" is under revision control" - return 1 + if [[ -d ".git" ]]; then + git ls-files --error-unmatch "$build" &> /dev/null + if [ $? == 0 ]; then + echo "invalid path, \"$build\" is under revision control" + return 1 + fi + else + echo ".git not found, skipping check for revision control" fi } @@ -214,7 +227,12 @@ if [ -z ${arch+set} ]; then fi if [ -z ${use_branch+set} ]; then - use_branch="$(git rev-parse --abbrev-ref HEAD)" + if [[ -d ".git" ]]; then + use_branch="$(git rev-parse --abbrev-ref HEAD)" + else + # it really doesn't matter if git isn't present + use_branch="master" + fi fi build_dir=".build-$arch" From e34ec195b42960a1e7ab49285bc46d1ee55cc74a Mon Sep 17 00:00:00 2001 From: boppyt <71049646+boppyt@users.noreply.github.com> Date: Sat, 28 Aug 2021 23:31:49 -0700 Subject: [PATCH 018/165] build script: check for BUILD_PROHIBIT_GIT --- build-packages.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build-packages.sh b/build-packages.sh index 039e56f8..366882e6 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -14,7 +14,7 @@ copy_directory_from_repo () { } _archive () { - if [[ -d ".git" ]]; then + if [[ -d ".git" || $BUILD_PROHIBIT_GIT -eq 1 ]]; then git archive "$1" "$2" --format=tar | tar xf - -C "$3" "$4" else echo ".git not found, falling back to UNIX operation" @@ -29,7 +29,7 @@ build_dir_is_usable () { echo "invalid build directory, no path allowed: \"$build\"" return 1 fi - if [[ -d ".git" ]]; then + if [[ -d ".git" || $BUILD_PROHIBIT_GIT -eq 1 ]]; then git ls-files --error-unmatch "$build" &> /dev/null if [ $? == 0 ]; then echo "invalid path, \"$build\" is under revision control" @@ -227,7 +227,7 @@ if [ -z ${arch+set} ]; then fi if [ -z ${use_branch+set} ]; then - if [[ -d ".git" ]]; then + if [[ -d ".git" || $BUILD_PROHIBIT_GIT -eq 1 ]]; then use_branch="$(git rev-parse --abbrev-ref HEAD)" else # it really doesn't matter if git isn't present From 97ca19c73fa062600293c994c2488b413bc38957 Mon Sep 17 00:00:00 2001 From: Zack A Date: Sun, 29 Aug 2021 16:16:14 -0700 Subject: [PATCH 019/165] build script: check if .git is present --- build-packages.sh | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/build-packages.sh b/build-packages.sh index 366882e6..ed85e175 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -14,10 +14,9 @@ copy_directory_from_repo () { } _archive () { - if [[ -d ".git" || $BUILD_PROHIBIT_GIT -eq 1 ]]; then + if [[ $git_available -eq 1 ]]; then git archive "$1" "$2" --format=tar | tar xf - -C "$3" "$4" else - echo ".git not found, falling back to UNIX operation" cp -r "$2" "$3" fi } @@ -29,14 +28,12 @@ build_dir_is_usable () { echo "invalid build directory, no path allowed: \"$build\"" return 1 fi - if [[ -d ".git" || $BUILD_PROHIBIT_GIT -eq 1 ]]; then + if [[ $git_available -eq 1 ]]; then git ls-files --error-unmatch "$build" &> /dev/null if [ $? == 0 ]; then echo "invalid path, \"$build\" is under revision control" return 1 fi - else - echo ".git not found, skipping check for revision control" fi } @@ -203,7 +200,6 @@ lite_copy_third_party_modules () { mv "$build/lite-colors-master/colors" "$build/third/data" rm -fr "$build/lite-colors-master" } - unset arch while [ ! -z {$1+x} ]; do case $1 in @@ -226,8 +222,15 @@ if [ -z ${arch+set} ]; then exit 1 fi + if [[ -d ".git" || $BUILD_PROHIBIT_GIT -ne 0 ]]; then + git_available=1; + else + echo "Use of git prohibited. Either '.git' wasn't found or BUILD_PROHIBIT_GIT was set." + git_available=0; + fi + if [ -z ${use_branch+set} ]; then - if [[ -d ".git" || $BUILD_PROHIBIT_GIT -eq 1 ]]; then + if [[ $git_available -eq 1 ]]; then use_branch="$(git rev-parse --abbrev-ref HEAD)" else # it really doesn't matter if git isn't present From 8dde1dbb8649f33e08e5d44e4931c3ede0ce0407 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Mon, 30 Aug 2021 14:27:31 +0200 Subject: [PATCH 020/165] Add renderer build options in build-packages.sh --- build-packages.sh | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/build-packages.sh b/build-packages.sh index ed85e175..ffe3537b 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -42,7 +42,7 @@ lite_build () { local build="$1" build_dir_is_usable "$build" || exit 1 rm -fr "$build" - setup_options=() + setup_options=("${@:2}") if [[ "$OSTYPE" == "darwin"* ]]; then setup_options+=(-Dbundle=true) fi @@ -55,7 +55,8 @@ lite_build_pgo () { local build="$1" build_dir_is_usable "$build" || exit 1 rm -fr "$build" - meson setup --buildtype=release -Db_pgo=generate "$build" || exit 1 + echo meson setup --buildtype=release "${@:2}" -Db_pgo=generate "$build" + meson setup --buildtype=release "${@:2}" -Db_pgo=generate "$build" || exit 1 ninja -C "$build" || exit 1 copy_directory_from_repo data "$build/src" "$build/src/lite-xl" @@ -200,9 +201,15 @@ lite_copy_third_party_modules () { mv "$build/lite-colors-master/colors" "$build/third/data" rm -fr "$build/lite-colors-master" } + +build_opts=() unset arch while [ ! -z {$1+x} ]; do case $1 in + -renderer) + build_opts+=("-Drenderer=true") + shift + ;; -pgo) pgo=true shift @@ -241,9 +248,9 @@ fi build_dir=".build-$arch" if [ -z ${pgo+set} ]; then - lite_build "$build_dir" + lite_build "$build_dir" "${build_opts[@]}" else - lite_build_pgo "$build_dir" + lite_build_pgo "$build_dir" "${build_opts[@]}" fi lite_copy_third_party_modules "$build_dir" lite_build_package "$build_dir" "$arch" From 68c1cc606fda70769db200d79421db2311d4313f Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Mon, 30 Aug 2021 14:35:42 +0200 Subject: [PATCH 021/165] Bring back min len autocomplete default to 3 --- data/plugins/autocomplete.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/plugins/autocomplete.lua b/data/plugins/autocomplete.lua index c41f233d..484199a9 100644 --- a/data/plugins/autocomplete.lua +++ b/data/plugins/autocomplete.lua @@ -12,7 +12,7 @@ local Doc = require "core.doc" config.plugins.autocomplete = { -- Amount of characters that need to be written for autocomplete - min_len = 1, + min_len = 3, -- The max amount of visible items max_height = 6, -- The max amount of scrollable items From 9d4e944549b48ae2c311860f913bc1f217fa6748 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 28 Aug 2021 13:42:06 -0400 Subject: [PATCH 022/165] Added in two new VSC-style multicursor shortcuts. --- data/core/commands/findreplace.lua | 32 ++++++++++++++++++++++++------ data/core/keymap-macos.lua | 1 + data/core/keymap.lua | 1 + 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 6dd7ddae..b9a424fa 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -96,13 +96,33 @@ local function has_selection() return core.active_view:is(DocView) and core.active_view.doc:has_selection() end -command.add(has_selection, { - ["find-replace:select-next"] = function() - local l1, c1, l2, c2 = doc():get_selection(true) - local text = doc():get_text(l1, c1, l2, c2) - l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) - if l2 then doc():set_selection(l2, c2, l1, c1) end +local function has_unique_selection() + local text = nil + for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do + if line1 == line2 and col1 == col2 then return false end + local selection = doc():get_text(line1, col1, line2, col2) + if text ~= nil and text ~= selection then return false end + text = selection end + return text ~= nil +end + +local function select_next(all) + local il1, ic1 = doc():get_selection(true) + for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do + local text = doc():get_text(l1, c1, l2, c2) + repeat + l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) + if l1 == il1 and c1 == ic1 then break end + if l2 then doc():add_selection(l2, c2, l1, c1) end + until not all or not l2 + break + end +end + +command.add(has_unique_selection, { + ["find-replace:select-next"] = function() select_next(false) end, + ["find-replace:select-all"] = function() select_next(true) end }) command.add("core.docview", { diff --git a/data/core/keymap-macos.lua b/data/core/keymap-macos.lua index 647cb132..89f68949 100644 --- a/data/core/keymap-macos.lua +++ b/data/core/keymap-macos.lua @@ -66,6 +66,7 @@ local function keymap_macos(keymap) ["cmd+a"] = "doc:select-all", ["cmd+d"] = { "find-replace:select-next", "doc:select-word" }, ["cmd+l"] = "doc:select-lines", + ["cmd+shift+l"] = { "find-replace:select-all", "doc:select-word" }, ["cmd+/"] = "doc:toggle-line-comments", ["cmd+up"] = "doc:move-lines-up", ["cmd+down"] = "doc:move-lines-down", diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 0b08259d..2be0dfc7 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -170,6 +170,7 @@ keymap.add_direct { ["ctrl+a"] = "doc:select-all", ["ctrl+d"] = { "find-replace:select-next", "doc:select-word" }, ["ctrl+l"] = "doc:select-lines", + ["ctrl+shift+l"] = { "find-replace:select-all", "doc:select-word" }, ["ctrl+/"] = "doc:toggle-line-comments", ["ctrl+up"] = "doc:move-lines-up", ["ctrl+down"] = "doc:move-lines-down", From d352eb1cb9169576f2ce8972b8bcab3b208bf204 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 29 Aug 2021 17:54:57 -0400 Subject: [PATCH 023/165] Fixed cursors moving around with removal and inserts with cursors. Also fixed drawing line highlights with multicursors. --- data/core/doc/init.lua | 19 +++++++++++++++++++ data/core/docview.lua | 10 +++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 4a231295..269d6d12 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -322,6 +322,7 @@ end function Doc:raw_insert(line, col, text, undo_stack, time) -- split text into lines and merge with line at insertion point local lines = split_lines(text) + local len = #lines[#lines] local before = self.lines[line]:sub(1, col - 1) local after = self.lines[line]:sub(col) for i = 1, #lines - 1 do @@ -338,6 +339,15 @@ function Doc:raw_insert(line, col, text, undo_stack, time) push_undo(undo_stack, time, "selection", unpack(self.selections)) push_undo(undo_stack, time, "remove", line, col, line2, col2) + -- keep cursors where they should be + for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true) do + if cline1 >= line then + local line_addition = line > cline1 or ccol1 > col and #lines - 1 or 0 + local column_addition = line == cline1 and ccol1 > col and len or 0 + self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition) + end + end + -- update highlighter and assure selection is in bounds self.highlighter:invalidate(line) self:sanitize_selection() @@ -356,6 +366,15 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) -- splice line into line array common.splice(self.lines, line1, line2 - line1 + 1, { before .. after }) + + -- move all cursors back if they share a line with the removed text + for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true) do + if cline1 >= line2 then + local line_removal = line2 - line1 + local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0 + self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal) + end + end -- update highlighter and assure selection is in bounds self.highlighter:invalidate(line1) diff --git a/data/core/docview.lua b/data/core/docview.lua index 17fb534a..161eac47 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -367,16 +367,20 @@ function DocView:draw_line_body(idx, x, y) local x1 = x + self:get_col_x_offset(idx, col1) local x2 = x + self:get_col_x_offset(idx, col2) local lh = self:get_line_height() - renderer.draw_rect(x1, y, x2 - x1, lh, style.selection) + if x1 ~= x2 then + renderer.draw_rect(x1, y, x2 - x1, lh, style.selection) + end end end + local draw_highlight = nil for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do -- draw line highlight if caret is on this line - if config.highlight_current_line and (line1 == line2 and col1 == col2) + if draw_highlight ~= false and config.highlight_current_line and line1 == idx and core.active_view == self then - self:draw_line_highlight(x + self.scroll.x, y) + draw_highlight = (line1 == line2 and col1 == col2) end end + if draw_highlight then self:draw_line_highlight(x + self.scroll.x, y) end -- draw line's text self:draw_line_text(idx, x, y) From 1d61cf989fd6d7ef023763acf7e1fd5459f49893 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 29 Aug 2021 20:05:58 -0400 Subject: [PATCH 024/165] Fixed cursor movement. --- data/core/doc/init.lua | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 269d6d12..ea219a8a 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -333,21 +333,20 @@ function Doc:raw_insert(line, col, text, undo_stack, time) -- splice lines into line array common.splice(self.lines, line, 1, lines) + + -- keep cursors where they should be + for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do + if cline1 < line then break end + local line_addition = (line < cline1 or col < ccol1) and #lines - 1 or 0 + local column_addition = line == cline1 and ccol1 > col and len or 0 + self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition) + end -- push undo local line2, col2 = self:position_offset(line, col, #text) push_undo(undo_stack, time, "selection", unpack(self.selections)) push_undo(undo_stack, time, "remove", line, col, line2, col2) - -- keep cursors where they should be - for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true) do - if cline1 >= line then - local line_addition = line > cline1 or ccol1 > col and #lines - 1 or 0 - local column_addition = line == cline1 and ccol1 > col and len or 0 - self:set_selections(idx, cline1 + line_addition, ccol1 + column_addition, cline2 + line_addition, ccol2 + column_addition) - end - end - -- update highlighter and assure selection is in bounds self.highlighter:invalidate(line) self:sanitize_selection() @@ -368,12 +367,11 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) common.splice(self.lines, line1, line2 - line1 + 1, { before .. after }) -- move all cursors back if they share a line with the removed text - for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true) do - if cline1 >= line2 then - local line_removal = line2 - line1 - local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0 - self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal) - end + for idx, cline1, ccol1, cline2, ccol2 in self:get_selections(true, true) do + if cline1 < line2 then break end + local line_removal = line2 - line1 + local column_removal = line2 == cline2 and col2 < ccol1 and (line2 == line1 and col2 - col1 or col2) or 0 + self:set_selections(idx, cline1 - line_removal, ccol1 - column_removal, cline2 - line_removal, ccol2 - column_removal) end -- update highlighter and assure selection is in bounds @@ -411,7 +409,7 @@ end function Doc:text_input(text, idx) - for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do + for sidx, line1, col1, line2, col2 in self:get_selections(true, idx or true) do if line1 ~= line2 or col1 ~= col2 then self:delete_to_cursor(sidx) end From 604626fa32380880bb73a210a4366337c6ab9d9b Mon Sep 17 00:00:00 2001 From: Timofffee Date: Mon, 30 Aug 2021 16:21:16 +0400 Subject: [PATCH 025/165] Fix macOS keymap --- data/core/keymap-macos.lua | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/data/core/keymap-macos.lua b/data/core/keymap-macos.lua index 89f68949..7f54ddbb 100644 --- a/data/core/keymap-macos.lua +++ b/data/core/keymap-macos.lua @@ -52,12 +52,14 @@ local function keymap_macos(keymap) ["shift+tab"] = "doc:unindent", ["backspace"] = "doc:backspace", ["shift+backspace"] = "doc:backspace", - ["cmd+backspace"] = "doc:delete-to-previous-word-start", + ["option+backspace"] = "doc:delete-to-previous-word-start", ["cmd+shift+backspace"] = "doc:delete-to-previous-word-start", + ["cmd+backspace"] = "doc:delete-to-start-of-indentation", ["delete"] = "doc:delete", ["shift+delete"] = "doc:delete", - ["cmd+delete"] = "doc:delete-to-next-word-end", + ["option+delete"] = "doc:delete-to-next-word-end", ["cmd+shift+delete"] = "doc:delete-to-next-word-end", + ["cmd+delete"] = "doc:delete-to-end-of-line", ["return"] = { "command:submit", "doc:newline", "dialog:select" }, ["keypad enter"] = { "command:submit", "doc:newline", "dialog:select" }, ["cmd+return"] = "doc:newline-below", @@ -68,8 +70,8 @@ local function keymap_macos(keymap) ["cmd+l"] = "doc:select-lines", ["cmd+shift+l"] = { "find-replace:select-all", "doc:select-word" }, ["cmd+/"] = "doc:toggle-line-comments", - ["cmd+up"] = "doc:move-lines-up", - ["cmd+down"] = "doc:move-lines-down", + ["option+up"] = "doc:move-lines-up", + ["option+down"] = "doc:move-lines-down", ["cmd+shift+d"] = "doc:duplicate-lines", ["cmd+shift+k"] = "doc:delete-lines", @@ -77,14 +79,16 @@ local function keymap_macos(keymap) ["right"] = { "doc:move-to-next-char", "dialog:next-entry"}, ["up"] = { "command:select-previous", "doc:move-to-previous-line" }, ["down"] = { "command:select-next", "doc:move-to-next-line" }, - ["cmd+left"] = "doc:move-to-previous-word-start", - ["cmd+right"] = "doc:move-to-next-word-end", + ["option+left"] = "doc:move-to-previous-word-start", + ["option+right"] = "doc:move-to-next-word-end", + ["cmd+left"] = "doc:move-to-start-of-indentation", + ["cmd+right"] = "doc:move-to-end-of-line", ["cmd+["] = "doc:move-to-previous-block-start", ["cmd+]"] = "doc:move-to-next-block-end", ["home"] = "doc:move-to-start-of-indentation", ["end"] = "doc:move-to-end-of-line", - ["cmd+home"] = "doc:move-to-start-of-doc", - ["cmd+end"] = "doc:move-to-end-of-doc", + ["cmd+up"] = "doc:move-to-start-of-doc", + ["cmd+down"] = "doc:move-to-end-of-doc", ["pageup"] = "doc:move-to-previous-page", ["pagedown"] = "doc:move-to-next-page", @@ -92,18 +96,20 @@ local function keymap_macos(keymap) ["shift+right"] = "doc:select-to-next-char", ["shift+up"] = "doc:select-to-previous-line", ["shift+down"] = "doc:select-to-next-line", - ["cmd+shift+left"] = "doc:select-to-previous-word-start", - ["cmd+shift+right"] = "doc:select-to-next-word-end", + ["option+shift+left"] = "doc:select-to-previous-word-start", + ["option+shift+right"] = "doc:select-to-next-word-end", + ["cmd+shift+left"] = "doc:select-to-start-of-indentation", + ["cmd+shift+right"] = "doc:select-to-end-of-line", ["cmd+shift+["] = "doc:select-to-previous-block-start", ["cmd+shift+]"] = "doc:select-to-next-block-end", ["shift+home"] = "doc:select-to-start-of-indentation", ["shift+end"] = "doc:select-to-end-of-line", - ["cmd+shift+home"] = "doc:select-to-start-of-doc", - ["cmd+shift+end"] = "doc:select-to-end-of-doc", + ["cmd+shift+up"] = "doc:select-to-start-of-doc", + ["cmd+shift+down"] = "doc:select-to-end-of-doc", ["shift+pageup"] = "doc:select-to-previous-page", ["shift+pagedown"] = "doc:select-to-next-page", - ["cmd+shift+up"] = "doc:create-cursor-previous-line", - ["cmd+shift+down"] = "doc:create-cursor-next-line" + ["cmd+option+up"] = "doc:create-cursor-previous-line", + ["cmd+option+down"] = "doc:create-cursor-next-line" } end From a75aca25380b404a2ccda61277071b7b86e1914e Mon Sep 17 00:00:00 2001 From: redtide Date: Tue, 31 Aug 2021 14:45:00 +0200 Subject: [PATCH 026/165] Renamed freedesktop resources --- build-packages.sh | 4 ++-- meson.build | 4 ++-- ...lite-xl.appdata.xml => org.lite_xl.lite_xl.appdata.xml} | 7 ++++++- ...lite-xl.lite-xl.desktop => org.lite_xl.lite_xl.desktop} | 2 +- scripts/appimage.sh | 2 +- 5 files changed, 12 insertions(+), 7 deletions(-) rename resources/linux/{org.lite-xl.lite-xl.appdata.xml => org.lite_xl.lite_xl.appdata.xml} (83%) rename resources/linux/{org.lite-xl.lite-xl.desktop => org.lite_xl.lite_xl.desktop} (80%) diff --git a/build-packages.sh b/build-packages.sh index ffe3537b..424d60ba 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -167,8 +167,8 @@ lite_build_package_linux () { strip "$bindir/lite-xl" if [ -z "$portable" ]; then mkdir -p "$pdir/share/applications" "$pdir/share/icons/hicolor/scalable/apps" "$pdir/share/metainfo" - cp "resources/linux/org.lite-xl.lite-xl.desktop" "$pdir/share/applications" - cp "resources/linux/org.lite-xl.lite-xl.appdata.xml" "$pdir/share/metainfo" + cp "resources/linux/org.lite_xl.lite_xl.desktop" "$pdir/share/applications" + cp "resources/linux/org.lite_xl.lite_xl.appdata.xml" "$pdir/share/metainfo" cp "resources/icons/lite-xl.svg" "$pdir/share/icons/hicolor/scalable/apps/lite-xl.svg" fi pushd ".package-build" diff --git a/meson.build b/meson.build index 0c5bc865..3d5f9da9 100644 --- a/meson.build +++ b/meson.build @@ -92,10 +92,10 @@ else install_data('resources/icons/lite-xl.svg', install_dir : 'share/icons/hicolor/scalable/apps' ) - install_data('resources/linux/org.lite-xl.lite-xl.desktop', + install_data('resources/linux/org.lite_xl.lite_xl.desktop', install_dir : 'share/applications' ) - install_data('resources/linux/org.lite-xl.lite-xl.appdata.xml', + install_data('resources/linux/org.lite_xl.lite_xl.appdata.xml', install_dir : 'share/metainfo' ) endif diff --git a/resources/linux/org.lite-xl.lite-xl.appdata.xml b/resources/linux/org.lite_xl.lite_xl.appdata.xml similarity index 83% rename from resources/linux/org.lite-xl.lite-xl.appdata.xml rename to resources/linux/org.lite_xl.lite_xl.appdata.xml index d2d0a73a..c5895178 100644 --- a/resources/linux/org.lite-xl.lite-xl.appdata.xml +++ b/resources/linux/org.lite_xl.lite_xl.appdata.xml @@ -1,10 +1,11 @@ - org.lite-xl.lite-xl + org.lite_xl.lite_xl MIT MIT Lite XL A lightweight text editor written in Lua +

@@ -25,4 +26,8 @@ lite-xl + + + + diff --git a/resources/linux/org.lite-xl.lite-xl.desktop b/resources/linux/org.lite_xl.lite_xl.desktop similarity index 80% rename from resources/linux/org.lite-xl.lite-xl.desktop rename to resources/linux/org.lite_xl.lite_xl.desktop index f2fa9610..c09fb5db 100644 --- a/resources/linux/org.lite-xl.lite-xl.desktop +++ b/resources/linux/org.lite_xl.lite_xl.desktop @@ -6,5 +6,5 @@ Exec=lite-xl %F Icon=lite-xl Terminal=false StartupNotify=false -Categories=Utility;TextEditor;Development; +Categories=Development;IDE; MimeType=text/plain; diff --git a/scripts/appimage.sh b/scripts/appimage.sh index b2cc5cd7..65dce3c4 100644 --- a/scripts/appimage.sh +++ b/scripts/appimage.sh @@ -117,7 +117,7 @@ generate_appimage(){ mv AppRun LiteXL.AppDir/ # These could be symlinks but it seems they doesn't work with AppimageLauncher cp resources/icons/lite-xl.svg LiteXL.AppDir/ - cp resources/linux/org.lite-xl.lite-xl.desktop LiteXL.AppDir/ + cp resources/linux/org.lite_xl.lite_xl.desktop LiteXL.AppDir/ if [[ $STATIC_BUILD == false ]]; then echo "Copying libraries..." From 7811660cafd01a977814378d57973b36f4cf15aa Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 30 Aug 2021 22:56:33 -0400 Subject: [PATCH 027/165] Fixed replace to make it multicursor-aware. --- data/core/doc/init.lua | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index ea219a8a..47869d5a 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -418,12 +418,7 @@ function Doc:text_input(text, idx) end end - -function Doc:replace(fn) - local line1, col1, line2, col2 = self:get_selection(true) - if line1 == line2 and col1 == col2 then - line1, col1, line2, col2 = 1, 1, #self.lines, #self.lines[#self.lines] - end +function Doc:replace_cursor(idx, line1, col1, line2, col2, fn) local old_text = self:get_text(line1, col1, line2, col2) local new_text, n = fn(old_text) if old_text ~= new_text then @@ -431,12 +426,26 @@ function Doc:replace(fn) self:remove(line1, col1, line2, col2) if line1 == line2 and col1 == col2 then line2, col2 = self:position_offset(line1, col1, #new_text) - self:set_selection(line1, col1, line2, col2) + self:set_selections(idx, line1, col1, line2, col2) end end return n end +function Doc:replace(fn) + local has_selection = false + for idx, line1, col1, line2, col2 in self:get_selections(true) do + if line1 ~= line2 or col1 ~= col2 then + self:replace_cursor(idx, line1, col1, line2, col2, fn) + has_selection = true + end + end + if not has_selection then + self:set_selection(table.unpack(self.selections)) + self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn) + end +end + function Doc:delete_to_cursor(idx, ...) for sidx, line1, col1, line2, col2 in self:get_selections(true, idx) do From b7f2d1ad03bc39df8fbe227d77934c10b2b64ee8 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 31 Aug 2021 16:21:40 -0400 Subject: [PATCH 028/165] Forgot to return an 'n'. --- data/core/doc/init.lua | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 47869d5a..aff31e94 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -433,17 +433,18 @@ function Doc:replace_cursor(idx, line1, col1, line2, col2, fn) end function Doc:replace(fn) - local has_selection = false + local has_selection, n = false, 0 for idx, line1, col1, line2, col2 in self:get_selections(true) do if line1 ~= line2 or col1 ~= col2 then - self:replace_cursor(idx, line1, col1, line2, col2, fn) + n = n + self:replace_cursor(idx, line1, col1, line2, col2, fn) has_selection = true end end if not has_selection then self:set_selection(table.unpack(self.selections)) - self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn) + n = n + self:replace_cursor(1, 1, 1, #self.lines, #self.lines[#self.lines], fn) end + return n end From de4072e207094f872be0e924c38755e02fc50624 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 1 Sep 2021 16:29:47 +0200 Subject: [PATCH 029/165] Avoid checking for unique selections on non-`DocView`s --- data/core/commands/findreplace.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index b9a424fa..48cbecaf 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -96,7 +96,8 @@ local function has_selection() return core.active_view:is(DocView) and core.active_view.doc:has_selection() end -local function has_unique_selection() +local function has_unique_selection() + if not core.active_view:is(DocView) then return false end local text = nil for idx, line1, col1, line2, col2 in doc():get_selections(true, true) do if line1 == line2 and col1 == col2 then return false end From 11521ff883aa28446f1af551d880d6df7df9c069 Mon Sep 17 00:00:00 2001 From: redtide Date: Wed, 1 Sep 2021 19:42:09 +0200 Subject: [PATCH 030/165] Specify the WM_CLASS of the application, used by some Linux DEs --- resources/linux/org.lite_xl.lite_xl.desktop | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/linux/org.lite_xl.lite_xl.desktop b/resources/linux/org.lite_xl.lite_xl.desktop index c09fb5db..d251c4dc 100644 --- a/resources/linux/org.lite_xl.lite_xl.desktop +++ b/resources/linux/org.lite_xl.lite_xl.desktop @@ -5,6 +5,6 @@ Comment=A lightweight text editor written in Lua Exec=lite-xl %F Icon=lite-xl Terminal=false -StartupNotify=false +StartupWMClass=lite-xl Categories=Development;IDE; MimeType=text/plain; From 59aa7f0090ecbd0049d15fdc2b9154ee857c1d8d Mon Sep 17 00:00:00 2001 From: Guldoman Date: Thu, 2 Sep 2021 18:58:24 +0200 Subject: [PATCH 031/165] Fix absolute path detection in `core.project_absolute_path` --- data/core/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/init.lua b/data/core/init.lua index 5b1cca26..3bf10211 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -809,7 +809,7 @@ end -- This function should get only filenames normalized using -- common.normalize_path function. function core.project_absolute_path(filename) - if filename:match('^%a:\\') or filename:find('/', 1, true) then + if filename:match('^%a:\\') or filename:find('/', 1, true) == 1 then return filename else return core.project_dir .. PATHSEP .. filename From 9887dd47469f83ceb5c10c546a5df62361bf4fa5 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Thu, 2 Sep 2021 19:09:29 +0200 Subject: [PATCH 032/165] Make open files follow renames --- data/plugins/treeview.lua | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua index f9a67aaf..9bb597f2 100644 --- a/data/plugins/treeview.lua +++ b/data/plugins/treeview.lua @@ -441,11 +441,24 @@ command.add(nil, { ["treeview:rename"] = function() local old_filename = view.hovered_item.filename + local old_abs_filename = view.hovered_item.abs_filename core.command_view:set_text(old_filename) core.command_view:enter("Rename", function(filename) - os.rename(old_filename, filename) + filename = core.normalize_to_project_dir(filename) + local abs_filename = core.project_absolute_path(filename) + local res, err = os.rename(old_abs_filename, abs_filename) + if res then -- successfully renamed + for _, doc in ipairs(core.docs) do + if doc.abs_filename and old_abs_filename == doc.abs_filename then + doc:set_filename(filename, abs_filename) -- make doc point to the new filename + break -- only first needed + end + end + core.log("Renamed \"%s\" to \"%s\"", old_filename, filename) + else + core.error("Error while renaming \"%s\" to \"%s\": %s", old_abs_filename, abs_filename, err) + end core.reschedule_project_scan() - core.log("Renamed \"%s\" to \"%s\"", old_filename, filename) end, common.path_suggest) end, From 3278eebdad8c30c337e2e7a77087f3ba30cb00af Mon Sep 17 00:00:00 2001 From: Guldoman Date: Thu, 2 Sep 2021 19:12:26 +0200 Subject: [PATCH 033/165] Avoid exposing `treeview` commands when not usable --- data/plugins/treeview.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua index 9bb597f2..48d6f5a7 100644 --- a/data/plugins/treeview.lua +++ b/data/plugins/treeview.lua @@ -437,8 +437,10 @@ menu:register( command.add(nil, { ["treeview:toggle"] = function() view.visible = not view.visible - end, + end}) + +command.add(function() return view.hovered_item ~= nil end, { ["treeview:rename"] = function() local old_filename = view.hovered_item.filename local old_abs_filename = view.hovered_item.abs_filename From 8d355bd3a058028306d1c3c004c069aa717435aa Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 3 Sep 2021 23:33:36 +0200 Subject: [PATCH 034/165] Add missing release flag in build-packages.sh --- build-packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-packages.sh b/build-packages.sh index 424d60ba..666fb9e2 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -46,7 +46,7 @@ lite_build () { if [[ "$OSTYPE" == "darwin"* ]]; then setup_options+=(-Dbundle=true) fi - meson setup "${setup_options[@]}" "$build" || exit 1 + meson setup --buildtype=release "${setup_options[@]}" "$build" || exit 1 ninja -C "$build" || exit 1 } From f9c7eeeeb8265b6f741cde5cabdb060c2a57c630 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sat, 4 Sep 2021 00:32:01 +0200 Subject: [PATCH 035/165] Check if session file returned anything --- data/core/init.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/core/init.lua b/data/core/init.lua index 3bf10211..6b770fd6 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -17,7 +17,7 @@ local core = {} local function load_session() local ok, t = pcall(dofile, USERDIR .. "/session.lua") - if ok then + if ok and t then return t.recents, t.window, t.window_mode end return {} @@ -441,7 +441,7 @@ function core.init() elseif window_mode == "maximized" then system.set_window_mode("maximized") end - core.recent_projects = recent_projects + core.recent_projects = recent_projects or {} end local project_dir = core.recent_projects[1] or "." From de038a633d140a4fa3be89cd1f3de7fac2d85146 Mon Sep 17 00:00:00 2001 From: Francesco Date: Sat, 4 Sep 2021 17:24:08 +0200 Subject: [PATCH 036/165] Update README to remove reference to notarization We assume we don't need to mention the macOS package is notarized. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 339747fd..32f63d70 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ A lightweight text editor written in Lua, adapted from [lite]. -* **[Get Lite XL]** — Download for Windows, Linux and Mac OS (notarized app). +* **[Get Lite XL]** — Download for Windows, Linux and Mac OS. * **[Get plugins]** — Add additional functionality, adapted for Lite XL. * **[Get color themes]** — Add additional colors themes. From 9246a16e67501c62593018d14d3eae91e8d09589 Mon Sep 17 00:00:00 2001 From: Timofffee Date: Fri, 3 Sep 2021 15:50:44 +0400 Subject: [PATCH 037/165] Bring back info.plist with meson configuration --- meson.build | 8 +++++++- resources/macos/{Info.plist => Info.plist.in} | 3 ++- 2 files changed, 9 insertions(+), 2 deletions(-) rename resources/macos/{Info.plist => Info.plist.in} (96%) diff --git a/meson.build b/meson.build index 3d5f9da9..7e9b4ab1 100644 --- a/meson.build +++ b/meson.build @@ -83,7 +83,13 @@ elif get_option('bundle') and host_machine.system() == 'darwin' lite_docdir = 'Contents/Resources' lite_datadir = 'Contents/Resources' install_data('resources/icons/icon.icns', install_dir : 'Contents/Resources') - install_data('resources/macos/Info.plist', install_dir : 'Contents') + configure_file( + input : 'resources/macos/Info.plist.in', + output : 'Info.plist', + configuration : conf_data, + install : true, + install_dir : 'Contents' + ) else lite_bindir = 'bin' lite_docdir = 'share/doc/lite-xl' diff --git a/resources/macos/Info.plist b/resources/macos/Info.plist.in similarity index 96% rename from resources/macos/Info.plist rename to resources/macos/Info.plist.in index d465479c..c15fd566 100644 --- a/resources/macos/Info.plist +++ b/resources/macos/Info.plist.in @@ -19,8 +19,9 @@ NSDesktopFolderUsageDescriptionTo access, edit and index your projects. NSDownloadsFolderUsageDescriptionTo access, edit and index your projects. CFBundleShortVersionString - 2.0 + @PROJECT_VERSION@ NSHumanReadableCopyright © 2019-2021 Francesco Abbate + From cc20849afdde351bcf5bd96a43b4586b212feca7 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sat, 4 Sep 2021 18:08:04 +0200 Subject: [PATCH 038/165] Fix build-packages to use generated Info.plist --- build-packages.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build-packages.sh b/build-packages.sh index 666fb9e2..6b874cd9 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -120,7 +120,7 @@ lite_build_package_macos () { cp -r "$build/third/data/$module_name" "$datadir" done cp resources/icons/icon.icns "$appdir/Contents/Resources/icon.icns" - cp resources/macos/Info.plist "$appdir/Contents/Info.plist" + cp "$build/Info.plist" "$appdir/Contents/Info.plist" cp "$build/src/lite-xl" "$bindir/lite-xl" strip "$bindir/lite-xl" pushd ".package-build" From 91da2ddfdd8fb0a63ed30d2ef94c88e13f907f27 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sat, 4 Sep 2021 18:15:07 +0200 Subject: [PATCH 039/165] Move innosetup meson config into scripts directory The purpose is to keep the main meson build file as simple as possible while keeping the innosetup config. --- meson.build | 8 +------- scripts/innosetup/innosetup.sh | 2 +- scripts/meson.build | 8 ++++++++ 3 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 scripts/meson.build diff --git a/meson.build b/meson.build index 7e9b4ab1..611d154a 100644 --- a/meson.build +++ b/meson.build @@ -14,13 +14,6 @@ conf_data.set('PROJECT_BUILD_DIR', meson.current_build_dir()) conf_data.set('PROJECT_SOURCE_DIR', meson.current_source_dir()) conf_data.set('PROJECT_VERSION', meson.project_version()) -if host_machine.system() == 'windows' - configure_file( - input : 'scripts/innosetup/innosetup.iss.in', - output : 'innosetup.iss', - configuration : conf_data - ) -endif #=============================================================================== # Compiler Settings #=============================================================================== @@ -127,3 +120,4 @@ configure_file( #=============================================================================== subdir('lib/font_renderer') subdir('src') +subdir('scripts') diff --git a/scripts/innosetup/innosetup.sh b/scripts/innosetup/innosetup.sh index b2d0e86c..44a6c787 100644 --- a/scripts/innosetup/innosetup.sh +++ b/scripts/innosetup/innosetup.sh @@ -51,5 +51,5 @@ else ARCH=Win32 fi -/c/Program\ Files\ \(x86\)/Inno\ Setup\ 6/ISCC.exe -dARCH=$ARCH $BUILD_DIR/innosetup.iss +/c/Program\ Files\ \(x86\)/Inno\ Setup\ 6/ISCC.exe -dARCH=$ARCH $BUILD_DIR/scripts/innosetup.iss mv $BUILD_DIR/LiteXL*.exe $(pwd) diff --git a/scripts/meson.build b/scripts/meson.build new file mode 100644 index 00000000..8b45814d --- /dev/null +++ b/scripts/meson.build @@ -0,0 +1,8 @@ +if host_machine.system() == 'windows' + configure_file( + input : 'innosetup/innosetup.iss.in', + output : 'innosetup.iss', + configuration : conf_data + ) +endif + From 261ab5daf226a44271f45219bb3c6ec1fe8d6ff9 Mon Sep 17 00:00:00 2001 From: redtide Date: Sun, 29 Aug 2021 10:15:55 +0200 Subject: [PATCH 040/165] Adapt all scripts to work together with build-packages.sh --- .github/workflows/build.yml | 121 ++++----- .gitignore | 2 + README.md | 16 +- build-packages.sh | 393 +++++++++++------------------ meson.build | 46 ++-- meson_options.txt | 1 + scripts/README.md | 28 ++ scripts/appimage.sh | 14 +- scripts/build.sh | 157 ++++++------ scripts/common.sh | 25 ++ scripts/innosetup/innosetup.iss.in | 19 +- scripts/innosetup/innosetup.sh | 82 +++--- scripts/install-dependencies.sh | 74 ++++++ scripts/lhelper.sh | 75 ++++++ scripts/msys2-package.sh | 56 ---- scripts/package.sh | 252 ++++++++++++++++++ scripts/source-package.sh | 69 ----- 17 files changed, 829 insertions(+), 601 deletions(-) create mode 100644 scripts/README.md create mode 100644 scripts/common.sh create mode 100644 scripts/install-dependencies.sh create mode 100644 scripts/lhelper.sh delete mode 100644 scripts/msys2-package.sh create mode 100644 scripts/package.sh delete mode 100644 scripts/source-package.sh diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6eec7a63..2778de79 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,26 +1,25 @@ name: CI +# All builds use lhelper only for releases, +# otherwise for normal builds dependencies are dynamically linked. + on: push: branches: - '*' - # Temporarily disabled - #tags: - #- 'v[0-9]*' +# tags: +# - 'v[0-9]*' pull_request: branches: - '*' jobs: - # Note: not using git-archive(-all) because it can't include subprojects ignored by git archive_source_code: name: Source Code Tarball runs-on: ubuntu-18.04 # Only on tags/releases if: startsWith(github.ref, 'refs/tags/') steps: - - name: Set Environment Variables - run: echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-src" >> "$GITHUB_ENV" - uses: actions/checkout@v2 - name: Python Setup uses: actions/setup-python@v2 @@ -30,16 +29,14 @@ jobs: run: | sudo apt-get install -qq ninja-build pip3 install meson - - name: Archive source code + - name: Package shell: bash - run: bash scripts/source-package.sh --destdir "${INSTALL_NAME}" + run: bash scripts/package.sh --version ${GITHUB_REF##*/} --debug --source - uses: actions/upload-artifact@v2 with: name: Source Code Tarball - path: ${{ env.INSTALL_NAME }}.tar.gz + path: "lite-xl-*-src.tar.gz" - # All builds use lhelper only for releases, using --static build argument, - # otherwise for normal builds dependencies are dynamically linked. build_linux: name: Linux runs-on: ubuntu-18.04 @@ -52,11 +49,12 @@ jobs: CC: ${{ matrix.config.cc }} CXX: ${{ matrix.config.cxx }} steps: - - name: Set Archive Name + - name: Set Environment Variables if: ${{ matrix.config.cc == 'gcc' }} run: | + echo "$HOME/.local/bin" >> "$GITHUB_PATH" echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" - echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux" >> "$GITHUB_ENV" + echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-linux-$(uname -m)" >> "$GITHUB_ENV" - uses: actions/checkout@v2 - name: Python Setup uses: actions/setup-python@v2 @@ -64,27 +62,24 @@ jobs: python-version: 3.6 - name: Update Packages run: sudo apt-get update - - name: Install Meson - run: | - sudo apt-get install -qq ninja-build - pip3 install meson - name: Install Dependencies if: ${{ !startsWith(github.ref, 'refs/tags/') }} - run: sudo apt-get install -qq libsdl2-dev libfreetype6 + run: bash scripts/install-dependencies.sh --debug + - name: Install Release Dependencies + if: ${{ startsWith(github.ref, 'refs/tags/') }} + run: | + bash scripts/install-dependencies.sh --debug --lhelper + bash scripts/lhelper.sh --debug - name: Build - if: ${{ matrix.config.cc == 'gcc' && !startsWith(github.ref, 'refs/tags/') }} - run: bash scripts/build.sh --prefix / - - name: Release Build - if: ${{ matrix.config.cc == 'gcc' && startsWith(github.ref, 'refs/tags/') }} - run: bash scripts/build.sh --prefix / --static + run: | + bash --version + bash scripts/build.sh --debug --forcefallback - name: Package if: ${{ matrix.config.cc == 'gcc' }} - run: | - DESTDIR="$(pwd)/$INSTALL_NAME" meson install --skip-subprojects -C build - tar czvf "${INSTALL_NAME}".tar.gz "${INSTALL_NAME}" + run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary - name: AppImage if: ${{ matrix.config.cc == 'gcc' && startsWith(github.ref, 'refs/tags/') }} - run: bash scripts/appimage.sh --nobuild --static --version ${{ env.INSTALL_REF }} + run: bash scripts/appimage.sh --nobuild --version ${INSTALL_REF} - name: Upload Artifacts uses: actions/upload-artifact@v2 if: ${{ matrix.config.cc == 'gcc' }} @@ -104,47 +99,42 @@ jobs: - name: System Information run: | system_profiler SPSoftwareDataType + bash --version gcc -v xcodebuild -version - - name: Set Archive Name + - name: Set Environment Variables run: | - echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos" >> "$GITHUB_ENV" - bash --version + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" + echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-macos-$(uname -m)" >> "$GITHUB_ENV" - uses: actions/checkout@v2 - name: Python Setup uses: actions/setup-python@v2 with: python-version: 3.9 - - name: Install Build Tools - run: | - brew install ninja - pip3 install meson - cd ~; npm install appdmg; cd - - ~/node_modules/appdmg/bin/appdmg.js --version - name: Install Dependencies if: ${{ !startsWith(github.ref, 'refs/tags/') }} - run: brew install sdl2 - - name: Install LHelper Dependencies - if: ${{ startsWith(github.ref, 'refs/tags/') }} - run: brew install bash md5sha1sum - - name: Build - if: ${{ !startsWith(github.ref, 'refs/tags/') }} - run: bash scripts/build.sh --prefix "${GITHUB_WORKSPACE}/Lite XL.app" - - name: Release Build + run: bash scripts/install-dependencies.sh --debug + - name: Install Release Dependencies if: ${{ startsWith(github.ref, 'refs/tags/') }} + run: | + bash scripts/install-dependencies.sh --debug --lhelper + bash scripts/lhelper.sh --debug + - name: Build run: | bash --version - bash scripts/build.sh --prefix "${GITHUB_WORKSPACE}/Lite XL.app" --static + bash scripts/build.sh --bundle --debug --forcefallback - name: Error Logs if: failure() run: | mkdir ${INSTALL_NAME} cp /usr/var/lhenv/lite-xl/logs/* ${INSTALL_NAME} tar czvf ${INSTALL_NAME}.tar.gz ${INSTALL_NAME} - - name: Install - run: meson install --skip-subprojects -C build +# - name: Package +# if: ${{ !startsWith(github.ref, 'refs/tags/') }} +# run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons - name: Create DMG Image - run: bash scripts/appdmg.sh ${{ env.INSTALL_NAME }} + run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --dmg - name: Upload DMG Image uses: actions/upload-artifact@v2 with: @@ -178,34 +168,20 @@ jobs: git zip - name: Set Environment Variables - run: echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-$(echo $MSYSTEM | awk '{print tolower($0)}')" >> "$GITHUB_ENV" + run: | + echo "$HOME/.local/bin" >> "$GITHUB_PATH" + echo "INSTALL_NAME=lite-xl-${GITHUB_REF##*/}-windows-$(uname -m)" >> "$GITHUB_ENV" + echo "INSTALL_REF=${GITHUB_REF##*/}" >> "$GITHUB_ENV" - name: Install Dependencies if: ${{ !startsWith(github.ref, 'refs/tags/') }} - run: | - pacman --noconfirm -S \ - ${MINGW_PACKAGE_PREFIX}-gcc \ - ${MINGW_PACKAGE_PREFIX}-meson \ - ${MINGW_PACKAGE_PREFIX}-ninja \ - ${MINGW_PACKAGE_PREFIX}-pkg-config \ - ${MINGW_PACKAGE_PREFIX}-freetype \ - ${MINGW_PACKAGE_PREFIX}-pcre2 \ - ${MINGW_PACKAGE_PREFIX}-SDL2 + run: bash scripts/install-dependencies.sh --debug - name: Install Release Dependencies if: ${{ startsWith(github.ref, 'refs/tags/') }} - run: | - pacman --noconfirm -S \ - ${MINGW_PACKAGE_PREFIX}-gcc \ - ${MINGW_PACKAGE_PREFIX}-meson \ - ${MINGW_PACKAGE_PREFIX}-ninja \ - ${MINGW_PACKAGE_PREFIX}-pkg-config + run: bash scripts/install-dependencies.sh --debug --lhelper - name: Build - if: ${{ !startsWith(github.ref, 'refs/tags/') }} - run: bash scripts/build.sh --prefix / - - name: Release Build - if: ${{ startsWith(github.ref, 'refs/tags/') }} run: | bash --version - bash scripts/build.sh --prefix / --static + bash scripts/build.sh --debug --forcefallback - name: Error Logs if: failure() run: | @@ -213,10 +189,10 @@ jobs: cp /usr/var/lhenv/lite-xl/logs/* ${INSTALL_NAME} tar czvf ${INSTALL_NAME}.tar.gz ${INSTALL_NAME} - name: Package - run: bash scripts/msys2-package.sh --destdir ${INSTALL_NAME} + run: bash scripts/package.sh --version ${INSTALL_REF} --debug --addons --binary - name: Build Installer if: ${{ startsWith(github.ref, 'refs/tags/') }} - run: bash scripts/innosetup/innosetup.sh + run: bash scripts/innosetup/innosetup.sh --debug - name: Upload Artifacts uses: actions/upload-artifact@v2 with: @@ -234,9 +210,8 @@ jobs: deploy: name: Deployment runs-on: ubuntu-18.04 - # Temporarily disabled +# if: startsWith(github.ref, 'refs/tags/') if: false - #if: startsWith(github.ref, 'refs/tags/') needs: - archive_source_code - build_linux diff --git a/.gitignore b/.gitignore index 7dc39e42..16974405 100644 --- a/.gitignore +++ b/.gitignore @@ -10,6 +10,7 @@ subprojects/reproc/ .lite-debug.log .run* *.diff +*.exe *.tar.gz *.zip *.DS_Store @@ -17,3 +18,4 @@ subprojects/reproc/ compile_commands.json error.txt lite-xl* +LiteXL* diff --git a/README.md b/README.md index 32f63d70..73204c57 100644 --- a/README.md +++ b/README.md @@ -49,11 +49,11 @@ If you compile Lite XL yourself, it is recommended to use the script `build-packages.sh`: ```sh -bash build-packages.sh +bash build-packages.sh -h ``` -The script will run Meson and create a zip file with the application or, -for Linux, a tar compressed archive. Lite XL can be easily installed +The script will run Meson and create a tar compressed archive with the application or, +for Windows, a zip file. Lite XL can be easily installed by unpacking the archive in any directory of your choice. Otherwise the following is an example of basic commands if you want to customize @@ -65,7 +65,15 @@ meson compile -C build DESTDIR="$(pwd)/lite-xl" meson install --skip-subprojects -C build ``` -where `` might be one of `/`, `/usr` or `/opt`, the default is `/usr/local`. +where `` might be one of `/`, `/usr` or `/opt`, the default is `/`. +To build a bundle application on macOS: + +```sh +meson setup --buildtype=release --Dbundle=true --prefix / build +meson compile -C build +DESTDIR="$(pwd)/Lite XL.app" meson install --skip-subprojects -C build +``` + Please note that the package is relocatable to any prefix and the option prefix affects only the place where the application is actually installed. diff --git a/build-packages.sh b/build-packages.sh index 6b874cd9..6014be96 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -1,259 +1,156 @@ #!/bin/bash +set -e -# strip-components is normally set to 1 to strip the initial "data" from the -# directory path. -copy_directory_from_repo () { - local tar_options=() - if [[ $1 == --strip-components=* ]]; then - tar_options+=($1) - shift - fi - local dirname="$1" - local destdir="$2" - _archive "$use_branch" "$dirname" "$destdir" "${tar_options[@]}" +if [ ! -e "src/api/api.h" ]; then + echo "Please run this script from the root directory of Lite XL."; exit 1 +fi + +source scripts/common.sh + +show_help() { + echo + echo "Usage: $0 " + echo + echo "Common options:" + echo + echo "-h --help Show this help and exit." + echo "-b --builddir DIRNAME Set the name of the build directory (not path)." + echo " Default: '$(get_default_build_dir)'." + echo "-p --prefix PREFIX Install directory prefix." + echo " Default: '/'." + echo " --debug Debug this script." + echo + echo "Build options:" + echo + echo "-f --forcefallback Force to build subprojects dependencies statically." + echo "-B --bundle Create an App bundle (macOS only)" + echo "-P --portable Create a portable package." + echo + echo "Package options:" + echo + echo "-d --destdir DIRNAME Set the name of the package directory (not path)." + echo " Default: 'lite-xl'." + echo "-v --version VERSION Sets the version on the package name." + echo "-A --appimage Create an AppImage (Linux only)." + echo "-D --dmg Create a DMG disk image (macOS only)." + echo " Requires NPM and AppDMG." + echo "-I --innosetup Create an InnoSetup installer (Windows only)." + echo "-S --source Create a source code package," + echo " including subprojects dependencies." + echo } -_archive () { - if [[ $git_available -eq 1 ]]; then - git archive "$1" "$2" --format=tar | tar xf - -C "$3" "$4" - else - cp -r "$2" "$3" - fi -} +main() { + local build_dir + local build_dir_option=() + local dest_dir + local dest_dir_option=() + local prefix + local prefix_option=() + local version + local version_option=() + local debug + local force_fallback + local appimage + local bundle + local innosetup + local portable -# Check if build directory is ok to be used to build. -build_dir_is_usable () { - local build="$1" - if [[ $build == */* || -z "$build" ]]; then - echo "invalid build directory, no path allowed: \"$build\"" - return 1 - fi - if [[ $git_available -eq 1 ]]; then - git ls-files --error-unmatch "$build" &> /dev/null - if [ $? == 0 ]; then - echo "invalid path, \"$build\" is under revision control" - return 1 - fi - fi -} - -# Ordinary release build -lite_build () { - local build="$1" - build_dir_is_usable "$build" || exit 1 - rm -fr "$build" - setup_options=("${@:2}") - if [[ "$OSTYPE" == "darwin"* ]]; then - setup_options+=(-Dbundle=true) - fi - meson setup --buildtype=release "${setup_options[@]}" "$build" || exit 1 - ninja -C "$build" || exit 1 -} - -# Build using Profile Guided Optimizations (PGO) -lite_build_pgo () { - local build="$1" - build_dir_is_usable "$build" || exit 1 - rm -fr "$build" - echo meson setup --buildtype=release "${@:2}" -Db_pgo=generate "$build" - meson setup --buildtype=release "${@:2}" -Db_pgo=generate "$build" || exit 1 - ninja -C "$build" || exit 1 - copy_directory_from_repo data "$build/src" - "$build/src/lite-xl" - meson configure -Db_pgo=use "$build" - ninja -C "$build" || exit 1 -} - -lite_build_package_windows () { - local portable="-msys" - if [ "$1" == "-portable" ]; then - portable="" - shift - fi - local build="$1" - local arch="$2" - local os="win" - local pdir=".package-build/lite-xl" - if [ -z "$portable" ]; then - local bindir="$pdir" - local datadir="$pdir/data" - else - local bindir="$pdir/bin" - local datadir="$pdir/share/lite-xl" - fi - mkdir -p "$bindir" - mkdir -p "$datadir" - for module_name in core plugins colors fonts; do - copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir" + for i in "$@"; do + case $i in + -h|--help) + show_help + exit 0 + ;; + -b|--builddir) + build_dir="$2" + shift + shift + ;; + -d|--destdir) + dest_dir="$2" + shift + shift + ;; + -f|--forcefallback) + force_fallback="--forcefallback" + shift + ;; + -p|--prefix) + prefix="$2" + shift + shift + ;; + -v|--version) + version="$2" + shift + shift + ;; + -A|--appimage) + appimage="--appimage" + shift + ;; + -B|--bundle) + bundle="--bundle" + shift + ;; + -D|--dmg) + dmg="--dmg" + shift + ;; + -I|--innosetup) + innosetup="--innosetup" + shift + ;; + -P|--portable) + portable="--portable" + shift + ;; + -S|--source) + source="--source" + shift + ;; + --debug) + debug="--debug" + set -x + shift + ;; + *) + # unknown option + ;; + esac done - # copy the meson generated start.lua file - cp "$build/start.lua" "$datadir/core" - for module_name in plugins colors; do - cp -r "$build/third/data/$module_name" "$datadir" - done - cp "$build/src/lite-xl.exe" "$bindir" - strip --strip-all "$bindir/lite-xl.exe" - pushd ".package-build" - local package_name="lite-xl-$os-$arch$portable.zip" - zip "$package_name" -r "lite-xl" - mv "$package_name" .. - popd - rm -fr ".package-build" - echo "created package $package_name" -} -lite_build_package_macos () { - local build="$1" - local arch="$2" - local os="macos" - - local appdir=".package-build/lite-xl.app" - local bindir="$appdir/Contents/MacOS" - local datadir="$appdir/Contents/Resources" - mkdir -p "$bindir" "$datadir" - for module_name in core plugins colors fonts; do - copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir" - done - # copy the meson generated start.lua file - cp "$build/start.lua" "$datadir/core" - for module_name in plugins colors; do - cp -r "$build/third/data/$module_name" "$datadir" - done - cp resources/icons/icon.icns "$appdir/Contents/Resources/icon.icns" - cp "$build/Info.plist" "$appdir/Contents/Info.plist" - cp "$build/src/lite-xl" "$bindir/lite-xl" - strip "$bindir/lite-xl" - pushd ".package-build" - local package_name="lite-xl-$os-$arch.zip" - zip "$package_name" -r "lite-xl.app" - mv "$package_name" .. - popd - rm -fr ".package-build" - echo "created package $package_name" -} - -lite_build_package_linux () { - local portable="" - if [ "$1" == "-portable" ]; then - portable="-portable" - shift - fi - local build="$1" - local arch="$2" - local os="linux" - local pdir=".package-build/lite-xl" - if [ "$portable" == "-portable" ]; then - local bindir="$pdir" - local datadir="$pdir/data" - local docdir="$pdir/doc" - else - local bindir="$pdir/bin" - local datadir="$pdir/share/lite-xl" - local docdir="$pdir/share/doc/lite-xl" - fi - mkdir -p "$bindir" - mkdir -p "$datadir" - mkdir -p "$docdir" - for module_name in core plugins colors fonts; do - copy_directory_from_repo --strip-components=1 "data/$module_name" "$datadir" - done - # copy the meson generated start.lua file - cp "$build/start.lua" "$datadir/core" - for module_name in plugins colors; do - cp -r "$build/third/data/$module_name" "$datadir" - done - cp "$build/src/lite-xl" "$bindir" - cp "licenses/licenses.md" "$docdir" - strip "$bindir/lite-xl" - if [ -z "$portable" ]; then - mkdir -p "$pdir/share/applications" "$pdir/share/icons/hicolor/scalable/apps" "$pdir/share/metainfo" - cp "resources/linux/org.lite_xl.lite_xl.desktop" "$pdir/share/applications" - cp "resources/linux/org.lite_xl.lite_xl.appdata.xml" "$pdir/share/metainfo" - cp "resources/icons/lite-xl.svg" "$pdir/share/icons/hicolor/scalable/apps/lite-xl.svg" - fi - pushd ".package-build" - local package_name="lite-xl-$os-$arch$portable.tar.gz" - tar czf "$package_name" "lite-xl" - mv "$package_name" .. - popd - rm -fr ".package-build" - echo "created package $package_name" -} - -lite_build_package () { - if [[ "$OSTYPE" == msys || "$OSTYPE" == win32 ]]; then - lite_build_package_windows "$@" - elif [[ "$OSTYPE" == "darwin"* ]]; then - lite_build_package_macos "$@" - elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then - lite_build_package_linux "$@" - else - echo "Unknown OS type \"$OSTYPE\"" + if [[ -n $1 ]]; then + show_help exit 1 fi + + if [[ -n $build_dir ]]; then build_dir_option=("--builddir" "${build_dir}"); fi + if [[ -n $dest_dir ]]; then dest_dir_option=("--destdir" "${dest_dir}"); fi + if [[ -n $prefix ]]; then prefix_option=("--prefix" "${prefix}"); fi + if [[ -n $version ]]; then version_option=("--version" "${version}"); fi + + source scripts/build.sh \ + ${build_dir_option[@]} \ + ${prefix_option[@]} \ + $debug \ + $force_fallback \ + $bundle \ + $portable + + source scripts/package.sh \ + ${build_dir_option[@]} \ + ${dest_dir_option[@]} \ + ${prefix_option[@]} \ + ${version_option[@]} \ + --binary \ + --addons \ + $debug \ + $appimage \ + $dmg \ + $innosetup \ + $source } -lite_copy_third_party_modules () { - local build="$1" - curl --insecure -L "https://github.com/rxi/lite-colors/archive/master.zip" -o "$build/rxi-lite-colors.zip" - mkdir -p "$build/third/data/colors" "$build/third/data/plugins" - unzip "$build/rxi-lite-colors.zip" -d "$build" - mv "$build/lite-colors-master/colors" "$build/third/data" - rm -fr "$build/lite-colors-master" -} - -build_opts=() -unset arch -while [ ! -z {$1+x} ]; do - case $1 in - -renderer) - build_opts+=("-Drenderer=true") - shift - ;; - -pgo) - pgo=true - shift - ;; - -branch=*) - use_branch="${1#-branch=}" - shift - ;; - *) - arch="$1" - break - esac -done - -if [ -z ${arch+set} ]; then - echo "usage: $0 [options] " - exit 1 -fi - - if [[ -d ".git" || $BUILD_PROHIBIT_GIT -ne 0 ]]; then - git_available=1; - else - echo "Use of git prohibited. Either '.git' wasn't found or BUILD_PROHIBIT_GIT was set." - git_available=0; - fi - -if [ -z ${use_branch+set} ]; then - if [[ $git_available -eq 1 ]]; then - use_branch="$(git rev-parse --abbrev-ref HEAD)" - else - # it really doesn't matter if git isn't present - use_branch="master" - fi -fi - -build_dir=".build-$arch" - -if [ -z ${pgo+set} ]; then - lite_build "$build_dir" "${build_opts[@]}" -else - lite_build_pgo "$build_dir" "${build_opts[@]}" -fi -lite_copy_third_party_modules "$build_dir" -lite_build_package "$build_dir" "$arch" -if [[ ! ( "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* || "$OSTYPE" == "darwin"* ) ]]; then - lite_build_package -portable "$build_dir" "$arch" -fi +main "$@" diff --git a/meson.build b/meson.build index 611d154a..9d3eac31 100644 --- a/meson.build +++ b/meson.build @@ -42,26 +42,28 @@ endif #=============================================================================== # Dependencies #=============================================================================== -libm = cc.find_library('m', required : false) -libdl = cc.find_library('dl', required : false) -lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep'], - default_options: ['shared=false', 'use_readline=false', 'app=false'] -) -pcre2_dep = dependency('libpcre2-8') -sdl_dep = dependency('sdl2', method: 'config-tool') -reproc_dep = dependency('reproc', fallback: ['reproc', 'reproc_dep'], - default_options: [ - 'default_library=static', 'multithreaded=false', - 'reproc-cpp=false', 'examples=false' - ] -) +if not get_option('source-only') + libm = cc.find_library('m', required : false) + libdl = cc.find_library('dl', required : false) + lua_dep = dependency('lua5.2', fallback: ['lua', 'lua_dep'], + default_options: ['shared=false', 'use_readline=false', 'app=false'] + ) + pcre2_dep = dependency('libpcre2-8') + sdl_dep = dependency('sdl2', method: 'config-tool') + reproc_dep = dependency('reproc', fallback: ['reproc', 'reproc_dep'], + default_options: [ + 'default_library=static', 'multithreaded=false', + 'reproc-cpp=false', 'examples=false' + ] + ) -lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl] + lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl] -if host_machine.system() == 'windows' - # Note that we need to explicitly add the windows socket DLL because - # the pkg-config file from reproc does not include it. - lite_deps += meson.get_compiler('cpp').find_library('ws2_32', required: true) + if host_machine.system() == 'windows' + # Note that we need to explicitly add the windows socket DLL because + # the pkg-config file from reproc does not include it. + lite_deps += meson.get_compiler('cpp').find_library('ws2_32', required: true) + endif endif #=============================================================================== # Install Configuration @@ -118,6 +120,8 @@ configure_file( #=============================================================================== # Targets #=============================================================================== -subdir('lib/font_renderer') -subdir('src') -subdir('scripts') +if not get_option('source-only') + subdir('lib/font_renderer') + subdir('src') + subdir('scripts') +endif diff --git a/meson_options.txt b/meson_options.txt index ce7eca98..1cf3e22f 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,3 +1,4 @@ option('bundle', type : 'boolean', value : false, description: 'Build a macOS bundle') +option('source-only', type : 'boolean', value : false, description: 'Configure source files only, doesn\'t checks for dependencies') option('portable', type : 'boolean', value : false, description: 'Portable install') option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer') diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 00000000..a236599c --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,28 @@ +# Scripts + +Various scripts and configurations used to configure, build, and package Lite XL. + +### Build + +- **build.sh** +- **build-packages.sh**: In root directory, as all in one script; relies to the + ones in this directory. + +### Package + +- **appdmg.sh**: Create a macOS DMG image using [AppDMG][1]. +- **appimage.sh**: [AppImage][2] builder. +- **innosetup.sh**: Creates a 32/64 bit [InnoSetup][3] installer package. +- **package.sh**: Creates all binary / DMG image / installer / source packages. + +### Utility + +- **common.sh**: Common functions used by other scripts. +- **install-dependencies.sh**: Installs required applications to build, package + and run Lite XL, mainly useful for CI and documentation purpose. + Preferably not to be used in user systems. +- **fontello-config.json**: Used by the icons generator. + +[1]: https://github.com/LinusU/node-appdmg +[2]: https://docs.appimage.org/ +[3]: https://jrsoftware.org/isinfo.php diff --git a/scripts/appimage.sh b/scripts/appimage.sh index 65dce3c4..8844fafe 100644 --- a/scripts/appimage.sh +++ b/scripts/appimage.sh @@ -6,9 +6,11 @@ if [ ! -e "src/api/api.h" ]; then exit 1 fi +source scripts/common.sh + show_help(){ echo - echo $0 + echo "Usage: $0 " echo echo "Available options:" echo @@ -23,7 +25,7 @@ show_help(){ } ARCH="$(uname -m)" -BUILD_DIR=build +BUILD_DIR="$(get_default_build_dir)" RUN_BUILD=true STATIC_BUILD=false @@ -67,7 +69,7 @@ if [[ -n $1 ]]; then exit 1 fi -setup_appimagetool(){ +setup_appimagetool() { if ! which appimagetool > /dev/null ; then if [ ! -e appimagetool ]; then if ! wget -O appimagetool "https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-${ARCH}.AppImage" ; then @@ -80,7 +82,7 @@ setup_appimagetool(){ fi } -download_appimage_apprun(){ +download_appimage_apprun() { if [ ! -e AppRun ]; then if ! wget -O AppRun "https://github.com/AppImage/AppImageKit/releases/download/continuous/AppRun-${ARCH}" ; then echo "Could not download AppRun for the arch '${ARCH}'." @@ -91,7 +93,7 @@ download_appimage_apprun(){ fi } -build_litexl(){ +build_litexl() { if [ -e build ]; then rm -rf build fi @@ -106,7 +108,7 @@ build_litexl(){ meson compile -C ${BUILD_DIR} } -generate_appimage(){ +generate_appimage() { if [ -e LiteXL.AppDir ]; then rm -rf LiteXL.AppDir fi diff --git a/scripts/build.sh b/scripts/build.sh index b0e013b1..7ceb3ce4 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -1,103 +1,102 @@ #!/bin/bash -set -ex +set -e if [ ! -e "src/api/api.h" ]; then - echo "Please run this script from the root directory of Lite XL." - exit 1 + echo "Please run this script from the root directory of Lite XL."; exit 1 fi -show_help(){ +source scripts/common.sh + +show_help() { echo echo "Usage: $0 " echo echo "Available options:" echo echo "-b --builddir DIRNAME Sets the name of the build directory (not path)." - echo " Default: 'build'." - echo "-p --prefix Install directory prefix. Mandatory." - echo "-s --static Specify if building using static libraries" - echo " by using lhelper tool." + echo " Default: '$(get_default_build_dir)'." + echo " --debug Debug this script." + echo "-f --forcefallback Force to build dependencies statically." + echo "-h --help Show this help and exit." + echo "-p --prefix PREFIX Install directory prefix. Default: '/'." + echo "-B --bundle Create an App bundle (macOS only)" + echo "-P --portable Create a portable binary package." + echo " macOS: disabled when used with --bundle," + echo " Windows: Implicit being the only option." echo } -install_lhelper() { - if [[ ! -d lhelper ]]; then - git clone https://github.com/franko/lhelper.git - pushd lhelper; bash install-github; popd +main() { + local platform="$(get_platform_name)" + local build_dir="$(get_default_build_dir)" + local prefix=/ + local force_fallback + local bundle + local portable - if [[ "$OSTYPE" == "darwin"* ]]; then - CC=clang CXX=clang++ lhelper create lite-xl -n - else - lhelper create lite-xl -n - fi + for i in "$@"; do + case $i in + -h|--help) + show_help + exit 0 + ;; + -b|--builddir) + build_dir="$2" + shift + shift + ;; + --debug) + set -x + shift + ;; + -f|--forcefallback) + force_fallback="--wrap-mode=forcefallback" + shift + ;; + -p|--prefix) + prefix="$2" + shift + shift + ;; + -B|--bundle) + if [[ "$platform" != "macos" ]]; then + echo "Warning: ignoring --bundle option, works only under macOS." + else + bundle="-Dbundle=true" + fi + shift + ;; + -P|--portable) + portable="-Dportable=true" + shift + ;; + *) + # unknown option + ;; + esac + done + + if [[ -n $1 ]]; then + show_help + exit 1 fi - # Not using `lhelper activate lite-xl` - source "$(lhelper env-source lite-xl)" - - lhelper install freetype2 - lhelper install sdl2 2.0.14-wait-event-timeout-1 - lhelper install pcre2 - - # Help MSYS2 to find the SDL2 include and lib directories to avoid errors - # during build and linking when using lhelper. - if [[ "$OSTYPE" == "msys" ]]; then - CFLAGS=-I${LHELPER_ENV_PREFIX}/include/SDL2 - LDFLAGS=-L${LHELPER_ENV_PREFIX}/lib + if [[ $platform == "macos" && -n $bundle && -n $portable ]]; then + echo "Warning: \"bundle\" and \"portable\" specified; excluding portable package." + portable="" fi -} -build() { + rm -rf "${build_dir}" + CFLAGS=$CFLAGS LDFLAGS=$LDFLAGS meson setup \ --buildtype=release \ - --prefix "$PREFIX" \ - --wrap-mode=forcefallback \ - "${BUILD_DIR}" + --prefix "$prefix" \ + $force_fallback \ + $bundle \ + $portable \ + "${build_dir}" - meson compile -C build + meson compile -C "${build_dir}" } -BUILD_DIR=build -STATIC_BUILD=false - -for i in "$@"; do - case $i in - -h|--belp) - show_help - exit 0 - ;; - -b|--builddir) - BUILD_DIR="$2" - shift - shift - ;; - -p|--prefix) - PREFIX="$2" - shift - shift - ;; - -s|--static) - STATIC_BUILD=true - shift - ;; - *) - # unknown option - ;; - esac -done - -if [[ -n $1 ]]; then - show_help - exit 1 -fi - -if [[ -z $PREFIX ]]; then - echo "ERROR: prefix argument is missing." - exit 1 -fi - -if [[ $STATIC_BUILD == true ]]; then - install_lhelper -fi - -build +main "$@" diff --git a/scripts/common.sh b/scripts/common.sh new file mode 100644 index 00000000..2b49d362 --- /dev/null +++ b/scripts/common.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +set -e + +get_platform_name() { + if [[ "$OSTYPE" == "msys" ]]; then + echo "windows" + elif [[ "$OSTYPE" == "darwin"* ]]; then + echo "macos" + elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then + echo "linux" + else + echo "UNSUPPORTED-OS" + fi +} + +get_default_build_dir() { + platform=$(get_platform_name) + echo "build-$platform-$(uname -m)" +} + +if [[ $(get_platform_name) == "UNSUPPORTED-OS" ]]; then + echo "Error: unknown OS type: \"$OSTYPE\"" + exit 1 +fi diff --git a/scripts/innosetup/innosetup.iss.in b/scripts/innosetup/innosetup.iss.in index 83a730d8..2b669fc0 100644 --- a/scripts/innosetup/innosetup.iss.in +++ b/scripts/innosetup/innosetup.iss.in @@ -9,7 +9,7 @@ ; Use /dArch option to create a setup for a different architecture, e.g.: ; iscc /dArch=x86 innosetup.iss #ifndef Arch -#define Arch "x64" + #define Arch "x64" #endif [Setup] @@ -27,8 +27,11 @@ AppSupportURL={#MyAppURL} AppUpdatesURL={#MyAppURL} #if Arch=="x64" -ArchitecturesAllowed=x64 -ArchitecturesInstallIn64BitMode={#Arch} + ArchitecturesAllowed=x64 + ArchitecturesInstallIn64BitMode=x64 + #define ArchInternal "x86_64" +#else + #define ArchInternal "i686" #endif AllowNoIcons=yes @@ -48,7 +51,7 @@ PrivilegesRequiredOverridesAllowed=dialog UsedUserAreasWarning=no OutputDir=. -OutputBaseFilename=LiteXL-{#MyAppVersion}-{#Arch}-setup +OutputBaseFilename=LiteXL-{#MyAppVersion}-{#ArchInternal}-setup ;DisableDirPage=yes ;DisableProgramGroupPage=yes @@ -67,18 +70,16 @@ Name: "portablemode"; Description: "Portable Mode"; Flags: unchecked [Files] Source: "{#BuildDir}/src/lite-xl.exe"; DestDir: "{app}"; Flags: ignoreversion -; MSYS2 produces no external dlls on 32 bit builds when using lhelper -#if Arch=="x64" -Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion -#endif +Source: "{#BuildDir}/mingwLibs{#Arch}/*"; DestDir: "{app}"; Flags: ignoreversion ; Check: DirExists(ExpandConstant('{#BuildDir}/mingwLibs{#Arch}')) Source: "{#SourceDir}/data/*"; DestDir: "{app}/data"; Flags: ignoreversion recursesubdirs ; NOTE: Don't use "Flags: ignoreversion" on any shared system files [Icons] +Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Check: not WizardIsTaskSelected('portablemode') Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"; Check: not WizardIsTaskSelected('portablemode') -Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: quicklaunchicon; Check: not WizardIsTaskSelected('portablemode') +; Name: "{usersendto}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}" [Run] Filename: "{app}/{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent diff --git a/scripts/innosetup/innosetup.sh b/scripts/innosetup/innosetup.sh index 44a6c787..4384d13c 100644 --- a/scripts/innosetup/innosetup.sh +++ b/scripts/innosetup/innosetup.sh @@ -1,55 +1,65 @@ #!/bin/bash -set -ex +set -e if [ ! -e "src/api/api.h" ]; then - echo "Please run this script from the root directory of Lite XL." - exit 1 + echo "Please run this script from the root directory of Lite XL."; exit 1 fi -show_help(){ +source scripts/common.sh + +show_help() { echo echo "Usage: $0 " echo echo "Available options:" echo echo "-b --builddir DIRNAME Sets the name of the build directory (not path)." - echo " Default: 'build'." + echo " Default: '$(get_default_build_dir)'." + echo " --debug Debug this script." echo } -BUILD_DIR=build +main() { + local build_dir=$(get_default_build_dir) + local arch -for i in "$@"; do - case $i in - -h|--belp) - show_help - exit 0 - ;; - -b|--BUILD_DIR) - BUILD_DIR="$2" - shift - shift - ;; - *) - # unknown option - ;; - esac -done + if [[ $MSYSTEM == "MINGW64" ]]; then arch=x64; else arch=Win32; fi -if [[ -n $1 ]]; then - show_help - exit 1 -fi + for i in "$@"; do + case $i in + -h|--help) + show_help + exit 0 + ;; + -b|--builddir) + build_dir="$2" + shift + shift + ;; + --debug) + set -x + shift + ;; + *) + # unknown option + ;; + esac + done -# TODO: Required MinGW dlls are built only (?) when using lhelper on 64 bit -if [[ $MSYSTEM == "MINGW64" ]]; then - ARCH=x64; - mingwLibsDir=$BUILD_DIR/mingwLibs$ARCH + if [[ -n $1 ]]; then + show_help + exit 1 + fi + + # Copy MinGW libraries dependencies. + # MSYS2 ldd command seems to be only 64bit, so use ntldd + # see https://github.com/msys2/MINGW-packages/issues/4164 + local mingwLibsDir="${build_dir}/mingwLibs$arch" mkdir -p "$mingwLibsDir" - ldd "$BUILD_DIR/src/lite-xl.exe" | grep mingw | awk '{print $3}' | xargs -I '{}' cp -v '{}' $mingwLibsDir -else - ARCH=Win32 -fi + ntldd -R "${build_dir}/src/lite-xl.exe" | grep mingw | awk '{print $3}' | sed 's#\\#/#g' | xargs -I '{}' cp -v '{}' $mingwLibsDir -/c/Program\ Files\ \(x86\)/Inno\ Setup\ 6/ISCC.exe -dARCH=$ARCH $BUILD_DIR/scripts/innosetup.iss -mv $BUILD_DIR/LiteXL*.exe $(pwd) + "/c/Program Files (x86)/Inno Setup 6/ISCC.exe" -dARCH=$arch "${build_dir}/scripts/innosetup.iss" + pushd "${build_dir}/scripts"; mv LiteXL*.exe "./../../"; popd +} + +main "$@" diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh new file mode 100644 index 00000000..bad3b358 --- /dev/null +++ b/scripts/install-dependencies.sh @@ -0,0 +1,74 @@ +#!/bin/bash +set -ex + +if [ ! -e "src/api/api.h" ]; then + echo "Please run this script from the root directory of Lite XL."; exit 1 +fi + +show_help() { + echo + echo "Lite XL dependecies installer. Mainly used for CI but can also work on users systems." + echo "USE IT AT YOUR OWN RISK!" + echo + echo "Usage: $0 " + echo + echo "Available options:" + echo + echo "-l --lhelper Install tools required by LHelper and doesn't" + echo " install external libraries." + echo " --debug Debug this script." + echo +} + +main() { + local lhelper=false + + for i in "$@"; do + case $i in + -s|--lhelper) + lhelper=true + shift + ;; + --debug) + set -x + shift + ;; + *) + # unknown option + ;; + esac + done + + if [[ -n $1 ]]; then + show_help + exit 1 + fi + + if [[ "$OSTYPE" == "linux"* ]]; then + if [[ $lhelper == true ]]; then + sudo apt-get install -qq ninja-build + else + sudo apt-get install -qq ninja-build libsdl2-dev libfreetype6 + fi + pip3 install meson + elif [[ "$OSTYPE" == "darwin"* ]]; then + if [[ $lhelper == true ]]; then + brew install bash md5sha1sum ninja + else + brew install ninja sdl2 + fi + pip3 install meson + cd ~; npm install appdmg; cd - + ~/node_modules/appdmg/bin/appdmg.js --version + elif [[ "$OSTYPE" == "msys" ]]; then + if [[ $lhelper == true ]]; then + pacman --noconfirm -S \ + ${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config} unzip + else + pacman --noconfirm -S \ + ${MINGW_PACKAGE_PREFIX}-{gcc,meson,ninja,ntldd,pkg-config,freetype,pcre2,SDL2} unzip + fi + fi +} + +main "$@" diff --git a/scripts/lhelper.sh b/scripts/lhelper.sh new file mode 100644 index 00000000..af6ae158 --- /dev/null +++ b/scripts/lhelper.sh @@ -0,0 +1,75 @@ +#!/bin/bash +set -e + +show_help() { + echo + echo "Usage: $0 " + echo + echo "Available options:" + echo + echo " --debug Debug this script." + echo "-h --help Show this help and exit." + echo "-p --prefix PREFIX Install directory prefix." + echo " Default: '$HOME/.local'." + echo +} + +main() { + local lhelper_prefix="$HOME/.local" + + for i in "$@"; do + case $i in + -h|--help) + show_help + exit 0 + ;; + -p|--prefix) + lhelper_prefix="$2" + echo "LHelper prefix set to: \"${lhelper_prefix}\"" + shift + shift + ;; + --debug) + set -x + shift + ;; + *) + # unknown option + ;; + esac + done + + if [[ -n $1 ]]; then show_help; exit 1; fi + + if [[ ! -f ${lhelper_prefix}/bin/lhelper ]]; then + + git clone https://github.com/franko/lhelper.git + + # FIXME: This should be set in ~/.bash_profile if not using CI + # export PATH="${HOME}/.local/bin:${PATH}" + mkdir -p "${lhelper_prefix}/bin" + pushd lhelper; bash install "${lhelper_prefix}"; popd + + if [[ "$OSTYPE" == "darwin"* ]]; then + CC=clang CXX=clang++ lhelper create lite-xl -n + else + lhelper create lite-xl -n + fi + fi + + # Not using $(lhelper activate lite-xl) to support CI + source "$(lhelper env-source lite-xl)" + + lhelper install freetype2 + lhelper install sdl2 2.0.14-wait-event-timeout-1 + lhelper install pcre2 + + # Help MSYS2 to find the SDL2 include and lib directories to avoid errors + # during build and linking when using lhelper. + if [[ "$OSTYPE" == "msys" ]]; then + CFLAGS=-I${LHELPER_ENV_PREFIX}/include/SDL2 + LDFLAGS=-L${LHELPER_ENV_PREFIX}/lib + fi +} + +main diff --git a/scripts/msys2-package.sh b/scripts/msys2-package.sh deleted file mode 100644 index 5138d8a3..00000000 --- a/scripts/msys2-package.sh +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/bash -set -ex - -if [ ! -e "src/api/api.h" ]; then - echo "Please run this script from the root directory of Lite XL." - exit 1 -fi - -# FIXME: For some strange reason sometimes an error occurs randomly on the -# MINGW32 build of GitHub Actions; the environment variable $INSTALL_NAME -# is correct, but it expands with a drive letter at the end (all builds). - -show_help(){ - echo - echo "Usage: $0 " - echo - echo "Available options:" - echo - echo "-b --builddir DIRNAME Sets the name of the build directory (not path)." - echo " Default: 'build'." - echo "-d --destdir DIRNAME Sets the name of the install directory (not path)." - echo -} - -BUILD_DIR=build - -for i in "$@"; do - case $i in - -h|--belp) - show_help - exit 0 - ;; - -b|--builddir) - BUILD_DIR="$2" - shift - shift - ;; - -d|--destdir) - DEST_DIR="$2" - shift - shift - ;; - *) - # unknown option - ;; - esac -done - -if [[ -n $1 ]]; then - show_help - exit 1 -fi - -DESTDIR="$(pwd)/${DEST_DIR}" meson install --skip-subprojects -C ${BUILD_DIR} - -zip -9rv ${DEST_DIR}.zip ${DEST_DIR}/* diff --git a/scripts/package.sh b/scripts/package.sh new file mode 100644 index 00000000..b014c41c --- /dev/null +++ b/scripts/package.sh @@ -0,0 +1,252 @@ +#!/bin/bash +set -e + +if [ ! -e "src/api/api.h" ]; then + echo "Please run this script from the root directory of Lite XL."; exit 1 +fi + +source scripts/common.sh + +show_help() { + echo + echo "Usage: $0 " + echo + echo "Available options:" + echo + echo "-b --builddir DIRNAME Sets the name of the build directory (not path)." + echo " Default: '$(get_default_build_dir)'." + echo "-d --destdir DIRNAME Set the name of the package directory (not path)." + echo " Default: 'lite-xl'." + echo "-h --help Show this help and exit." + echo "-p --prefix PREFIX Install directory prefix. Default: '/'." + echo "-v --version VERSION Sets the version on the package name." + echo " --addons Install 3rd party addons (currently RXI colors)." + echo " --debug Debug this script." + echo "-A --appimage Create an AppImage (Linux only)." + echo "-B --binary Create a normal / portable package or macOS bundle," + echo " depending on how the build was configured. (Default.)" + echo "-D --dmg Create a DMG disk image with AppDMG (macOS only)." + echo "-I --innosetup Create a InnoSetup package (Windows only)." + echo "-S --source Create a source code package," + echo " including subprojects dependencies." + echo +} + +# Addons installation: some distributions forbid external downloads +# so make it as optional module. +install_addons() { + local build_dir="$1" + local data_dir="$2" + + if [[ -d "${build_dir}/third/data/colors" ]]; then + echo "Warning: found previous colors addons installation, skipping." + return 0 + fi + + # Copy third party color themes + curl --insecure \ + -L "https://github.com/rxi/lite-colors/archive/master.zip" \ + -o "${build_dir}/rxi-lite-colors.zip" + + mkdir -p "${build_dir}/third/data/colors" + unzip "${build_dir}/rxi-lite-colors.zip" -d "${build_dir}" + mv "${build_dir}/lite-colors-master/colors" "${build_dir}/third/data" + rm -rf "${build_dir}/lite-colors-master" + + for module_name in colors; do + cp -r "${build_dir}/third/data/$module_name" "${data_dir}" + done +} + +source_package() { + local build_dir=build-src + local package_name=$1 + + rm -rf ${build_dir} + rm -rf ${package_name} + rm -f ${package_name}.tar.gz + + meson subprojects download + meson setup ${build_dir} -Dsource-only=true + + # Note: not using git-archive(-all) because it can't include subprojects ignored by git + rsync -arv \ + --exclude /*build*/ \ + --exclude *.git* \ + --exclude lhelper \ + --exclude lite-xl* \ + --exclude submodules \ + . ${package_name} + + cp "${build_dir}/start.lua" "${package_name}/data/core" + + tar rf ${package_name}.tar ${package_name} + gzip -9 ${package_name}.tar +} + +main() { + local arch="$(uname -m)" + local platform="$(get_platform_name)" + local build_dir="$(get_default_build_dir)" + local dest_dir=lite-xl + local prefix=/ + local version + local addons=false + local appimage=false + local binary=false + local dmg=false + local innosetup=false + local source=false + + for i in "$@"; do + case $i in + -b|--builddir) + build_dir="$2" + shift + shift + ;; + -d|--destdir) + dest_dir="$2" + shift + shift + ;; + -h|--help) + show_help + exit 0 + ;; + -p|--prefix) + prefix="$2" + shift + shift + ;; + -v|--version) + if [[ -n $2 ]]; then version="-$2"; fi + shift + shift + ;; + -A|--appimage) + if [[ "$platform" != "linux" ]]; then + echo "Warning: ignoring --appimage option, works only under Linux." + else + appimage=true + fi + shift + ;; + -B|--binary) + binary=true + shift + ;; + -D|--dmg) + if [[ "$platform" != "macos" ]]; then + echo "Warning: ignoring --dmg option, works only under macOS." + else + dmg=true + fi + shift + ;; + -I|--innosetup) + if [[ "$platform" != "windows" ]]; then + echo "Warning: ignoring --innosetup option, works only under Windows." + else + innosetup=true + fi + shift + ;; + -S|--source) + source=true + shift + ;; + --addons) + addons=true + shift + ;; + --debug) + set -x + shift + ;; + *) + # unknown option + ;; + esac + done + + if [[ -n $1 ]]; then show_help; exit 1; fi + + # The source package doesn't require a previous build, + # nor the following install step, so run it now. + if [[ $source == true ]]; then source_package "lite-xl$version-src"; fi + + # No packages request + if [[ $appimage == false && $binary == false && $dmg == false && $innosetup == false ]]; then + # Source only, return. + if [[ $source == true ]]; then return 0; fi + # Build the binary package as default instead doing nothing. + binary=true + fi + + rm -rf "${dest_dir}" + + DESTDIR="$(pwd)/${dest_dir}" meson install -C "${build_dir}" + + local data_dir="$(pwd)/${dest_dir}/data" + local exe_file="$(pwd)/${dest_dir}/lite-xl" + local package_name=lite-xl$version-$platform-$arch + local bundle=false + local stripcmd="strip" + + if [[ -d "${data_dir}" ]]; then + # Portable archive + exe_file="$(pwd)/${dest_dir}/lite-xl" + if [[ $platform == "windows" ]]; then + exe_file="${exe_file}.exe" + stripcmd="strip --strip-all" + else + # Windows archive is always portable + package_name+="-portable" + fi + elif [[ $platform == "macos" && ! -d "${data_dir}" ]]; then + # macOS bundle app + bundle=true + # Specify "bundle" on compressed archive only, implicit on images + if [[ $dmg == false ]]; then package_name+="-bundle"; fi + rm -rf "Lite XL.app"; mv "${dest_dir}" "Lite XL.app" + dest_dir="Lite XL.app" + data_dir="$(pwd)/${dest_dir}/Contents/Resources" + exe_file="$(pwd)/${dest_dir}/Contents/MacOS/lite-xl" + else + data_dir="$(pwd)/${dest_dir}/$prefix/share/lite-xl" + exe_file="$(pwd)/${dest_dir}/$prefix/bin/lite-xl" + fi + + mkdir -p "${data_dir}" + + if [[ $addons == true ]]; then install_addons "${build_dir}" "${data_dir}"; fi + + # TODO: use --skip-subprojects when 0.58.0 will be available on supported + # distributions to avoid subprojects' include and lib directories to be copied. + # Install Meson with PIP to get the latest version is not always possible. + pushd "${dest_dir}" + find . -type d -name 'include' -prune -exec rm -rf {} \; + find . -type d -name 'lib' -prune -exec rm -rf {} \; + find . -type d -empty -delete + popd + + $stripcmd "${exe_file}" + + if [[ $binary == true ]]; then + rm -f "${package_name}".tar.gz + rm -f "${package_name}".zip + + if [[ $platform == "windows" ]]; then + zip -9rv ${package_name}.zip ${dest_dir}/* + else + tar czvf "${package_name}".tar.gz "${dest_dir}" + fi + fi + + if [[ $appimage == true ]]; then source scripts/appimage.sh; fi + if [[ $bundle == true && $dmg == true ]]; then source scripts/appdmg.sh "${package_name}"; fi + if [[ $innosetup == true ]]; then source scripts/innosetup/innosetup.sh -b "${build_dir}"; fi +} + +main "$@" diff --git a/scripts/source-package.sh b/scripts/source-package.sh deleted file mode 100644 index aff80c9d..00000000 --- a/scripts/source-package.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash -set -ex - -if [ ! -e "src/api/api.h" ]; then - echo "Please run this script from the root directory of Lite XL." - exit 1 -fi - -show_help(){ - echo - echo "Usage: $0 " - echo - echo "Available options:" - echo - echo "-b --builddir DIRNAME Sets the name of the build directory (no path)." - echo " Default: 'build'." - echo "-d --destdir DIRNAME Sets the name of the install directory (no path)." - echo -} - -BUILD_DIR=build -DEST_DIR=lite-xl-src - -for i in "$@"; do - case $i in - -h|--belp) - show_help - exit 0 - ;; - -b|--builddir) - BUILD_DIR="$2" - shift - shift - ;; - -d|--destdir) - DEST_DIR="$2" - shift - shift - ;; - *) - # unknown option - ;; - esac -done - -if [[ -n $1 ]]; then - show_help - exit 1 -fi - -if test -d ${BUILD_DIR}; then rm -rf ${BUILD_DIR}; fi -if test -d ${DEST_DIR}; then rm -rf ${DEST_DIR}; fi -if test -f ${DEST_DIR}.tar.gz; then rm ${DEST_DIR}.tar.gz; fi - -meson subprojects download -meson setup ${BUILD_DIR} - -rsync -arv \ - --exclude /*build*/ \ - --exclude *.git* \ - --exclude lhelper \ - --exclude lite-xl* \ - --exclude submodules \ - . ${DEST_DIR} - -cp "${BUILD_DIR}/start.lua" "${DEST_DIR}/data/core" - -tar rf ${DEST_DIR}.tar ${DEST_DIR} -gzip -9 ${DEST_DIR}.tar From b4080ba14831d3a1bfa4ef198811a2cfb1e7ce22 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sun, 5 Sep 2021 15:43:22 +0200 Subject: [PATCH 041/165] Bring back pgo option in new build package script --- build-packages.sh | 10 +++++++++- scripts/build.sh | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/build-packages.sh b/build-packages.sh index 6014be96..4ecda0a0 100755 --- a/build-packages.sh +++ b/build-packages.sh @@ -25,6 +25,8 @@ show_help() { echo "-f --forcefallback Force to build subprojects dependencies statically." echo "-B --bundle Create an App bundle (macOS only)" echo "-P --portable Create a portable package." + echo "-O --pgo Use profile guided optimizations (pgo)." + echo " Requires running the application iteractively." echo echo "Package options:" echo @@ -55,6 +57,7 @@ main() { local bundle local innosetup local portable + local pgo for i in "$@"; do case $i in @@ -110,6 +113,10 @@ main() { source="--source" shift ;; + -O|--pgo) + pgo="--pgo" + shift + ;; --debug) debug="--debug" set -x @@ -137,7 +144,8 @@ main() { $debug \ $force_fallback \ $bundle \ - $portable + $portable \ + $pgo source scripts/package.sh \ ${build_dir_option[@]} \ diff --git a/scripts/build.sh b/scripts/build.sh index 7ceb3ce4..75212468 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -21,6 +21,7 @@ show_help() { echo "-p --prefix PREFIX Install directory prefix. Default: '/'." echo "-B --bundle Create an App bundle (macOS only)" echo "-P --portable Create a portable binary package." + echo "-O --pgo Use profile guided optimizations (pgo)." echo " macOS: disabled when used with --bundle," echo " Windows: Implicit being the only option." echo @@ -33,6 +34,7 @@ main() { local force_fallback local bundle local portable + local pgo for i in "$@"; do case $i in @@ -70,6 +72,10 @@ main() { portable="-Dportable=true" shift ;; + -O|--pgo) + pgo="-Db_pgo=generate" + shift + ;; *) # unknown option ;; @@ -94,9 +100,18 @@ main() { $force_fallback \ $bundle \ $portable \ + $pgo \ "${build_dir}" meson compile -C "${build_dir}" + + if [ ! -z ${pgo+x} ]; then + cp -r data "${build_dir}/src" + "${build_dir}/src/lite-xl" + meson configure -Db_pgo=use "${build_dir}" + meson compile -C "${build_dir}" + rm -fr "${build_dir}/data" + fi } main "$@" From aa9e2e2df516bf2204c3c8dce380c3a3c9bee656 Mon Sep 17 00:00:00 2001 From: redtide Date: Sun, 5 Sep 2021 19:29:17 +0200 Subject: [PATCH 042/165] Fixed some build scripts issues, keep bash always updated on macOS --- scripts/install-dependencies.sh | 2 +- scripts/package.sh | 25 ++++++++++++++++--------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/scripts/install-dependencies.sh b/scripts/install-dependencies.sh index bad3b358..2f9519b1 100644 --- a/scripts/install-dependencies.sh +++ b/scripts/install-dependencies.sh @@ -55,7 +55,7 @@ main() { if [[ $lhelper == true ]]; then brew install bash md5sha1sum ninja else - brew install ninja sdl2 + brew install bash ninja sdl2 fi pip3 install meson cd ~; npm install appdmg; cd - diff --git a/scripts/package.sh b/scripts/package.sh index b014c41c..1370aee8 100644 --- a/scripts/package.sh +++ b/scripts/package.sh @@ -192,10 +192,12 @@ main() { local exe_file="$(pwd)/${dest_dir}/lite-xl" local package_name=lite-xl$version-$platform-$arch local bundle=false + local portable=false local stripcmd="strip" if [[ -d "${data_dir}" ]]; then - # Portable archive + echo "Creating a portable, compressed archive..." + portable=true exe_file="$(pwd)/${dest_dir}/lite-xl" if [[ $platform == "windows" ]]; then exe_file="${exe_file}.exe" @@ -205,15 +207,20 @@ main() { package_name+="-portable" fi elif [[ $platform == "macos" && ! -d "${data_dir}" ]]; then - # macOS bundle app - bundle=true - # Specify "bundle" on compressed archive only, implicit on images - if [[ $dmg == false ]]; then package_name+="-bundle"; fi - rm -rf "Lite XL.app"; mv "${dest_dir}" "Lite XL.app" - dest_dir="Lite XL.app" data_dir="$(pwd)/${dest_dir}/Contents/Resources" - exe_file="$(pwd)/${dest_dir}/Contents/MacOS/lite-xl" - else + if [[ -d "${data_dir}" ]]; then + echo "Creating a macOS bundle application..." + bundle=true + # Specify "bundle" on compressed archive only, implicit on images + if [[ $dmg == false ]]; then package_name+="-bundle"; fi + rm -rf "Lite XL.app"; mv "${dest_dir}" "Lite XL.app" + dest_dir="Lite XL.app" + exe_file="$(pwd)/${dest_dir}/Contents/MacOS/lite-xl" + fi + fi + + if [[ $bundle == false && $portable == false ]]; then + echo "Creating a compressed archive..." data_dir="$(pwd)/${dest_dir}/$prefix/share/lite-xl" exe_file="$(pwd)/${dest_dir}/$prefix/bin/lite-xl" fi From 90c721b823eec21930ddb91d486f1858d4cbf93c Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 15:11:20 +0200 Subject: [PATCH 043/165] Adopt bigger fonts by default --- data/core/init.lua | 4 ++-- data/core/style.lua | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/data/core/init.lua b/data/core/init.lua index 6b770fd6..018beadb 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -320,8 +320,8 @@ local style = require "core.style" ------------------------------- Fonts ---------------------------------------- -- customize fonts: --- style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 13 * SCALE) --- style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE) +-- 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) -- -- font names used by lite: -- style.font : user interface diff --git a/data/core/style.lua b/data/core/style.lua index 7cf16eb1..faca166e 100644 --- a/data/core/style.lua +++ b/data/core/style.lua @@ -21,11 +21,11 @@ style.tab_width = common.round(170 * SCALE) -- -- On High DPI monitor or non RGB monitor you may consider using antialiasing grayscale instead. -- The antialiasing grayscale with full hinting is interesting for crisp font rendering. -style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 13 * SCALE) +style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 14 * SCALE) style.big_font = style.font:copy(40 * SCALE) -style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 14 * SCALE, {antialiasing="grayscale", hinting="full"}) -style.icon_big_font = style.icon_font:copy(20 * SCALE) -style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 13 * SCALE) +style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 16 * SCALE, {antialiasing="grayscale", hinting="full"}) +style.icon_big_font = style.icon_font:copy(24 * SCALE) +style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 14 * SCALE) style.background = { common.color "#2e2e32" } style.background2 = { common.color "#252529" } From 67d7b894ae90e04413a2075d5e2f7cf4a8976bef Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 15:23:41 +0200 Subject: [PATCH 044/165] Add initial suggestion in open-project-folder --- data/core/commands/core.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/core/commands/core.lua b/data/core/commands/core.lua index 215ff654..432ded89 100644 --- a/data/core/commands/core.lua +++ b/data/core/commands/core.lua @@ -164,6 +164,10 @@ command.add(nil, { end, ["core:open-project-folder"] = function() + local dirname = common.dirname(core.project_dir) + if dirname then + core.command_view:set_text(common.home_encode(dirname) .. PATHSEP) + end core.command_view:enter("Open Project", function(text, item) text = common.home_expand(item and item.text or text) local path_stat = system.get_file_info(text) From 2b277bb50224fdc52aa76c838b66640010dfa4bb Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 06:44:15 -0700 Subject: [PATCH 045/165] Fix problem with -psn argument on macOS --- data/core/init.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/core/init.lua b/data/core/init.lua index 018beadb..e84792ac 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -461,7 +461,10 @@ function core.init() project_dir = arg_filename project_dir_explicit = true else - delayed_error = string.format("error: invalid file or directory %q", ARGS[i]) + -- on macOS we can get an argument like "-psn_0_52353" that we just ignore. + if not ARGS[i]:match("^-psn") then + delayed_error = string.format("error: invalid file or directory %q", ARGS[i]) + end end end From 0a36e66abaa908fe84306938db178431c81ce25b Mon Sep 17 00:00:00 2001 From: Guldoman Date: Tue, 7 Sep 2021 22:14:40 +0200 Subject: [PATCH 046/165] Fix `treeview:open-in-system` command on Windows The first argument is the title for the `CMD` window. --- data/plugins/treeview.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua index 48d6f5a7..84d5dd28 100644 --- a/data/plugins/treeview.lua +++ b/data/plugins/treeview.lua @@ -536,7 +536,7 @@ command.add(function() return view.hovered_item ~= nil end, { local hovered_item = view.hovered_item if PLATFORM == "Windows" then - system.exec("start " .. hovered_item.abs_filename) + system.exec(string.format("start \"\" %q", hovered_item.abs_filename)) elseif string.find(PLATFORM, "Mac") then system.exec(string.format("open %q", hovered_item.abs_filename)) elseif PLATFORM == "Linux" then From 48c709a95fc2e84e12f667c20349e7de0d6022a2 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 22:46:58 +0200 Subject: [PATCH 047/165] Prepare 2.0.2 version and changelog --- changelog.md | 35 +++++++++++++++++++++++++++++++++++ meson.build | 2 +- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index d6e30519..2cf29061 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,40 @@ This files document the changes done in Lite XL for each release. +### 2.0.2 + +Fix problem project directory when starting the application from Launcher on macOS. + +Improved LogView by @takase1121. + +Fix problem when trying to close an unsaved new document. +Fix `treeview:open-in-system` command on Windows. +Fix rename command to update name of document if opened. +Contributed by @Guldoman. + +Improve the find and replace dialog so that previously used expressions can be recalled +using "up" and "down" keys. + +Multi-cursors fixes and improvement by @adamharrison. + +Build package script rewrite and enhancement by @redtide. + +Use bigger fonts by default. + +Other minor improvements and fixes. + +### 2.0.1 + +Fix a few bugs and we mandate the mod-version 2 for plugins. +This means that users should ensure they have up-to-date plugins for Lite XL 2.0. + +Here some details about the bug fixes: + +- fix a bug that created a fatal error when using the command to change project folder or when closing all the active documents +- add a limit to avoid scaling fonts too much and fix a related invalid memory access for very small fonts +- fix focus problem with NagView when switching project directory +- fix error that prevented the verification of plugins versions +- fix error on X11 that caused a bug window event on exit + ### 2.0 The 2.0 version of lite contains *breaking changes* to lite, in terms of how plugin settings are structured; diff --git a/meson.build b/meson.build index 9d3eac31..047ce90e 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('lite-xl', ['c', 'cpp'], - version : '2.0.1', + version : '2.0.2', license : 'MIT', meson_version : '>= 0.54', default_options : ['c_std=gnu11', 'cpp_std=c++03'] From 16170e8db9628d83b80fa24f077ea2b9fdef574f Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Wed, 8 Sep 2021 10:27:45 +0200 Subject: [PATCH 048/165] Improve info.plist for macOS package config As suggested by @redtide and @Timofffee. --- resources/macos/Info.plist.in | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/resources/macos/Info.plist.in b/resources/macos/Info.plist.in index c15fd566..4d715f2f 100644 --- a/resources/macos/Info.plist.in +++ b/resources/macos/Info.plist.in @@ -2,24 +2,28 @@ -CFBundleExecutable + CFBundleExecutable lite-xl CFBundleGetInfoString lite-xl CFBundleIconFile - icon + icon.icns CFBundleName - lite-xl + Lite XL CFBundlePackageType APPL NSHighResolutionCapable - LSMinimumSystemVersion10.11 - NSDocumentsFolderUsageDescriptionTo access, edit and index your projects. - NSDesktopFolderUsageDescriptionTo access, edit and index your projects. - NSDownloadsFolderUsageDescriptionTo access, edit and index your projects. + LSMinimumSystemVersion + 10.11 + NSDocumentsFolderUsageDescription + To access, edit and index your projects. + NSDesktopFolderUsageDescription + To access, edit and index your projects. + NSDownloadsFolderUsageDescription + To access, edit and index your projects. CFBundleShortVersionString - @PROJECT_VERSION@ + @PROJECT_VERSION@ NSHumanReadableCopyright © 2019-2021 Francesco Abbate From 1a21c66353826dd387aaeda6b36c60bf815a941c Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Wed, 8 Sep 2021 22:46:21 +0800 Subject: [PATCH 049/165] add autocomplete:cycle Some (probably lots) of people are used to tabbing through autocomplete. now, tab is binded to autocomplete:cycle while enter is binded to autocomplete:complete. --- data/plugins/autocomplete.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/data/plugins/autocomplete.lua b/data/plugins/autocomplete.lua index 484199a9..f490556c 100644 --- a/data/plugins/autocomplete.lua +++ b/data/plugins/autocomplete.lua @@ -10,7 +10,7 @@ local RootView = require "core.rootview" local DocView = require "core.docview" local Doc = require "core.doc" -config.plugins.autocomplete = { +config.plugins.autocomplete = { -- Amount of characters that need to be written for autocomplete min_len = 3, -- The max amount of visible items @@ -502,6 +502,11 @@ command.add(predicate, { suggestions_idx = math.min(suggestions_idx + 1, #suggestions) end, + ["autocomplete:cycle"] = function() + local newidx = suggestions_idx + 1 + suggestions_idx = newidx > #suggestions and 1 or newidx + end, + ["autocomplete:cancel"] = function() reset_suggestions() end, @@ -511,9 +516,11 @@ command.add(predicate, { -- Keymaps -- keymap.add { - ["tab"] = "autocomplete:complete", + ["return"] = "autocomplete:complete", + ["keypad enter"] = "autocomplete:complete", ["up"] = "autocomplete:previous", ["down"] = "autocomplete:next", + ["tab"] = "autocomplete:cycle", ["escape"] = "autocomplete:cancel", } From 7a24dbb17ee13255a77531978c4b0bdc8848e044 Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Thu, 9 Sep 2021 11:55:25 +0800 Subject: [PATCH 050/165] revert new keyboard bindings --- data/plugins/autocomplete.lua | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/data/plugins/autocomplete.lua b/data/plugins/autocomplete.lua index f490556c..fde9487e 100644 --- a/data/plugins/autocomplete.lua +++ b/data/plugins/autocomplete.lua @@ -516,11 +516,9 @@ command.add(predicate, { -- Keymaps -- keymap.add { - ["return"] = "autocomplete:complete", - ["keypad enter"] = "autocomplete:complete", + ["tab"] = "autocomplete:complete", ["up"] = "autocomplete:previous", ["down"] = "autocomplete:next", - ["tab"] = "autocomplete:cycle", ["escape"] = "autocomplete:cancel", } From dfb64fbdf196aa85ef8b41ec279d372b15badec9 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 9 Sep 2021 15:39:41 +0200 Subject: [PATCH 051/165] Do not add selection with newlines in replace If the selected text containes newlines it doesn't make sense to use it as the initial text in the "replace text" command view. Do not use the selected text if a newline is found in the selection. Fix #511. --- data/core/commands/findreplace.lua | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 48cbecaf..03aa7737 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -135,7 +135,9 @@ command.add("core.docview", { end, ["find-replace:replace"] = function() - replace("Text", doc():get_text(doc():get_selection(true)), function(text, old, new) + local selected_text = doc():get_text(doc():get_selection()) + local has_newlines = selected_text:find("\n", 1, true) + replace("Text", has_newlines and "" or selected_text, function(text, old, new) if not find_regex then return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil) end From f85fe102d99aa6af042ac1f6f7855bfaf7c07f92 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 17:06:19 +0200 Subject: [PATCH 052/165] Implement hidden suggestions for find dialog --- data/core/commands/findreplace.lua | 5 ++++- data/core/commandview.lua | 23 +++++++++++++++++++---- data/core/init.lua | 1 + 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 03aa7737..73a5ae07 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -49,7 +49,9 @@ local function find(label, search_fn) core.command_view:set_text(text, true) core.status_view:show_tooltip(get_find_tooltip()) - core.command_view:enter(label, function(text) + core.command_view:set_hidden_suggestions() + core.command_view:enter(label, function(text, item) + table.insert(core.previous_find, text) core.status_view:remove_tooltip() if found then last_fn, last_text = search_fn, text @@ -61,6 +63,7 @@ local function find(label, search_fn) end, function(text) found = update_preview(last_sel, search_fn, text) last_fn, last_text = search_fn, text + return core.previous_find end, function(explicit) core.status_view:remove_tooltip() if explicit then diff --git a/data/core/commandview.lua b/data/core/commandview.lua index eb7febc7..d41db0d5 100644 --- a/data/core/commandview.lua +++ b/data/core/commandview.lua @@ -34,6 +34,7 @@ function CommandView:new() self.suggestion_idx = 1 self.suggestions = {} self.suggestions_height = 0 + self.show_suggestions = true self.last_change_id = 0 self.gutter_width = 0 self.gutter_text_brightness = 0 @@ -45,6 +46,11 @@ function CommandView:new() end +function CommandView:set_hidden_suggestions() + self.show_suggestions = false +end + + function CommandView:get_name() return View.get_name(self) end @@ -83,10 +89,16 @@ end function CommandView:move_suggestion_idx(dir) - local n = self.suggestion_idx + dir - self.suggestion_idx = common.clamp(n, 1, #self.suggestions) + local current_suggestion = self.suggestions[self.suggestion_idx].text + if self.show_suggestions or self:get_text() == current_suggestion then + local n = self.suggestion_idx + dir + self.suggestion_idx = common.clamp(n, 1, #self.suggestions) + end self:complete() self.last_change_id = self.doc:get_change_id() + if not self.show_suggestions then + self.state.suggest(self:get_text()) + end end @@ -134,6 +146,7 @@ function CommandView:exit(submitted, inexplicit) self.doc:reset() self.suggestions = {} if not submitted then cancel(not inexplicit) end + self.show_suggestions = true end @@ -187,7 +200,7 @@ function CommandView:update() -- update suggestions box height local lh = self:get_suggestion_line_height() - local dest = math.min(#self.suggestions, max_suggestions) * lh + local dest = self.show_suggestions and math.min(#self.suggestions, max_suggestions) * lh or 0 self:move_towards("suggestions_height", dest) -- update suggestion cursor offset @@ -256,7 +269,9 @@ end function CommandView:draw() CommandView.super.draw(self) - core.root_view:defer_draw(draw_suggestions_box, self) + if self.show_suggestions then + core.root_view:defer_draw(draw_suggestions_box, self) + end end diff --git a/data/core/init.lua b/data/core/init.lua index e84792ac..b1072b38 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -496,6 +496,7 @@ function core.init() core.redraw = true core.visited_files = {} + core.previous_find = {} core.restart_request = false core.quit_request = false core.replacements = whitespace_replacements() From 4bcc1cc07c76cfdac92aba13322e0d5687dd8f1a Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 18:07:26 +0200 Subject: [PATCH 053/165] Fix error with hidden suggestions Avoid indexing a nil if there are no suggestions. --- data/core/commandview.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/commandview.lua b/data/core/commandview.lua index d41db0d5..5ccb8d3a 100644 --- a/data/core/commandview.lua +++ b/data/core/commandview.lua @@ -89,7 +89,7 @@ end function CommandView:move_suggestion_idx(dir) - local current_suggestion = self.suggestions[self.suggestion_idx].text + local current_suggestion = #self.suggestions > 0 and self.suggestions[self.suggestion_idx].text if self.show_suggestions or self:get_text() == current_suggestion then local n = self.suggestion_idx + dir self.suggestion_idx = common.clamp(n, 1, #self.suggestions) From fa8b3b33b180d265f472877a23fb8df9a9eca191 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 18:08:54 +0200 Subject: [PATCH 054/165] Use hidden suggestions also for replace dialog --- data/core/commands/findreplace.lua | 7 +++++-- data/core/init.lua | 1 + 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 73a5ae07..7027f293 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -78,19 +78,22 @@ local function replace(kind, default, fn) core.command_view:set_text(default, true) core.status_view:show_tooltip(get_find_tooltip()) + core.command_view:set_hidden_suggestions() core.command_view:enter("Find To Replace " .. kind, function(old) core.command_view:set_text(old, true) local s = string.format("Replace %s %q With", kind, old) + core.command_view:set_hidden_suggestions() core.command_view:enter(s, function(new) + table.insert(core.previous_replace, new) local n = doc():replace(function(text) return fn(text, old, new) end) core.log("Replaced %d instance(s) of %s %q with %q", n, kind, old, new) - end, function() end, function() + end, function() return core.previous_replace end, function() core.status_view:remove_tooltip() end) - end, function() end, function() + end, function() return core.previous_find end, function() core.status_view:remove_tooltip() end) end diff --git a/data/core/init.lua b/data/core/init.lua index b1072b38..1978ce38 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -497,6 +497,7 @@ function core.init() core.redraw = true core.visited_files = {} core.previous_find = {} + core.previous_replace = {} core.restart_request = false core.quit_request = false core.replacements = whitespace_replacements() From b440a2258118c916dfec657c676befca53e108ac Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 18:33:58 +0200 Subject: [PATCH 055/165] Remeber initial user text for hidden suggestions When using hidden suggestions remember the text user was typing when navigating suggestions. Ensure also that in the previously searched expressiosn we have no duplicate entries. --- data/core/commands/findreplace.lua | 15 +++++++++++++-- data/core/commandview.lua | 26 ++++++++++++++++++++------ 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 7027f293..56761690 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -41,6 +41,16 @@ local function update_preview(sel, search_fn, text) end end + +local function insert_unique(t, v) + local n = #t + for i = 1, n do + if t[i] == v then return end + end + t[n + 1] = v +end + + local function find(label, search_fn) last_view, last_sel, last_finds = core.active_view, { core.active_view.doc:get_selection() }, {} @@ -51,7 +61,7 @@ local function find(label, search_fn) core.command_view:set_hidden_suggestions() core.command_view:enter(label, function(text, item) - table.insert(core.previous_find, text) + insert_unique(core.previous_find, text) core.status_view:remove_tooltip() if found then last_fn, last_text = search_fn, text @@ -80,12 +90,13 @@ local function replace(kind, default, fn) core.status_view:show_tooltip(get_find_tooltip()) core.command_view:set_hidden_suggestions() core.command_view:enter("Find To Replace " .. kind, function(old) + insert_unique(core.previous_find, old) core.command_view:set_text(old, true) local s = string.format("Replace %s %q With", kind, old) core.command_view:set_hidden_suggestions() core.command_view:enter(s, function(new) - table.insert(core.previous_replace, new) + insert_unique(core.previous_replace, new) local n = doc():replace(function(text) return fn(text, old, new) end) diff --git a/data/core/commandview.lua b/data/core/commandview.lua index 5ccb8d3a..b91f1394 100644 --- a/data/core/commandview.lua +++ b/data/core/commandview.lua @@ -89,14 +89,27 @@ end function CommandView:move_suggestion_idx(dir) - local current_suggestion = #self.suggestions > 0 and self.suggestions[self.suggestion_idx].text - if self.show_suggestions or self:get_text() == current_suggestion then + if self.show_suggestions then local n = self.suggestion_idx + dir self.suggestion_idx = common.clamp(n, 1, #self.suggestions) - end - self:complete() - self.last_change_id = self.doc:get_change_id() - if not self.show_suggestions then + self:complete() + self.last_change_id = self.doc:get_change_id() + else + local current_suggestion = #self.suggestions > 0 and self.suggestions[self.suggestion_idx].text + local text = self:get_text() + if text == current_suggestion then + local n = self.suggestion_idx + dir + if n == 0 and self.save_suggestion then + self:set_text(self.save_suggestion) + else + self.suggestion_idx = common.clamp(n, 1, #self.suggestions) + self:complete() + end + else + self.save_suggestion = text + self:complete() + end + self.last_change_id = self.doc:get_change_id() self.state.suggest(self:get_text()) end end @@ -147,6 +160,7 @@ function CommandView:exit(submitted, inexplicit) self.suggestions = {} if not submitted then cancel(not inexplicit) end self.show_suggestions = true + self.save_suggestion = nil end From 403b7f6fb6d619f920e350ed9bb468dbb8300eb3 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 18:42:36 +0200 Subject: [PATCH 056/165] Add missing remove tooltip call --- data/core/commands/findreplace.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 56761690..f41a748a 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -96,6 +96,7 @@ local function replace(kind, default, fn) local s = string.format("Replace %s %q With", kind, old) core.command_view:set_hidden_suggestions() core.command_view:enter(s, function(new) + core.status_view:remove_tooltip() insert_unique(core.previous_replace, new) local n = doc():replace(function(text) return fn(text, old, new) From 04250a206a64bec1e207d2dfa74c36862a52daea Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Tue, 7 Sep 2021 22:31:36 +0200 Subject: [PATCH 057/165] Add previous find and replace in session --- data/core/init.lua | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/data/core/init.lua b/data/core/init.lua index 1978ce38..af291767 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -17,10 +17,7 @@ local core = {} local function load_session() local ok, t = pcall(dofile, USERDIR .. "/session.lua") - if ok and t then - return t.recents, t.window, t.window_mode - end - return {} + return ok and t or {} end @@ -30,6 +27,8 @@ local function save_session() 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()), + ", previous_find=", common.serialize(core.previous_find), + ", previous_replace=", common.serialize(core.previous_replace), "}\n") fp:close() end @@ -435,13 +434,15 @@ function core.init() end do - local recent_projects, window_position, window_mode = load_session() - if window_mode == "normal" then - system.set_window_size(table.unpack(window_position)) - elseif window_mode == "maximized" then + local session = load_session() + if session.window_mode == "normal" then + system.set_window_size(table.unpack(session.window)) + elseif session.window_mode == "maximized" then system.set_window_mode("maximized") end - core.recent_projects = recent_projects or {} + core.recent_projects = session.recents or {} + core.previous_find = session.previous_find or {} + core.previous_replace = session.previous_replace or {} end local project_dir = core.recent_projects[1] or "." @@ -496,8 +497,6 @@ function core.init() core.redraw = true core.visited_files = {} - core.previous_find = {} - core.previous_replace = {} core.restart_request = false core.quit_request = false core.replacements = whitespace_replacements() From 17185075c61efa2431a5336d3ecf142b4c2a8d22 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 8 Sep 2021 22:47:37 +0200 Subject: [PATCH 058/165] Allow `find-replace:select-next` to select more occurrences after wrap The initial position for the search is defined by the last selection towards the end of the file. After reaching the end of the file, it would always select the same selection to start the search from. Now, we start the search from each selection, until a new occurrence is found. --- data/core/commands/findreplace.lua | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 48cbecaf..4236b2ea 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -108,6 +108,20 @@ local function has_unique_selection() return text ~= nil end +local function is_in_selection(line, col, l1, c1, l2, c2) + if line < l1 or line > l2 then return false end + if line == l1 and col <= c1 then return false end + if line == l2 and col > c2 then return false end + return true +end + +local function is_in_any_selection(line, col) + for idx, l1, c1, l2, c2 in doc():get_selections(true, false) do + if is_in_selection(line, col, l1, c1, l2, c2) then return true end + end + return false +end + local function select_next(all) local il1, ic1 = doc():get_selection(true) for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do @@ -115,9 +129,15 @@ local function select_next(all) repeat l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) if l1 == il1 and c1 == ic1 then break end - if l2 then doc():add_selection(l2, c2, l1, c1) end + if l2 and (all or not is_in_any_selection(l2, c2)) then + doc():add_selection(l2, c2, l1, c1) + if not all then + core.active_view:scroll_to_make_visible(l2, c2) + return + end + end until not all or not l2 - break + if all then break end end end From 4964c30e1271a4a5734e722c012d787fbe652e8e Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 9 Sep 2021 19:12:56 +0200 Subject: [PATCH 059/165] Bring back command find-replace:select-next Bring back the command like before to keep single selection but with ctrl+f3 keybindings. Change the name of the new multi-cursor command but keep the ctrl+d keybinding. --- changelog.md | 5 +++++ data/core/commands/findreplace.lua | 10 ++++++++-- data/core/keymap-macos.lua | 5 +++-- data/core/keymap.lua | 5 +++-- 4 files changed, 19 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index d6e30519..71170dda 100644 --- a/changelog.md +++ b/changelog.md @@ -1,5 +1,10 @@ This files document the changes done in Lite XL for each release. +### 2.0.2 + +Change behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence. +The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+F3`. + ### 2.0 The 2.0 version of lite contains *breaking changes* to lite, in terms of how plugin settings are structured; diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 4236b2ea..8ef97365 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -142,8 +142,14 @@ local function select_next(all) end command.add(has_unique_selection, { - ["find-replace:select-next"] = function() select_next(false) end, - ["find-replace:select-all"] = function() select_next(true) end + ["find-replace:select-next"] = function() + local l1, c1, l2, c2 = doc():get_selection(true) + local text = doc():get_text(l1, c1, l2, c2) + l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) + if l2 then doc():set_selection(l2, c2, l1, c1) end + end, + ["find-replace:select-add-next"] = function() select_next(false) end, + ["find-replace:select-add-all"] = function() select_next(true) end }) command.add("core.docview", { diff --git a/data/core/keymap-macos.lua b/data/core/keymap-macos.lua index 7f54ddbb..53a20468 100644 --- a/data/core/keymap-macos.lua +++ b/data/core/keymap-macos.lua @@ -66,9 +66,10 @@ local function keymap_macos(keymap) ["cmd+shift+return"] = "doc:newline-above", ["cmd+j"] = "doc:join-lines", ["cmd+a"] = "doc:select-all", - ["cmd+d"] = { "find-replace:select-next", "doc:select-word" }, + ["cmd+d"] = { "find-replace:select-add-next", "doc:select-word" }, + ["cmd+f3"] = "find-replace:select-next", ["cmd+l"] = "doc:select-lines", - ["cmd+shift+l"] = { "find-replace:select-all", "doc:select-word" }, + ["cmd+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["cmd+/"] = "doc:toggle-line-comments", ["option+up"] = "doc:move-lines-up", ["option+down"] = "doc:move-lines-down", diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 2be0dfc7..50eadec6 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -168,9 +168,10 @@ keymap.add_direct { ["ctrl+shift+return"] = "doc:newline-above", ["ctrl+j"] = "doc:join-lines", ["ctrl+a"] = "doc:select-all", - ["ctrl+d"] = { "find-replace:select-next", "doc:select-word" }, + ["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" }, + ["ctrl+f3"] = "find-replace:select-next", ["ctrl+l"] = "doc:select-lines", - ["ctrl+shift+l"] = { "find-replace:select-all", "doc:select-word" }, + ["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["ctrl+/"] = "doc:toggle-line-comments", ["ctrl+up"] = "doc:move-lines-up", ["ctrl+down"] = "doc:move-lines-down", From aa0e083cb9d0399cb3aafdfd4735ffbe5394e6c7 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 8 Sep 2021 22:47:37 +0200 Subject: [PATCH 060/165] Allow `find-replace:select-next` to select more occurrences after wrap The initial position for the search is defined by the last selection towards the end of the file. After reaching the end of the file, it would always select the same selection to start the search from. Now, we start the search from each selection, until a new occurrence is found. --- data/core/commands/findreplace.lua | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index f41a748a..902623be 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -126,6 +126,20 @@ local function has_unique_selection() return text ~= nil end +local function is_in_selection(line, col, l1, c1, l2, c2) + if line < l1 or line > l2 then return false end + if line == l1 and col <= c1 then return false end + if line == l2 and col > c2 then return false end + return true +end + +local function is_in_any_selection(line, col) + for idx, l1, c1, l2, c2 in doc():get_selections(true, false) do + if is_in_selection(line, col, l1, c1, l2, c2) then return true end + end + return false +end + local function select_next(all) local il1, ic1 = doc():get_selection(true) for idx, l1, c1, l2, c2 in doc():get_selections(true, true) do @@ -133,9 +147,15 @@ local function select_next(all) repeat l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) if l1 == il1 and c1 == ic1 then break end - if l2 then doc():add_selection(l2, c2, l1, c1) end + if l2 and (all or not is_in_any_selection(l2, c2)) then + doc():add_selection(l2, c2, l1, c1) + if not all then + core.active_view:scroll_to_make_visible(l2, c2) + return + end + end until not all or not l2 - break + if all then break end end end From d9afc40a174122485dec73d2025fd8079fb58c88 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 9 Sep 2021 19:12:56 +0200 Subject: [PATCH 061/165] Bring back command find-replace:select-next Bring back the command like before to keep single selection but with ctrl+f3 keybindings. Change the name of the new multi-cursor command but keep the ctrl+d keybinding. --- changelog.md | 3 +++ data/core/commands/findreplace.lua | 10 ++++++++-- data/core/keymap-macos.lua | 5 +++-- data/core/keymap.lua | 5 +++-- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 2cf29061..5ea7b87c 100644 --- a/changelog.md +++ b/changelog.md @@ -35,6 +35,9 @@ Here some details about the bug fixes: - fix error that prevented the verification of plugins versions - fix error on X11 that caused a bug window event on exit +Change behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence. +The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+f3`. + ### 2.0 The 2.0 version of lite contains *breaking changes* to lite, in terms of how plugin settings are structured; diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 902623be..c5f2bf07 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -160,8 +160,14 @@ local function select_next(all) end command.add(has_unique_selection, { - ["find-replace:select-next"] = function() select_next(false) end, - ["find-replace:select-all"] = function() select_next(true) end + ["find-replace:select-next"] = function() + local l1, c1, l2, c2 = doc():get_selection(true) + local text = doc():get_text(l1, c1, l2, c2) + l1, c1, l2, c2 = search.find(doc(), l2, c2, text, { wrap = true }) + if l2 then doc():set_selection(l2, c2, l1, c1) end + end, + ["find-replace:select-add-next"] = function() select_next(false) end, + ["find-replace:select-add-all"] = function() select_next(true) end }) command.add("core.docview", { diff --git a/data/core/keymap-macos.lua b/data/core/keymap-macos.lua index 7f54ddbb..53a20468 100644 --- a/data/core/keymap-macos.lua +++ b/data/core/keymap-macos.lua @@ -66,9 +66,10 @@ local function keymap_macos(keymap) ["cmd+shift+return"] = "doc:newline-above", ["cmd+j"] = "doc:join-lines", ["cmd+a"] = "doc:select-all", - ["cmd+d"] = { "find-replace:select-next", "doc:select-word" }, + ["cmd+d"] = { "find-replace:select-add-next", "doc:select-word" }, + ["cmd+f3"] = "find-replace:select-next", ["cmd+l"] = "doc:select-lines", - ["cmd+shift+l"] = { "find-replace:select-all", "doc:select-word" }, + ["cmd+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["cmd+/"] = "doc:toggle-line-comments", ["option+up"] = "doc:move-lines-up", ["option+down"] = "doc:move-lines-down", diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 2be0dfc7..50eadec6 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -168,9 +168,10 @@ keymap.add_direct { ["ctrl+shift+return"] = "doc:newline-above", ["ctrl+j"] = "doc:join-lines", ["ctrl+a"] = "doc:select-all", - ["ctrl+d"] = { "find-replace:select-next", "doc:select-word" }, + ["ctrl+d"] = { "find-replace:select-add-next", "doc:select-word" }, + ["ctrl+f3"] = "find-replace:select-next", ["ctrl+l"] = "doc:select-lines", - ["ctrl+shift+l"] = { "find-replace:select-all", "doc:select-word" }, + ["ctrl+shift+l"] = { "find-replace:select-add-all", "doc:select-word" }, ["ctrl+/"] = "doc:toggle-line-comments", ["ctrl+up"] = "doc:move-lines-up", ["ctrl+down"] = "doc:move-lines-down", From 83607aec4a5553a2a6b7a6d448996df2212be2db Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 9 Sep 2021 22:37:26 +0200 Subject: [PATCH 062/165] Reword changelog --- changelog.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index 5ea7b87c..57ab9646 100644 --- a/changelog.md +++ b/changelog.md @@ -4,24 +4,33 @@ This files document the changes done in Lite XL for each release. Fix problem project directory when starting the application from Launcher on macOS. -Improved LogView by @takase1121. +Improved LogView. Entries can now be expanded and there is a context menu to copy the item's content. + +Change the behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence. +The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+f3`. + +Added a command to create a multi-cursor with all the occurrences of the current selection. +Activated with the shortcut `ctrl+shift+l`. Fix problem when trying to close an unsaved new document. + +No longer shows an error for the `-psn` argument passed to the application on macOS. + Fix `treeview:open-in-system` command on Windows. + Fix rename command to update name of document if opened. -Contributed by @Guldoman. Improve the find and replace dialog so that previously used expressions can be recalled using "up" and "down" keys. -Multi-cursors fixes and improvement by @adamharrison. - -Build package script rewrite and enhancement by @redtide. +Build package script rewrite with many improvements. Use bigger fonts by default. Other minor improvements and fixes. +With many thanks to the contributors: @adamharrison, @takase1121, @Guldoman, @redtide, @Timofffee, @boppyt, @Jan200101. + ### 2.0.1 Fix a few bugs and we mandate the mod-version 2 for plugins. @@ -35,9 +44,6 @@ Here some details about the bug fixes: - fix error that prevented the verification of plugins versions - fix error on X11 that caused a bug window event on exit -Change behavior of `ctrl+d` to add a multi-cursor selection to the next occurrence. -The old behavior to move the selection to the next occurrence is now done using the shortcut `ctrl+f3`. - ### 2.0 The 2.0 version of lite contains *breaking changes* to lite, in terms of how plugin settings are structured; From cec1e4efb9cffeb759165d6e4521721ae2ef2eb5 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 9 Sep 2021 23:30:18 +0200 Subject: [PATCH 063/165] Do not fail search if there was an option change --- data/core/commands/findreplace.lua | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index c5f2bf07..2412c4e1 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -12,6 +12,7 @@ local last_finds, last_view, last_fn, last_text, last_sel local case_sensitive = config.find_case_sensitive or false local find_regex = config.find_regex or false +local found_expression local function doc() return core.active_view:is(DocView) and core.active_view.doc or last_view.doc @@ -34,10 +35,10 @@ local function update_preview(sel, search_fn, text) if ok and line1 and text ~= "" then last_view.doc:set_selection(line2, col2, line1, col1) last_view:scroll_to_line(line2, true) - return true + found_expression = true else last_view.doc:set_selection(unpack(sel)) - return false + found_expression = false end end @@ -54,7 +55,8 @@ end local function find(label, search_fn) last_view, last_sel, last_finds = core.active_view, { core.active_view.doc:get_selection() }, {} - local text, found = last_view.doc:get_text(unpack(last_sel)), false + local text = last_view.doc:get_text(unpack(last_sel)) + found_expression = false core.command_view:set_text(text, true) core.status_view:show_tooltip(get_find_tooltip()) @@ -63,7 +65,7 @@ local function find(label, search_fn) core.command_view:enter(label, function(text, item) insert_unique(core.previous_find, text) core.status_view:remove_tooltip() - if found then + if found_expression then last_fn, last_text = search_fn, text else core.error("Couldn't find %q", text) @@ -71,7 +73,7 @@ local function find(label, search_fn) last_view:scroll_to_make_visible(unpack(last_sel)) end end, function(text) - found = update_preview(last_sel, search_fn, text) + update_preview(last_sel, search_fn, text) last_fn, last_text = search_fn, text return core.previous_find end, function(explicit) From afd067219769686076d74a8e3c31290e3e4ec566 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 10 Sep 2021 14:54:55 +0200 Subject: [PATCH 064/165] Use line/col to identify selection in replace command --- data/core/commands/findreplace.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 2412c4e1..f8e8e45a 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -181,9 +181,9 @@ command.add("core.docview", { end, ["find-replace:replace"] = function() - local selected_text = doc():get_text(doc():get_selection()) - local has_newlines = selected_text:find("\n", 1, true) - replace("Text", has_newlines and "" or selected_text, function(text, old, new) + local l1, c1, l2, c2 = doc():get_selection() + local selected_text = doc():get_text(l1, c1, l2, c2) + replace("Text", l1 == l2 and selected_text or "", function(text, old, new) if not find_regex then return text:gsub(old:gsub("%W", "%%%1"), new:gsub("%%", "%%%%"), nil) end From 218999dff899872fb38d3c8889d5bc27cc600ed1 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 10 Sep 2021 14:55:04 +0200 Subject: [PATCH 065/165] Avoid bug when replacement stop at end of string Detect when we are past the end of the string to avoid by checking if byte is not nil. Fix #510. --- data/core/regex.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/regex.lua b/data/core/regex.lua index 19306e04..69203cbd 100644 --- a/data/core/regex.lua +++ b/data/core/regex.lua @@ -23,7 +23,7 @@ end -- Moves to the end of the identified character. local function end_character(str, index) local byte = string.byte(str, index + 1) - while byte >= 128 and byte < 192 do + while byte and byte >= 128 and byte < 192 do index = index + 1 byte = string.byte(str, index + 1) end From 8dd530e5cf4c1be080fcd387fc38aa6afc44be83 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 10 Sep 2021 15:47:33 +0200 Subject: [PATCH 066/165] Add -branch option in repackage script --- scripts/repackage.sh | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/scripts/repackage.sh b/scripts/repackage.sh index 99368582..3239ea5e 100644 --- a/scripts/repackage.sh +++ b/scripts/repackage.sh @@ -10,7 +10,7 @@ copy_directory_from_repo () { fi local dirname="$1" local destdir="$2" - git archive master "$dirname" --format=tar | tar xf - -C "$destdir" "${tar_options[@]}" + git archive "$lite_branch" "$dirname" --format=tar | tar xf - -C "$destdir" "${tar_options[@]}" } lite_copy_third_party_modules () { @@ -23,12 +23,17 @@ lite_copy_third_party_modules () { rm "$build/rxi-lite-colors.zip" } +lite_branch=master while [ ! -z ${1+x} ]; do case "$1" in -dir) use_dir="$(realpath $2)" shift 2 ;; + -branch) + lite_branch="$2" + shift 2 + ;; *) echo "unknown option: $1" exit 1 From 18189e63b603040bb534d2aabcc2029f3f964421 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Fri, 10 Sep 2021 15:48:43 +0200 Subject: [PATCH 067/165] Fix repackage script to restore project version --- scripts/repackage.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/repackage.sh b/scripts/repackage.sh index 3239ea5e..f8da579f 100644 --- a/scripts/repackage.sh +++ b/scripts/repackage.sh @@ -78,6 +78,8 @@ for filename in $(ls -1 *.zip *.tar.*); do fi rm "$filename" find lite-xl -name lite -exec chmod a+x '{}' \; + start_file=$(find lite-xl -name start.lua) + lite_version=$(cat "$start_file" | awk 'match($0, /^\s*VERSION\s*=\s*"(.+)"/, a) { print(a[1]) }') xcoredir="$(find lite-xl -type d -name 'core')" coredir="$(dirname $xcoredir)" echo "coredir: $coredir" @@ -86,6 +88,7 @@ for filename in $(ls -1 *.zip *.tar.*); do rm -fr "$coredir/$module_name" (cd .. && copy_directory_from_repo --strip-components=1 "data/$module_name" "$workdir/$coredir") done + sed -i "s/@PROJECT_VERSION@/$lite_version/g" "$start_file" for module_name in plugins colors; do cp -r "third/data/$module_name" "$coredir" done From a66a76f9c9b1888a1c3aea8b917083c2db789aa1 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 13 Sep 2021 23:40:01 -0400 Subject: [PATCH 068/165] Added in searcher. --- data/core/start.lua | 1 + src/api/system.c | 98 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/data/core/start.lua b/data/core/start.lua index 71050057..7fab5b37 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -19,3 +19,4 @@ package.path = DATADIR .. '/?.lua;' .. package.path package.path = DATADIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path +package.searchers[3] = system.searcher_plugin diff --git a/src/api/system.c b/src/api/system.c index 2f1bf763..dbcdbc3e 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -638,6 +638,103 @@ static int f_set_window_opacity(lua_State *L) { } + +typedef struct { + const char* symbol; + void* address; +} lua_function_node; + +#define P(FUNC) { "lua_" #FUNC, (void*)(lua_##FUNC) } +static void* api_require(const char* symbol) { + static lua_function_node nodes[] = { + P(absindex), P(arith), P(atpanic), P(callk), P(checkstack), + P(close), P(compare), P(concat), P(copy), P(createtable), P(dump), + P(error), P(gc), P(getallocf), P(getctx), P(getfield), P(getglobal), + P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal), + P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue), + P(getuservalue), P(insert), P(isnumber), + P(isstring), P(isuserdata), P(len), P(load), + P(newstate), P(newthread), P(newuserdata), P(next), + P(pcallk), P(pushboolean), P(pushcclosure), + P(pushfstring), P(pushinteger), P(pushlightuserdata), + P(pushlstring), P(pushnil), P(pushnumber), P(pushstring), + P(pushthread), P(pushunsigned), P(pushvalue), P(pushvfstring), P(rawequal), + P(rawget), P(rawgeti), P(rawgetp), P(rawlen), P(rawset), P(rawseti), + P(rawsetp), P(remove), P(replace), P(resume), P(setallocf), + P(setfield), P(setglobal), P(sethook), P(setlocal), P(setmetatable), + P(settable), P(settop), P(setupvalue), P(setuservalue), P(status), + P(tocfunction), P(tointegerx), P(tolstring), P(toboolean), + P(tonumberx), P(topointer), P(tothread), + P(tounsignedx), P(touserdata), P(type), P(typename), P(upvalueid), + P(upvaluejoin), P(version), P(xmove), P(yieldk) + }; + for (int i = 0; i < sizeof(nodes) / sizeof(lua_function_node); ++i) { + if (strcmp(nodes[i].symbol, symbol) == 0) + return nodes[i].address; + } + return NULL; +} + +static int loader_plugin(lua_State* L) { + size_t sname, modlen, pathlen; + char buffer[512] = "lua_open_"; + const char* modname = luaL_checklstring(L, -2, &modlen); + const char* path = luaL_checklstring(L, -1, &pathlen); + void* library = SDL_LoadObject(path); + if (modlen == 0 || !library) + return luaL_error(L, "Unable to load %s: %s", modname, SDL_GetError()); + for (sname = modlen - 1; sname > 0 && modname[sname] != '.'; --sname); + strncat(buffer, &modname[sname], modlen - sname + 1); + int (*entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, buffer); + if (!entrypoint) { + return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a \ + function defined as int %s(lua_State* L, void* (*symbol)(const char*))", + path, buffer); + } + lua_pushlightuserdata(L, library); + lua_setfield(L, LUA_REGISTRYINDEX, modname); + if (entrypoint(L, api_require) == 0) + return luaL_error(L, "Unable to load %s: Your entrypoint must return at\ + least one argument."); + return 1; +} + +#if _WIN32 + #define PATH_SEPARATOR '\\' +#else + #define PATH_SEPARATOR '/' +#endif +static int f_searcher_plugin(lua_State *L) { + size_t len, cpath_len, cpath_idx = 0, cpath_q = 0; + const char* modname = luaL_checklstring(L, -1, &len); + lua_getglobal(L, "package"); + lua_getfield(L, -1, "cpath"); + const char* cpath = luaL_checklstring(L, -1, &cpath_len); + char lib[8192]; + for (size_t i = 0; i <= cpath_len; ++i) { + if (i == cpath_len || cpath[i] == ';') { + if (cpath_q) { + size_t offset = cpath_q - cpath_idx; + strncpy(lib, &cpath[cpath_idx], offset); + for (size_t j = 0; j < len && j < sizeof(lib) - 1; ++j) + lib[offset++] = modname[j] != '.' ? modname[j] : PATH_SEPARATOR; + lib[offset++] = '\0'; + struct stat s; + if (!stat(lib, &s)) { + lua_pushcfunction(L, loader_plugin); + lua_pushlstring(L, lib, offset); + return 2; + } + } + cpath_idx = i + 1; + cpath_q = 0; + } else if (cpath[i] == '?') { + cpath_q = i; + } + } + return 0; +} + static const luaL_Reg lib[] = { { "poll_event", f_poll_event }, { "wait_event", f_wait_event }, @@ -664,6 +761,7 @@ static const luaL_Reg lib[] = { { "exec", f_exec }, { "fuzzy_match", f_fuzzy_match }, { "set_window_opacity", f_set_window_opacity }, + { "searcher_plugin", f_searcher_plugin }, { NULL, NULL } }; From e9f48ce949e8f283a4c45cb15fe238e52837fb32 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 14 Sep 2021 00:13:30 -0400 Subject: [PATCH 069/165] Added in sample plugin and tested things out. Works. --- build_plugin.sh | 1 + data/core/init.lua | 3 ++- data/core/start.lua | 1 + sample_plugin.c | 14 ++++++++++++++ src/api/system.c | 21 ++++++++++++++------- src/main.c | 6 +++++- 6 files changed, 37 insertions(+), 9 deletions(-) create mode 100755 build_plugin.sh create mode 100644 sample_plugin.c diff --git a/build_plugin.sh b/build_plugin.sh new file mode 100755 index 00000000..b270caf7 --- /dev/null +++ b/build_plugin.sh @@ -0,0 +1 @@ +gcc sample_plugin.c -shared -o data/plugins/sample.so diff --git a/data/core/init.lua b/data/core/init.lua index af291767..1cb98979 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -426,6 +426,8 @@ function core.init() NagView = require "core.nagview" DocView = require "core.docview" Doc = require "core.doc" + local PluginTest = require "plugins.sample" + print(PluginTest.example) if PATHSEP == '\\' then USERDIR = common.normalize_path(USERDIR) @@ -675,7 +677,6 @@ local function check_plugin_version(filename) return true, version_match end - function core.load_plugins() local no_errors = true local refused_list = { diff --git a/data/core/start.lua b/data/core/start.lua index 7fab5b37..b9360d4b 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -19,4 +19,5 @@ package.path = DATADIR .. '/?.lua;' .. package.path package.path = DATADIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path +package.cpath = DATADIR .. '/?.' .. (MACOS and 'lib' or (WINDOWS and 'dll' or 'so')) .. ';' .. package.cpath package.searchers[3] = system.searcher_plugin diff --git a/sample_plugin.c b/sample_plugin.c new file mode 100644 index 00000000..8cd58bc3 --- /dev/null +++ b/sample_plugin.c @@ -0,0 +1,14 @@ + +typedef struct lua_State lua_State; +#define SYMBOL(NAME, RTYPE, ...) RTYPE (*lua_##NAME)(lua_State*, __VA_ARGS__) = (RTYPE (*)(lua_State*, __VA_ARGS__))symbol("lua_" #NAME); + +int lua_open_sample(lua_State* L, void* (*symbol(const char*))) { + SYMBOL(createtable, void, int, int); + SYMBOL(setfield, void, int, const char*); + SYMBOL(pushstring, const char*, const char*); + + lua_createtable(L, 0, 0); + lua_pushstring(L, "value"); + lua_setfield(L, -2, "example"); + return 1; +} diff --git a/src/api/system.c b/src/api/system.c index dbcdbc3e..791d839f 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -684,12 +684,12 @@ static int loader_plugin(lua_State* L) { if (modlen == 0 || !library) return luaL_error(L, "Unable to load %s: %s", modname, SDL_GetError()); for (sname = modlen - 1; sname > 0 && modname[sname] != '.'; --sname); - strncat(buffer, &modname[sname], modlen - sname + 1); + strcat(buffer, &modname[sname+1]); int (*entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, buffer); if (!entrypoint) { - return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a \ - function defined as int %s(lua_State* L, void* (*symbol)(const char*))", - path, buffer); + return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a " + "function defined as int %s(lua_State* L, void* (*symbol)(const char*))", + modname, buffer); } lua_pushlightuserdata(L, library); lua_setfield(L, LUA_REGISTRYINDEX, modname); @@ -701,7 +701,13 @@ static int loader_plugin(lua_State* L) { #if _WIN32 #define PATH_SEPARATOR '\\' -#else + #define DYNAMIC_SUFFIX "dll" +#else + #if __APPLE__ + #define DYNAMIC_SUFFIX "lib" + #else + #define DYNAMIC_SUFFIX "so" + #endif #define PATH_SEPARATOR '/' #endif static int f_searcher_plugin(lua_State *L) { @@ -716,13 +722,14 @@ static int f_searcher_plugin(lua_State *L) { if (cpath_q) { size_t offset = cpath_q - cpath_idx; strncpy(lib, &cpath[cpath_idx], offset); - for (size_t j = 0; j < len && j < sizeof(lib) - 1; ++j) + for (size_t j = 0; j < len && j < sizeof(lib) - 3; ++j) lib[offset++] = modname[j] != '.' ? modname[j] : PATH_SEPARATOR; lib[offset++] = '\0'; + strcat(lib, "." DYNAMIC_SUFFIX); struct stat s; if (!stat(lib, &s)) { lua_pushcfunction(L, loader_plugin); - lua_pushlstring(L, lib, offset); + lua_pushstring(L, lib); return 2; } } diff --git a/src/main.c b/src/main.c index 470f0b5e..3e99c735 100644 --- a/src/main.c +++ b/src/main.c @@ -139,13 +139,17 @@ init_lua: lua_pushstring(L, exename); lua_setglobal(L, "EXEFILE"); -#ifdef __APPLE__ lua_pushboolean(L, true); +#ifdef __APPLE__ lua_setglobal(L, "MACOS"); enable_momentum_scroll(); #ifdef MACOS_USE_BUNDLE set_macos_bundle_resources(L); #endif +#elsif _WIN32 + lua_setglobal(L, "WINDOWS"); +#else + lua_setglobal(L, "LINUX"); #endif const char *init_lite_code = \ From 1e8031a0e8d7d9a5d0598fce148b5800f76b1fd7 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Tue, 14 Sep 2021 04:16:46 +0200 Subject: [PATCH 070/165] Fix checking if sibling is locked when removing `View`s We only checked if sibling was locked in the `x` direction. --- data/core/rootview.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/core/rootview.lua b/data/core/rootview.lua index 9d017268..836f9e9a 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -142,7 +142,8 @@ function Node:remove_view(root, view) local parent = self:get_parent_node(root) local is_a = (parent.a == self) local other = parent[is_a and "b" or "a"] - if other:get_locked_size() then + local locked_size_x, locked_size_y = other:get_locked_size() + if locked_size_x or locked_size_y then self.views = {} self:add_view(EmptyView()) else From fb955e4e125da49f4f2f85bab42936c7bfaf5402 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 15 Sep 2021 03:42:59 +0200 Subject: [PATCH 071/165] Only check if sibling is locked in the split direction If the sibling is not locked in the direction of the split, it should fill the space. --- data/core/rootview.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/rootview.lua b/data/core/rootview.lua index 836f9e9a..c2895cf5 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -143,7 +143,7 @@ function Node:remove_view(root, view) local is_a = (parent.a == self) local other = parent[is_a and "b" or "a"] local locked_size_x, locked_size_y = other:get_locked_size() - if locked_size_x or locked_size_y then + if (parent.type == "hsplit" and locked_size_x or locked_size_y) then self.views = {} self:add_view(EmptyView()) else From 9bfec4aca56b5180da2fc91dbf0053e187a3c87b Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 15 Sep 2021 03:46:29 +0200 Subject: [PATCH 072/165] Ensure that the primary node always has a `View` --- data/core/rootview.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/core/rootview.lua b/data/core/rootview.lua index c2895cf5..bd0d7423 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -143,7 +143,8 @@ function Node:remove_view(root, view) local is_a = (parent.a == self) local other = parent[is_a and "b" or "a"] local locked_size_x, locked_size_y = other:get_locked_size() - if (parent.type == "hsplit" and locked_size_x or locked_size_y) then + if self.is_primary_node + or (parent.type == "hsplit" and locked_size_x or locked_size_y) then self.views = {} self:add_view(EmptyView()) else From 66bfff2e26c20c8d8b4e3325da4ac0dabc9c0b04 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Wed, 15 Sep 2021 17:30:16 +0200 Subject: [PATCH 073/165] Fix wrong locked sibling check Previously if the split type was "hsplit", but `locked_size_x` was falsy, `locked_size_y` was wrongly used. --- data/core/rootview.lua | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/data/core/rootview.lua b/data/core/rootview.lua index bd0d7423..0856a619 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -143,8 +143,13 @@ function Node:remove_view(root, view) local is_a = (parent.a == self) local other = parent[is_a and "b" or "a"] local locked_size_x, locked_size_y = other:get_locked_size() - if self.is_primary_node - or (parent.type == "hsplit" and locked_size_x or locked_size_y) then + local locked_size + if parent.type == "hsplit" then + locked_size = locked_size_x + else + locked_size = locked_size_y + end + if self.is_primary_node or locked_size then self.views = {} self:add_view(EmptyView()) else From 377ce1cd06abd5ba50386920732ac62d8e6b2637 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 12:37:17 -0400 Subject: [PATCH 074/165] Moved things around, fixed a few things up. --- build_plugin.sh | 2 +- data/core/start.lua | 16 ++- generate_header.sh | 17 +++ lite_plugin_api.h | 300 ++++++++++++++++++++++++++++++++++++++++++++ sample_plugin.c | 8 +- src/api/system.c | 46 +------ 6 files changed, 336 insertions(+), 53 deletions(-) create mode 100755 generate_header.sh create mode 100644 lite_plugin_api.h diff --git a/build_plugin.sh b/build_plugin.sh index b270caf7..437f9b58 100755 --- a/build_plugin.sh +++ b/build_plugin.sh @@ -1 +1 @@ -gcc sample_plugin.c -shared -o data/plugins/sample.so +gcc -g sample_plugin.c -shared -o data/plugins/sample.so diff --git a/data/core/start.lua b/data/core/start.lua index b9360d4b..7658d6c7 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -19,5 +19,17 @@ package.path = DATADIR .. '/?.lua;' .. package.path package.path = DATADIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path -package.cpath = DATADIR .. '/?.' .. (MACOS and 'lib' or (WINDOWS and 'dll' or 'so')) .. ';' .. package.cpath -package.searchers[3] = system.searcher_plugin + +local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') +package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath +package.searchers[3] = function(modname) + local s,e = 0 + repeat + e = package.cpath:find(";", s) or #package.cpath + local path = package.cpath:sub(s, e - 1):gsub("?", modname:gsub("%.", "/")) + if system.get_file_info(path) then + return system.load_native_plugin, path + end + s = e + 1 + until s > #package.cpath +end diff --git a/generate_header.sh b/generate_header.sh new file mode 100755 index 00000000..fb03e712 --- /dev/null +++ b/generate_header.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# Takes lua folder, outputs a header file that includes all the necessary macros for writing a lite plugin. +# Takes a minimal approach, and strips out problematic functions. Should be good enough for most purposes. + +echo "// This file was automatically generated by generate_header.sh. Do not modify directly." +echo "#include " +echo "typedef struct lua_State lua_State; typedef double lua_Number; typedef int (*lua_CFunction)(lua_State*); typedef ptrdiff_t lua_Integer;" +echo "typedef unsigned long lua_Unsigned; typedef struct luaL_Buffer luaL_Buffer; typedef struct luaL_Reg luaL_Reg; typedef struct lua_Debug lua_Debug;" +echo "typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);" +echo "typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);" +echo "typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);" +grep -h "^LUA\(LIB\)*_API" $1/*.h | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^/static /" +grep -h "#define luaL*_" $1/*.h | grep -v "\\\s*$" | grep -v "\(assert\|lock\)" | grep -v "\(asm\|int32\)" | grep -v "#define lua_number2integer(i,n)\s*lua_number2int(i, n)" +echo "#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name)" +echo "static void lite_init_plugin(void* (*symbol(const char*))) {" +grep -h "^LUA\(LIB\)*_API" $1/*.h | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^\([^)]*\)(\*\(lua\w*\))\s*(/\tIMPORT_SYMBOL(\2, \1,/" +echo "}" diff --git a/lite_plugin_api.h b/lite_plugin_api.h new file mode 100644 index 00000000..b6c03204 --- /dev/null +++ b/lite_plugin_api.h @@ -0,0 +1,300 @@ +// This file was automatically generated by generate_header.sh. Do not modify directly. +#include +typedef struct lua_State lua_State; typedef double lua_Number; typedef int (*lua_CFunction)(lua_State*); typedef ptrdiff_t lua_Integer; +typedef unsigned long lua_Unsigned; typedef struct luaL_Buffer luaL_Buffer; typedef struct luaL_Reg luaL_Reg; typedef struct lua_Debug lua_Debug; +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); +static void (*luaL_checkversion_) (lua_State *L, lua_Number ver); +static int (*luaL_getmetafield) (lua_State *L, int obj, const char *e); +static int (*luaL_callmeta) (lua_State *L, int obj, const char *e); +static const char *(*luaL_tolstring) (lua_State *L, int idx, size_t *len); +static int (*luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +static lua_Number (*luaL_checknumber) (lua_State *L, int numArg); +static lua_Number (*luaL_optnumber) (lua_State *L, int nArg, lua_Number def); +static lua_Integer (*luaL_checkinteger) (lua_State *L, int numArg); +static lua_Unsigned (*luaL_checkunsigned) (lua_State *L, int numArg); +static void (*luaL_checkstack) (lua_State *L, int sz, const char *msg); +static void (*luaL_checktype) (lua_State *L, int narg, int t); +static void (*luaL_checkany) (lua_State *L, int narg); +static int (*luaL_newmetatable) (lua_State *L, const char *tname); +static void (*luaL_setmetatable) (lua_State *L, const char *tname); +static void *(*luaL_testudata) (lua_State *L, int ud, const char *tname); +static void *(*luaL_checkudata) (lua_State *L, int ud, const char *tname); +static void (*luaL_where) (lua_State *L, int lvl); +static int (*luaL_error) (lua_State *L, const char *fmt, ...); +static int (*luaL_fileresult) (lua_State *L, int stat, const char *fname); +static int (*luaL_execresult) (lua_State *L, int stat); +static int (*luaL_ref) (lua_State *L, int t); +static void (*luaL_unref) (lua_State *L, int t, int ref); +static int (*luaL_loadstring) (lua_State *L, const char *s); +static lua_State *(*luaL_newstate) (void); +static int (*luaL_len) (lua_State *L, int idx); +static void (*luaL_setfuncs) (lua_State *L, const luaL_Reg *l, int nup); +static int (*luaL_getsubtable) (lua_State *L, int idx, const char *fname); +static void (*luaL_buffinit) (lua_State *L, luaL_Buffer *B); +static char *(*luaL_prepbuffsize) (luaL_Buffer *B, size_t sz); +static void (*luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +static void (*luaL_addstring) (luaL_Buffer *B, const char *s); +static void (*luaL_addvalue) (luaL_Buffer *B); +static void (*luaL_pushresult) (luaL_Buffer *B); +static void (*luaL_pushresultsize) (luaL_Buffer *B, size_t sz); +static char *(*luaL_buffinitsize) (lua_State *L, luaL_Buffer *B, size_t sz); +static lua_State *(*lua_newstate) (lua_Alloc f, void *ud); +static void (*lua_close) (lua_State *L); +static lua_State *(*lua_newthread) (lua_State *L); +static lua_CFunction (*lua_atpanic) (lua_State *L, lua_CFunction panicf); +static const lua_Number *(*lua_version) (lua_State *L); +static int (*lua_absindex) (lua_State *L, int idx); +static int (*lua_gettop) (lua_State *L); +static void (*lua_settop) (lua_State *L, int idx); +static void (*lua_pushvalue) (lua_State *L, int idx); +static void (*lua_remove) (lua_State *L, int idx); +static void (*lua_insert) (lua_State *L, int idx); +static void (*lua_replace) (lua_State *L, int idx); +static void (*lua_copy) (lua_State *L, int fromidx, int toidx); +static int (*lua_checkstack) (lua_State *L, int sz); +static void (*lua_xmove) (lua_State *from, lua_State *to, int n); +static int (*lua_isnumber) (lua_State *L, int idx); +static int (*lua_isstring) (lua_State *L, int idx); +static int (*lua_iscfunction) (lua_State *L, int idx); +static int (*lua_isuserdata) (lua_State *L, int idx); +static int (*lua_type) (lua_State *L, int idx); +static const char *(*lua_typename) (lua_State *L, int tp); +static lua_Number (*lua_tonumberx) (lua_State *L, int idx, int *isnum); +static lua_Integer (*lua_tointegerx) (lua_State *L, int idx, int *isnum); +static lua_Unsigned (*lua_tounsignedx) (lua_State *L, int idx, int *isnum); +static int (*lua_toboolean) (lua_State *L, int idx); +static const char *(*lua_tolstring) (lua_State *L, int idx, size_t *len); +static size_t (*lua_rawlen) (lua_State *L, int idx); +static lua_CFunction (*lua_tocfunction) (lua_State *L, int idx); +static void *(*lua_touserdata) (lua_State *L, int idx); +static lua_State *(*lua_tothread) (lua_State *L, int idx); +static const void *(*lua_topointer) (lua_State *L, int idx); +static void (*lua_arith) (lua_State *L, int op); +static int (*lua_rawequal) (lua_State *L, int idx1, int idx2); +static int (*lua_compare) (lua_State *L, int idx1, int idx2, int op); +static void (*lua_pushnil) (lua_State *L); +static void (*lua_pushnumber) (lua_State *L, lua_Number n); +static void (*lua_pushinteger) (lua_State *L, lua_Integer n); +static void (*lua_pushunsigned) (lua_State *L, lua_Unsigned n); +static const char *(*lua_pushlstring) (lua_State *L, const char *s, size_t l); +static const char *(*lua_pushstring) (lua_State *L, const char *s); +static const char *(*lua_pushfstring) (lua_State *L, const char *fmt, ...); +static void (*lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +static void (*lua_pushboolean) (lua_State *L, int b); +static void (*lua_pushlightuserdata) (lua_State *L, void *p); +static int (*lua_pushthread) (lua_State *L); +static void (*lua_getglobal) (lua_State *L, const char *var); +static void (*lua_gettable) (lua_State *L, int idx); +static void (*lua_getfield) (lua_State *L, int idx, const char *k); +static void (*lua_rawget) (lua_State *L, int idx); +static void (*lua_rawgeti) (lua_State *L, int idx, int n); +static void (*lua_rawgetp) (lua_State *L, int idx, const void *p); +static void (*lua_createtable) (lua_State *L, int narr, int nrec); +static void *(*lua_newuserdata) (lua_State *L, size_t sz); +static int (*lua_getmetatable) (lua_State *L, int objindex); +static void (*lua_getuservalue) (lua_State *L, int idx); +static void (*lua_setglobal) (lua_State *L, const char *var); +static void (*lua_settable) (lua_State *L, int idx); +static void (*lua_setfield) (lua_State *L, int idx, const char *k); +static void (*lua_rawset) (lua_State *L, int idx); +static void (*lua_rawseti) (lua_State *L, int idx, int n); +static void (*lua_rawsetp) (lua_State *L, int idx, const void *p); +static int (*lua_setmetatable) (lua_State *L, int objindex); +static void (*lua_setuservalue) (lua_State *L, int idx); +static int (*lua_getctx) (lua_State *L, int *ctx); +static int (*lua_dump) (lua_State *L, lua_Writer writer, void *data); +static int (*lua_resume) (lua_State *L, lua_State *from, int narg); +static int (*lua_status) (lua_State *L); +static int (*lua_gc) (lua_State *L, int what, int data); +static int (*lua_error) (lua_State *L); +static int (*lua_next) (lua_State *L, int idx); +static void (*lua_concat) (lua_State *L, int n); +static void (*lua_len) (lua_State *L, int idx); +static lua_Alloc (*lua_getallocf) (lua_State *L, void **ud); +static void (*lua_setallocf) (lua_State *L, lua_Alloc f, void *ud); +static int (*lua_getstack) (lua_State *L, int level, lua_Debug *ar); +static int (*lua_getinfo) (lua_State *L, const char *what, lua_Debug *ar); +static const char *(*lua_getlocal) (lua_State *L, const lua_Debug *ar, int n); +static const char *(*lua_setlocal) (lua_State *L, const lua_Debug *ar, int n); +static const char *(*lua_getupvalue) (lua_State *L, int funcindex, int n); +static const char *(*lua_setupvalue) (lua_State *L, int funcindex, int n); +static void *(*lua_upvalueid) (lua_State *L, int fidx, int n); +static int (*lua_sethook) (lua_State *L, lua_Hook func, int mask, int count); +static lua_Hook (*lua_gethook) (lua_State *L); +static int (*lua_gethookmask) (lua_State *L); +static int (*lua_gethookcount) (lua_State *L); +static void (*luaL_openlibs) (lua_State *L); +#define luaL_checkversion(L) luaL_checkversion_(L, LUA_VERSION_NUM) +#define luaL_loadfile(L,f) luaL_loadfilex(L,f,NULL) +#define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) +#define luaL_loadbuffer(L,s,sz,n) luaL_loadbufferx(L,s,sz,n,NULL) +#define luaL_addsize(B,s) ((B)->n += (s)) +#define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) +#define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0)) +#define lua_number2int(i,n) ((i)=(int)(n)) +#define lua_number2integer(i,n) ((i)=(lua_Integer)(n)) +#define lua_number2unsigned(i,n) ((i)=(lua_Unsigned)(n)) +#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol) +#define lua_h +#define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) +#define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) +#define lua_pcall(L,n,r,f) lua_pcallk(L, (n), (r), (f), 0, NULL) +#define lua_yield(L,n) lua_yieldk(L, (n), 0, NULL) +#define lua_tonumber(L,i) lua_tonumberx(L,i,NULL) +#define lua_tointeger(L,i) lua_tointegerx(L,i,NULL) +#define lua_tounsigned(L,i) lua_tounsignedx(L,i,NULL) +#define lua_pop(L,n) lua_settop(L, -(n)-1) +#define lua_newtable(L) lua_createtable(L, 0, 0) +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) +#define lua_strlen(L,i) lua_rawlen(L, (i)) +#define lua_objlen(L,i) lua_rawlen(L, (i)) +#define lua_equal(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPEQ) +#define lua_lessthan(L,idx1,idx2) lua_compare(L,(idx1),(idx2),LUA_OPLT) +#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define lua_str2number(s,p) strtod((s), (p)) +#define lua_strx2number(s,p) strtod((s), (p)) +#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name) +static void lite_init_plugin(void* (*symbol(const char*))) { + IMPORT_SYMBOL(luaL_checkversion_, void ,lua_State *L, lua_Number ver); + IMPORT_SYMBOL(luaL_getmetafield, int ,lua_State *L, int obj, const char *e); + IMPORT_SYMBOL(luaL_callmeta, int ,lua_State *L, int obj, const char *e); + IMPORT_SYMBOL(luaL_tolstring, const char *,lua_State *L, int idx, size_t *len); + IMPORT_SYMBOL(luaL_argerror, int ,lua_State *L, int numarg, const char *extramsg); + IMPORT_SYMBOL(luaL_checknumber, lua_Number ,lua_State *L, int numArg); + IMPORT_SYMBOL(luaL_optnumber, lua_Number ,lua_State *L, int nArg, lua_Number def); + IMPORT_SYMBOL(luaL_checkinteger, lua_Integer ,lua_State *L, int numArg); + IMPORT_SYMBOL(luaL_checkunsigned, lua_Unsigned ,lua_State *L, int numArg); + IMPORT_SYMBOL(luaL_checkstack, void ,lua_State *L, int sz, const char *msg); + IMPORT_SYMBOL(luaL_checktype, void ,lua_State *L, int narg, int t); + IMPORT_SYMBOL(luaL_checkany, void ,lua_State *L, int narg); + IMPORT_SYMBOL(luaL_newmetatable, int ,lua_State *L, const char *tname); + IMPORT_SYMBOL(luaL_setmetatable, void ,lua_State *L, const char *tname); + IMPORT_SYMBOL(luaL_testudata, void *,lua_State *L, int ud, const char *tname); + IMPORT_SYMBOL(luaL_checkudata, void *,lua_State *L, int ud, const char *tname); + IMPORT_SYMBOL(luaL_where, void ,lua_State *L, int lvl); + IMPORT_SYMBOL(luaL_error, int ,lua_State *L, const char *fmt, ...); + IMPORT_SYMBOL(luaL_fileresult, int ,lua_State *L, int stat, const char *fname); + IMPORT_SYMBOL(luaL_execresult, int ,lua_State *L, int stat); + IMPORT_SYMBOL(luaL_ref, int ,lua_State *L, int t); + IMPORT_SYMBOL(luaL_unref, void ,lua_State *L, int t, int ref); + IMPORT_SYMBOL(luaL_loadstring, int ,lua_State *L, const char *s); + IMPORT_SYMBOL(luaL_newstate, lua_State *,void); + IMPORT_SYMBOL(luaL_len, int ,lua_State *L, int idx); + IMPORT_SYMBOL(luaL_setfuncs, void ,lua_State *L, const luaL_Reg *l, int nup); + IMPORT_SYMBOL(luaL_getsubtable, int ,lua_State *L, int idx, const char *fname); + IMPORT_SYMBOL(luaL_buffinit, void ,lua_State *L, luaL_Buffer *B); + IMPORT_SYMBOL(luaL_prepbuffsize, char *,luaL_Buffer *B, size_t sz); + IMPORT_SYMBOL(luaL_addlstring, void ,luaL_Buffer *B, const char *s, size_t l); + IMPORT_SYMBOL(luaL_addstring, void ,luaL_Buffer *B, const char *s); + IMPORT_SYMBOL(luaL_addvalue, void ,luaL_Buffer *B); + IMPORT_SYMBOL(luaL_pushresult, void ,luaL_Buffer *B); + IMPORT_SYMBOL(luaL_pushresultsize, void ,luaL_Buffer *B, size_t sz); + IMPORT_SYMBOL(luaL_buffinitsize, char *,lua_State *L, luaL_Buffer *B, size_t sz); + IMPORT_SYMBOL(lua_newstate, lua_State *,lua_Alloc f, void *ud); + IMPORT_SYMBOL(lua_close, void ,lua_State *L); + IMPORT_SYMBOL(lua_newthread, lua_State *,lua_State *L); + IMPORT_SYMBOL(lua_atpanic, lua_CFunction ,lua_State *L, lua_CFunction panicf); + IMPORT_SYMBOL(lua_version, const lua_Number *,lua_State *L); + IMPORT_SYMBOL(lua_absindex, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_gettop, int ,lua_State *L); + IMPORT_SYMBOL(lua_settop, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_pushvalue, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_remove, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_insert, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_replace, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_copy, void ,lua_State *L, int fromidx, int toidx); + IMPORT_SYMBOL(lua_checkstack, int ,lua_State *L, int sz); + IMPORT_SYMBOL(lua_xmove, void ,lua_State *from, lua_State *to, int n); + IMPORT_SYMBOL(lua_isnumber, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_isstring, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_iscfunction, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_isuserdata, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_type, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_typename, const char *,lua_State *L, int tp); + IMPORT_SYMBOL(lua_tonumberx, lua_Number ,lua_State *L, int idx, int *isnum); + IMPORT_SYMBOL(lua_tointegerx, lua_Integer ,lua_State *L, int idx, int *isnum); + IMPORT_SYMBOL(lua_tounsignedx, lua_Unsigned ,lua_State *L, int idx, int *isnum); + IMPORT_SYMBOL(lua_toboolean, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_tolstring, const char *,lua_State *L, int idx, size_t *len); + IMPORT_SYMBOL(lua_rawlen, size_t ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_tocfunction, lua_CFunction ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_touserdata, void *,lua_State *L, int idx); + IMPORT_SYMBOL(lua_tothread, lua_State *,lua_State *L, int idx); + IMPORT_SYMBOL(lua_topointer, const void *,lua_State *L, int idx); + IMPORT_SYMBOL(lua_arith, void ,lua_State *L, int op); + IMPORT_SYMBOL(lua_rawequal, int ,lua_State *L, int idx1, int idx2); + IMPORT_SYMBOL(lua_compare, int ,lua_State *L, int idx1, int idx2, int op); + IMPORT_SYMBOL(lua_pushnil, void ,lua_State *L); + IMPORT_SYMBOL(lua_pushnumber, void ,lua_State *L, lua_Number n); + IMPORT_SYMBOL(lua_pushinteger, void ,lua_State *L, lua_Integer n); + IMPORT_SYMBOL(lua_pushunsigned, void ,lua_State *L, lua_Unsigned n); + IMPORT_SYMBOL(lua_pushlstring, const char *,lua_State *L, const char *s, size_t l); + IMPORT_SYMBOL(lua_pushstring, const char *,lua_State *L, const char *s); + IMPORT_SYMBOL(lua_pushfstring, const char *,lua_State *L, const char *fmt, ...); + IMPORT_SYMBOL(lua_pushcclosure, void ,lua_State *L, lua_CFunction fn, int n); + IMPORT_SYMBOL(lua_pushboolean, void ,lua_State *L, int b); + IMPORT_SYMBOL(lua_pushlightuserdata, void ,lua_State *L, void *p); + IMPORT_SYMBOL(lua_pushthread, int ,lua_State *L); + IMPORT_SYMBOL(lua_getglobal, void ,lua_State *L, const char *var); + IMPORT_SYMBOL(lua_gettable, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_getfield, void ,lua_State *L, int idx, const char *k); + IMPORT_SYMBOL(lua_rawget, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_rawgeti, void ,lua_State *L, int idx, int n); + IMPORT_SYMBOL(lua_rawgetp, void ,lua_State *L, int idx, const void *p); + IMPORT_SYMBOL(lua_createtable, void ,lua_State *L, int narr, int nrec); + IMPORT_SYMBOL(lua_newuserdata, void *,lua_State *L, size_t sz); + IMPORT_SYMBOL(lua_getmetatable, int ,lua_State *L, int objindex); + IMPORT_SYMBOL(lua_getuservalue, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_setglobal, void ,lua_State *L, const char *var); + IMPORT_SYMBOL(lua_settable, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_setfield, void ,lua_State *L, int idx, const char *k); + IMPORT_SYMBOL(lua_rawset, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_rawseti, void ,lua_State *L, int idx, int n); + IMPORT_SYMBOL(lua_rawsetp, void ,lua_State *L, int idx, const void *p); + IMPORT_SYMBOL(lua_setmetatable, int ,lua_State *L, int objindex); + IMPORT_SYMBOL(lua_setuservalue, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_getctx, int ,lua_State *L, int *ctx); + IMPORT_SYMBOL(lua_dump, int ,lua_State *L, lua_Writer writer, void *data); + IMPORT_SYMBOL(lua_resume, int ,lua_State *L, lua_State *from, int narg); + IMPORT_SYMBOL(lua_status, int ,lua_State *L); + IMPORT_SYMBOL(lua_gc, int ,lua_State *L, int what, int data); + IMPORT_SYMBOL(lua_error, int ,lua_State *L); + IMPORT_SYMBOL(lua_next, int ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_concat, void ,lua_State *L, int n); + IMPORT_SYMBOL(lua_len, void ,lua_State *L, int idx); + IMPORT_SYMBOL(lua_getallocf, lua_Alloc ,lua_State *L, void **ud); + IMPORT_SYMBOL(lua_setallocf, void ,lua_State *L, lua_Alloc f, void *ud); + IMPORT_SYMBOL(lua_getstack, int ,lua_State *L, int level, lua_Debug *ar); + IMPORT_SYMBOL(lua_getinfo, int ,lua_State *L, const char *what, lua_Debug *ar); + IMPORT_SYMBOL(lua_getlocal, const char *,lua_State *L, const lua_Debug *ar, int n); + IMPORT_SYMBOL(lua_setlocal, const char *,lua_State *L, const lua_Debug *ar, int n); + IMPORT_SYMBOL(lua_getupvalue, const char *,lua_State *L, int funcindex, int n); + IMPORT_SYMBOL(lua_setupvalue, const char *,lua_State *L, int funcindex, int n); + IMPORT_SYMBOL(lua_upvalueid, void *,lua_State *L, int fidx, int n); + IMPORT_SYMBOL(lua_sethook, int ,lua_State *L, lua_Hook func, int mask, int count); + IMPORT_SYMBOL(lua_gethook, lua_Hook ,lua_State *L); + IMPORT_SYMBOL(lua_gethookmask, int ,lua_State *L); + IMPORT_SYMBOL(lua_gethookcount, int ,lua_State *L); + IMPORT_SYMBOL(luaL_openlibs, void ,lua_State *L); +} diff --git a/sample_plugin.c b/sample_plugin.c index 8cd58bc3..96e021c9 100644 --- a/sample_plugin.c +++ b/sample_plugin.c @@ -1,12 +1,8 @@ -typedef struct lua_State lua_State; -#define SYMBOL(NAME, RTYPE, ...) RTYPE (*lua_##NAME)(lua_State*, __VA_ARGS__) = (RTYPE (*)(lua_State*, __VA_ARGS__))symbol("lua_" #NAME); +#include "lite_plugin_api.h" int lua_open_sample(lua_State* L, void* (*symbol(const char*))) { - SYMBOL(createtable, void, int, int); - SYMBOL(setfield, void, int, const char*); - SYMBOL(pushstring, const char*, const char*); - + lite_init_plugin(symbol); lua_createtable(L, 0, 0); lua_pushstring(L, "value"); lua_setfield(L, -2, "example"); diff --git a/src/api/system.c b/src/api/system.c index 791d839f..78b3e700 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -675,7 +675,7 @@ static void* api_require(const char* symbol) { return NULL; } -static int loader_plugin(lua_State* L) { +static int f_load_native_plugin(lua_State* L) { size_t sname, modlen, pathlen; char buffer[512] = "lua_open_"; const char* modname = luaL_checklstring(L, -2, &modlen); @@ -699,48 +699,6 @@ static int loader_plugin(lua_State* L) { return 1; } -#if _WIN32 - #define PATH_SEPARATOR '\\' - #define DYNAMIC_SUFFIX "dll" -#else - #if __APPLE__ - #define DYNAMIC_SUFFIX "lib" - #else - #define DYNAMIC_SUFFIX "so" - #endif - #define PATH_SEPARATOR '/' -#endif -static int f_searcher_plugin(lua_State *L) { - size_t len, cpath_len, cpath_idx = 0, cpath_q = 0; - const char* modname = luaL_checklstring(L, -1, &len); - lua_getglobal(L, "package"); - lua_getfield(L, -1, "cpath"); - const char* cpath = luaL_checklstring(L, -1, &cpath_len); - char lib[8192]; - for (size_t i = 0; i <= cpath_len; ++i) { - if (i == cpath_len || cpath[i] == ';') { - if (cpath_q) { - size_t offset = cpath_q - cpath_idx; - strncpy(lib, &cpath[cpath_idx], offset); - for (size_t j = 0; j < len && j < sizeof(lib) - 3; ++j) - lib[offset++] = modname[j] != '.' ? modname[j] : PATH_SEPARATOR; - lib[offset++] = '\0'; - strcat(lib, "." DYNAMIC_SUFFIX); - struct stat s; - if (!stat(lib, &s)) { - lua_pushcfunction(L, loader_plugin); - lua_pushstring(L, lib); - return 2; - } - } - cpath_idx = i + 1; - cpath_q = 0; - } else if (cpath[i] == '?') { - cpath_q = i; - } - } - return 0; -} static const luaL_Reg lib[] = { { "poll_event", f_poll_event }, @@ -768,7 +726,7 @@ static const luaL_Reg lib[] = { { "exec", f_exec }, { "fuzzy_match", f_fuzzy_match }, { "set_window_opacity", f_set_window_opacity }, - { "searcher_plugin", f_searcher_plugin }, + { "load_native_plugin", f_load_native_plugin }, { NULL, NULL } }; From 29875540978d9f898db17e993414bb67194ea15d Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 15:47:12 -0400 Subject: [PATCH 075/165] Moving things into final positions. --- build_plugin.sh | 1 - data/core/init.lua | 2 -- data/core/start.lua | 1 + generate_header.sh | 17 ---------- .../lite_xl_plugin_api.h | 33 ++++++++++++++++--- sample_plugin.c | 10 ------ 6 files changed, 29 insertions(+), 35 deletions(-) delete mode 100755 build_plugin.sh delete mode 100755 generate_header.sh rename lite_plugin_api.h => resources/lite_xl_plugin_api.h (91%) delete mode 100644 sample_plugin.c diff --git a/build_plugin.sh b/build_plugin.sh deleted file mode 100755 index 437f9b58..00000000 --- a/build_plugin.sh +++ /dev/null @@ -1 +0,0 @@ -gcc -g sample_plugin.c -shared -o data/plugins/sample.so diff --git a/data/core/init.lua b/data/core/init.lua index 1cb98979..e05d5c2b 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -426,8 +426,6 @@ function core.init() NagView = require "core.nagview" DocView = require "core.docview" Doc = require "core.doc" - local PluginTest = require "plugins.sample" - print(PluginTest.example) if PATHSEP == '\\' then USERDIR = common.normalize_path(USERDIR) diff --git a/data/core/start.lua b/data/core/start.lua index 7658d6c7..0bbaadee 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -22,6 +22,7 @@ package.path = USERDIR .. '/?/init.lua;' .. package.path local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath +package.cpath = USERDIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath package.searchers[3] = function(modname) local s,e = 0 repeat diff --git a/generate_header.sh b/generate_header.sh deleted file mode 100755 index fb03e712..00000000 --- a/generate_header.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/sh -# Takes lua folder, outputs a header file that includes all the necessary macros for writing a lite plugin. -# Takes a minimal approach, and strips out problematic functions. Should be good enough for most purposes. - -echo "// This file was automatically generated by generate_header.sh. Do not modify directly." -echo "#include " -echo "typedef struct lua_State lua_State; typedef double lua_Number; typedef int (*lua_CFunction)(lua_State*); typedef ptrdiff_t lua_Integer;" -echo "typedef unsigned long lua_Unsigned; typedef struct luaL_Buffer luaL_Buffer; typedef struct luaL_Reg luaL_Reg; typedef struct lua_Debug lua_Debug;" -echo "typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);" -echo "typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);" -echo "typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);" -grep -h "^LUA\(LIB\)*_API" $1/*.h | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^/static /" -grep -h "#define luaL*_" $1/*.h | grep -v "\\\s*$" | grep -v "\(assert\|lock\)" | grep -v "\(asm\|int32\)" | grep -v "#define lua_number2integer(i,n)\s*lua_number2int(i, n)" -echo "#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name)" -echo "static void lite_init_plugin(void* (*symbol(const char*))) {" -grep -h "^LUA\(LIB\)*_API" $1/*.h | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^\([^)]*\)(\*\(lua\w*\))\s*(/\tIMPORT_SYMBOL(\2, \1,/" -echo "}" diff --git a/lite_plugin_api.h b/resources/lite_xl_plugin_api.h similarity index 91% rename from lite_plugin_api.h rename to resources/lite_xl_plugin_api.h index b6c03204..7ac9c5e8 100644 --- a/lite_plugin_api.h +++ b/resources/lite_xl_plugin_api.h @@ -1,4 +1,30 @@ -// This file was automatically generated by generate_header.sh. Do not modify directly. +#ifndef LITE_XL_PLUGIN_API +#define LITE_XL_PLUGIN_API +/* This file was automatically generated by the below code. Do not modify directly. +#!/bin/sh +# Takes lua folder, outputs a header file that includes all the necessary macros for writing a lite plugin. +# Takes a minimal approach, and strips out problematic functions. Should be good enough for most purposes. + +echo "#ifndef LITE_XL_PLUGIN_API" +echo "#define LITE_XL_PLUGIN_API" +echo "/* This file was automatically generated by the below code. Do not modify directly." +cat $0 +echo "*""/" +echo "#include " +echo "typedef struct lua_State lua_State; typedef double lua_Number; typedef int (*lua_CFunction)(lua_State*); typedef ptrdiff_t lua_Integer;" +echo "typedef unsigned long lua_Unsigned; typedef struct luaL_Buffer luaL_Buffer; typedef struct luaL_Reg luaL_Reg; typedef struct lua_Debug lua_Debug;" +echo "typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar);" +echo "typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize);" +echo "typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud);" +LUA_HEADERS=`pkg-config --cflags lua5.2 | sed 's/^-I//' | sed 's/$/\/*.h/'` +grep -h "^LUA\(LIB\)*_API" $LUA_HEADERS | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^/static /" +grep -h "#define luaL*_" $LUA_HEADERS | grep -v "\\\s*$" | grep -v "\(assert\|lock\)" | grep -v "\(asm\|int32\)" | grep -v "#define lua_number2integer(i,n)\s*lua_number2int(i, n)" +echo "#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name)" +echo "static void lite_init_plugin(void* (*symbol(const char*))) {" +grep -h "^LUA\(LIB\)*_API" $LUA_HEADERS | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^\([^)]*\)(\*\(lua\w*\))\s*(/\tIMPORT_SYMBOL(\2, \1,/" +echo "}" +echo "#endif" +*/ #include typedef struct lua_State lua_State; typedef double lua_Number; typedef int (*lua_CFunction)(lua_State*); typedef ptrdiff_t lua_Integer; typedef unsigned long lua_Unsigned; typedef struct luaL_Buffer luaL_Buffer; typedef struct luaL_Reg luaL_Reg; typedef struct lua_Debug lua_Debug; @@ -142,10 +168,6 @@ static void (*luaL_openlibs) (lua_State *L); #define luaL_addsize(B,s) ((B)->n += (s)) #define luaL_prepbuffer(B) luaL_prepbuffsize(B, LUAL_BUFFERSIZE) #define luaL_register(L,n,l) (luaL_openlib(L,(n),(l),0)) -#define lua_number2int(i,n) ((i)=(int)(n)) -#define lua_number2integer(i,n) ((i)=(lua_Integer)(n)) -#define lua_number2unsigned(i,n) ((i)=(lua_Unsigned)(n)) -#define luaL_newstate() lua_newstate(debug_realloc, &l_memcontrol) #define lua_h #define lua_upvalueindex(i) (LUA_REGISTRYINDEX - (i)) #define lua_call(L,n,r) lua_callk(L, (n), (r), 0, NULL) @@ -298,3 +320,4 @@ static void lite_init_plugin(void* (*symbol(const char*))) { IMPORT_SYMBOL(lua_gethookcount, int ,lua_State *L); IMPORT_SYMBOL(luaL_openlibs, void ,lua_State *L); } +#endif diff --git a/sample_plugin.c b/sample_plugin.c deleted file mode 100644 index 96e021c9..00000000 --- a/sample_plugin.c +++ /dev/null @@ -1,10 +0,0 @@ - -#include "lite_plugin_api.h" - -int lua_open_sample(lua_State* L, void* (*symbol(const char*))) { - lite_init_plugin(symbol); - lua_createtable(L, 0, 0); - lua_pushstring(L, "value"); - lua_setfield(L, -2, "example"); - return 1; -} From 03b467d9d98ef0e7d9c369b92c7b5a49a0d462cf Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 16:08:21 -0400 Subject: [PATCH 076/165] Cleaned up, added utility API. --- resources/lite_xl_plugin_api.h | 6 ++-- src/api/system.c | 62 +++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/resources/lite_xl_plugin_api.h b/resources/lite_xl_plugin_api.h index 7ac9c5e8..bd2d3dba 100644 --- a/resources/lite_xl_plugin_api.h +++ b/resources/lite_xl_plugin_api.h @@ -20,7 +20,8 @@ LUA_HEADERS=`pkg-config --cflags lua5.2 | sed 's/^-I//' | sed 's/$/\/*.h/'` grep -h "^LUA\(LIB\)*_API" $LUA_HEADERS | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^/static /" grep -h "#define luaL*_" $LUA_HEADERS | grep -v "\\\s*$" | grep -v "\(assert\|lock\)" | grep -v "\(asm\|int32\)" | grep -v "#define lua_number2integer(i,n)\s*lua_number2int(i, n)" echo "#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name)" -echo "static void lite_init_plugin(void* (*symbol(const char*))) {" +echo "static void lite_xl_plugin_init(void* XL) {" +echo "\tvoid* (*symbol)(const char*) = (void* (*)(const char*))XL;" grep -h "^LUA\(LIB\)*_API" $LUA_HEADERS | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^\([^)]*\)(\*\(lua\w*\))\s*(/\tIMPORT_SYMBOL(\2, \1,/" echo "}" echo "#endif" @@ -197,7 +198,8 @@ static void (*luaL_openlibs) (lua_State *L); #define lua_str2number(s,p) strtod((s), (p)) #define lua_strx2number(s,p) strtod((s), (p)) #define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name) -static void lite_init_plugin(void* (*symbol(const char*))) { +static void lite_xl_plugin_init(void* XL) { + void* (*symbol)(const char*) = (void* (*)(const char*))XL; IMPORT_SYMBOL(luaL_checkversion_, void ,lua_State *L, lua_Number ver); IMPORT_SYMBOL(luaL_getmetafield, int ,lua_State *L, int obj, const char *e); IMPORT_SYMBOL(luaL_callmeta, int ,lua_State *L, int obj, const char *e); diff --git a/src/api/system.c b/src/api/system.c index 78b3e700..01cf72a7 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -645,6 +645,7 @@ typedef struct { } lua_function_node; #define P(FUNC) { "lua_" #FUNC, (void*)(lua_##FUNC) } +#define U(FUNC) { "luaL_" #FUNC, (void*)(luaL_##FUNC) } static void* api_require(const char* symbol) { static lua_function_node nodes[] = { P(absindex), P(arith), P(atpanic), P(callk), P(checkstack), @@ -652,21 +653,26 @@ static void* api_require(const char* symbol) { P(error), P(gc), P(getallocf), P(getctx), P(getfield), P(getglobal), P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal), P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue), - P(getuservalue), P(insert), P(isnumber), - P(isstring), P(isuserdata), P(len), P(load), - P(newstate), P(newthread), P(newuserdata), P(next), - P(pcallk), P(pushboolean), P(pushcclosure), - P(pushfstring), P(pushinteger), P(pushlightuserdata), - P(pushlstring), P(pushnil), P(pushnumber), P(pushstring), - P(pushthread), P(pushunsigned), P(pushvalue), P(pushvfstring), P(rawequal), - P(rawget), P(rawgeti), P(rawgetp), P(rawlen), P(rawset), P(rawseti), - P(rawsetp), P(remove), P(replace), P(resume), P(setallocf), - P(setfield), P(setglobal), P(sethook), P(setlocal), P(setmetatable), - P(settable), P(settop), P(setupvalue), P(setuservalue), P(status), - P(tocfunction), P(tointegerx), P(tolstring), P(toboolean), - P(tonumberx), P(topointer), P(tothread), - P(tounsignedx), P(touserdata), P(type), P(typename), P(upvalueid), - P(upvaluejoin), P(version), P(xmove), P(yieldk) + P(getuservalue), P(insert), P(isnumber), P(isstring), P(isuserdata), + P(len), P(load), P(newstate), P(newthread), P(newuserdata), P(next), + P(pcallk), P(pushboolean), P(pushcclosure), P(pushfstring), P(pushinteger), + P(pushlightuserdata), P(pushlstring), P(pushnil), P(pushnumber), + P(pushstring), P(pushthread), P(pushunsigned), P(pushvalue), + P(pushvfstring), P(rawequal), P(rawget), P(rawgeti), P(rawgetp), P(rawlen), + P(rawset), P(rawseti), P(rawsetp), P(remove), P(replace), P(resume), + P(setallocf), P(setfield), P(setglobal), P(sethook), P(setlocal), + P(setmetatable), P(settable), P(settop), P(setupvalue), P(setuservalue), + P(status), P(tocfunction), P(tointegerx), P(tolstring), P(toboolean), + P(tonumberx), P(topointer), P(tothread), P(tounsignedx), P(touserdata), + P(type), P(typename), P(upvalueid), P(upvaluejoin), P(version), P(xmove), + P(yieldk), U(checkversion_), U(getmetafield), U(callmeta), U(tolstring), + U(argerror), U(checknumber), U(optnumber), U(checkinteger), + U(checkunsigned), U(checkstack), U(checktype), U(checkany), + U(newmetatable), U(setmetatable), U(testudata), U(checkudata), U(where), + U(error), U(fileresult), U(execresult), U(ref), U(unref), U(loadstring), + U(newstate), U(len), U(setfuncs), U(getsubtable), U(buffinit), + U(prepbuffsize), U(addlstring), U(addstring), U(addvalue), U(pushresult), + U(pushresultsize), U(buffinitsize) }; for (int i = 0; i < sizeof(nodes) / sizeof(lua_function_node); ++i) { if (strcmp(nodes[i].symbol, symbol) == 0) @@ -676,26 +682,26 @@ static void* api_require(const char* symbol) { } static int f_load_native_plugin(lua_State* L) { - size_t sname, modlen, pathlen; - char buffer[512] = "lua_open_"; - const char* modname = luaL_checklstring(L, -2, &modlen); + size_t sname, namelen, pathlen; + char olib[512]; + const char* name = luaL_checklstring(L, -2, &namelen); const char* path = luaL_checklstring(L, -1, &pathlen); void* library = SDL_LoadObject(path); - if (modlen == 0 || !library) - return luaL_error(L, "Unable to load %s: %s", modname, SDL_GetError()); - for (sname = modlen - 1; sname > 0 && modname[sname] != '.'; --sname); - strcat(buffer, &modname[sname+1]); - int (*entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, buffer); + if (name == 0 || !library) + return luaL_error(L, "Unable to load %s: %s", name, SDL_GetError()); + for (sname = namelen - 1; sname > 0 && name[sname] != '.'; --sname); + snprintf(olib, sizeof(olib), "lua_open_%s", &name[sname+1]); olib[511] = 0; + int (*entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, olib); if (!entrypoint) { return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a " - "function defined as int %s(lua_State* L, void* (*symbol)(const char*))", - modname, buffer); + "function defined as int %s(lua_State* L, void* XL)", + name, olib); } lua_pushlightuserdata(L, library); - lua_setfield(L, LUA_REGISTRYINDEX, modname); + lua_setfield(L, LUA_REGISTRYINDEX, name); if (entrypoint(L, api_require) == 0) - return luaL_error(L, "Unable to load %s: Your entrypoint must return at\ - least one argument."); + return luaL_error(L, "Unable to load %s: Your entrypoint must return at " + " least one value.", name); return 1; } From 801b7dd0d9ce3addb1855a014b792e8feae3d14b Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 16:22:33 -0400 Subject: [PATCH 077/165] Added some comments. --- resources/lite_xl_plugin_api.h | 41 +++++++++++++++++++++++++++++----- src/api/system.c | 4 ++-- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/resources/lite_xl_plugin_api.h b/resources/lite_xl_plugin_api.h index bd2d3dba..ba9b053f 100644 --- a/resources/lite_xl_plugin_api.h +++ b/resources/lite_xl_plugin_api.h @@ -1,13 +1,44 @@ #ifndef LITE_XL_PLUGIN_API #define LITE_XL_PLUGIN_API -/* This file was automatically generated by the below code. Do not modify directly. -#!/bin/sh -# Takes lua folder, outputs a header file that includes all the necessary macros for writing a lite plugin. -# Takes a minimal approach, and strips out problematic functions. Should be good enough for most purposes. +/* +The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long +as it has an entrypoint that looks like the following, where xxxxx is the plugin name: +#include "lite_xl_plugin_api.h" + +int lua_open_xxxxx(lua_State* L, void* XL) { + lite_xl_plugin_init(XL); + ... + return 1; +} + +In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple! +Due to the way the API is structured, you *should not* link or include lua libraries. + +This file was automatically generated by the below code. Do NOT MODIFY DIRECTLY. + + +#!/bin/sh echo "#ifndef LITE_XL_PLUGIN_API" echo "#define LITE_XL_PLUGIN_API" -echo "/* This file was automatically generated by the below code. Do not modify directly." +echo "/* " +echo "The lite_xl plugin API is quite simple. Any shared library can be a plugin file, so long" +echo "as it has an entrypoint that looks like the following, where xxxxx is the plugin name:" +echo +echo '#include "lite_xl_plugin_api.h"' +echo +echo "int lua_open_xxxxx(lua_State* L, void* XL) {" +echo " lite_xl_plugin_init(XL);" +echo " ..." +echo " return 1;" +echo "}" +echo +echo "In linux, to compile this file, you'd do: 'gcc -o xxxxx.so -shared xxxxx.c'. Simple!" +echo "Due to the way the API is structured, you *should not* link or include lua libraries." +echo +echo "This file was automatically generated by the below code. Do NOT MODIFY DIRECTLY." +echo +echo cat $0 echo "*""/" echo "#include " diff --git a/src/api/system.c b/src/api/system.c index 01cf72a7..e5ec18cd 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -637,8 +637,8 @@ static int f_set_window_opacity(lua_State *L) { return 1; } - - +// Symbol table for native plugin loading. Allows for a statically +// bound lua library to be used by native plugins. typedef struct { const char* symbol; void* address; From 10c3c9d4cf14cd1fdf5f2db97b1473c3f7d5a4db Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 16:24:07 -0400 Subject: [PATCH 078/165] Undid deletion. --- data/core/init.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/data/core/init.lua b/data/core/init.lua index e05d5c2b..af291767 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -675,6 +675,7 @@ local function check_plugin_version(filename) return true, version_match end + function core.load_plugins() local no_errors = true local refused_list = { From 1721b8f1c9859f037bef49480c24430805c2e521 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 16:28:02 -0400 Subject: [PATCH 079/165] Should be elif. --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 3e99c735..4b15ef34 100644 --- a/src/main.c +++ b/src/main.c @@ -146,7 +146,7 @@ init_lua: #ifdef MACOS_USE_BUNDLE set_macos_bundle_resources(L); #endif -#elsif _WIN32 +#elif _WIN32 lua_setglobal(L, "WINDOWS"); #else lua_setglobal(L, "LINUX"); From fbc11c00eb2c422a0347a8c1c92d533c212c4ad4 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 16:55:33 -0400 Subject: [PATCH 080/165] Split entrypoints in half. --- resources/lite_xl_plugin_api.h | 4 ++-- src/api/system.c | 30 +++++++++++++++++++----------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/resources/lite_xl_plugin_api.h b/resources/lite_xl_plugin_api.h index ba9b053f..08e22137 100644 --- a/resources/lite_xl_plugin_api.h +++ b/resources/lite_xl_plugin_api.h @@ -6,7 +6,7 @@ as it has an entrypoint that looks like the following, where xxxxx is the plugin #include "lite_xl_plugin_api.h" -int lua_open_xxxxx(lua_State* L, void* XL) { +int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) { lite_xl_plugin_init(XL); ... return 1; @@ -27,7 +27,7 @@ echo "as it has an entrypoint that looks like the following, where xxxxx is the echo echo '#include "lite_xl_plugin_api.h"' echo -echo "int lua_open_xxxxx(lua_State* L, void* XL) {" +echo "int lua_open_lite_xl_xxxxx(lua_State* L, void* XL) {" echo " lite_xl_plugin_init(XL);" echo " ..." echo " return 1;" diff --git a/src/api/system.c b/src/api/system.c index e5ec18cd..47709344 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -683,26 +683,34 @@ static void* api_require(const char* symbol) { static int f_load_native_plugin(lua_State* L) { size_t sname, namelen, pathlen; - char olib[512]; + int results; + char olib[512]; olib[sizeof(olib)-1] = 0; const char* name = luaL_checklstring(L, -2, &namelen); const char* path = luaL_checklstring(L, -1, &pathlen); void* library = SDL_LoadObject(path); if (name == 0 || !library) return luaL_error(L, "Unable to load %s: %s", name, SDL_GetError()); - for (sname = namelen - 1; sname > 0 && name[sname] != '.'; --sname); - snprintf(olib, sizeof(olib), "lua_open_%s", &name[sname+1]); olib[511] = 0; - int (*entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, olib); - if (!entrypoint) { - return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a " - "function defined as int %s(lua_State* L, void* XL)", - name, olib); - } lua_pushlightuserdata(L, library); lua_setfield(L, LUA_REGISTRYINDEX, name); - if (entrypoint(L, api_require) == 0) + for (sname = namelen - 1; sname > 0 && name[sname] != '.'; --sname); + snprintf(olib, sizeof(olib), "lua_open_lite_xl_%s", &name[sname+1]); + int (*ext_entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, olib); + if (!ext_entrypoint) { + snprintf(olib, sizeof(olib), "lua_open_%s", &name[sname+1]); + int (*entrypoint)(lua_State* L) = SDL_LoadFunction(library, olib); + if (!entrypoint) { + return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a " + "function defined as int lua_open_lite_xl_%s(lua_State* L, void* XL)", + name, &name[sname+1]); + } + results = entrypoint(L); + } else { + results = ext_entrypoint(L, api_require); + } + if (!results) return luaL_error(L, "Unable to load %s: Your entrypoint must return at " " least one value.", name); - return 1; + return results; } From 4b828eff653dcf899bbaf18db2f8a9ca7dc870e2 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 17:29:44 -0400 Subject: [PATCH 081/165] Markdown subsyntax highlighting. --- data/plugins/language_md.lua | 45 +++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/data/plugins/language_md.lua b/data/plugins/language_md.lua index 6e6e4255..213bb6cd 100644 --- a/data/plugins/language_md.lua +++ b/data/plugins/language_md.lua @@ -1,22 +1,41 @@ -- mod-version:2 -- lite-xl 2.0 local syntax = require "core.syntax" + + syntax.add { files = { "%.md$", "%.markdown$" }, patterns = { - { pattern = "\\.", type = "normal" }, - { pattern = { "" }, type = "comment" }, - { pattern = { "```", "```" }, type = "string" }, - { pattern = { "``", "``", "\\" }, type = "string" }, - { pattern = { "`", "`", "\\" }, type = "string" }, - { pattern = { "~~", "~~", "\\" }, type = "keyword2" }, - { pattern = "%-%-%-+", type = "comment" }, - { pattern = "%*%s+", type = "operator" }, - { pattern = { "%*", "[%*\n]", "\\" }, type = "operator" }, - { pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" }, - { pattern = "#.-\n", type = "keyword" }, - { pattern = "!?%[.-%]%(.-%)", type = "function" }, - { pattern = "https?://%S+", type = "function" }, + { pattern = "\\.", type = "normal" }, + { pattern = { "" }, type = "comment" }, + { pattern = { "```c", "```" }, type = "string", syntax = ".c" }, + { pattern = { "```c++", "```" }, type = "string", syntax = ".cpp" }, + { pattern = { "```python", "```" }, type = "string", syntax = ".py" }, + { pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" }, + { pattern = { "```perl", "```" }, type = "string", syntax = ".pl" }, + { pattern = { "```php", "```" }, type = "string", syntax = ".php" }, + { pattern = { "```javascript", "```" }, type = "string", syntax = ".js" },, + { pattern = { "```html", "```" }, type = "string", syntax = ".html" }, + { pattern = { "```xml", "```" }, type = "string", syntax = ".xml" }, + { pattern = { "```css", "```" }, type = "string", syntax = ".css" }, + { pattern = { "```lua", "```" }, type = "string", syntax = ".lua" }, + { pattern = { "```bash", "```" }, type = "string", syntax = ".sh" }, + { pattern = { "```java", "```" }, type = "string", syntax = ".java" }, + { pattern = { "```c#", "```" }, type = "string", syntax = ".cs" }, + { pattern = { "```cmake", "```" }, type = "string", syntax = ".cmake" }, + { pattern = { "```d", "```" }, type = "string", syntax = ".d" }, + { pattern = { "```glsl", "```" }, type = "string", syntax = ".glsl" }, + { pattern = { "```", "```" }, type = "string" }, + { pattern = { "``", "``", "\\" }, type = "string" }, + { pattern = { "`", "`", "\\" }, type = "string" }, + { pattern = { "~~", "~~", "\\" }, type = "keyword2" }, + { pattern = "%-%-%-+", type = "comment" }, + { pattern = "%*%s+", type = "operator" }, + { pattern = { "%*", "[%*\n]", "\\" }, type = "operator" }, + { pattern = { "%_", "[%_\n]", "\\" }, type = "keyword2" }, + { pattern = "#.-\n", type = "keyword" }, + { pattern = "!?%[.-%]%(.-%)", type = "function" }, + { pattern = "https?://%S+", type = "function" }, }, symbols = { }, } From 2ac7c7f09bc8cf648123fb3aa2fe66b2839dc389 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 17:43:17 -0400 Subject: [PATCH 082/165] Syntax. --- data/plugins/language_md.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/plugins/language_md.lua b/data/plugins/language_md.lua index 213bb6cd..3c1c329a 100644 --- a/data/plugins/language_md.lua +++ b/data/plugins/language_md.lua @@ -14,7 +14,7 @@ syntax.add { { pattern = { "```ruby", "```" }, type = "string", syntax = ".rb" }, { pattern = { "```perl", "```" }, type = "string", syntax = ".pl" }, { pattern = { "```php", "```" }, type = "string", syntax = ".php" }, - { pattern = { "```javascript", "```" }, type = "string", syntax = ".js" },, + { pattern = { "```javascript", "```" }, type = "string", syntax = ".js" }, { pattern = { "```html", "```" }, type = "string", syntax = ".html" }, { pattern = { "```xml", "```" }, type = "string", syntax = ".xml" }, { pattern = { "```css", "```" }, type = "string", syntax = ".css" }, From 6a3f59c423660f691f297b38b524610b35ec5395 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Thu, 16 Sep 2021 23:26:11 +0200 Subject: [PATCH 083/165] Move single tab drawing to its own function --- data/core/rootview.lua | 91 +++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 41 deletions(-) diff --git a/data/core/rootview.lua b/data/core/rootview.lua index 0856a619..8a927077 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -513,6 +513,53 @@ function Node:update() end end +function Node:draw_tab(text, is_active, is_hovered, is_close_hovered, x, y, w, h, standalone) + local ds = style.divider_size + local dots_width = style.font:get_width("…") + local color = style.dim + local padding_y = style.padding.y + renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim) + if standalone then + renderer.draw_rect(x-1, y-1, w+2, h+2, style.background2) + end + if is_active then + color = style.text + renderer.draw_rect(x, y, w, h, style.background) + renderer.draw_rect(x + w, y, ds, h, style.divider) + renderer.draw_rect(x - ds, y, ds, h, style.divider) + end + local cx, cw, cspace = close_button_location(x, w) + local show_close_button = ((is_active or is_hovered) and not standalone and config.tab_close_button) + if show_close_button then + local close_style = is_close_hovered and style.text or style.dim + common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h) + end + if is_hovered then + color = style.text + end + local padx = style.padding.x + -- Normally we should substract "cspace" from text_avail_width and from the + -- clipping width. It is the padding space we give to the left and right of the + -- close button. However, since we are using dots to terminate filenames, we + -- choose to ignore "cspace" accepting that the text can possibly "touch" the + -- close button. + local text_avail_width = cx - x - padx + core.push_clip_rect(x, y, cx - x, h) + x, w = x + padx, w - padx * 2 + local align = "center" + if style.font:get_width(text) > text_avail_width then + align = "left" + for i = 1, #text do + local reduced_text = text:sub(1, #text - i) + if style.font:get_width(reduced_text) + dots_width <= text_avail_width then + text = reduced_text .. "…" + break + end + end + end + common.draw_text(style.font, color, text, align, x, y, w, h) + core.pop_clip_rect() +end function Node:draw_tabs() local x, y, w, h, scroll_padding = self:get_scroll_button_rect(1) @@ -537,47 +584,9 @@ function Node:draw_tabs() for i = self.tab_offset, self.tab_offset + tabs_number - 1 do local view = self.views[i] local x, y, w, h = self:get_tab_rect(i) - local text = view:get_name() - local color = style.dim - local padding_y = style.padding.y - renderer.draw_rect(x + w, y + padding_y, ds, h - padding_y * 2, style.dim) - if view == self.active_view then - color = style.text - renderer.draw_rect(x, y, w, h, style.background) - renderer.draw_rect(x + w, y, ds, h, style.divider) - renderer.draw_rect(x - ds, y, ds, h, style.divider) - end - local cx, cw, cspace = close_button_location(x, w) - local show_close_button = ((view == self.active_view or i == self.hovered_tab) and config.tab_close_button) - if show_close_button then - local close_style = self.hovered_close == i and style.text or style.dim - common.draw_text(style.icon_font, close_style, "C", nil, cx, y, 0, h) - end - if i == self.hovered_tab then - color = style.text - end - local padx = style.padding.x - -- Normally we should substract "cspace" from text_avail_width and from the - -- clipping width. It is the padding space we give to the left and right of the - -- close button. However, since we are using dots to terminate filenames, we - -- choose to ignore "cspace" accepting that the text can possibly "touch" the - -- close button. - local text_avail_width = cx - x - padx - core.push_clip_rect(x, y, cx - x, h) - x, w = x + padx, w - padx * 2 - local align = "center" - if style.font:get_width(text) > text_avail_width then - align = "left" - for i = 1, #text do - local reduced_text = text:sub(1, #text - i) - if style.font:get_width(reduced_text) + dots_width <= text_avail_width then - text = reduced_text .. "…" - break - end - end - end - common.draw_text(style.font, color, text, align, x, y, w, h) - core.pop_clip_rect() + self:draw_tab(view:get_name(), view == self.active_view, + i == self.hovered_tab, i == self.hovered_close, + x, y, w, h) end core.pop_clip_rect() From 3eba7cd7f17781786de4b1a431bc8668a03e8fe5 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 17 Sep 2021 02:47:34 +0200 Subject: [PATCH 084/165] Implement tab drag and drop --- data/core/common.lua | 5 + data/core/rootview.lua | 272 +++++++++++++++++++++++++++++++++++++---- data/core/style.lua | 2 + 3 files changed, 256 insertions(+), 23 deletions(-) diff --git a/data/core/common.lua b/data/core/common.lua index 9f3102bb..1a1b22cd 100644 --- a/data/core/common.lua +++ b/data/core/common.lua @@ -41,6 +41,11 @@ function common.lerp(a, b, t) end +function common.distance(x1, y1, x2, y2) + return math.sqrt(math.pow(x2-x1, 2)+math.pow(y2-y1, 2)) +end + + function common.color(str) local r, g, b, a = str:match("#(%x%x)(%x%x)(%x%x)") if r then diff --git a/data/core/rootview.lua b/data/core/rootview.lua index 8a927077..bf438aa5 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -712,6 +712,62 @@ function Node:resize(axis, value) end +function Node:get_split_type(mouse_x, mouse_y) + local x, y = self.position.x, self.position.y + local w, h = self.size.x, self.size.y + local _, _, _, tab_h = self:get_scroll_button_rect(1) + y = y + tab_h + h = h - tab_h + + local local_mouse_x = mouse_x - x + local local_mouse_y = mouse_y - y + + if local_mouse_y < 0 then + return "tab" + else + local left_pct = local_mouse_x * 100 / w + local top_pct = local_mouse_y * 100 / h + if left_pct <= 30 then + return "left" + elseif left_pct >= 70 then + return "right" + elseif top_pct <= 30 then + return "up" + elseif top_pct >= 70 then + return "down" + end + return "middle" + end +end + + +function Node:get_drag_overlay_tab_position(x, y, dragged_node, dragged_index) + local tab_index = self:get_tab_overlapping_point(x, y) + if not tab_index then + local first_tab_x = self:get_tab_rect(1) + if x < first_tab_x then + -- mouse before first visible tab + tab_index = self.tab_offset or 1 + else + -- mouse after last visible tab + tab_index = self:get_visible_tabs_number() + (self.tab_offset - 1 or 0) + end + end + local tab_x, tab_y, tab_w, tab_h = self:get_tab_rect(tab_index) + if x > tab_x + tab_w / 2 and tab_index <= #self.views then + -- use next tab + tab_x = tab_x + tab_w + tab_index = tab_index + 1 + end + if self == dragged_node and dragged_index and tab_index > dragged_index then + -- the tab we are moving is counted in tab_index + tab_index = tab_index - 1 + tab_x = tab_x - tab_w + end + return tab_index, tab_x, tab_y, tab_w, tab_h +end + + local RootView = View:extend() function RootView:new() @@ -719,6 +775,14 @@ function RootView:new() self.root_node = Node() self.deferred_draws = {} self.mouse = { x = 0, y = 0 } + self.drag_overlay = { x = 0, y = 0, w = 0, h = 0, visible = false, opacity = 0, + base_color = style.drag_overlay, + color = { table.unpack(style.drag_overlay) } } + self.drag_overlay.to = { x = 0, y = 0, w = 0, h = 0 } + self.drag_overlay_tab = { x = 0, y = 0, w = 0, h = 0, visible = false, opacity = 0, + base_color = style.drag_overlay_tab, + color = { table.unpack(style.drag_overlay_tab) } } + self.drag_overlay_tab.to = { x = 0, y = 0, w = 0, h = 0 } end @@ -802,10 +866,12 @@ function RootView:on_mouse_pressed(button, x, y, clicks) if button == "middle" or node.hovered_close == idx then node:close_view(self.root_node, node.views[idx]) else - self.dragged_node = { node, idx } + if button == "left" then + self.dragged_node = { node = node, idx = idx, dragging = false, drag_start_x = x, drag_start_y = y} + end node:set_active_view(node.views[idx]) end - else + elseif not self.dragged_node then -- avoid sending on_mouse_pressed events when dragging tabs core.set_active_view(node.active_view) if not self.on_view_mouse_pressed(button, x, y, clicks) then node.active_view:on_mouse_pressed(button, x, y, clicks) @@ -814,14 +880,73 @@ function RootView:on_mouse_pressed(button, x, y, clicks) end -function RootView:on_mouse_released(...) +function RootView:get_overlay_base_color(overlay) + if overlay == self.drag_overlay then + return style.drag_overlay + else + return style.drag_overlay_tab + end +end + + +function RootView:set_show_overlay(overlay, status) + overlay.visible = status + if status then -- reset colors + -- reload base_color + overlay.base_color = self:get_overlay_base_color(overlay) + overlay.color[1] = overlay.base_color[1] + overlay.color[2] = overlay.base_color[2] + overlay.color[3] = overlay.base_color[3] + overlay.color[4] = overlay.base_color[4] + overlay.opacity = 0 + end +end + + +function RootView:on_mouse_released(button, x, y, ...) if self.dragged_divider then self.dragged_divider = nil end if self.dragged_node then - self.dragged_node = nil + if button == "left" then + if self.dragged_node.dragging then + local node = self.root_node:get_child_overlapping_point(self.mouse.x, self.mouse.y) + local dragged_node = self.dragged_node.node + + if node and not node.locked + -- don't do anything if dragging onto own node, with only one view + and (node ~= dragged_node or #node.views > 1) then + local split_type = node:get_split_type(self.mouse.x, self.mouse.y) + local view = dragged_node.views[self.dragged_node.idx] + + if split_type ~= "middle" and split_type ~= "tab" then -- needs splitting + local new_node = node:split(split_type) + self.root_node:get_node_for_view(view):remove_view(self.root_node, view) + new_node:add_view(view) + elseif split_type == "middle" and node ~= dragged_node then -- move to other node + dragged_node:remove_view(self.root_node, view) + node:add_view(view) + self.root_node:get_node_for_view(view):set_active_view(view) + elseif split_type == "tab" then -- move besides other tabs + local tab_index = node:get_drag_overlay_tab_position(self.mouse.x, self.mouse.y, dragged_node, self.dragged_node.idx) + dragged_node:remove_view(self.root_node, view) + node:add_view(view, tab_index) + self.root_node:get_node_for_view(view):set_active_view(view) + end + self.root_node:update_layout() + core.redraw = true + end + end + self:set_show_overlay(self.drag_overlay, false) + self:set_show_overlay(self.drag_overlay_tab, false) + if self.dragged_node and self.dragged_node.dragging then + core.request_cursor("arrow") + end + self.dragged_node = nil + end + else -- avoid sending on_mouse_released events when dragging tabs + self.root_node:on_mouse_released(button, x, y, ...) end - self.root_node:on_mouse_released(...) end @@ -857,6 +982,19 @@ function RootView:on_mouse_moved(x, y, dx, dy) end self.mouse.x, self.mouse.y = x, y + + local dn = self.dragged_node + if dn and not dn.dragging then + -- start dragging only after enough movement + dn.dragging = common.distance(x, y, dn.drag_start_x, dn.drag_start_y) > style.tab_width * .05 + if dn.dragging then + core.request_cursor("hand") + end + end + + -- avoid sending on_mouse_moved events when dragging tabs + if dn then return end + self.root_node:on_mouse_moved(x, y, dx, dy) local node = self.root_node:get_child_overlapping_point(x, y) @@ -871,24 +1009,6 @@ function RootView:on_mouse_moved(x, y, dx, dy) elseif node then core.request_cursor(node.active_view.cursor) end - if node and self.dragged_node and (self.dragged_node[1] ~= node or (tab_index and self.dragged_node[2] ~= tab_index)) - and node.type == "leaf" and #node.views > 0 and node.views[1]:is(DocView) then - local tab = self.dragged_node[1].views[self.dragged_node[2]] - if self.dragged_node[1] ~= node then - for i, v in ipairs(node.views) do if v.doc == tab.doc then tab = nil break end end - if tab then - self.dragged_node[1]:remove_view(self.root_node, tab) - node:add_view(tab, tab_index) - self.root_node:update_layout() - self.dragged_node = { node, tab_index or #node.views } - core.redraw = true - end - else - table.remove(self.dragged_node[1].views, self.dragged_node[2]) - table.insert(node.views, tab_index, tab) - self.dragged_node = { node, tab_index } - end - end end @@ -909,10 +1029,110 @@ function RootView:on_focus_lost(...) core.redraw = true end + +function RootView:interpolate_drag_overlay(overlay) + self:move_towards(overlay, "x", overlay.to.x) + self:move_towards(overlay, "y", overlay.to.y) + self:move_towards(overlay, "w", overlay.to.w) + self:move_towards(overlay, "h", overlay.to.h) + + self:move_towards(overlay, "opacity", overlay.visible and 100 or 0) + overlay.color[4] = overlay.base_color[4] * overlay.opacity / 100 +end + + function RootView:update() copy_position_and_size(self.root_node, self) self.root_node:update() self.root_node:update_layout() + + self:update_drag_overlay() + self:interpolate_drag_overlay(self.drag_overlay) + self:interpolate_drag_overlay(self.drag_overlay_tab) +end + + +function RootView:set_drag_overlay(overlay, x, y, w, h, immediate) + overlay.to.x = x + overlay.to.y = y + overlay.to.w = w + overlay.to.h = h + if immediate then + overlay.x = x + overlay.y = y + overlay.w = w + overlay.h = h + end + if not overlay.visible then + self:set_show_overlay(overlay, true) + end +end + + +local function get_split_sizes(split_type, x, y, w, h) + if split_type == "left" then + w = w * .5 + elseif split_type == "right" then + x = x + w * .5 + w = w * .5 + elseif split_type == "up" then + h = h * .5 + elseif split_type == "down" then + y = y + h * .5 + h = h * .5 + end + return x, y, w, h +end + + +function RootView:update_drag_overlay() + if not (self.dragged_node and self.dragged_node.dragging) then return end + local over = self.root_node:get_child_overlapping_point(self.mouse.x, self.mouse.y) + if over and not over.locked then + local _, _, _, tab_h = over:get_scroll_button_rect(1) + local x, y = over.position.x, over.position.y + local w, h = over.size.x, over.size.y + local split_type = over:get_split_type(self.mouse.x, self.mouse.y) + + if split_type == "tab" and (over ~= self.dragged_node.node or #over.views > 1) then + local tab_index, tab_x, tab_y, tab_w, tab_h = over:get_drag_overlay_tab_position(self.mouse.x, self.mouse.y) + self:set_drag_overlay(self.drag_overlay_tab, + tab_x + (tab_index and 0 or tab_w), tab_y, + style.caret_width, tab_h, + -- avoid showing tab overlay moving between nodes + over ~= self.drag_overlay_tab.last_over) + self:set_show_overlay(self.drag_overlay, false) + self.drag_overlay_tab.last_over = over + else + if (over ~= self.dragged_node.node or #over.views > 1) then + y = y + tab_h + h = h - tab_h + x, y, w, h = get_split_sizes(split_type, x, y, w, h) + end + self:set_drag_overlay(self.drag_overlay, x, y, w, h) + self:set_show_overlay(self.drag_overlay_tab, false) + end + else + self:set_show_overlay(self.drag_overlay, false) + self:set_show_overlay(self.drag_overlay_tab, false) + end +end + + +function RootView:draw_grabbed_tab() + local dn = self.dragged_node + local _,_, w, h = dn.node:get_tab_rect(dn.idx) + local x = self.mouse.x - w / 2 + local y = self.mouse.y - h / 2 + local text = dn.node.views[dn.idx] and dn.node.views[dn.idx]:get_name() or "" + self.root_node:draw_tab(text, true, true, false, x, y, w, h, true) +end + + +function RootView:draw_drag_overlay(ov) + if ov.opacity > 0 then + renderer.draw_rect(ov.x, ov.y, ov.w, ov.h, ov.color) + end end @@ -922,6 +1142,12 @@ function RootView:draw() local t = table.remove(self.deferred_draws) t.fn(table.unpack(t)) end + + self:draw_drag_overlay(self.drag_overlay) + self:draw_drag_overlay(self.drag_overlay_tab) + if self.dragged_node and self.dragged_node.dragging then + self:draw_grabbed_tab() + end if core.cursor_change_req then system.set_cursor(core.cursor_change_req) core.cursor_change_req = nil diff --git a/data/core/style.lua b/data/core/style.lua index faca166e..9a6efb50 100644 --- a/data/core/style.lua +++ b/data/core/style.lua @@ -44,6 +44,8 @@ style.scrollbar2 = { common.color "#4b4b52" } style.nagbar = { common.color "#FF0000" } style.nagbar_text = { common.color "#FFFFFF" } style.nagbar_dim = { common.color "rgba(0, 0, 0, 0.45)" } +style.drag_overlay = { common.color "rgba(255,255,255,0.1)" } +style.drag_overlay_tab = { common.color "#93DDFA" } style.syntax = {} style.syntax["normal"] = { common.color "#e1e1e6" } From f6b66348681e1d41baa86c3da091f97ef69299c9 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 17 Sep 2021 02:48:20 +0200 Subject: [PATCH 085/165] Force showing tabs when dragging them --- data/core/rootview.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/data/core/rootview.lua b/data/core/rootview.lua index bf438aa5..0d219474 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -280,7 +280,10 @@ end function Node:should_show_tabs() if self.locked then return false end - if #self.views > 1 then return true + local dn = core.root_view.dragged_node + if #self.views > 1 + or (dn and dn.dragging) then -- show tabs while dragging + return true elseif config.always_show_tabs then return not self.views[1]:is(EmptyView) end From c018ca3c6061da7360081d7df0d23c5fc9c6997b Mon Sep 17 00:00:00 2001 From: Francesco Date: Fri, 17 Sep 2021 22:38:09 +0200 Subject: [PATCH 086/165] Fix numpad fn keys (#532) * Fix the numeric keypad function keys As suggested in: https://github.com/lite-xl/lite-xl/issues/64 * Apply scancode lookup to KEY_UP events --- src/api/system.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/src/api/system.c b/src/api/system.c index 2f1bf763..f69de477 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -26,14 +26,11 @@ static const char* button_name(int button) { } -static char* key_name(char *dst, int sym) { - strcpy(dst, SDL_GetKeyName(sym)); - char *p = dst; +static void str_tolower(char *p) { while (*p) { *p = tolower(*p); p++; } - return dst; } struct HitTestInfo { @@ -93,6 +90,23 @@ static SDL_HitTestResult SDLCALL hit_test(SDL_Window *window, const SDL_Point *p return SDL_HITTEST_NORMAL; } +static const char *numpad[] = { "end", "down", "pagedown", "left", "", "right", "home", "up", "pageup", "ins", "delete" }; + +static const char *get_key_name(const SDL_Event *e, char *buf) { + SDL_Scancode scancode = e->key.keysym.scancode; + /* Is the scancode from the keypad and the number-lock off? + ** We assume that SDL_SCANCODE_KP_1 up to SDL_SCANCODE_KP_9 and SDL_SCANCODE_KP_0 + ** and SDL_SCANCODE_KP_PERIOD are declared in SDL2 in that order. */ + if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_1 + 10 && + !(KMOD_NUM & SDL_GetModState())) { + return numpad[scancode - SDL_SCANCODE_KP_1]; + } else { + strcpy(buf, SDL_GetKeyName(e->key.keysym.sym)); + str_tolower(buf); + return buf; + } +} + static int f_poll_event(lua_State *L) { char buf[16]; int mx, my, wx, wy; @@ -162,7 +176,7 @@ top: } #endif lua_pushstring(L, "keypressed"); - lua_pushstring(L, key_name(buf, e.key.keysym.sym)); + lua_pushstring(L, get_key_name(&e, buf)); return 2; case SDL_KEYUP: @@ -176,7 +190,7 @@ top: } #endif lua_pushstring(L, "keyreleased"); - lua_pushstring(L, key_name(buf, e.key.keysym.sym)); + lua_pushstring(L, get_key_name(&e, buf)); return 2; case SDL_TEXTINPUT: From 80a6b2245ea36d1b2abfb3dbb796f43d322a28e4 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Fri, 17 Sep 2021 23:41:14 +0200 Subject: [PATCH 087/165] Reset syntax highlighting on file rename --- data/plugins/treeview.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua index 84d5dd28..26481398 100644 --- a/data/plugins/treeview.lua +++ b/data/plugins/treeview.lua @@ -453,6 +453,7 @@ command.add(function() return view.hovered_item ~= nil end, { for _, doc in ipairs(core.docs) do if doc.abs_filename and old_abs_filename == doc.abs_filename then doc:set_filename(filename, abs_filename) -- make doc point to the new filename + doc:reset_syntax() break -- only first needed end end From 075061b80c2e4a99ef986754de4edab1f1c0d39d Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 18 Sep 2021 15:56:23 -0400 Subject: [PATCH 088/165] Added in custom runtime environment variable for ease of testing. (#538) --- src/main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.c b/src/main.c index 470f0b5e..67d260b2 100644 --- a/src/main.c +++ b/src/main.c @@ -155,7 +155,7 @@ init_lua: " local exedir = EXEFILE:match('^(.*)" LITE_PATHSEP_PATTERN LITE_NONPATHSEP_PATTERN "$')\n" " local prefix = exedir:match('^(.*)" LITE_PATHSEP_PATTERN "bin$')\n" " dofile((MACOS_RESOURCES or (prefix and prefix .. '/share/lite-xl' or exedir .. '/data')) .. '/core/start.lua')\n" - " core = require('core')\n" + " core = require(os.getenv('LITE_XL_RUNTIME') or 'core')\n" " core.init()\n" " core.run()\n" "end, function(err)\n" From 849614a3cb0f7cacf7855528e4f9ad5be1c7059f Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sun, 19 Sep 2021 18:42:36 +0200 Subject: [PATCH 089/165] Avoid unnecessary call to SDL_GetModState --- src/api/system.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/system.c b/src/api/system.c index f69de477..d84f86dd 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -98,7 +98,7 @@ static const char *get_key_name(const SDL_Event *e, char *buf) { ** We assume that SDL_SCANCODE_KP_1 up to SDL_SCANCODE_KP_9 and SDL_SCANCODE_KP_0 ** and SDL_SCANCODE_KP_PERIOD are declared in SDL2 in that order. */ if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_1 + 10 && - !(KMOD_NUM & SDL_GetModState())) { + !(e->key.keysym.mod & KMOD_NUM)) { return numpad[scancode - SDL_SCANCODE_KP_1]; } else { strcpy(buf, SDL_GetKeyName(e->key.keysym.sym)); From 8d3680ab45f68c62615842e20607e7e6d3311210 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sun, 19 Sep 2021 18:51:44 +0200 Subject: [PATCH 090/165] Scale custom syntax fonts for scale plugin Close #539. --- data/plugins/scale.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/data/plugins/scale.lua b/data/plugins/scale.lua index 8d16304b..acf3c7bb 100644 --- a/data/plugins/scale.lua +++ b/data/plugins/scale.lua @@ -56,6 +56,10 @@ local function set_scale(scale) renderer.font.set_size(style.code_font, s * style.code_font:get_size()) end + for _, font in pairs(style.syntax_fonts) do + renderer.font.set_size(font, s * font:get_size()) + end + -- restore scroll positions for view, n in pairs(scrolls) do view.scroll.y = n * (view:get_scrollable_size() - view.size.y) From 14dd6f1cd6535c1d752a557030cbf11b7e6a37d7 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Sun, 19 Sep 2021 23:50:50 +0200 Subject: [PATCH 091/165] Normalize to project dir in treeview open When left-clicking in a TreeView file we use now core.normalize_to_project_dir to normalize correctly the file name. --- data/plugins/treeview.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua index 26481398..fa3ab53a 100644 --- a/data/plugins/treeview.lua +++ b/data/plugins/treeview.lua @@ -243,7 +243,7 @@ function TreeView:on_mouse_pressed(button, x, y, clicks) end else core.try(function() - local doc_filename = common.relative_path(core.project_dir, hovered_item.abs_filename) + local doc_filename = core.normalize_to_project_dir(hovered_item.abs_filename) core.root_view:open_doc(core.open_doc(doc_filename)) end) end From 3ca127793af09eb30d01085c3572addcf9fdcafc Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 20 Sep 2021 23:33:12 -0400 Subject: [PATCH 092/165] Incorporated some suggestions, and some functions. --- data/core/start.lua | 15 ++++----------- resources/lite_xl_plugin_api.h | 4 ++++ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/data/core/start.lua b/data/core/start.lua index 0bbaadee..2b340d5d 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -21,16 +21,9 @@ package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') -package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath -package.cpath = USERDIR .. '/?.' .. dynamic_suffix .. ';' .. package.cpath +package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix package.searchers[3] = function(modname) - local s,e = 0 - repeat - e = package.cpath:find(";", s) or #package.cpath - local path = package.cpath:sub(s, e - 1):gsub("?", modname:gsub("%.", "/")) - if system.get_file_info(path) then - return system.load_native_plugin, path - end - s = e + 1 - until s > #package.cpath + local path = package.searchpath(modname, package.cpath) + if not path then return nil end + return system.load_native_plugin, path end diff --git a/resources/lite_xl_plugin_api.h b/resources/lite_xl_plugin_api.h index 08e22137..44c1b4d3 100644 --- a/resources/lite_xl_plugin_api.h +++ b/resources/lite_xl_plugin_api.h @@ -50,6 +50,8 @@ echo "typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* u LUA_HEADERS=`pkg-config --cflags lua5.2 | sed 's/^-I//' | sed 's/$/\/*.h/'` grep -h "^LUA\(LIB\)*_API" $LUA_HEADERS | sed "s/LUA\(LIB\)*_API //" | sed "s/(lua/(*lua/" | grep -v ",\s*$" | sed "s/^/static /" grep -h "#define luaL*_" $LUA_HEADERS | grep -v "\\\s*$" | grep -v "\(assert\|lock\)" | grep -v "\(asm\|int32\)" | grep -v "#define lua_number2integer(i,n)\s*lua_number2int(i, n)" +echo "#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1)" +echo "#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS)" echo "#define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name)" echo "static void lite_xl_plugin_init(void* XL) {" echo "\tvoid* (*symbol)(const char*) = (void* (*)(const char*))XL;" @@ -228,6 +230,8 @@ static void (*luaL_openlibs) (lua_State *L); #define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) #define lua_str2number(s,p) strtod((s), (p)) #define lua_strx2number(s,p) strtod((s), (p)) +#define lua_pushliteral(L, s) lua_pushlstring(L, s, (sizeof(s)/sizeof(char))-1) +#define lua_pushglobaltable(L) lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_GLOBALS) #define IMPORT_SYMBOL(name, ret, ...) name = (ret (*)(__VA_ARGS__))symbol(#name) static void lite_xl_plugin_init(void* XL) { void* (*symbol)(const char*) = (void* (*)(const char*))XL; From c01c5a23b087a5926803911514a06a488bb58de9 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 20 Sep 2021 23:38:10 -0400 Subject: [PATCH 093/165] Added in plugin table. --- src/api/system.c | 4 +++- src/main.c | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/api/system.c b/src/api/system.c index 47709344..e35e4558 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -690,8 +690,10 @@ static int f_load_native_plugin(lua_State* L) { void* library = SDL_LoadObject(path); if (name == 0 || !library) return luaL_error(L, "Unable to load %s: %s", name, SDL_GetError()); + lua_getfield(L, LUA_REGISTRYINDEX, "native_plugins"); lua_pushlightuserdata(L, library); - lua_setfield(L, LUA_REGISTRYINDEX, name); + lua_setfield(L, -2, name); + lua_pop(L, 1); for (sname = namelen - 1; sname > 0 && name[sname] != '.'; --sname); snprintf(olib, sizeof(olib), "lua_open_lite_xl_%s", &name[sname+1]); int (*ext_entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, olib); diff --git a/src/main.c b/src/main.c index 4b15ef34..4a95a76b 100644 --- a/src/main.c +++ b/src/main.c @@ -152,6 +152,9 @@ init_lua: lua_setglobal(L, "LINUX"); #endif + lua_newtable(L); + lua_setfield(L, LUA_REGISTRYINDEX, "native_plugins"); + const char *init_lite_code = \ "local core\n" "xpcall(function()\n" From e13529444fcbbef190a98fd909e9a87929289f4b Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 20 Sep 2021 23:42:39 -0400 Subject: [PATCH 094/165] Less C code, and more namespacing is better. --- data/core/start.lua | 1 + src/api/system.c | 3 ++- src/main.c | 3 --- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/data/core/start.lua b/data/core/start.lua index 2b340d5d..a84b4faa 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -22,6 +22,7 @@ package.path = USERDIR .. '/?/init.lua;' .. package.path local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix +package.native_plugins = {} package.searchers[3] = function(modname) local path = package.searchpath(modname, package.cpath) if not path then return nil end diff --git a/src/api/system.c b/src/api/system.c index e35e4558..68d25ae9 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -690,7 +690,8 @@ static int f_load_native_plugin(lua_State* L) { void* library = SDL_LoadObject(path); if (name == 0 || !library) return luaL_error(L, "Unable to load %s: %s", name, SDL_GetError()); - lua_getfield(L, LUA_REGISTRYINDEX, "native_plugins"); + lua_getglobal(L, "package"); + lua_getfield(L, -1, "native_plugins"); lua_pushlightuserdata(L, library); lua_setfield(L, -2, name); lua_pop(L, 1); diff --git a/src/main.c b/src/main.c index 4a95a76b..4b15ef34 100644 --- a/src/main.c +++ b/src/main.c @@ -152,9 +152,6 @@ init_lua: lua_setglobal(L, "LINUX"); #endif - lua_newtable(L); - lua_setfield(L, LUA_REGISTRYINDEX, "native_plugins"); - const char *init_lite_code = \ "local core\n" "xpcall(function()\n" From 713ef787c2963d2b4f2e474b320f1f0adcb3582c Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 20 Sep 2021 23:50:06 -0400 Subject: [PATCH 095/165] Removed extra macros, used PLATFORM. Also removed MACOS, as it's redundant C code that's already encapsulated within PLATFORM. --- data/core/keymap.lua | 2 +- data/core/start.lua | 2 +- src/main.c | 5 ----- 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 50eadec6..7f7c0fe2 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -5,7 +5,7 @@ keymap.modkeys = {} keymap.map = {} keymap.reverse_map = {} -local macos = rawget(_G, "MACOS") +local macos = PLATFORM == "Mac OS X" -- Thanks to mathewmariani, taken from his lite-macos github repository. local modkeys_os = require("core.modkeys-" .. (macos and "macos" or "generic")) diff --git a/data/core/start.lua b/data/core/start.lua index a84b4faa..0311774d 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -20,7 +20,7 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path package.path = USERDIR .. '/?/init.lua;' .. package.path -local dynamic_suffix = MACOS and 'lib' or (WINDOWS and 'dll' or 'so') +local dynamic_suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so') package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix package.native_plugins = {} package.searchers[3] = function(modname) diff --git a/src/main.c b/src/main.c index 4b15ef34..ac97e698 100644 --- a/src/main.c +++ b/src/main.c @@ -141,15 +141,10 @@ init_lua: lua_pushboolean(L, true); #ifdef __APPLE__ - lua_setglobal(L, "MACOS"); enable_momentum_scroll(); #ifdef MACOS_USE_BUNDLE set_macos_bundle_resources(L); #endif -#elif _WIN32 - lua_setglobal(L, "WINDOWS"); -#else - lua_setglobal(L, "LINUX"); #endif const char *init_lite_code = \ From 5ffe4eae909fe6694a8eec8f04b864acb4c93d36 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 20 Sep 2021 23:54:52 -0400 Subject: [PATCH 096/165] Removed extra boolean. --- src/main.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main.c b/src/main.c index ac97e698..ee9c68fe 100644 --- a/src/main.c +++ b/src/main.c @@ -139,7 +139,6 @@ init_lua: lua_pushstring(L, exename); lua_setglobal(L, "EXEFILE"); - lua_pushboolean(L, true); #ifdef __APPLE__ enable_momentum_scroll(); #ifdef MACOS_USE_BUNDLE From b8da46e10ea3fa002c28ab2445b41f058af006e6 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Wed, 22 Sep 2021 17:24:22 -0400 Subject: [PATCH 097/165] Removed searchers[4]. --- data/core/start.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/core/start.lua b/data/core/start.lua index 0311774d..9c2e64e4 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -23,8 +23,8 @@ package.path = USERDIR .. '/?/init.lua;' .. package.path local dynamic_suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so') package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix package.native_plugins = {} -package.searchers[3] = function(modname) +package.searchers = { package.seachers[1], package.searchers[2], function(modname) local path = package.searchpath(modname, package.cpath) if not path then return nil end return system.load_native_plugin, path -end +end } From 466464d8a4f3ca815cc9eb0d94a97a4b5e40b978 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Wed, 22 Sep 2021 17:25:16 -0400 Subject: [PATCH 098/165] Mispelling. --- data/core/start.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/start.lua b/data/core/start.lua index 9c2e64e4..330c42f5 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -23,7 +23,7 @@ package.path = USERDIR .. '/?/init.lua;' .. package.path local dynamic_suffix = PLATFORM == "Mac OS X" and 'lib' or (PLATFORM == "Windows" and 'dll' or 'so') package.cpath = DATADIR .. '/?.' .. dynamic_suffix .. ";" .. USERDIR .. '/?.' .. dynamic_suffix package.native_plugins = {} -package.searchers = { package.seachers[1], package.searchers[2], function(modname) +package.searchers = { package.searchers[1], package.searchers[2], function(modname) local path = package.searchpath(modname, package.cpath) if not path then return nil end return system.load_native_plugin, path From e25f2e9c5c1e165aa2c57d915894d19bb1eff198 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 22:22:30 -0400 Subject: [PATCH 099/165] Removed font renderer. --- data/core/docview.lua | 23 +- data/core/init.lua | 10 - data/plugins/drawwhitespace.lua | 31 + lib/font_renderer/agg_font_freetype.cpp | 1161 ----------------- lib/font_renderer/agg_font_freetype.h | 198 --- lib/font_renderer/agg_lcd_distribution_lut.h | 73 -- lib/font_renderer/agg_pixfmt_alpha8.h | 93 -- lib/font_renderer/font_renderer.cpp | 445 ------- lib/font_renderer/font_renderer.h | 58 - lib/font_renderer/font_renderer_alpha.h | 164 --- lib/font_renderer/meson.build | 19 - .../notes-lite-font-rendering.md | 125 -- src/api/api.c | 4 +- src/api/api.h | 9 +- src/api/cp_replace.c | 42 - src/api/regex.c | 18 +- src/api/renderer.c | 136 +- src/api/renderer_font.c | 158 --- src/fontdesc.c | 77 -- src/fontdesc.h | 33 - src/rencache.c | 85 +- src/rencache.h | 4 +- src/renderer.c | 520 ++++---- src/renderer.h | 61 +- 24 files changed, 420 insertions(+), 3127 deletions(-) create mode 100644 data/plugins/drawwhitespace.lua delete mode 100644 lib/font_renderer/agg_font_freetype.cpp delete mode 100644 lib/font_renderer/agg_font_freetype.h delete mode 100644 lib/font_renderer/agg_lcd_distribution_lut.h delete mode 100644 lib/font_renderer/agg_pixfmt_alpha8.h delete mode 100644 lib/font_renderer/font_renderer.cpp delete mode 100644 lib/font_renderer/font_renderer.h delete mode 100644 lib/font_renderer/font_renderer_alpha.h delete mode 100644 lib/font_renderer/meson.build delete mode 100644 lib/font_renderer/notes-lite-font-rendering.md delete mode 100644 src/api/cp_replace.c delete mode 100644 src/api/renderer_font.c delete mode 100644 src/fontdesc.c delete mode 100644 src/fontdesc.h diff --git a/data/core/docview.lua b/data/core/docview.lua index 161eac47..553a9888 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -150,14 +150,14 @@ function DocView:get_col_x_offset(line, col) local font = style.syntax_fonts[type] or default_font for char in common.utf8_chars(text) do if column == col then - return xoffset / font:subpixel_scale() + return xoffset end - xoffset = xoffset + font:get_width_subpixel(char) + xoffset = xoffset + font:get_width(char) column = column + #char end end - return xoffset / default_font:subpixel_scale() + return xoffset end @@ -166,14 +166,12 @@ function DocView:get_x_offset_col(line, x) local xoffset, last_i, i = 0, 1, 1 local default_font = self:get_font() - local subpixel_scale = default_font:subpixel_scale() - local x_subpixel = subpixel_scale * x + subpixel_scale / 2 for _, type, text in self.doc.highlighter:each_token(line) do local font = style.syntax_fonts[type] or default_font for char in common.utf8_chars(text) do - local w = font:get_width_subpixel(char) - if xoffset >= subpixel_scale * x then - return (xoffset - x_subpixel > w / 2) and last_i or i + local w = font:get_width(char) + if xoffset >= x then + return (xoffset - x > w / 2) and last_i or i end xoffset = xoffset + w last_i = i @@ -339,16 +337,11 @@ end function DocView:draw_line_text(idx, x, y) local default_font = self:get_font() - local subpixel_scale = default_font:subpixel_scale() - local tx, ty = subpixel_scale * x, y + self:get_line_text_y_offset() + local tx, ty = x, y + self:get_line_text_y_offset() for _, type, text in self.doc.highlighter:each_token(idx) do local color = style.syntax[type] local font = style.syntax_fonts[type] or default_font - if config.draw_whitespace then - tx = renderer.draw_text_subpixel(font, text, tx, ty, color, core.replacements, style.syntax.comment) - else - tx = renderer.draw_text_subpixel(font, text, tx, ty, color) - end + tx = renderer.draw_text(font, text, tx, ty, color) end end diff --git a/data/core/init.lua b/data/core/init.lua index af291767..cab93c11 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -393,15 +393,6 @@ function core.remove_project_directory(path) return false end - -local function whitespace_replacements() - local r = renderer.replacements.new() - r:add(" ", "·") - r:add("\t", "»") - return r -end - - local function reload_on_user_module_save() -- auto-realod style when user's module is saved by overriding Doc:Save() local doc_save = Doc.save @@ -499,7 +490,6 @@ function core.init() core.visited_files = {} core.restart_request = false core.quit_request = false - core.replacements = whitespace_replacements() core.root_view = RootView() core.command_view = CommandView() diff --git a/data/plugins/drawwhitespace.lua b/data/plugins/drawwhitespace.lua new file mode 100644 index 00000000..d6700615 --- /dev/null +++ b/data/plugins/drawwhitespace.lua @@ -0,0 +1,31 @@ +-- mod-version:2 -- lite-xl 2.0 + +local style = require "core.style" +local DocView = require "core.docview" +local common = require "core.common" + +local draw_line_text = DocView.draw_line_text + +function DocView:draw_line_text(idx, x, y) + local font = (self:get_font() or style.syntax_fonts["comment"]) + local color = style.syntax.comment + local ty, tx = y + self:get_line_text_y_offset() + local text, offset, s, e = self.doc.lines[idx], 1 + while true do + s, e = text:find(" +", offset) + if not s then break end + tx = self:get_col_x_offset(idx, s) + x + renderer.draw_text(font, string.rep("·", e - s + 1), tx, ty, color) + offset = e + 1 + end + while true do + s, e = text:find("\t", offset) + if not s then break end + tx = self:get_col_x_offset(idx, s) + x + renderer.draw_text(font, "»", tx, ty, color) + offset = e + 1 + end + draw_line_text(self, idx, x, y) +end + + diff --git a/lib/font_renderer/agg_font_freetype.cpp b/lib/font_renderer/agg_font_freetype.cpp deleted file mode 100644 index c240927e..00000000 --- a/lib/font_renderer/agg_font_freetype.cpp +++ /dev/null @@ -1,1161 +0,0 @@ -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.4 -// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://www.antigrain.com -//---------------------------------------------------------------------------- - - -#include -#include "agg_font_freetype.h" -#include "agg_bitset_iterator.h" -#include "agg_renderer_scanline.h" - - -namespace agg -{ - - //------------------------------------------------------------------------------ - // - // This code implements the AUTODIN II polynomial - // The variable corresponding to the macro argument "crc" should - // be an unsigned long. - // Oroginal code by Spencer Garrett - // - - // generated using the AUTODIN II polynomial - // x^32 + x^26 + x^23 + x^22 + x^16 + - // x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 - // - //------------------------------------------------------------------------------ - - static const unsigned crc32tab[256] = - { - 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, - 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, - 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, - 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, - 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, - 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, - 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, - 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, - 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, - 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, - 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, - 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, - 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, - 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, - 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, - 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, - 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, - 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, - 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, - 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, - 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, - 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, - 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, - 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, - 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, - 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, - 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, - 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, - 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, - 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, - 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, - 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, - 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, - 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, - 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, - 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, - 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, - 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, - 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, - 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, - 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, - 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, - 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, - 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, - 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, - 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, - 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, - 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, - 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, - 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, - 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, - 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, - 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, - 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, - 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, - 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, - 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, - 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, - 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, - 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, - 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, - 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, - 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, - 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, - }; - - - //------------------------------------------------------------------------------ - - static unsigned calc_crc32(const unsigned char* buf, unsigned size) - { - unsigned crc = (unsigned)~0; - const unsigned char* p; - unsigned len = 0; - unsigned nr = size; - - for (len += nr, p = buf; nr--; ++p) - { - crc = (crc >> 8) ^ crc32tab[(crc ^ *p) & 0xff]; - } - return ~crc; - } - - //------------------------------------------------------------------------ - static inline int dbl_to_plain_fx(double d) - { - return int(d * 65536.0); - } - - //------------------------------------------------------------------------ - static inline double int26p6_to_dbl(int p) - { - return double(p) / 64.0; - } - - //------------------------------------------------------------------------ - static inline int dbl_to_int26p6(double p) - { - return int(p * 64.0 + 0.5); - } - - - //------------------------------------------------------------------------ - template - bool decompose_ft_outline(const FT_Outline& outline, - bool flip_y, - const trans_affine& mtx, - PathStorage& path) - { - typedef typename PathStorage::value_type value_type; - - FT_Vector v_last; - FT_Vector v_control; - FT_Vector v_start; - double x1, y1, x2, y2, x3, y3; - - FT_Vector* point; - FT_Vector* limit; - char* tags; - - int n; // index of contour in outline - int first; // index of first point in contour - char tag; // current point's state - - first = 0; - - for(n = 0; n < outline.n_contours; n++) - { - int last; // index of last point in contour - - last = outline.contours[n]; - limit = outline.points + last; - - v_start = outline.points[first]; - v_last = outline.points[last]; - - v_control = v_start; - - point = outline.points + first; - tags = outline.tags + first; - tag = FT_CURVE_TAG(tags[0]); - - // A contour cannot start with a cubic control point! - if(tag == FT_CURVE_TAG_CUBIC) return false; - - // check first point to determine origin - if( tag == FT_CURVE_TAG_CONIC) - { - // first point is conic control. Yes, this happens. - if(FT_CURVE_TAG(outline.tags[last]) == FT_CURVE_TAG_ON) - { - // start at last point if it is on the curve - v_start = v_last; - limit--; - } - else - { - // if both first and last points are conic, - // start at their middle and record its position - // for closure - v_start.x = (v_start.x + v_last.x) / 2; - v_start.y = (v_start.y + v_last.y) / 2; - - v_last = v_start; - } - point--; - tags--; - } - - x1 = int26p6_to_dbl(v_start.x); - y1 = int26p6_to_dbl(v_start.y); - if(flip_y) y1 = -y1; - mtx.transform(&x1, &y1); - path.move_to(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1))); - - while(point < limit) - { - point++; - tags++; - - tag = FT_CURVE_TAG(tags[0]); - switch(tag) - { - case FT_CURVE_TAG_ON: // emit a single line_to - { - x1 = int26p6_to_dbl(point->x); - y1 = int26p6_to_dbl(point->y); - if(flip_y) y1 = -y1; - mtx.transform(&x1, &y1); - path.line_to(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1))); - //path.line_to(conv(point->x), flip_y ? -conv(point->y) : conv(point->y)); - continue; - } - - case FT_CURVE_TAG_CONIC: // consume conic arcs - { - v_control.x = point->x; - v_control.y = point->y; - - Do_Conic: - if(point < limit) - { - FT_Vector vec; - FT_Vector v_middle; - - point++; - tags++; - tag = FT_CURVE_TAG(tags[0]); - - vec.x = point->x; - vec.y = point->y; - - if(tag == FT_CURVE_TAG_ON) - { - x1 = int26p6_to_dbl(v_control.x); - y1 = int26p6_to_dbl(v_control.y); - x2 = int26p6_to_dbl(vec.x); - y2 = int26p6_to_dbl(vec.y); - if(flip_y) { y1 = -y1; y2 = -y2; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - path.curve3(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2))); - continue; - } - - if(tag != FT_CURVE_TAG_CONIC) return false; - - v_middle.x = (v_control.x + vec.x) / 2; - v_middle.y = (v_control.y + vec.y) / 2; - - x1 = int26p6_to_dbl(v_control.x); - y1 = int26p6_to_dbl(v_control.y); - x2 = int26p6_to_dbl(v_middle.x); - y2 = int26p6_to_dbl(v_middle.y); - if(flip_y) { y1 = -y1; y2 = -y2; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - path.curve3(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2))); - - //path.curve3(conv(v_control.x), - // flip_y ? -conv(v_control.y) : conv(v_control.y), - // conv(v_middle.x), - // flip_y ? -conv(v_middle.y) : conv(v_middle.y)); - - v_control = vec; - goto Do_Conic; - } - - x1 = int26p6_to_dbl(v_control.x); - y1 = int26p6_to_dbl(v_control.y); - x2 = int26p6_to_dbl(v_start.x); - y2 = int26p6_to_dbl(v_start.y); - if(flip_y) { y1 = -y1; y2 = -y2; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - path.curve3(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2))); - - //path.curve3(conv(v_control.x), - // flip_y ? -conv(v_control.y) : conv(v_control.y), - // conv(v_start.x), - // flip_y ? -conv(v_start.y) : conv(v_start.y)); - goto Close; - } - - default: // FT_CURVE_TAG_CUBIC - { - FT_Vector vec1, vec2; - - if(point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC) - { - return false; - } - - vec1.x = point[0].x; - vec1.y = point[0].y; - vec2.x = point[1].x; - vec2.y = point[1].y; - - point += 2; - tags += 2; - - if(point <= limit) - { - FT_Vector vec; - - vec.x = point->x; - vec.y = point->y; - - x1 = int26p6_to_dbl(vec1.x); - y1 = int26p6_to_dbl(vec1.y); - x2 = int26p6_to_dbl(vec2.x); - y2 = int26p6_to_dbl(vec2.y); - x3 = int26p6_to_dbl(vec.x); - y3 = int26p6_to_dbl(vec.y); - if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - mtx.transform(&x3, &y3); - path.curve4(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2)), - value_type(dbl_to_int26p6(x3)), - value_type(dbl_to_int26p6(y3))); - - //path.curve4(conv(vec1.x), - // flip_y ? -conv(vec1.y) : conv(vec1.y), - // conv(vec2.x), - // flip_y ? -conv(vec2.y) : conv(vec2.y), - // conv(vec.x), - // flip_y ? -conv(vec.y) : conv(vec.y)); - continue; - } - - x1 = int26p6_to_dbl(vec1.x); - y1 = int26p6_to_dbl(vec1.y); - x2 = int26p6_to_dbl(vec2.x); - y2 = int26p6_to_dbl(vec2.y); - x3 = int26p6_to_dbl(v_start.x); - y3 = int26p6_to_dbl(v_start.y); - if(flip_y) { y1 = -y1; y2 = -y2; y3 = -y3; } - mtx.transform(&x1, &y1); - mtx.transform(&x2, &y2); - mtx.transform(&x3, &y3); - path.curve4(value_type(dbl_to_int26p6(x1)), - value_type(dbl_to_int26p6(y1)), - value_type(dbl_to_int26p6(x2)), - value_type(dbl_to_int26p6(y2)), - value_type(dbl_to_int26p6(x3)), - value_type(dbl_to_int26p6(y3))); - - //path.curve4(conv(vec1.x), - // flip_y ? -conv(vec1.y) : conv(vec1.y), - // conv(vec2.x), - // flip_y ? -conv(vec2.y) : conv(vec2.y), - // conv(v_start.x), - // flip_y ? -conv(v_start.y) : conv(v_start.y)); - goto Close; - } - } - } - - path.close_polygon(); - - Close: - first = last + 1; - } - - return true; - } - - - - //------------------------------------------------------------------------ - template - void decompose_ft_bitmap_mono(const FT_Bitmap& bitmap, - int x, int y, - bool flip_y, - Scanline& sl, - ScanlineStorage& storage) - { - int i; - const int8u* buf = (const int8u*)bitmap.buffer; - const int bitmap_rows = bitmap.rows, bitmap_width = bitmap.width; - int pitch = bitmap.pitch; - sl.reset(x, x + bitmap.width); - storage.prepare(); - if(flip_y) - { - buf += bitmap.pitch * (bitmap.rows - 1); - y += bitmap.rows; - pitch = -pitch; - } - for(i = 0; i < bitmap_rows; i++) - { - sl.reset_spans(); - bitset_iterator bits(buf, 0); - int j; - for(j = 0; j < bitmap_width; j++) - { - if(bits.bit()) sl.add_cell(x + j, cover_full); - ++bits; - } - buf += pitch; - if(sl.num_spans()) - { - sl.finalize(y - i - 1); - storage.render(sl); - } - } - } - - - - //------------------------------------------------------------------------ - template - void decompose_ft_bitmap_gray8(const FT_Bitmap& bitmap, - int x, int y, - bool flip_y, - Rasterizer& ras, - Scanline& sl, - ScanlineStorage& storage) - { - int i, j; - const int8u* buf = (const int8u*)bitmap.buffer; - const int bitmap_rows = bitmap.rows, bitmap_width = bitmap.width; - int pitch = bitmap.pitch; - sl.reset(x, x + bitmap.width); - storage.prepare(); - if(flip_y) - { - buf += bitmap.pitch * (bitmap.rows - 1); - y += bitmap.rows; - pitch = -pitch; - } - for(i = 0; i < bitmap_rows; i++) - { - sl.reset_spans(); - const int8u* p = buf; - for(j = 0; j < bitmap_width; j++) - { - if(*p) sl.add_cell(x + j, ras.apply_gamma(*p)); - ++p; - } - buf += pitch; - if(sl.num_spans()) - { - sl.finalize(y - i - 1); - storage.render(sl); - } - } - } - - - - - - - - - - - - - - //------------------------------------------------------------------------ - font_engine_freetype_base::~font_engine_freetype_base() - { - unsigned i; - for(i = 0; i < m_num_faces; ++i) - { - delete [] m_face_names[i]; - FT_Done_Face(m_faces[i]); - } - delete [] m_face_names; - delete [] m_faces; - delete [] m_signature; - if(m_library_initialized) FT_Done_FreeType(m_library); - } - - - //------------------------------------------------------------------------ - font_engine_freetype_base::font_engine_freetype_base(bool flag32, - unsigned max_faces) : - m_flag32(flag32), - m_change_stamp(0), - m_last_error(0), - m_name(0), - m_name_len(256-16-1), - m_face_index(0), - m_char_map(FT_ENCODING_NONE), - m_signature(new char [256+256-16]), - m_height(0), - m_width(0), - m_hinting(true), - m_flip_y(false), - m_library_initialized(false), - m_library(0), - m_faces(new FT_Face [max_faces]), - m_face_names(new char* [max_faces]), - m_num_faces(0), - m_max_faces(max_faces), - m_cur_face(0), - m_resolution(0), - m_glyph_rendering(glyph_ren_native_gray8), - m_glyph_index(0), - m_data_size(0), - m_data_type(glyph_data_invalid), - m_bounds(1,1,0,0), - m_advance_x(0.0), - m_advance_y(0.0), - - m_path16(), - m_path32(), - m_curves16(m_path16), - m_curves32(m_path32), - m_scanline_aa(), - m_scanline_bin(), - m_scanlines_aa(), - m_scanlines_bin(), - m_rasterizer() - { - m_curves16.approximation_scale(4.0); - m_curves32.approximation_scale(4.0); - m_last_error = FT_Init_FreeType(&m_library); - if(m_last_error == 0) m_library_initialized = true; - } - - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::resolution(unsigned dpi) - { - m_resolution = dpi; - update_char_size(); - } - - - //------------------------------------------------------------------------ - int font_engine_freetype_base::find_face(const char* face_name) const - { - unsigned i; - for(i = 0; i < m_num_faces; ++i) - { - if(strcmp(face_name, m_face_names[i]) == 0) return i; - } - return -1; - } - - //------------------------------------------------------------------------ - int font_engine_freetype_base::face_height() const - { - return (m_cur_face ? m_cur_face->height : -1); - } - - int font_engine_freetype_base::face_units_em() const - { - return (m_cur_face ? m_cur_face->units_per_EM : -1); - } - - //------------------------------------------------------------------------ - double font_engine_freetype_base::ascender() const - { - if(m_cur_face) - { - return m_cur_face->ascender * height() / m_cur_face->height; - } - return 0.0; - } - - //------------------------------------------------------------------------ - double font_engine_freetype_base::descender() const - { - if(m_cur_face) - { - return m_cur_face->descender * height() / m_cur_face->height; - } - return 0.0; - } - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::load_font(const char* font_name, - unsigned face_index, - glyph_rendering ren_type, - const char* font_mem, - const long font_mem_size) - { - bool ret = false; - - if(m_library_initialized) - { - m_last_error = 0; - - int idx = find_face(font_name); - if(idx >= 0) - { - m_cur_face = m_faces[idx]; - m_name = m_face_names[idx]; - } - else - { - if(m_num_faces >= m_max_faces) - { - delete [] m_face_names[0]; - FT_Done_Face(m_faces[0]); - memcpy(m_faces, - m_faces + 1, - (m_max_faces - 1) * sizeof(FT_Face)); - memcpy(m_face_names, - m_face_names + 1, - (m_max_faces - 1) * sizeof(char*)); - m_num_faces = m_max_faces - 1; - } - - if (font_mem && font_mem_size) - { - m_last_error = FT_New_Memory_Face(m_library, - (const FT_Byte*)font_mem, - font_mem_size, - face_index, - &m_faces[m_num_faces]); - } - else - { - m_last_error = FT_New_Face(m_library, - font_name, - face_index, - &m_faces[m_num_faces]); - } - - if(m_last_error == 0) - { - m_face_names[m_num_faces] = new char [strlen(font_name) + 1]; - strcpy(m_face_names[m_num_faces], font_name); - m_cur_face = m_faces[m_num_faces]; - m_name = m_face_names[m_num_faces]; - ++m_num_faces; - } - else - { - m_face_names[m_num_faces] = 0; - m_cur_face = 0; - m_name = 0; - } - } - - - if(m_last_error == 0) - { - ret = true; - - switch(ren_type) - { - case glyph_ren_native_mono: - m_glyph_rendering = glyph_ren_native_mono; - break; - - case glyph_ren_native_gray8: - m_glyph_rendering = glyph_ren_native_gray8; - break; - - case glyph_ren_outline: - if(FT_IS_SCALABLE(m_cur_face)) - { - m_glyph_rendering = glyph_ren_outline; - } - else - { - m_glyph_rendering = glyph_ren_native_gray8; - } - break; - - case glyph_ren_agg_mono: - if(FT_IS_SCALABLE(m_cur_face)) - { - m_glyph_rendering = glyph_ren_agg_mono; - } - else - { - m_glyph_rendering = glyph_ren_native_mono; - } - break; - - case glyph_ren_agg_gray8: - if(FT_IS_SCALABLE(m_cur_face)) - { - m_glyph_rendering = glyph_ren_agg_gray8; - } - else - { - m_glyph_rendering = glyph_ren_native_gray8; - } - break; - } - update_signature(); - } - } - return ret; - } - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::attach(const char* file_name) - { - if(m_cur_face) - { - m_last_error = FT_Attach_File(m_cur_face, file_name); - return m_last_error == 0; - } - return false; - } - - //------------------------------------------------------------------------ - unsigned font_engine_freetype_base::num_faces() const - { - if(m_cur_face) - { - return m_cur_face->num_faces; - } - return 0; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::char_map(FT_Encoding char_map) - { - if(m_cur_face) - { - m_last_error = FT_Select_Charmap(m_cur_face, m_char_map); - if(m_last_error == 0) - { - update_signature(); - return true; - } - } - return false; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::height(double h) - { - m_height = int(h * 64.0); - if(m_cur_face) - { - update_char_size(); - return true; - } - return false; - } - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::width(double w) - { - m_width = int(w * 64.0); - if(m_cur_face) - { - update_char_size(); - return true; - } - return false; - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::hinting(bool h) - { - m_hinting = h; - if(m_cur_face) - { - update_signature(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::flip_y(bool f) - { - m_flip_y = f; - if(m_cur_face) - { - update_signature(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::transform(const trans_affine& affine) - { - m_affine = affine; - if(m_cur_face) - { - update_signature(); - } - } - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_signature() - { - if(m_cur_face && m_name) - { - unsigned name_len = strlen(m_name); - if(name_len > m_name_len) - { - delete [] m_signature; - m_signature = new char [name_len + 32 + 256]; - m_name_len = name_len + 32 - 1; - } - - unsigned gamma_hash = 0; - if(m_glyph_rendering == glyph_ren_native_gray8 || - m_glyph_rendering == glyph_ren_agg_mono || - m_glyph_rendering == glyph_ren_agg_gray8) - { - unsigned char gamma_table[rasterizer_scanline_aa<>::aa_scale]; - unsigned i; - for(i = 0; i < rasterizer_scanline_aa<>::aa_scale; ++i) - { - gamma_table[i] = m_rasterizer.apply_gamma(i); - } - gamma_hash = calc_crc32(gamma_table, sizeof(gamma_table)); - } - - sprintf(m_signature, - "%s,%u,%d,%d,%d:%dx%d,%d,%d,%08X", - m_name, - m_char_map, - m_face_index, - int(m_glyph_rendering), - m_resolution, - m_height, - m_width, - int(m_hinting), - int(m_flip_y), - gamma_hash); - if(m_glyph_rendering == glyph_ren_outline || - m_glyph_rendering == glyph_ren_agg_mono || - m_glyph_rendering == glyph_ren_agg_gray8) - { - double mtx[6]; - char buf[100]; - m_affine.store_to(mtx); - sprintf(buf, ",%08X%08X%08X%08X%08X%08X", - dbl_to_plain_fx(mtx[0]), - dbl_to_plain_fx(mtx[1]), - dbl_to_plain_fx(mtx[2]), - dbl_to_plain_fx(mtx[3]), - dbl_to_plain_fx(mtx[4]), - dbl_to_plain_fx(mtx[5])); - strcat(m_signature, buf); - } - ++m_change_stamp; - } - } - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::update_char_size() - { - if(m_cur_face) - { - if(m_resolution) - { - FT_Set_Char_Size(m_cur_face, - m_width, // char_width in 1/64th of points - m_height, // char_height in 1/64th of points - m_resolution, // horizontal device resolution - m_resolution); // vertical device resolution - } - else - { - FT_Set_Pixel_Sizes(m_cur_face, - m_width >> 6, // pixel_width - m_height >> 6); // pixel_height - } - update_signature(); - } - } - - - - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::prepare_glyph(unsigned glyph_code) - { - m_glyph_index = FT_Get_Char_Index(m_cur_face, glyph_code); - // For hinting FT_LOAD_DEFAULT could be used but it gives severe - // visual artefacts when scaling fonts x100 along X like - // done by AGG. - m_last_error = FT_Load_Glyph(m_cur_face, - m_glyph_index, - m_hinting ? FT_LOAD_FORCE_AUTOHINT : FT_LOAD_NO_HINTING); - if(m_last_error == 0) - { - switch(m_glyph_rendering) - { - case glyph_ren_native_mono: - m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_MONO); - if(m_last_error == 0) - { - decompose_ft_bitmap_mono(m_cur_face->glyph->bitmap, - m_cur_face->glyph->bitmap_left, - m_flip_y ? -m_cur_face->glyph->bitmap_top : - m_cur_face->glyph->bitmap_top, - m_flip_y, - m_scanline_bin, - m_scanlines_bin); - m_bounds.x1 = m_scanlines_bin.min_x(); - m_bounds.y1 = m_scanlines_bin.min_y(); - m_bounds.x2 = m_scanlines_bin.max_x() + 1; - m_bounds.y2 = m_scanlines_bin.max_y() + 1; - m_data_size = m_scanlines_bin.byte_size(); - m_data_type = glyph_data_mono; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - return true; - } - break; - - - case glyph_ren_native_gray8: - m_last_error = FT_Render_Glyph(m_cur_face->glyph, FT_RENDER_MODE_NORMAL); - if(m_last_error == 0) - { - decompose_ft_bitmap_gray8(m_cur_face->glyph->bitmap, - m_cur_face->glyph->bitmap_left, - m_flip_y ? -m_cur_face->glyph->bitmap_top : - m_cur_face->glyph->bitmap_top, - m_flip_y, - m_rasterizer, - m_scanline_aa, - m_scanlines_aa); - m_bounds.x1 = m_scanlines_aa.min_x(); - m_bounds.y1 = m_scanlines_aa.min_y(); - m_bounds.x2 = m_scanlines_aa.max_x() + 1; - m_bounds.y2 = m_scanlines_aa.max_y() + 1; - m_data_size = m_scanlines_aa.byte_size(); - m_data_type = glyph_data_gray8; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - return true; - } - break; - - - case glyph_ren_outline: - if(m_last_error == 0) - { - if(m_flag32) - { - m_path32.remove_all(); - if(decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path32)) - { - rect_d bnd = m_path32.bounding_rect(); - m_data_size = m_path32.byte_size(); - m_data_type = glyph_data_outline; - m_bounds.x1 = int(floor(bnd.x1)); - m_bounds.y1 = int(floor(bnd.y1)); - m_bounds.x2 = int(ceil(bnd.x2)); - m_bounds.y2 = int(ceil(bnd.y2)); - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - } - else - { - m_path16.remove_all(); - if(decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path16)) - { - rect_d bnd = m_path16.bounding_rect(); - m_data_size = m_path16.byte_size(); - m_data_type = glyph_data_outline; - m_bounds.x1 = int(floor(bnd.x1)); - m_bounds.y1 = int(floor(bnd.y1)); - m_bounds.x2 = int(ceil(bnd.x2)); - m_bounds.y2 = int(ceil(bnd.y2)); - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - } - } - return false; - - case glyph_ren_agg_mono: - if(m_last_error == 0) - { - m_rasterizer.reset(); - if(m_flag32) - { - m_path32.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path32); - m_rasterizer.add_path(m_curves32); - } - else - { - m_path16.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path16); - m_rasterizer.add_path(m_curves16); - } - m_scanlines_bin.prepare(); // Remove all - render_scanlines(m_rasterizer, m_scanline_bin, m_scanlines_bin); - m_bounds.x1 = m_scanlines_bin.min_x(); - m_bounds.y1 = m_scanlines_bin.min_y(); - m_bounds.x2 = m_scanlines_bin.max_x() + 1; - m_bounds.y2 = m_scanlines_bin.max_y() + 1; - m_data_size = m_scanlines_bin.byte_size(); - m_data_type = glyph_data_mono; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - return false; - - - case glyph_ren_agg_gray8: - if(m_last_error == 0) - { - m_rasterizer.reset(); - if(m_flag32) - { - m_path32.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path32); - m_rasterizer.add_path(m_curves32); - } - else - { - m_path16.remove_all(); - decompose_ft_outline(m_cur_face->glyph->outline, - m_flip_y, - m_affine, - m_path16); - m_rasterizer.add_path(m_curves16); - } - m_scanlines_aa.prepare(); // Remove all - render_scanlines(m_rasterizer, m_scanline_aa, m_scanlines_aa); - m_bounds.x1 = m_scanlines_aa.min_x(); - m_bounds.y1 = m_scanlines_aa.min_y(); - m_bounds.x2 = m_scanlines_aa.max_x() + 1; - m_bounds.y2 = m_scanlines_aa.max_y() + 1; - m_data_size = m_scanlines_aa.byte_size(); - m_data_type = glyph_data_gray8; - m_advance_x = int26p6_to_dbl(m_cur_face->glyph->advance.x); - m_advance_y = int26p6_to_dbl(m_cur_face->glyph->advance.y); - m_affine.transform(&m_advance_x, &m_advance_y); - return true; - } - return false; - } - } - return false; - } - - - - - //------------------------------------------------------------------------ - void font_engine_freetype_base::write_glyph_to(int8u* data) const - { - if(data && m_data_size) - { - switch(m_data_type) - { - default: return; - case glyph_data_mono: m_scanlines_bin.serialize(data); break; - case glyph_data_gray8: m_scanlines_aa.serialize(data); break; - case glyph_data_outline: - if(m_flag32) - { - m_path32.serialize(data); - } - else - { - m_path16.serialize(data); - } - break; - case glyph_data_invalid: break; - } - } - } - - - - //------------------------------------------------------------------------ - bool font_engine_freetype_base::add_kerning(unsigned first, unsigned second, - double* x, double* y) - { - if(m_cur_face && first && second && FT_HAS_KERNING(m_cur_face)) - { - FT_Vector delta; - FT_Get_Kerning(m_cur_face, first, second, - FT_KERNING_DEFAULT, &delta); - double dx = int26p6_to_dbl(delta.x); - double dy = int26p6_to_dbl(delta.y); - if(m_glyph_rendering == glyph_ren_outline || - m_glyph_rendering == glyph_ren_agg_mono || - m_glyph_rendering == glyph_ren_agg_gray8) - { - m_affine.transform_2x2(&dx, &dy); - } - *x += dx; - *y += dy; - - return true; - } - return false; - } - - - -} - - diff --git a/lib/font_renderer/agg_font_freetype.h b/lib/font_renderer/agg_font_freetype.h deleted file mode 100644 index 6d83999f..00000000 --- a/lib/font_renderer/agg_font_freetype.h +++ /dev/null @@ -1,198 +0,0 @@ -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.4 -// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://www.antigrain.com -//---------------------------------------------------------------------------- -// -// See implementation agg_font_freetype.cpp -// -//---------------------------------------------------------------------------- - -#ifndef AGG_FONT_FREETYPE_INCLUDED -#define AGG_FONT_FREETYPE_INCLUDED - -#include -#include FT_FREETYPE_H - - -#include "agg_scanline_storage_aa.h" -#include "agg_scanline_storage_bin.h" -#include "agg_scanline_u.h" -#include "agg_scanline_bin.h" -#include "agg_path_storage_integer.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_conv_curve.h" -#include "agg_font_cache_manager.h" -#include "agg_trans_affine.h" - -namespace agg -{ - - - //-----------------------------------------------font_engine_freetype_base - class font_engine_freetype_base - { - public: - //-------------------------------------------------------------------- - typedef serialized_scanlines_adaptor_aa gray8_adaptor_type; - typedef serialized_scanlines_adaptor_bin mono_adaptor_type; - typedef scanline_storage_aa8 scanlines_aa_type; - typedef scanline_storage_bin scanlines_bin_type; - - //-------------------------------------------------------------------- - ~font_engine_freetype_base(); - font_engine_freetype_base(bool flag32, unsigned max_faces = 32); - - // Set font parameters - //-------------------------------------------------------------------- - void resolution(unsigned dpi); - bool load_font(const char* font_name, unsigned face_index, glyph_rendering ren_type, - const char* font_mem = 0, const long font_mem_size = 0); - bool attach(const char* file_name); - bool char_map(FT_Encoding map); - bool height(double h); - bool width(double w); - void hinting(bool h); - void flip_y(bool f); - void transform(const trans_affine& affine); - - // Set Gamma - //-------------------------------------------------------------------- - template void gamma(const GammaF& f) - { - m_rasterizer.gamma(f); - } - - // Accessors - //-------------------------------------------------------------------- - int last_error() const { return m_last_error; } - unsigned resolution() const { return m_resolution; } - const char* name() const { return m_name; } - unsigned num_faces() const; - FT_Encoding char_map() const { return m_char_map; } - double height() const { return double(m_height) / 64.0; } - double width() const { return double(m_width) / 64.0; } - double ascender() const; - double descender() const; - int face_height() const; - int face_units_em()const; - bool hinting() const { return m_hinting; } - bool flip_y() const { return m_flip_y; } - - - // Interface mandatory to implement for font_cache_manager - //-------------------------------------------------------------------- - const char* font_signature() const { return m_signature; } - int change_stamp() const { return m_change_stamp; } - - bool prepare_glyph(unsigned glyph_code); - unsigned glyph_index() const { return m_glyph_index; } - unsigned data_size() const { return m_data_size; } - glyph_data_type data_type() const { return m_data_type; } - const rect_i& bounds() const { return m_bounds; } - double advance_x() const { return m_advance_x; } - double advance_y() const { return m_advance_y; } - void write_glyph_to(int8u* data) const; - bool add_kerning(unsigned first, unsigned second, - double* x, double* y); - - private: - font_engine_freetype_base(const font_engine_freetype_base&); - const font_engine_freetype_base& operator = (const font_engine_freetype_base&); - - void update_char_size(); - void update_signature(); - int find_face(const char* face_name) const; - - bool m_flag32; - int m_change_stamp; - int m_last_error; - char* m_name; - unsigned m_name_len; - unsigned m_face_index; - FT_Encoding m_char_map; - char* m_signature; - unsigned m_height; - unsigned m_width; - bool m_hinting; - bool m_flip_y; - bool m_library_initialized; - FT_Library m_library; // handle to library - FT_Face* m_faces; // A pool of font faces - char** m_face_names; - unsigned m_num_faces; - unsigned m_max_faces; - FT_Face m_cur_face; // handle to the current face object - int m_resolution; - glyph_rendering m_glyph_rendering; - unsigned m_glyph_index; - unsigned m_data_size; - glyph_data_type m_data_type; - rect_i m_bounds; - double m_advance_x; - double m_advance_y; - trans_affine m_affine; - - path_storage_integer m_path16; - path_storage_integer m_path32; - conv_curve > m_curves16; - conv_curve > m_curves32; - scanline_u8 m_scanline_aa; - scanline_bin m_scanline_bin; - scanlines_aa_type m_scanlines_aa; - scanlines_bin_type m_scanlines_bin; - rasterizer_scanline_aa<> m_rasterizer; - }; - - - - - //------------------------------------------------font_engine_freetype_int16 - // This class uses values of type int16 (10.6 format) for the vector cache. - // The vector cache is compact, but when rendering glyphs of height - // more that 200 there integer overflow can occur. - // - class font_engine_freetype_int16 : public font_engine_freetype_base - { - public: - typedef serialized_integer_path_adaptor path_adaptor_type; - typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type; - typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type; - typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type; - typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type; - - font_engine_freetype_int16(unsigned max_faces = 32) : - font_engine_freetype_base(false, max_faces) {} - }; - - //------------------------------------------------font_engine_freetype_int32 - // This class uses values of type int32 (26.6 format) for the vector cache. - // The vector cache is twice larger than in font_engine_freetype_int16, - // but it allows you to render glyphs of very large sizes. - // - class font_engine_freetype_int32 : public font_engine_freetype_base - { - public: - typedef serialized_integer_path_adaptor path_adaptor_type; - typedef font_engine_freetype_base::gray8_adaptor_type gray8_adaptor_type; - typedef font_engine_freetype_base::mono_adaptor_type mono_adaptor_type; - typedef font_engine_freetype_base::scanlines_aa_type scanlines_aa_type; - typedef font_engine_freetype_base::scanlines_bin_type scanlines_bin_type; - - font_engine_freetype_int32(unsigned max_faces = 32) : - font_engine_freetype_base(true, max_faces) {} - }; - - -} - -#endif diff --git a/lib/font_renderer/agg_lcd_distribution_lut.h b/lib/font_renderer/agg_lcd_distribution_lut.h deleted file mode 100644 index 5e305d70..00000000 --- a/lib/font_renderer/agg_lcd_distribution_lut.h +++ /dev/null @@ -1,73 +0,0 @@ -// Adapted by Francesco Abbate for GSL Shell -// Original code's copyright below. -//---------------------------------------------------------------------------- -// Anti-Grain Geometry - Version 2.4 -// Copyright (C) 2002-2005 Maxim Shemanarev (http://www.antigrain.com) -// -// Permission to copy, use, modify, sell and distribute this software -// is granted provided this copyright notice appears in all copies. -// This software is provided "as is" without express or implied -// warranty, and with no claim as to its suitability for any purpose. -// -//---------------------------------------------------------------------------- -// Contact: mcseem@antigrain.com -// mcseemagg@yahoo.com -// http://www.antigrain.com -//---------------------------------------------------------------------------- - -#ifndef AGG_LCD_DISTRIBUTION_LUT_INCLUDED -#define AGG_LCD_DISTRIBUTION_LUT_INCLUDED - -#include - -#include "agg_basics.h" - -namespace agg -{ - - //=====================================================lcd_distribution_lut - class lcd_distribution_lut - { - public: - lcd_distribution_lut(double prim, double second, double tert) - { - double norm = 1.0 / (prim + second*2 + tert*2); - prim *= norm; - second *= norm; - tert *= norm; - for(unsigned i = 0; i < 256; i++) - { - unsigned b = (i << 8); - unsigned s = round(second * b); - unsigned t = round(tert * b); - unsigned p = b - (2*s + 2*t); - - m_data[3*i + 1] = s; /* secondary */ - m_data[3*i + 2] = t; /* tertiary */ - m_data[3*i ] = p; /* primary */ - } - } - - unsigned convolution(const int8u* covers, int i0, int i_min, int i_max) const - { - unsigned sum = 0; - int k_min = (i0 >= i_min + 2 ? -2 : i_min - i0); - int k_max = (i0 <= i_max - 2 ? 2 : i_max - i0); - for (int k = k_min; k <= k_max; k++) - { - /* select the primary, secondary or tertiary channel */ - int channel = std::abs(k) % 3; - int8u c = covers[i0 + k]; - sum += m_data[3*c + channel]; - } - - return (sum + 128) >> 8; - } - - private: - unsigned short m_data[256*3]; - }; - -} - -#endif diff --git a/lib/font_renderer/agg_pixfmt_alpha8.h b/lib/font_renderer/agg_pixfmt_alpha8.h deleted file mode 100644 index f91378fe..00000000 --- a/lib/font_renderer/agg_pixfmt_alpha8.h +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -#include -#include "agg_basics.h" -#include "agg_rendering_buffer.h" - -namespace agg -{ - // This is a special purpose color type that only has the alpha channel. - // It can be thought as a gray color but with the intensity always on. - // It is actually used to store coverage information. - struct alpha8 - { - typedef int8u value_type; - typedef int32u calc_type; - typedef int32 long_type; - enum base_scale_e - { - base_shift = 8, - base_scale = 1 << base_shift, - base_mask = base_scale - 1 - }; - - value_type a; - - //-------------------------------------------------------------------- - alpha8(unsigned a_=base_mask) : - a(int8u(a_)) {} - }; - - // Pixer format to store coverage information. - class pixfmt_alpha8 - { - public: - typedef alpha8 color_type; - typedef int8u value_type; - typedef int32u calc_type; - typedef agg::rendering_buffer::row_data row_data; - - //-------------------------------------------------------------------- - pixfmt_alpha8(rendering_buffer& rb): m_rbuf(&rb) - { - } - - //-------------------------------------------------------------------- - unsigned width() const { - return m_rbuf->width(); - } - unsigned height() const { - return m_rbuf->height(); - } - - // This method should never be called when using the scanline_u8. - // The use of scanline_p8 should be avoided because if does not works - // properly for rendering fonts because single hspan are split in many - // hline/hspan elements and pixel whitening happens. - void blend_hline(int x, int y, unsigned len, - const color_type& c, int8u cover) - { } - - void copy_hline(int x, int y, unsigned len, const color_type& c) - { - value_type* p = (value_type*) m_rbuf->row_ptr(y) + x; - do - { - *p = c.a; - p++; - } - while(--len); - } - - //-------------------------------------------------------------------- - void blend_solid_hspan(int x, int y, - unsigned len, - const color_type& c, - const int8u* covers) - { - value_type* p = (value_type*) m_rbuf->row_ptr(y) + x; - do - { - calc_type alpha = (calc_type(c.a) * (calc_type(*covers) + 1)) >> 8; - *p = alpha; - p++; - ++covers; - } - while(--len); - } - - private: - rendering_buffer* m_rbuf; - }; - -} diff --git a/lib/font_renderer/font_renderer.cpp b/lib/font_renderer/font_renderer.cpp deleted file mode 100644 index 14110107..00000000 --- a/lib/font_renderer/font_renderer.cpp +++ /dev/null @@ -1,445 +0,0 @@ -#include "font_renderer.h" - -#include "agg_lcd_distribution_lut.h" -#include "agg_pixfmt_rgb.h" -#include "agg_pixfmt_rgba.h" - -#include "font_renderer_alpha.h" - -// Important: when a subpixel scale is used the width below will be the width in logical pixel. -// As each logical pixel contains 3 subpixels it means that the 'pixels' pointer -// will hold enough space for '3 * width' uint8_t values. -struct FR_Bitmap { - agg::int8u *pixels; - int width, height; -}; - -class FR_Renderer { -public: - // Conventional LUT values: (1./3., 2./9., 1./9.) - // The values below are fine tuned as in the Elementary Plot library. - - FR_Renderer(bool hinting, bool kerning, bool subpixel, bool prescale_x) : - m_renderer(hinting, kerning, subpixel, prescale_x), - m_lcd_lut(0.448, 0.184, 0.092), - m_subpixel(subpixel) - { } - - font_renderer_alpha& renderer_alpha() { return m_renderer; } - agg::lcd_distribution_lut& lcd_distribution_lut() { return m_lcd_lut; } - int subpixel_scale() const { return (m_subpixel ? 3 : 1); } - -private: - font_renderer_alpha m_renderer; - agg::lcd_distribution_lut m_lcd_lut; - int m_subpixel; -}; - -FR_Renderer *FR_Renderer_New(unsigned int flags) { - bool hinting = ((flags & FR_HINTING) != 0); - bool kerning = ((flags & FR_KERNING) != 0); - bool subpixel = ((flags & FR_SUBPIXEL) != 0); - bool prescale_x = ((flags & FR_PRESCALE_X) != 0); - return new FR_Renderer(hinting, kerning, subpixel, prescale_x); -} - -FR_Bitmap* FR_Bitmap_New(FR_Renderer *font_renderer, int width, int height) { - const int subpixel_scale = font_renderer->subpixel_scale(); - FR_Bitmap *image = (FR_Bitmap *) malloc(sizeof(FR_Bitmap) + width * height * subpixel_scale); - if (!image) { return NULL; } - image->pixels = (agg::int8u *) (image + 1); - image->width = width; - image->height = height; - return image; -} - -void FR_Bitmap_Free(FR_Bitmap *image) { - free(image); -} - -void FR_Renderer_Free(FR_Renderer *font_renderer) { - delete font_renderer; -} - -int FR_Subpixel_Scale(FR_Renderer *font_renderer) { - return font_renderer->subpixel_scale(); -} - -int FR_Load_Font(FR_Renderer *font_renderer, const char *filename) { - bool success = font_renderer->renderer_alpha().load_font(filename); - return (success ? 0 : 1); -} - -int FR_Get_Font_Height(FR_Renderer *font_renderer, float size) { - font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha(); - double ascender, descender; - renderer_alpha.get_font_vmetrics(ascender, descender); - int face_height = renderer_alpha.get_face_height(); - float scale = renderer_alpha.scale_for_em_to_pixels(size); - return int((ascender - descender) * face_height * scale + 0.5); -} - -static void glyph_trim_rect(agg::rendering_buffer& ren_buf, FR_Bitmap_Glyph_Metrics& gli, int subpixel_scale) { - const int height = ren_buf.height(); - int x0 = gli.x0 * subpixel_scale, x1 = gli.x1 * subpixel_scale; - int y0 = gli.y0, y1 = gli.y1; - for (int y = gli.y0; y < gli.y1; y++) { - const uint8_t *row = ren_buf.row_ptr(height - 1 - y); - unsigned int row_bitsum = 0; - for (int x = x0; x < x1; x++) { - row_bitsum |= row[x]; - } - if (row_bitsum == 0) { - y0++; - } else { - break; - } - } - for (int y = gli.y1 - 1; y >= y0; y--) { - const uint8_t *row = ren_buf.row_ptr(height - 1 - y); - unsigned int row_bitsum = 0; - for (int x = x0; x < x1; x++) { - row_bitsum |= row[x]; - } - if (row_bitsum == 0) { - y1--; - } else { - break; - } - } - for (int x = gli.x0 * subpixel_scale; x < gli.x1 * subpixel_scale; x += subpixel_scale) { - unsigned int xaccu = 0; - for (int y = y0; y < y1; y++) { - const uint8_t *row = ren_buf.row_ptr(height - 1 - y); - for (int i = 0; i < subpixel_scale; i++) { - xaccu |= row[x + i]; - } - } - if (xaccu == 0) { - x0 += subpixel_scale; - } else { - break; - } - } - for (int x = (gli.x1 - 1) * subpixel_scale; x >= x0; x -= subpixel_scale) { - unsigned int xaccu = 0; - for (int y = y0; y < y1; y++) { - const uint8_t *row = ren_buf.row_ptr(height - 1 - y); - for (int i = 0; i < subpixel_scale; i++) { - xaccu |= row[x + i]; - } - } - if (xaccu == 0) { - x1 -= subpixel_scale; - } else { - break; - } - } - gli.xoff += (x0 / subpixel_scale) - gli.x0; - gli.yoff += (y0 - gli.y0); - gli.x0 = x0 / subpixel_scale; - gli.y0 = y0; - gli.x1 = x1 / subpixel_scale; - gli.y1 = y1; -} - -static void glyph_lut_convolution(agg::rendering_buffer ren_buf, agg::lcd_distribution_lut& lcd_lut, agg::int8u *covers_buf, FR_Bitmap_Glyph_Metrics& gli) { - const int subpixel = 3; - const int x0 = gli.x0, y0 = gli.y0, x1 = gli.x1, y1 = gli.y1; - const int len = (x1 - x0) * subpixel; - const int height = ren_buf.height(); - for (int y = y0; y < y1; y++) { - agg::int8u *covers = ren_buf.row_ptr(height - 1 - y) + x0 * subpixel; - memcpy(covers_buf, covers, len); - for (int x = x0 - 1; x < x1 + 1; x++) { - for (int i = 0; i < subpixel; i++) { - const int cx = (x - x0) * subpixel + i; - covers[cx] = lcd_lut.convolution(covers_buf, cx, 0, len - 1); - } - } - } - gli.x0 -= 1; - gli.x1 += 1; - gli.xoff -= 1; -} - -// The two functions below are needed because in C and C++ integer division -// is rounded toward zero. - -// euclidean division rounded toward positive infinite -static int div_pos(int n, int p) { - return n >= 0 ? (n + p - 1) / p : (n / p); -} - -// euclidean division rounded toward negative infinite -static int div_neg(int n, int p) { - return n >= 0 ? (n / p) : ((n - p + 1) / p); -} - -FR_Bitmap *FR_Bake_Font_Bitmap(FR_Renderer *font_renderer, int font_height, - int first_char, int num_chars, FR_Bitmap_Glyph_Metrics *glyphs) -{ - font_renderer_alpha& renderer_alpha = font_renderer->renderer_alpha(); - agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut(); - const int subpixel_scale = font_renderer->subpixel_scale(); - - double ascender, descender; - renderer_alpha.get_font_vmetrics(ascender, descender); - const int ascender_px = int(ascender * font_height); - const int pad_y = 1; - - // When using subpixel font rendering it is needed to leave a padding pixel on the left and on the right. - // Since each pixel is composed by n subpixel we set below x_start to subpixel_scale instead than zero. - // In addition we need one more pixel on the left because of subpixel positioning so - // it adds up to 2 * subpixel_scale. - // Note about the coordinates: they are AGG-like so x is positive toward the right and - // y is positive in the upper direction. - const int x_start = 2 * subpixel_scale; - const agg::alpha8 text_color(0xff); -#ifdef FONT_RENDERER_HEIGHT_HACK - const int font_height_reduced = (font_height * 86) / 100; -#else - const int font_height_reduced = font_height; -#endif - renderer_alpha.set_font_height(font_height_reduced); - - int *index = (int *) malloc(num_chars * sizeof(int)); - agg::rect_i *bounds = (agg::rect_i *) malloc(num_chars * sizeof(agg::rect_i)); - if (!index || !bounds) { - free(index); - free(bounds); - return NULL; - } - - int x_size_sum = 0, glyph_count = 0; - for (int i = 0; i < num_chars; i++) { - int codepoint = first_char + i; - index[i] = i; - if (renderer_alpha.codepoint_bounds(codepoint, subpixel_scale, bounds[i])) { - // Invalid glyph - bounds[i].x1 = 0; - bounds[i].y1 = 0; - bounds[i].x2 = -1; - bounds[i].y2 = -1; - } else { - if (bounds[i].x2 > bounds[i].x1) { - x_size_sum += bounds[i].x2 - bounds[i].x1; - glyph_count++; - } - bounds[i].x1 = subpixel_scale * div_neg(bounds[i].x1, subpixel_scale); - bounds[i].x2 = subpixel_scale * div_pos(bounds[i].x2, subpixel_scale); - } - } - - // Simple insertion sort algorithm: https://en.wikipedia.org/wiki/Insertion_sort - int i = 1; - while (i < num_chars) { - int j = i; - while (j > 0 && bounds[index[j-1]].y2 - bounds[index[j-1]].y1 > bounds[index[j]].y2 - bounds[index[j]].y1) { - int tmp = index[j]; - index[j] = index[j-1]; - index[j-1] = tmp; - j = j - 1; - } - i = i + 1; - } - - const int glyph_avg_width = glyph_count > 0 ? x_size_sum / (glyph_count * subpixel_scale) : font_height; - const int pixels_width = glyph_avg_width > 0 ? glyph_avg_width * 28 : 28; - - // dry run simulating pixel position to estimate required image's height - int x = x_start, y = 0, y_bottom = y; - for (int i = 0; i < num_chars; i++) { - const agg::rect_i& gbounds = bounds[index[i]]; - if (gbounds.x2 < gbounds.x1) continue; - // 1. It is very important to ensure that the x's increment below (1) and in - // (2), (3) and (4) are perfectly the same. - // Note that x_step below is always an integer multiple of subpixel_scale. - const int x_step = gbounds.x2 + 3 * subpixel_scale; - if (x + x_step >= pixels_width * subpixel_scale) { - x = x_start; - y = y_bottom; - } - // 5. Ensure that y's increment below is exactly the same to the one used in (6) - const int glyph_y_bottom = y - 2 * pad_y - (gbounds.y2 - gbounds.y1); - y_bottom = (y_bottom > glyph_y_bottom ? glyph_y_bottom : y_bottom); - // 2. Ensure x's increment is aligned with (1) - x = x + x_step; - } - - agg::int8u *cover_swap_buffer = (agg::int8u *) malloc(sizeof(agg::int8u) * (pixels_width * subpixel_scale)); - if (!cover_swap_buffer) { - free(index); - free(bounds); - return NULL; - } - - const int pixels_height = -y_bottom + 1; - const int pixel_size = 1; - FR_Bitmap *image = FR_Bitmap_New(font_renderer, pixels_width, pixels_height); - if (!image) { - free(index); - free(bounds); - free(cover_swap_buffer); - return NULL; - } - - agg::int8u *pixels = image->pixels; - memset(pixels, 0x00, pixels_width * pixels_height * subpixel_scale * pixel_size); - agg::rendering_buffer ren_buf(pixels, pixels_width * subpixel_scale, pixels_height, -pixels_width * subpixel_scale * pixel_size); - - // The variable y_bottom will be used to go down to the next row by taking into - // account the space occupied by each glyph of the current row along the y direction. - x = x_start; - // Set y to the image's height minus one to begin writing glyphs in the upper part of the image. - y = pixels_height - 1; - y_bottom = y; - for (int i = 0; i < num_chars; i++) { - // Important: the variable x in this loop should always be an integer multiple - // of subpixel_scale. - int codepoint = first_char + index[i]; - const agg::rect_i& gbounds = bounds[index[i]]; - if (gbounds.x2 < gbounds.x1) continue; - - // 3. Ensure x's increment is aligned with (1) - // Note that x_step below is always an integer multiple of subpixel_scale. - // We need 3 * subpixel_scale because: - // . +1 pixel on the left, because of RGB color filter - // . +1 pixel on the right, because of RGB color filter - // . +1 pixel on the right, because of subpixel positioning - // and each pixel requires "subpixel_scale" sub-pixels. - const int x_step = gbounds.x2 + 3 * subpixel_scale; - if (x + x_step >= pixels_width * subpixel_scale) { - // No more space along x, begin writing the row below. - x = x_start; - y = y_bottom; - } - - const int y_baseline = y - pad_y - gbounds.y2; - // 6. Ensure the y's increment below is aligned with the increment used in (5) - const int glyph_y_bottom = y - 2 * pad_y - (gbounds.y2 - gbounds.y1); - y_bottom = (y_bottom > glyph_y_bottom ? glyph_y_bottom : y_bottom); - - double x_next = x, y_next = y_baseline; - renderer_alpha.render_codepoint(ren_buf, text_color, x_next, y_next, codepoint, subpixel_scale); - - // The y coordinate for the glyph below is positive in the bottom direction, - // like is used by Lite's drawing system. - FR_Bitmap_Glyph_Metrics& glyph_info = glyphs[index[i]]; - glyph_info.x0 = x / subpixel_scale; - glyph_info.y0 = pixels_height - 1 - (y_baseline + gbounds.y2 + pad_y); - glyph_info.x1 = div_pos(x_next + 0.5, subpixel_scale); - glyph_info.y1 = pixels_height - 1 - (y_baseline + gbounds.y1 - pad_y); - - glyph_info.xoff = 0; - glyph_info.yoff = -pad_y - gbounds.y2 + ascender_px; - // Note that below the xadvance is in pixels times the subpixel_scale. - // This is meant for subpixel positioning. - glyph_info.xadvance = roundf(x_next - x); - - if (subpixel_scale != 1 && glyph_info.x1 > glyph_info.x0) { - glyph_lut_convolution(ren_buf, lcd_lut, cover_swap_buffer, glyph_info); - } - glyph_trim_rect(ren_buf, glyph_info, subpixel_scale); - - // When subpixel is activated we need one padding pixel on the left and on the right - // and one more because of subpixel positioning. - // 4. Ensure x's increment is aligned with (1) - x = x + x_step; - } - - free(index); - free(bounds); - free(cover_swap_buffer); - return image; -} - -template -void blend_solid_hspan(agg::rendering_buffer& rbuf, int x, int y, unsigned len, - const agg::rgba8& c, const agg::int8u* covers) -{ - const int pixel_size = 4; - agg::int8u* p = rbuf.row_ptr(y) + x * pixel_size; - do - { - const unsigned alpha = *covers; - const unsigned r = p[Order::R], g = p[Order::G], b = p[Order::B]; - p[Order::R] = (((unsigned(c.r) - r) * alpha) >> 8) + r; - p[Order::G] = (((unsigned(c.g) - g) * alpha) >> 8) + g; - p[Order::B] = (((unsigned(c.b) - b) * alpha) >> 8) + b; - // Leave p[3], the alpha channel value unmodified. - p += 4; - ++covers; - } - while(--len); -} - -template -void blend_solid_hspan_subpixel(agg::rendering_buffer& rbuf, agg::lcd_distribution_lut& lcd_lut, - const int x, const int y, unsigned len, - const agg::rgba8& c, - const agg::int8u* covers) -{ - const int pixel_size = 4; - const unsigned rgb[3] = { c.r, c.g, c.b }; - agg::int8u* p = rbuf.row_ptr(y) + x * pixel_size; - - // Indexes to adress RGB colors in a BGRA32 format. - const int pixel_index[3] = {Order::R, Order::G, Order::B}; - for (unsigned cx = 0; cx < len; cx += 3) - { - for (int i = 0; i < 3; i++) { - const unsigned cover_value = covers[cx + i]; - const unsigned alpha = (cover_value + 1) * (c.a + 1); - const unsigned src_col = *(p + pixel_index[i]); - *(p + pixel_index[i]) = (((rgb[i] - src_col) * alpha) + (src_col << 16)) >> 16; - } - // Leave p[3], the alpha channel value unmodified. - p += 4; - } -} - -// destination implicitly BGRA32. Source implictly single-byte renderer_alpha coverage with subpixel scale = 3. -// FIXME: consider using something like RenColor* instead of uint8_t * for dst. -void FR_Blend_Glyph(FR_Renderer *font_renderer, FR_Clip_Area *clip, int x_mult, int y, uint8_t *dst, int dst_width, const FR_Bitmap *glyphs_bitmap, const FR_Bitmap_Glyph_Metrics *glyph, FR_Color color) { - agg::lcd_distribution_lut& lcd_lut = font_renderer->lcd_distribution_lut(); - const int subpixel_scale = font_renderer->subpixel_scale(); - const int pixel_size = 4; // Pixel size for BGRA32 format. - - int x = x_mult / subpixel_scale; - - x += glyph->xoff; - y += glyph->yoff; - - int glyph_x = glyph->x0, glyph_y = glyph->y0; - int glyph_x_subpixel = -(x_mult % subpixel_scale); - int glyph_width = glyph->x1 - glyph->x0; - int glyph_height = glyph->y1 - glyph->y0; - - int n; - if ((n = clip->left - x) > 0) { glyph_width -= n; glyph_x += n; x += n; } - if ((n = clip->top - y) > 0) { glyph_height -= n; glyph_y += n; y += n; } - if ((n = x + glyph_width - clip->right ) > 0) { glyph_width -= n; } - if ((n = y + glyph_height - clip->bottom) > 0) { glyph_height -= n; } - - if (glyph_width <= 0 || glyph_height <= 0) { - return; - } - - dst += (x + y * dst_width) * pixel_size; - agg::rendering_buffer dst_ren_buf(dst, glyph_width, glyph_height, dst_width * pixel_size); - - uint8_t *src = glyphs_bitmap->pixels + (glyph_x + glyph_y * glyphs_bitmap->width) * subpixel_scale + glyph_x_subpixel; - int src_stride = glyphs_bitmap->width * subpixel_scale; - - const agg::rgba8 color_a(color.r, color.g, color.b); - for (int x = 0, y = 0; y < glyph_height; y++) { - agg::int8u *covers = src + y * src_stride; - if (subpixel_scale == 1) { - blend_solid_hspan(dst_ren_buf, x, y, glyph_width, color_a, covers); - } else { - blend_solid_hspan_subpixel(dst_ren_buf, lcd_lut, x, y, glyph_width * subpixel_scale, color_a, covers); - } - } -} - diff --git a/lib/font_renderer/font_renderer.h b/lib/font_renderer/font_renderer.h deleted file mode 100644 index 019d2ae7..00000000 --- a/lib/font_renderer/font_renderer.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef FONT_RENDERER_H -#define FONT_RENDERER_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - unsigned short x0, y0, x1, y1; - float xoff, yoff, xadvance; -} FR_Bitmap_Glyph_Metrics; - -typedef struct FR_Bitmap FR_Bitmap; - -#ifdef __cplusplus -class FR_Renderer; -#else -struct FR_Renderer; -typedef struct FR_Renderer FR_Renderer; -#endif - -enum { - FR_HINTING = 1 << 0, - FR_KERNING = 1 << 1, - FR_SUBPIXEL = 1 << 2, - FR_PRESCALE_X = 1 << 3, -}; - -typedef struct { - uint8_t r, g, b; -} FR_Color; - -typedef struct { - int left, top, right, bottom; -} FR_Clip_Area; - -FR_Renderer * FR_Renderer_New(unsigned int flags); -void FR_Renderer_Free(FR_Renderer *); -int FR_Load_Font(FR_Renderer *, const char *filename); -FR_Bitmap* FR_Bitmap_New(FR_Renderer *, int width, int height); -void FR_Bitmap_Free(FR_Bitmap *image); -int FR_Get_Font_Height(FR_Renderer *, float size); -FR_Bitmap * FR_Bake_Font_Bitmap(FR_Renderer *, int font_height, - int first_char, int num_chars, FR_Bitmap_Glyph_Metrics *glyph_info); -void FR_Blend_Glyph(FR_Renderer *font_renderer, - FR_Clip_Area *clip, int x, int y, - uint8_t *dst, int dst_width, - const FR_Bitmap *glyphs_bitmap, - const FR_Bitmap_Glyph_Metrics *glyph, FR_Color color); -int FR_Subpixel_Scale(FR_Renderer *); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/lib/font_renderer/font_renderer_alpha.h b/lib/font_renderer/font_renderer_alpha.h deleted file mode 100644 index a0f45a27..00000000 --- a/lib/font_renderer/font_renderer_alpha.h +++ /dev/null @@ -1,164 +0,0 @@ -#pragma once - -#include "agg_basics.h" -#include "agg_conv_curve.h" -#include "agg_conv_transform.h" -#include "agg_gamma_lut.h" -#include "agg_font_freetype.h" -#include "agg_pixfmt_alpha8.h" -#include "agg_rasterizer_scanline_aa.h" -#include "agg_renderer_primitives.h" -#include "agg_renderer_scanline.h" -#include "agg_rendering_buffer.h" -#include "agg_scanline_u.h" - -class font_renderer_alpha -{ - typedef agg::pixfmt_alpha8 pixfmt_type; - typedef agg::renderer_base base_ren_type; - typedef agg::renderer_scanline_aa_solid renderer_solid; - typedef agg::font_engine_freetype_int32 font_engine_type; - typedef agg::font_cache_manager font_manager_type; - - font_engine_type m_feng; - font_manager_type m_fman; - - // Font rendering options. - bool m_hinting; - bool m_kerning; - bool m_subpixel; - bool m_prescale_x; - - bool m_font_loaded; - - // Pipeline to process the vectors glyph paths (curves + contour) - agg::trans_affine m_mtx; - agg::conv_curve m_curves; - agg::conv_transform > m_trans; -public: - typedef agg::pixfmt_alpha8::color_type color_type; - - font_renderer_alpha(bool hinting, bool kerning, bool subpixel, bool prescale_x): - m_feng(), - m_fman(m_feng), - m_hinting(hinting), - m_kerning(kerning), - m_subpixel(subpixel), - m_prescale_x(prescale_x), - m_font_loaded(false), - m_curves(m_fman.path_adaptor()), - m_trans(m_curves, m_mtx) - { } - - int get_face_height() const { - return m_feng.face_height(); - } - - void get_font_vmetrics(double& ascender, double& descender) { - double current_height = m_feng.height(); - m_feng.height(1.0); - ascender = m_feng.ascender(); - descender = m_feng.descender(); - m_feng.height(current_height); -} - - float scale_for_em_to_pixels(float size) { - int units_per_em = m_feng.face_units_em(); - if (units_per_em > 0) { - return size / units_per_em; - } - return 0.0; - } - - bool load_font(const char *font_filename) { - if(m_feng.load_font(font_filename, 0, agg::glyph_ren_outline)) { - m_font_loaded = true; - m_feng.hinting(m_hinting); - } - return m_font_loaded; - } - - void set_font_height(double height) { - const double scale_x = (m_prescale_x ? 100.0 : 1.0); - m_feng.height(height); - m_feng.width(height * scale_x); - } - - template - void draw_codepoint(Rasterizer& ras, Scanline& sl, - RenSolid& ren_solid, const color_type color, - int codepoint, double& x, double& y, const int subpixel_scale) - { - const double scale_x = (m_prescale_x ? 100.0 : 1.0); - // Coefficient to scale back the glyph to the final scale. - const double cx_inv_scale = subpixel_scale / scale_x; - - // Represent the delta in x scaled by scale_x. - double x_delta = 0; - double start_x = x; - - const agg::glyph_cache* glyph = m_fman.glyph(codepoint); - if(glyph) - { - if(m_kerning) - { - m_fman.add_kerning(&x_delta, &y); - } - - m_fman.init_embedded_adaptors(glyph, 0, 0); - if(glyph->data_type == agg::glyph_data_outline) - { - double ty = m_hinting ? floor(y + 0.5) : y; - ras.reset(); - m_mtx.reset(); - m_mtx *= agg::trans_affine_scaling(cx_inv_scale, 1); - m_mtx *= agg::trans_affine_translation(start_x + cx_inv_scale * x_delta, ty); - ras.add_path(m_trans); - ren_solid.color(color); - agg::render_scanlines(ras, sl, ren_solid); - } - - y += glyph->advance_y; - x += cx_inv_scale * (x_delta + glyph->advance_x); - } - } - - void clear(agg::rendering_buffer& ren_buf, const color_type color) { - pixfmt_type pf(ren_buf); - base_ren_type ren_base(pf); - ren_base.clear(color); - } - - void render_codepoint(agg::rendering_buffer& ren_buf, - const color_type text_color, - double& x, double& y, - int codepoint, const int subpixel_scale) - { - if (!m_font_loaded) { - return; - } - agg::scanline_u8 sl; - agg::rasterizer_scanline_aa<> ras; - ras.clip_box(0, 0, ren_buf.width(), ren_buf.height()); - - agg::pixfmt_alpha8 pf(ren_buf); - base_ren_type ren_base(pf); - renderer_solid ren_solid(ren_base); - draw_codepoint(ras, sl, ren_solid, text_color, codepoint, x, y, subpixel_scale); - } - - int codepoint_bounds(int codepoint, const int subpixel_scale, agg::rect_i& bounds) - { - if (!m_font_loaded) return 1; - const double scale_x = (m_prescale_x ? 100.0 : 1.0); - const double cx_inv_scale = subpixel_scale / scale_x; - const agg::glyph_cache* glyph = m_fman.glyph(codepoint); - if (glyph) { - bounds = glyph->bounds; - bounds.x1 *= cx_inv_scale; - bounds.x2 *= cx_inv_scale; - return 0; - } - return 1; - } -}; diff --git a/lib/font_renderer/meson.build b/lib/font_renderer/meson.build deleted file mode 100644 index d596e152..00000000 --- a/lib/font_renderer/meson.build +++ /dev/null @@ -1,19 +0,0 @@ -freetype_dep = dependency('freetype2') - -libagg_dep = dependency('libagg', fallback: ['libagg', 'libagg_dep']) - -font_renderer_sources = [ - 'agg_font_freetype.cpp', - 'font_renderer.cpp', -] - -font_renderer_cdefs = ['-DFONT_RENDERER_HEIGHT_HACK'] - -font_renderer_include = include_directories('.') - -libfontrenderer = static_library('fontrenderer', - font_renderer_sources, - dependencies: [libagg_dep, freetype_dep], - cpp_args: font_renderer_cdefs, -) - diff --git a/lib/font_renderer/notes-lite-font-rendering.md b/lib/font_renderer/notes-lite-font-rendering.md deleted file mode 100644 index 175da8fc..00000000 --- a/lib/font_renderer/notes-lite-font-rendering.md +++ /dev/null @@ -1,125 +0,0 @@ -```c -stbtt_InitFont - -stbtt_ScaleForMappingEmToPixels x 3 -stbtt_ScaleForPixelHeight -stbtt_BakeFontBitmap -stbtt_GetFontVMetrics x 2 - -typedef struct { - unsigned short x0, y0, x1, y1; // coordinates of bbox in bitmap - float xoff, yoff, xadvance; -} stbtt_bakedchar; - -struct RenImage { - RenColor *pixels; - int width, height; -}; - -typedef struct { - RenImage *image; - stbtt_bakedchar glyphs[256]; -} GlyphSet; - -struct RenFont { - void *data; - stbtt_fontinfo stbfont; - GlyphSet *sets[MAX_GLYPHSET]; - float size; - int height; -}; - -``` - -The function stbtt_BakeFontBitmap is used to write bitmap data into set->image->pixels (where set is a GlyphSet). -Note that set->image->pixels need data in RGB format. After stbtt_BakeFontBitmap call the bitmap data are converted into RGB. -With a single call many glyphs corresponding to a range of codepoints, all in a -single image. - -## STB truetype font metrics - -stbtt_ScaleForPixelHeight takes a float 'height' and returns height / (ascent - descent). - -stbtt_ScaleForMappingEmToPixels take a float 'pixels' and returns pixels / unitsPerEm. - -### Computing RenFont - -When loading a font, in renderer.c, the font->height is determined as: - -```c -int ascent, descent, linegap; -stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); -float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size); -font->height = (ascent - descent + linegap) * scale + 0.5; -``` - -so, mathematically - -```c -font->height = (ascent - descent + linegap) * font->size / unitsPerEm + 0.5; -``` - -**TO DO**: find out for what font->height is actually used. - -### Call to BakeFontBitmap - -In the same file, renderer.c, to create the glyphset image it computes: - -```c -// Using stbtt functions -float s = ScaleForMappingEmToPixels(1) / ScaleForPixelHeight(1); -``` - -so 's' is actually equal to (ascent - descent) / unitsPerEm. - -Then BakeFontBitmap is called and `font->size * s` is used for the pixel_height argument. -So BakeFontBitmap gets - -```c -pixel_height = (ascent - descent) * font->size / unitsPerEm; -``` - -In turns BakeFontBitmap passes pixel_height to ScaleForPixelHeight() and uses the -resulting 'scale' to scale the glyphs by passing it to MakeGlyphBitmap(). - -This is equal almost equal to font->height except the 0.5, the missing linegap calculation -and the fact that this latter is an integer instead of a float. - -## AGG Font Engine - -Calls - -`FT_Init_FreeType` (initialize the library) - -In `load_font()` method: -`FT_New_Face` or `FT_New_Memory_Face` (use `FT_Done_Face` when done) - -`FT_Attach_File` -`FT_Select_Charmap` - -In `update_char_size()` method: -`FT_Set_Char_Size` or `FT_Set_Pixel_Sizes` - -In `prepare_glyph()` method: -`FT_Get_Char_Index` -`FT_Load_Glyph` -`FT_Render_Glyph` (if glyph_render_native_mono or native_gray8) - -in `add_kerning()` method -`FT_Get_Kerning` - -`FT_Done_FreeType` (end with library) - -## Freetype2's metrics related function and structs - -`FT_New_Face` return a `FT_Face` (a pointer) with font face information. - -## AGG font engine's font size - -The variable `m_height` is the size of the font muliplied by 64. -It will be used to set font size with: - -- `FT_Set_Char_Size` if m_resolution is set (> 0) -- `FT_Set_Pixel_Sizes`, divided by 64, if m_resolution is not set (= 0) - -The method height() returns m_height / 64; diff --git a/src/api/api.c b/src/api/api.c index c479ca4a..9f74f6b5 100644 --- a/src/api/api.c +++ b/src/api/api.c @@ -6,7 +6,6 @@ int luaopen_renderer(lua_State *L); int luaopen_regex(lua_State *L); int luaopen_process(lua_State *L); - static const luaL_Reg libs[] = { { "system", luaopen_system }, { "renderer", luaopen_renderer }, @@ -16,7 +15,6 @@ static const luaL_Reg libs[] = { }; void api_load_libs(lua_State *L) { - for (int i = 0; libs[i].name; i++) { + for (int i = 0; libs[i].name; i++) luaL_requiref(L, libs[i].name, libs[i].func, 1); - } } diff --git a/src/api/api.h b/src/api/api.h index 51ebb9a8..e7bc57ea 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -1,14 +1,15 @@ #ifndef API_H #define API_H -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" +#include +#include +#include #define API_TYPE_FONT "Font" -#define API_TYPE_REPLACE "Replace" #define API_TYPE_PROCESS "Process" +#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key)) + void api_load_libs(lua_State *L); #endif diff --git a/src/api/cp_replace.c b/src/api/cp_replace.c deleted file mode 100644 index a0fb3ac8..00000000 --- a/src/api/cp_replace.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "api.h" -#include "renderer.h" - - -static int f_new(lua_State *L) { - CPReplaceTable *rep_table = lua_newuserdata(L, sizeof(CPReplaceTable)); - luaL_setmetatable(L, API_TYPE_REPLACE); - ren_cp_replace_init(rep_table); - return 1; -} - - -static int f_gc(lua_State *L) { - CPReplaceTable *rep_table = luaL_checkudata(L, 1, API_TYPE_REPLACE); - ren_cp_replace_free(rep_table); - return 0; -} - - -static int f_add(lua_State *L) { - CPReplaceTable *rep_table = luaL_checkudata(L, 1, API_TYPE_REPLACE); - const char *src = luaL_checkstring(L, 2); - const char *dst = luaL_checkstring(L, 3); - ren_cp_replace_add(rep_table, src, dst); - return 0; -} - - -static const luaL_Reg lib[] = { - { "__gc", f_gc }, - { "new", f_new }, - { "add", f_add }, - { NULL, NULL } -}; - -int luaopen_renderer_replacements(lua_State *L) { - luaL_newmetatable(L, API_TYPE_REPLACE); - luaL_setfuncs(L, lib, 0); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - return 1; -} diff --git a/src/api/regex.c b/src/api/regex.c index 1043b1c5..e0f94056 100644 --- a/src/api/regex.c +++ b/src/api/regex.c @@ -101,17 +101,11 @@ int luaopen_regex(lua_State *L) { lua_setfield(L, -2, "__name"); lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, "regex"); - lua_pushnumber(L, PCRE2_ANCHORED); - lua_setfield(L, -2, "ANCHORED"); - lua_pushnumber(L, PCRE2_ANCHORED) ; - lua_setfield(L, -2, "ENDANCHORED"); - lua_pushnumber(L, PCRE2_NOTBOL); - lua_setfield(L, -2, "NOTBOL"); - lua_pushnumber(L, PCRE2_NOTEOL); - lua_setfield(L, -2, "NOTEOL"); - lua_pushnumber(L, PCRE2_NOTEMPTY); - lua_setfield(L, -2, "NOTEMPTY"); - lua_pushnumber(L, PCRE2_NOTEMPTY_ATSTART); - lua_setfield(L, -2, "NOTEMPTY_ATSTART"); + API_CONSTANT_DEFINE(L, -1, "ANCHORED", PCRE2_ANCHORED); + API_CONSTANT_DEFINE(L, -1, "ENDANCHORED", PCRE2_ENDANCHORED); + API_CONSTANT_DEFINE(L, -1, "NOTBOL", PCRE2_NOTBOL); + API_CONSTANT_DEFINE(L, -1, "NOTEOL", PCRE2_NOTEOL); + API_CONSTANT_DEFINE(L, -1, "NOTEMPTY", PCRE2_NOTEMPTY); + API_CONSTANT_DEFINE(L, -1, "NOTEMPTY_ATSTART", PCRE2_NOTEMPTY_ATSTART); return 1; } diff --git a/src/api/renderer.c b/src/api/renderer.c index 8dc13ada..8571dac6 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -2,6 +2,92 @@ #include "renderer.h" #include "rencache.h" +static int f_font_load(lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + float size = luaL_checknumber(L, 2); + unsigned int font_hinting = FONT_HINTING_SLIGHT; + bool subpixel = true; + if (lua_gettop(L) > 2 && lua_istable(L, 3)) { + lua_getfield(L, 3, "antialiasing"); + if (lua_isstring(L, -1)) { + const char *antialiasing = lua_tostring(L, -1); + if (antialiasing) { + if (strcmp(antialiasing, "grayscale") == 0) { + subpixel = false; + } else if (strcmp(antialiasing, "subpixel") == 0) { + subpixel = true; + } else { + return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing); + } + } + } + lua_pop(L, 1); + lua_getfield(L, 3, "hinting"); + if (lua_isstring(L, -1)) { + const char *hinting = lua_tostring(L, -1); + if (hinting) { + if (strcmp(hinting, "slight") == 0) { + font_hinting = FONT_HINTING_SLIGHT; + } else if (strcmp(hinting, "none") == 0) { + font_hinting = FONT_HINTING_NONE; + } else if (strcmp(hinting, "full") == 0) { + font_hinting = FONT_HINTING_FULL; + } else { + return luaL_error(L, "error in renderer.font.load, unknown hinting option: \"%s\"", hinting); + } + } + } + lua_pop(L, 1); + } + RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); + *font = ren_font_load(filename, size, subpixel, font_hinting); + if (!*font) + return luaL_error(L, "failed to load font"); + luaL_setmetatable(L, API_TYPE_FONT); + return 1; +} + +static int f_font_copy(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_get_height(*self); + RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); + *font = ren_font_copy(*self, size); + if (!*font) + return luaL_error(L, "failed to copy font"); + luaL_setmetatable(L, API_TYPE_FONT); + return 1; +} + +static int f_font_set_tab_size(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + int n = luaL_checknumber(L, 2); + ren_font_set_tab_size(*self, n); + return 0; +} + +static int f_font_gc(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + ren_font_free(*self); + return 0; +} + +static int f_font_get_width(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_font_get_width(*self, luaL_checkstring(L, 2))); + return 1; +} + +static int f_font_get_height(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_font_get_height(*self)); + return 1; +} + +static int f_font_get_size(lua_State *L) { + RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + lua_pushnumber(L, ren_font_get_size(*self)); + return 1; +} static RenColor checkcolor(lua_State *L, int idx, int def) { RenColor color; @@ -71,40 +157,17 @@ static int f_draw_rect(lua_State *L) { return 0; } -static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { - FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT); +static int f_draw_text(lua_State *L) { + RenFont** font = luaL_checkudata(L, 1, API_TYPE_FONT); const char *text = luaL_checkstring(L, 2); - /* The coordinate below will be in subpixel iff draw_subpixel is true. - Otherwise it will be in pixels. */ int x_subpixel = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - - CPReplaceTable *rep_table; - RenColor replace_color; - if (lua_gettop(L) >= 7) { - rep_table = luaL_checkudata(L, 6, API_TYPE_REPLACE); - replace_color = checkcolor(L, 7, 255); - } else { - rep_table = NULL; - replace_color = (RenColor) {0}; - } - - x_subpixel = rencache_draw_text(L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); + x_subpixel = rencache_draw_text(L, *font, text, x_subpixel, y, color); lua_pushnumber(L, x_subpixel); return 1; } -static int f_draw_text(lua_State *L) { - return draw_text_subpixel_impl(L, false); -} - - -static int f_draw_text_subpixel(lua_State *L) { - return draw_text_subpixel_impl(L, true); -} - - static const luaL_Reg lib[] = { { "show_debug", f_show_debug }, { "get_size", f_get_size }, @@ -113,19 +176,26 @@ static const luaL_Reg lib[] = { { "set_clip_rect", f_set_clip_rect }, { "draw_rect", f_draw_rect }, { "draw_text", f_draw_text }, - { "draw_text_subpixel", f_draw_text_subpixel }, { NULL, NULL } }; - -int luaopen_renderer_font(lua_State *L); -int luaopen_renderer_replacements(lua_State *L); +static const luaL_Reg fontLib[] = { + { "__gc", f_font_gc }, + { "load", f_font_load }, + { "copy", f_font_copy }, + { "set_tab_size", f_font_set_tab_size }, + { "get_width", f_font_get_width }, + { "get_height", f_font_get_height }, + { "get_size", f_font_get_size }, + { NULL, NULL } +}; int luaopen_renderer(lua_State *L) { luaL_newlib(L, lib); - luaopen_renderer_font(L); + luaL_newmetatable(L, API_TYPE_FONT); + luaL_setfuncs(L, fontLib, 0); + lua_pushvalue(L, -1); + lua_setfield(L, -2, "__index"); lua_setfield(L, -2, "font"); - luaopen_renderer_replacements(L); - lua_setfield(L, -2, "replacements"); return 1; } diff --git a/src/api/renderer_font.c b/src/api/renderer_font.c deleted file mode 100644 index 7c6587ea..00000000 --- a/src/api/renderer_font.c +++ /dev/null @@ -1,158 +0,0 @@ -#include -#include - -#include "api.h" -#include "fontdesc.h" -#include "renderer.h" -#include "rencache.h" - -static int f_load(lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - float size = luaL_checknumber(L, 2); - unsigned int font_options = 0; - if (lua_gettop(L) > 2 && lua_istable(L, 3)) { - lua_getfield(L, 3, "antialiasing"); - if (lua_isstring(L, -1)) { - const char *antialiasing = lua_tostring(L, -1); - if (antialiasing) { - if (strcmp(antialiasing, "grayscale") == 0) { - font_options |= RenFontGrayscale; - } else if (strcmp(antialiasing, "subpixel") == 0) { - font_options |= RenFontSubpixel; - } else { - return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing); - } - } - } - lua_pop(L, 1); - - lua_getfield(L, 3, "hinting"); - if (lua_isstring(L, -1)) { - const char *hinting = lua_tostring(L, -1); - if (hinting) { - if (strcmp(hinting, "slight") == 0) { - font_options |= RenFontHintingSlight; - } else if (strcmp(hinting, "none") == 0) { - font_options |= RenFontHintingNone; - } else if (strcmp(hinting, "full") == 0) { - font_options |= RenFontHintingFull; - } else { - return luaL_error(L, "error in renderer.font.load, unknown hinting option: \"%s\"", hinting); - } - } - } - lua_pop(L, 1); - } - - if (ren_verify_font(filename)) { - luaL_error(L, "failed to load font"); - } - - FontDesc *font_desc = lua_newuserdata(L, font_desc_alloc_size(filename)); - font_desc_init(font_desc, filename, size, font_options); - luaL_setmetatable(L, API_TYPE_FONT); - return 1; -} - - -static int f_copy(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - float size; - if (lua_gettop(L) >= 2) { - size = luaL_checknumber(L, 2); - } else { - size = self->size; - } - FontDesc *new_font_desc = lua_newuserdata(L, font_desc_alloc_size(self->filename)); - font_desc_init(new_font_desc, self->filename, size, self->options); - luaL_setmetatable(L, API_TYPE_FONT); - return 1; -} - - -static int f_set_tab_size(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - int n = luaL_checknumber(L, 2); - font_desc_set_tab_size(self, n); - return 0; -} - - -static int f_gc(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - font_desc_clear(self); - return 0; -} - -static int f_get_width(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - const char *text = luaL_checkstring(L, 2); - /* By calling ren_get_font_width with NULL as third arguments - we will obtain the width in points. */ - int w = ren_get_font_width(self, text, NULL); - lua_pushnumber(L, w); - return 1; -} - - -static int f_subpixel_scale(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_get_font_subpixel_scale(self)); - return 1; -} - -static int f_get_width_subpixel(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - const char *text = luaL_checkstring(L, 2); - int subpixel_scale; - /* We need to pass a non-null subpixel_scale pointer to force - subpixel width calculation. */ - lua_pushnumber(L, ren_get_font_width(self, text, &subpixel_scale)); - return 1; -} - - -static int f_get_height(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_get_font_height(self) ); - return 1; -} - - -static int f_get_size(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, self->size); - return 1; -} - - -static int f_set_size(lua_State *L) { - FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT); - float new_size = luaL_checknumber(L, 2); - font_desc_clear(self); - self->size = new_size; - return 0; -} - - -static const luaL_Reg lib[] = { - { "__gc", f_gc }, - { "load", f_load }, - { "copy", f_copy }, - { "set_tab_size", f_set_tab_size }, - { "get_width", f_get_width }, - { "get_width_subpixel", f_get_width_subpixel }, - { "get_height", f_get_height }, - { "subpixel_scale", f_subpixel_scale }, - { "get_size", f_get_size }, - { "set_size", f_set_size }, - { NULL, NULL } -}; - -int luaopen_renderer_font(lua_State *L) { - luaL_newmetatable(L, API_TYPE_FONT); - luaL_setfuncs(L, lib, 0); - lua_pushvalue(L, -1); - lua_setfield(L, -2, "__index"); - return 1; -} diff --git a/src/fontdesc.c b/src/fontdesc.c deleted file mode 100644 index 44460a6d..00000000 --- a/src/fontdesc.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include - -#include "fontdesc.h" -#include "renderer.h" - - -int font_desc_alloc_size(const char *filename) { - return offsetof(FontDesc, filename) + strlen(filename) + 1; -} - -void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options) { - memcpy(font_desc->filename, filename, strlen(filename) + 1); - font_desc->size = size; - font_desc->options = font_options; - font_desc->tab_size = 4; - font_desc->cache_length = 0; - font_desc->cache_last_index = 0; /* Normally no need to initialize. */ -} - -void font_desc_clear(FontDesc *font_desc) { - for (int i = 0; i < font_desc->cache_length; i++) { - ren_free_font(font_desc->cache[i].font); - } - font_desc->cache_length = 0; - font_desc->cache_last_index = 0; -} - -void font_desc_set_tab_size(FontDesc *font_desc, int tab_size) { - font_desc->tab_size = tab_size; - for (int i = 0; i < font_desc->cache_length; i++) { - ren_set_font_tab_size(font_desc->cache[i].font, tab_size); - } -} - -int font_desc_get_tab_size(FontDesc *font_desc) { - return font_desc->tab_size; -} - -static void load_scaled_font(FontDesc *font_desc, int index, int scale) { - RenFont *font = ren_load_font(font_desc->filename, scale * font_desc->size, font_desc->options); - if (!font) { - /* The font was able to load when initially loaded using renderer.load.font. - If now is no longer available we just abort the application. */ - fprintf(stderr, "Fatal error: unable to load font %s. Application will abort.\n", - font_desc->filename); - exit(1); - } - font_desc->cache[index].font = font; - font_desc->cache[index].scale = scale; -} - -RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale) { - int index = -1; - for (int i = 0; i < font_desc->cache_length; i++) { - if (font_desc->cache[i].scale == scale) { - index = i; - break; - } - } - if (index < 0) { - index = font_desc->cache_length; - if (index < FONT_CACHE_ARRAY_MAX) { - load_scaled_font(font_desc, index, scale); - font_desc->cache_length = index + 1; - } else { - // FIXME: should not print into the stderr or stdout. - fprintf(stderr, "Warning: max array of font scale reached.\n"); - index = (font_desc->cache_last_index == 0 ? 1 : 0); - ren_free_font(font_desc->cache[index].font); - load_scaled_font(font_desc, index, scale); - } - } - font_desc->cache_last_index = index; - return font_desc->cache[index].font; -} - diff --git a/src/fontdesc.h b/src/fontdesc.h deleted file mode 100644 index bf591801..00000000 --- a/src/fontdesc.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef FONT_DESC_H -#define FONT_DESC_H - -typedef struct RenFont RenFont; - -struct FontInstance { - RenFont *font; - short int scale; -}; -typedef struct FontInstance FontInstance; - -#define FONT_CACHE_ARRAY_MAX 2 - -struct FontDesc { - float size; - unsigned int options; - short int tab_size; - FontInstance cache[FONT_CACHE_ARRAY_MAX]; - short int cache_length; - short int cache_last_index; /* More recently used instance. */ - char filename[0]; -}; -typedef struct FontDesc FontDesc; - -void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options); -int font_desc_alloc_size(const char *filename); -int font_desc_get_tab_size(FontDesc *font_desc); -void font_desc_set_tab_size(FontDesc *font_desc, int tab_size); -void font_desc_clear(FontDesc *font_desc); -RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale); - -#endif - diff --git a/src/rencache.c b/src/rencache.c index 31165e90..7bbb9df6 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -16,32 +16,18 @@ #define COMMAND_BUF_SIZE (1024 * 512) #define COMMAND_BARE_SIZE offsetof(Command, text) -enum { SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL }; +enum { SET_CLIP, DRAW_TEXT, DRAW_RECT }; typedef struct { int8_t type; int8_t tab_size; - int8_t subpixel_scale; - int8_t x_subpixel_offset; int32_t size; RenRect rect; RenColor color; - FontDesc *font_desc; - CPReplaceTable *replacements; - RenColor replace_color; + RenFont *font; char text[0]; } Command; -#define FONT_REFS_MAX 12 -struct FontRef { - FontDesc *font_desc; - int index; -}; -typedef struct FontRef FontRef; -FontRef font_refs[FONT_REFS_MAX]; -int font_refs_len = 0; - - static unsigned cells_buf1[CELLS_X * CELLS_Y]; static unsigned cells_buf2[CELLS_X * CELLS_Y]; static unsigned *cells_prev = cells_buf1; @@ -52,36 +38,9 @@ static int command_buf_idx; static RenRect screen_rect; static bool show_debug; - static inline int min(int a, int b) { return a < b ? a : b; } static inline int max(int a, int b) { return a > b ? a : b; } -static int font_refs_add(lua_State *L, FontDesc *font_desc, int index) { - for (int i = 0; i < font_refs_len; i++) { - if (font_refs[i].font_desc == font_desc) { - return font_refs[i].index; - } - } - - if (font_refs_len >= FONT_REFS_MAX) { - fprintf(stderr, "Warning: (" __FILE__ "): exhausted font reference buffer\n"); - return LUA_NOREF; - } - - lua_pushvalue(L, index); - int ref = luaL_ref(L, LUA_REGISTRYINDEX); - - font_refs[font_refs_len++] = (FontRef) { font_desc, ref }; - return ref; -} - - -static void font_refs_clear(lua_State *L) { - for (int i = 0; i < font_refs_len; i++) { - luaL_unref(L, LUA_REGISTRYINDEX, font_refs[i].index); - } - font_refs_len = 0; -} /* 32bit fnv-1a hash */ #define HASH_INITIAL 2166136261 @@ -168,35 +127,21 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } -int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, - const char *text, int x, int y, RenColor color, bool draw_subpixel, - CPReplaceTable *replacements, RenColor replace_color) +int rencache_draw_text(lua_State *L, RenFont *font, const char *text, int x, int y, RenColor color) { - int subpixel_scale; - int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale); - RenRect rect; - rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x); - rect.y = y; - rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); - rect.height = ren_get_font_height(font_desc); - - if (rects_overlap(screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) { + RenRect rect = { x, y, ren_font_get_width(font, text), ren_font_get_height(font) }; + if (rects_overlap(screen_rect, rect)) { int sz = strlen(text) + 1; - Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz); + Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz); if (cmd) { memcpy(cmd->text, text, sz); cmd->color = color; - cmd->font_desc = font_desc; + cmd->font = font; cmd->rect = rect; - cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1); - cmd->x_subpixel_offset = x - subpixel_scale * rect.x; - cmd->tab_size = font_desc_get_tab_size(font_desc); - cmd->replacements = replacements; - cmd->replace_color = replace_color; + cmd->tab_size = ren_font_get_tab_size(font); } } - - return x + (draw_subpixel ? w_subpixel : rect.width); + return x + rect.width; } @@ -214,7 +159,6 @@ void rencache_begin_frame(lua_State *L) { screen_rect.height = h; rencache_invalidate(); } - font_refs_clear(L); } @@ -301,15 +245,8 @@ void rencache_end_frame(lua_State *L) { ren_draw_rect(cmd->rect, cmd->color); break; case DRAW_TEXT: - font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); - ren_draw_text(cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color, - cmd->replacements, cmd->replace_color); - break; - case DRAW_TEXT_SUBPIXEL: - font_desc_set_tab_size(cmd->font_desc, cmd->tab_size); - ren_draw_text_subpixel(cmd->font_desc, cmd->text, - cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color, - cmd->replacements, cmd->replace_color); + ren_font_set_tab_size(cmd->font, cmd->tab_size); + ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); break; } } diff --git a/src/rencache.h b/src/rencache.h index 1d0f45a6..7a7f80f5 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -8,8 +8,8 @@ void rencache_show_debug(bool enable); void rencache_set_clip_rect(RenRect rect); void rencache_draw_rect(RenRect rect, RenColor color); -int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, - bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); +int rencache_draw_text(lua_State *L, RenFont *font, + const char *text, int x, int y, RenColor color); void rencache_invalidate(void); void rencache_begin_frame(lua_State *L); void rencache_end_frame(lua_State *L); diff --git a/src/renderer.c b/src/renderer.c index cd269687..93394a9c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -2,37 +2,21 @@ #include #include #include -#include "font_renderer.h" +#include +#include +#include +#include FT_FREETYPE_H + #include "renderer.h" #include "renwindow.h" +#define DIVIDE_BY_255_SIGNED(x, sign_val) (((x) + (sign_val) + ((x)>>8)) >> 8) +#define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1) #define MAX_GLYPHSET 256 -#define REPLACEMENT_CHUNK_SIZE 8 - -struct RenImage { - RenColor *pixels; - int width, height; -}; - -struct GlyphSet { - FR_Bitmap *image; - FR_Bitmap_Glyph_Metrics glyphs[256]; -}; -typedef struct GlyphSet GlyphSet; - -/* The field "padding" below must be there just before GlyphSet *sets[MAX_GLYPHSET] - because the field "sets" can be indexed and writted with an index -1. For this - reason the "padding" field must be there but is never explicitly used. */ -struct RenFont { - GlyphSet *padding; - GlyphSet *sets[MAX_GLYPHSET]; - float size; - int height; - int space_advance; - FR_Renderer *renderer; -}; +#define SUBPIXEL_BITMAPS_CACHED 3 static RenWindow window_renderer = {0}; +static FT_Library library; static void* check_alloc(void *ptr) { if (!ptr) { @@ -42,6 +26,28 @@ static void* check_alloc(void *ptr) { return ptr; } +/************************* Fonts *************************/ + +typedef struct { + unsigned short x0, x1, y0, y1; + short bitmap_left, bitmap_top; + float xadvance; +} GlyphMetric; + +typedef struct { + SDL_Surface* surface[SUBPIXEL_BITMAPS_CACHED]; + GlyphMetric metrics[256]; +} GlyphSet; + +typedef struct RenFont { + FT_Face face; + GlyphSet* sets[MAX_GLYPHSET]; + float size; + short max_height, space_advance, tab_advance; + bool subpixel; + ERenFontHinting hinting; + char path[0]; +} RenFont; static const char* utf8_to_codepoint(const char *p, unsigned *dst) { unsigned res, n; @@ -59,213 +65,206 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) { return p + 1; } - -void ren_cp_replace_init(CPReplaceTable *rep_table) { - rep_table->size = 0; - rep_table->replacements = NULL; +static int font_set_load_options(RenFont* font) { + switch (font->hinting) { + case FONT_HINTING_SLIGHT: return FT_LOAD_TARGET_LIGHT | FT_LOAD_FORCE_AUTOHINT; + case FONT_HINTING_FULL: return FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT; + case FONT_HINTING_NONE: return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; + } + return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING; } - -void ren_cp_replace_free(CPReplaceTable *rep_table) { - free(rep_table->replacements); -} - - -void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst) { - int table_size = rep_table->size; - if (table_size % REPLACEMENT_CHUNK_SIZE == 0) { - CPReplace *old_replacements = rep_table->replacements; - const int new_size = (table_size / REPLACEMENT_CHUNK_SIZE + 1) * REPLACEMENT_CHUNK_SIZE; - rep_table->replacements = malloc(new_size * sizeof(CPReplace)); - if (!rep_table->replacements) { - rep_table->replacements = old_replacements; - return; +static int font_set_render_options(RenFont* font) { + if (font->subpixel) { + switch (font->hinting) { + case FONT_HINTING_NONE: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE); break; + case FONT_HINTING_SLIGHT: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_LIGHT); break; + case FONT_HINTING_FULL: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_DEFAULT); break; + } + return FT_RENDER_MODE_LCD; + } else { + switch (font->hinting) { + case FONT_HINTING_NONE: return FT_RENDER_MODE_NORMAL; break; + case FONT_HINTING_SLIGHT: return FT_RENDER_MODE_LIGHT; break; + case FONT_HINTING_FULL: return FT_RENDER_MODE_LIGHT; break; } - memcpy(rep_table->replacements, old_replacements, table_size * sizeof(CPReplace)); - free(old_replacements); } - CPReplace *rep = &rep_table->replacements[table_size]; - utf8_to_codepoint(src, &rep->codepoint_src); - utf8_to_codepoint(dst, &rep->codepoint_dst); - rep_table->size = table_size + 1; -} - -void ren_free_window_resources() { - renwin_free(&window_renderer); -} - -void ren_init(SDL_Window *win) { - assert(win); - window_renderer.window = win; - renwin_init_surface(&window_renderer); - renwin_clip_to_surface(&window_renderer); -} - - -void ren_resize_window() { - renwin_resize_surface(&window_renderer); -} - - -void ren_update_rects(RenRect *rects, int count) { - static bool initial_frame = true; - if (initial_frame) { - renwin_show_window(&window_renderer); - initial_frame = false; - } - renwin_update_rects(&window_renderer, rects, count); -} - - -void ren_set_clip_rect(RenRect rect) { - renwin_set_clip_rect(&window_renderer, rect); -} - - -void ren_get_size(int *x, int *y) { - RenWindow *ren = &window_renderer; - const int scale = renwin_surface_scale(ren); - SDL_Surface *surface = renwin_get_surface(ren); - *x = surface->w / scale; - *y = surface->h / scale; -} - - -RenImage* ren_new_image(int width, int height) { - assert(width > 0 && height > 0); - RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); - check_alloc(image); - image->pixels = (void*) (image + 1); - image->width = width; - image->height = height; - return image; -} - -void ren_free_image(RenImage *image) { - free(image); -} - -static GlyphSet* load_glyphset(RenFont *font, int idx) { - GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); - - set->image = FR_Bake_Font_Bitmap(font->renderer, font->height, idx << 8, 256, set->glyphs); - check_alloc(set->image); - - return set; -} - - -static GlyphSet* get_glyphset(RenFont *font, int codepoint) { - int idx = (codepoint >> 8) % MAX_GLYPHSET; - if (!font->sets[idx]) { - font->sets[idx] = load_glyphset(font, idx); - } - return font->sets[idx]; -} - - -int ren_verify_font(const char *filename) { - RenFont font[1]; - font->renderer = FR_Renderer_New(0); - if (FR_Load_Font(font->renderer, filename)) { - return 1; - } - FR_Renderer_Free(font->renderer); return 0; } - -RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags) { - RenFont *font = NULL; - - /* init font */ - font = check_alloc(calloc(1, sizeof(RenFont))); - font->size = size; - - unsigned int fr_renderer_flags = 0; - if ((renderer_flags & RenFontAntialiasingMask) == RenFontSubpixel) { - fr_renderer_flags |= FR_SUBPIXEL; +static GlyphSet* font_load_glyphset(RenFont* font, int idx) { + GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); + unsigned int render_option = font_set_render_options(font), load_option = font_set_load_options(font); + int pen_x = 0, bitmaps_cached = font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1; + unsigned int byte_width = font->subpixel ? 3 : 1; + for (int i = 0; i < 256; ++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 | FT_LOAD_BITMAP_METRICS_ONLY) || FT_Render_Glyph(font->face->glyph, render_option)) + continue; + FT_GlyphSlot slot = font->face->glyph; + int glyph_width = slot->bitmap.width / byte_width; + set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; + pen_x += glyph_width; + font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; + // xadvance for some reason should be computed like this. + if (FT_Load_Glyph(font->face, glyph_index, FT_LOAD_BITMAP_METRICS_ONLY) || FT_Render_Glyph(font->face->glyph, render_option)) + continue; + set->metrics[i].xadvance = (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f; + } - if ((renderer_flags & RenFontHintingMask) == RenFontHintingSlight) { - fr_renderer_flags |= (FR_HINTING | FR_PRESCALE_X); - } else if ((renderer_flags & RenFontHintingMask) == RenFontHintingFull) { - fr_renderer_flags |= FR_HINTING; - } - font->renderer = FR_Renderer_New(fr_renderer_flags); - if (FR_Load_Font(font->renderer, filename)) { - free(font); - return NULL; - } - font->height = FR_Get_Font_Height(font->renderer, size); - - FR_Bitmap_Glyph_Metrics *gs = get_glyphset(font, ' ')->glyphs; - font->space_advance = gs[' '].xadvance; - - /* make tab and newline glyphs invisible */ - FR_Bitmap_Glyph_Metrics *g = get_glyphset(font, '\n')->glyphs; - g['\t'].x1 = g['\t'].x0; - g['\n'].x1 = g['\n'].x0; - - return font; -} - - -void ren_free_font(RenFont *font) { - for (int i = 0; i < MAX_GLYPHSET; i++) { - GlyphSet *set = font->sets[i]; - if (set) { - FR_Bitmap_Free(set->image); - free(set); + for (int j = 0; j < bitmaps_cached; ++j) { + set->surface[j] = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0)); + unsigned char* pixels = set->surface[j]->pixels; + for (int i = 0; i < 256; ++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)) + continue; + FT_GlyphSlot slot = font->face->glyph; + FT_Outline_Translate(&slot->outline, (64 / bitmaps_cached) * j, 0 ); + if (FT_Render_Glyph(slot, render_option)) + continue; + for (int line = 0; line < slot->bitmap.rows; ++line) { + int target_offset = set->surface[j]->pitch * line + set->metrics[i].x0 * byte_width; + int source_offset = line * slot->bitmap.pitch; + memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width); + } } } - FR_Renderer_Free(font->renderer); + return set; +} + +static void font_free_glyphset(GlyphSet* set) { + for (int i = 0; i < SUBPIXEL_BITMAPS_CACHED; ++i) { + if (set->surface[i]) + SDL_FreeSurface(set->surface[i]); + } + free(set); +} + +static GlyphSet* font_get_glyphset(RenFont* font, int codepoint) { + int idx = (codepoint >> 8) % MAX_GLYPHSET; + if (!font->sets[idx]) + font->sets[idx] = font_load_glyphset(font, idx); + return font->sets[idx]; +} + +RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting) { + FT_Face face; + if (FT_New_Face( library, path, 0, &face)) + return NULL; + if (FT_Set_Pixel_Sizes(face, 0, (int)size)) + goto failure; + int len = strlen(path); + RenFont* font = check_alloc(calloc(1, sizeof(RenFont) + len + 1)); + strcpy(font->path, path); + font->face = face; + font->size = size; + font->subpixel = subpixel; + font->hinting = hinting; + GlyphSet* set = font_get_glyphset(font, ' '); + font->space_advance = (int)set->metrics[' '].xadvance; + font->tab_advance = font->space_advance * 2; + return font; + failure: + FT_Done_Face(face); + return NULL; +} + +RenFont* ren_font_copy(RenFont* font, float size) { + return ren_font_load(font->path, size, font->subpixel, font->hinting); +} + +void ren_font_free(RenFont* font) { + for (int i = 0; i < MAX_GLYPHSET; ++i) { + if (font->sets[i]) + font_free_glyphset(font->sets[i]); + } + FT_Done_Face(font->face); free(font); } - -void ren_set_font_tab_size(RenFont *font, int n) { - GlyphSet *set = get_glyphset(font, '\t'); - set->glyphs['\t'].xadvance = font->space_advance * n; +void ren_font_set_tab_size(RenFont *font, int n) { + font_get_glyphset(font, '\t')->metrics['\t'].xadvance = font->space_advance * n; } - -int ren_get_font_tab_size(RenFont *font) { - GlyphSet *set = get_glyphset(font, '\t'); - return set->glyphs['\t'].xadvance / font->space_advance; +int ren_font_get_tab_size(RenFont *font) { + return font_get_glyphset(font, '\t')->metrics['\t'].xadvance / font->space_advance; } - -/* Important: if subpixel_scale is NULL we will return width in points. Otherwise we will - return width in subpixels. */ -int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale) { - int x = 0; - const char *p = text; - unsigned codepoint; - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - while (*p) { - p = utf8_to_codepoint(p, &codepoint); - GlyphSet *set = get_glyphset(font, codepoint); - FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; - x += g->xadvance; +int ren_font_get_width(RenFont *font, const char *text) { + int width = 0; + const char* end = text + strlen(text); + while (text < end) { + unsigned int codepoint; + text = utf8_to_codepoint(text, &codepoint); + width += font_get_glyphset(font, codepoint)->metrics[codepoint % 256].xadvance; } - /* At this point here x is in subpixel units */ - const int x_scale_to_points = FR_Subpixel_Scale(font->renderer) * surface_scale; - if (subpixel_scale) { - *subpixel_scale = x_scale_to_points; - return x; + return width; +} + +float ren_font_get_size(RenFont *font) { + return font->size; +} +int ren_font_get_height(RenFont *font) { + return font->size + 3; +} + +int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) { + SDL_Surface *surface = renwin_get_surface(&window_renderer); + const RenRect clip = window_renderer.clip; + float pen_x = x; + int bytes_per_pixel = surface->format->BytesPerPixel; + const char* end = text + strlen(text); + unsigned char* destination_pixels = surface->pixels; + int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height; + while (text < end) { + unsigned int codepoint, r, g, b; + text = utf8_to_codepoint(text, &codepoint); + GlyphSet* set = font_get_glyphset(font, codepoint); + GlyphMetric* metric = &set->metrics[codepoint % 256]; + int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; + unsigned char* source_pixels = set->surface[bitmap_index]->pixels; + int start_x = pen_x + metric->bitmap_left, endX = metric->x1 - metric->x0 + pen_x; + int glyph_end = metric->x1, glyphStart = metric->x0; + if (color.a > 0 && endX >= clip.x && start_x <= clip_end_x) { + for (int line = metric->y0; line < metric->y1; ++line) { + int targetY = line + y - metric->y0 - metric->bitmap_top + font->size; + if (targetY < clip.y) + continue; + if (targetY >= clip_end_y) + break; + unsigned int* destination_pixel = (unsigned int*)&destination_pixels[surface->pitch * targetY + start_x * bytes_per_pixel]; + unsigned char* source_pixel = &source_pixels[line * set->surface[bitmap_index]->pitch + metric->x0 * (font->subpixel ? 3 : 1)]; + for (int x = glyphStart; x < glyph_end; ++x) { + unsigned int destination_color = *destination_pixel; + SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; + if (font->subpixel) { + SDL_Color src = { *source_pixel++, *source_pixel++, *source_pixel++ }; + r = color.r * src.r + dst.r * (255 - src.r) + 127; + r = DIVIDE_BY_255(r); + g = color.g * src.g + dst.g * (255 - src.g) + 127; + g = DIVIDE_BY_255(g); + b = color.b * src.b + dst.b * (255 - src.b) + 127; + b = DIVIDE_BY_255(b); + } else { + unsigned char intensity = *source_pixel++; + r = color.r * intensity + dst.r * (255 - intensity) + 127; + r = DIVIDE_BY_255(r); + g = color.g * intensity + dst.g * (255 - intensity) + 127; + g = DIVIDE_BY_255(g); + b = color.b * intensity + dst.b * (255 - intensity) + 127; + b = DIVIDE_BY_255(b); + } + *destination_pixel++ = dst.a << 24 | r << 16 | g << 8 | b; + } + } + } + pen_x += metric->xadvance ? metric->xadvance : font->space_advance; } - return (x + x_scale_to_points / 2) / x_scale_to_points; + return pen_x; } - -int ren_get_font_height(FontDesc *font_desc) { - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - return (font->height + surface_scale / 2) / surface_scale; -} - - +/******************* Rectangles **********************/ static inline RenColor blend_pixel(RenColor dst, RenColor src) { int ia = 0xff - src.a; dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8; @@ -316,88 +315,49 @@ void ren_draw_rect(RenRect rect, RenColor color) { } } +/*************** Window Management ****************/ +void ren_free_window_resources() { + renwin_free(&window_renderer); +} -static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { - for (int i = 0; i < rep_table->size; i++) { - const CPReplace *rep = &rep_table->replacements[i]; - if (*codepoint == rep->codepoint_src) { - *codepoint = rep->codepoint_dst; - return 1; - } +void 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; } - return 0; + window_renderer.window = win; + renwin_init_surface(&window_renderer); + renwin_clip_to_surface(&window_renderer); } -static FR_Clip_Area clip_area_from_rect(const RenRect r) { - return (FR_Clip_Area) {r.x, r.y, r.x + r.width, r.y + r.height}; +void ren_resize_window() { + renwin_resize_surface(&window_renderer); } -static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color, - CPReplaceTable *replacements, RenColor replace_color) -{ - SDL_Surface *surf = renwin_get_surface(&window_renderer); - FR_Clip_Area clip = clip_area_from_rect(window_renderer.clip); - const char *p = text; - unsigned codepoint; - const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; - while (*p) { - FR_Color color_rep; - p = utf8_to_codepoint(p, &codepoint); - GlyphSet *set = get_glyphset(font, codepoint); - FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; - const int xadvance_original_cp = g->xadvance; - const int replaced = replacements ? codepoint_replace(replacements, &codepoint) : 0; - if (replaced) { - set = get_glyphset(font, codepoint); - g = &set->glyphs[codepoint & 0xff]; - color_rep = (FR_Color) { .r = replace_color.r, .g = replace_color.g, .b = replace_color.b}; - } else { - color_rep = color_fr; - } - if (color.a != 0) { - FR_Blend_Glyph(font->renderer, &clip, - x_subpixel, y_pixel, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep); - } - x_subpixel += xadvance_original_cp; +void ren_update_rects(RenRect *rects, int count) { + static bool initial_frame = true; + if (initial_frame) { + renwin_show_window(&window_renderer); + initial_frame = false; } + renwin_update_rects(&window_renderer, rects, count); } -void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, - CPReplaceTable *replacements, RenColor replace_color) -{ - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - draw_text_impl(font, text, x_subpixel, surface_scale * y, color, replacements, replace_color); -} - -void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, - CPReplaceTable *replacements, RenColor replace_color) -{ - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer); - draw_text_impl(font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color); -} - -// Could be declared as static inline -int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) { - int w_mult; - if (orientation < 0) { - w_mult = width; - } else if (orientation == 0) { - w_mult = width + subpixel_scale / 2; - } else { - w_mult = width + subpixel_scale - 1; - } - return w_mult / subpixel_scale; +void ren_set_clip_rect(RenRect rect) { + renwin_set_clip_rect(&window_renderer, rect); } -int ren_get_font_subpixel_scale(FontDesc *font_desc) { - const int surface_scale = renwin_surface_scale(&window_renderer); - RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale); - return FR_Subpixel_Scale(font->renderer) * surface_scale; +void ren_get_size(int *x, int *y) { + RenWindow *ren = &window_renderer; + const int scale = renwin_surface_scale(ren); + SDL_Surface *surface = renwin_get_surface(ren); + *x = surface->w / scale; + *y = surface->h / scale; } + diff --git a/src/renderer.h b/src/renderer.h index bab059bb..dfc21c06 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -3,37 +3,24 @@ #include #include -#include "fontdesc.h" - -typedef struct RenImage RenImage; - -enum { - RenFontAntialiasingMask = 1, - RenFontGrayscale = 1, - RenFontSubpixel = 0, - - RenFontHintingMask = 3 << 1, - RenFontHintingSlight = 0 << 1, - RenFontHintingNone = 1 << 1, - RenFontHintingFull = 2 << 1, -}; +#include +typedef struct RenFont RenFont; +typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting; typedef struct { uint8_t b, g, r, a; } RenColor; typedef struct { int x, y, width, height; } RenRect; -struct CPReplace { - unsigned codepoint_src; - unsigned codepoint_dst; -}; -typedef struct CPReplace CPReplace; - - -struct CPReplaceTable { - int size; - CPReplace *replacements; -}; -typedef struct CPReplaceTable CPReplaceTable; +RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting); +RenFont* ren_font_copy(RenFont* font, float size); +void ren_font_free(RenFont *font); +void ren_font_set_tab_size(RenFont *font, int n); +int ren_font_get_tab_size(RenFont *font); +int ren_font_get_width(RenFont *font, const char *text); +int ren_font_get_height(RenFont *font); +float ren_font_get_size(RenFont *font); +int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color); +void ren_draw_rect(RenRect rect, RenColor color); void ren_init(SDL_Window *win); void ren_resize_window(); @@ -42,27 +29,5 @@ void ren_set_clip_rect(RenRect rect); void ren_get_size(int *x, int *y); /* Reports the size in points. */ void ren_free_window_resources(); -RenImage* ren_new_image(int width, int height); -void ren_free_image(RenImage *image); - -RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags); -int ren_verify_font(const char *filename); -void ren_free_font(RenFont *font); -void ren_set_font_tab_size(RenFont *font, int n); -int ren_get_font_tab_size(RenFont *font); - -int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale); -int ren_get_font_height(FontDesc *font_desc); -int ren_get_font_subpixel_scale(FontDesc *font_desc); -int ren_font_subpixel_round(int width, int subpixel_scale, int orientation); - -void ren_draw_rect(RenRect rect, RenColor color); -void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); -void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color); - -void ren_cp_replace_init(CPReplaceTable *rep_table); -void ren_cp_replace_free(CPReplaceTable *rep_table); -void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst); -void ren_cp_replace_clear(CPReplaceTable *rep_table); #endif From 2209c327a733d4699a039cfb803d382f05d766c4 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 22:24:03 -0400 Subject: [PATCH 100/165] Subprojects. --- .gitignore | 1 - subprojects/libagg.wrap | 4 ---- 2 files changed, 5 deletions(-) delete mode 100644 subprojects/libagg.wrap diff --git a/.gitignore b/.gitignore index 16974405..41183a14 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ build*/ lhelper/ submodules/ subprojects/lua/ -subprojects/libagg/ subprojects/reproc/ /appimage* .ccls-cache diff --git a/subprojects/libagg.wrap b/subprojects/libagg.wrap deleted file mode 100644 index d5618583..00000000 --- a/subprojects/libagg.wrap +++ /dev/null @@ -1,4 +0,0 @@ -[wrap-git] -directory = libagg -url = https://github.com/franko/agg -revision = v2.4-lhelper4 From be6bcbcacc9d788f53381ac57ab7a4c9a1f79841 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 22:25:10 -0400 Subject: [PATCH 101/165] Meson build updated. --- meson.build | 1 - src/meson.build | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/meson.build b/meson.build index 047ce90e..62e43d11 100644 --- a/meson.build +++ b/meson.build @@ -121,7 +121,6 @@ configure_file( # Targets #=============================================================================== if not get_option('source-only') - subdir('lib/font_renderer') subdir('src') subdir('scripts') endif diff --git a/src/meson.build b/src/meson.build index 707e04e9..b0dc5247 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,14 +1,11 @@ lite_sources = [ 'api/api.c', - 'api/cp_replace.c', 'api/renderer.c', - 'api/renderer_font.c', 'api/regex.c', 'api/system.c', 'api/process.c', 'renderer.c', 'renwindow.c', - 'fontdesc.c', 'rencache.c', 'main.c', ] @@ -25,7 +22,7 @@ lite_include = include_directories('.') executable('lite-xl', lite_sources + lite_rc, - include_directories: [lite_include, font_renderer_include], + include_directories: [lite_include], dependencies: lite_deps, c_args: lite_cargs, objc_args: lite_cargs, From c879e016cc84cbc0cc4835e9f084c6d4dd331270 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 22:32:05 -0400 Subject: [PATCH 102/165] Removed lib font renderer mention. --- src/meson.build | 1 - 1 file changed, 1 deletion(-) diff --git a/src/meson.build b/src/meson.build index b0dc5247..6fefd257 100644 --- a/src/meson.build +++ b/src/meson.build @@ -26,7 +26,6 @@ executable('lite-xl', dependencies: lite_deps, c_args: lite_cargs, objc_args: lite_cargs, - link_with: libfontrenderer, link_args: lite_link_args, install_dir: lite_bindir, install: true, From 19b90aae1846671e88683f79b44158478533b7d3 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 22:48:31 -0400 Subject: [PATCH 103/165] Added freetype. --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 62e43d11..5844ab77 100644 --- a/meson.build +++ b/meson.build @@ -49,6 +49,7 @@ if not get_option('source-only') default_options: ['shared=false', 'use_readline=false', 'app=false'] ) pcre2_dep = dependency('libpcre2-8') + freetype_dep = dependency('freetype2') sdl_dep = dependency('sdl2', method: 'config-tool') reproc_dep = dependency('reproc', fallback: ['reproc', 'reproc_dep'], default_options: [ @@ -57,7 +58,7 @@ if not get_option('source-only') ] ) - lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl] + lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl, freetype_dep] if host_machine.system() == 'windows' # Note that we need to explicitly add the windows socket DLL because From 67032f72ac9468b04a0876590efb6cd06baa44e7 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 22:54:05 -0400 Subject: [PATCH 104/165] Removed subpixel mentions. --- data/plugins/lineguide.lua | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/plugins/lineguide.lua b/data/plugins/lineguide.lua index 61debbff..5a17c8ca 100644 --- a/data/plugins/lineguide.lua +++ b/data/plugins/lineguide.lua @@ -7,8 +7,7 @@ local draw_overlay = DocView.draw_overlay function DocView:draw_overlay(...) local ns = ("n"):rep(config.line_limit) - local ss = self:get_font():subpixel_scale() - local offset = self:get_font():get_width_subpixel(ns) / ss + local offset = self:get_font():get_width(ns) local x = self:get_line_screen_position(1) + offset local y = self.position.y local w = math.ceil(SCALE * 1) From 425a4f600bdba3324e47c5a9282333fcdd3d0906 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 23:09:33 -0400 Subject: [PATCH 105/165] Forgot to reset offset. --- data/plugins/drawwhitespace.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/data/plugins/drawwhitespace.lua b/data/plugins/drawwhitespace.lua index d6700615..da9d1b12 100644 --- a/data/plugins/drawwhitespace.lua +++ b/data/plugins/drawwhitespace.lua @@ -18,6 +18,7 @@ function DocView:draw_line_text(idx, x, y) renderer.draw_text(font, string.rep("·", e - s + 1), tx, ty, color) offset = e + 1 end + offset = 1 while true do s, e = text:find("\t", offset) if not s then break end From b6829cb041a27dd236cb9d9f280a37e4d4e5eb7c Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 10 Sep 2021 23:52:36 -0400 Subject: [PATCH 106/165] Used copy. --- data/plugins/scale.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/data/plugins/scale.lua b/data/plugins/scale.lua index acf3c7bb..8f8602f3 100644 --- a/data/plugins/scale.lua +++ b/data/plugins/scale.lua @@ -50,10 +50,10 @@ local function set_scale(scale) style.tab_width = style.tab_width * s for _, name in ipairs {"font", "big_font", "icon_font", "icon_big_font", "code_font"} do - renderer.font.set_size(style[name], s * style[name]:get_size()) + style[name] = renderer.font.copy(style[name], s * style[name]:get_size()) end else - renderer.font.set_size(style.code_font, s * style.code_font:get_size()) + style.code_font = renderer.font.copy(style.code_font, s * style.code_font:get_size()) end for _, font in pairs(style.syntax_fonts) do From a68fff2fff6808445f89bf27da9b80f4048adf7d Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 11 Sep 2021 01:17:55 -0400 Subject: [PATCH 107/165] Removed C++ --- meson.build | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index 5844ab77..a4421dd6 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,9 @@ project('lite-xl', - ['c', 'cpp'], + ['c'], version : '2.0.2', license : 'MIT', meson_version : '>= 0.54', - default_options : ['c_std=gnu11', 'cpp_std=c++03'] + default_options : ['c_std=gnu11'] ) #=============================================================================== @@ -33,7 +33,7 @@ endif #=============================================================================== lite_link_args = [] if cc.get_id() == 'gcc' and get_option('buildtype') == 'release' - lite_link_args += ['-static-libgcc', '-static-libstdc++'] + lite_link_args += ['-static-libgcc'] endif if host_machine.system() == 'darwin' From 10f28079ba86b95b3a82fef3fbc0a9a85a3d3603 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 11 Sep 2021 02:08:01 -0400 Subject: [PATCH 108/165] Removed another cpp mention. --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index a4421dd6..53883266 100644 --- a/meson.build +++ b/meson.build @@ -63,7 +63,7 @@ if not get_option('source-only') if host_machine.system() == 'windows' # Note that we need to explicitly add the windows socket DLL because # the pkg-config file from reproc does not include it. - lite_deps += meson.get_compiler('cpp').find_library('ws2_32', required: true) + lite_deps += meson.get_compiler('c').find_library('ws2_32', required: true) endif endif #=============================================================================== From 16deedc8a39fc37ae0fe9696823440a7b805f801 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 18:41:54 -0400 Subject: [PATCH 109/165] Fixed up some naming conventions, and also added bolding and italics. --- src/api/renderer.c | 10 ++++++++-- src/renderer.c | 35 ++++++++++++++++++++++++----------- src/renderer.h | 3 ++- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/src/api/renderer.c b/src/api/renderer.c index 8571dac6..3d89f041 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -5,7 +5,7 @@ static int f_font_load(lua_State *L) { const char *filename = luaL_checkstring(L, 1); float size = luaL_checknumber(L, 2); - unsigned int font_hinting = FONT_HINTING_SLIGHT; + unsigned int font_hinting = FONT_HINTING_SLIGHT, font_style = 0; bool subpixel = true; if (lua_gettop(L) > 2 && lua_istable(L, 3)) { lua_getfield(L, 3, "antialiasing"); @@ -37,10 +37,16 @@ static int f_font_load(lua_State *L) { } } } + lua_getfield(L, 3, "italic"); + if (lua_toboolean(L, -1)) + font_style |= FONT_STYLE_ITALIC; + lua_getfield(L, 3, "bold"); + if (lua_toboolean(L, -1)) + font_style |= FONT_STYLE_BOLD; lua_pop(L, 1); } RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); - *font = ren_font_load(filename, size, subpixel, font_hinting); + *font = ren_font_load(filename, size, subpixel, font_hinting, font_style); if (!*font) return luaL_error(L, "failed to load font"); luaL_setmetatable(L, API_TYPE_FONT); diff --git a/src/renderer.c b/src/renderer.c index 93394a9c..435fdeb9 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -46,6 +46,7 @@ typedef struct RenFont { short max_height, space_advance, tab_advance; bool subpixel; ERenFontHinting hinting; + unsigned char style; char path[0]; } RenFont; @@ -92,6 +93,17 @@ static int font_set_render_options(RenFont* font) { return 0; } +static int font_set_style(FT_Outline* outline, int x_translation, unsigned char style) { + FT_Outline_Translate(outline, x_translation, 0 ); + if (style & FONT_STYLE_BOLD) + FT_Outline_EmboldenXY(outline, 1 << 5, 0); + if (style & FONT_STYLE_ITALIC) { + FT_Matrix matrix = { 1 << 16, 1 << 14, 0, 1 << 16 }; + FT_Outline_Transform(outline, &matrix); + } + return 0; +} + static GlyphSet* font_load_glyphset(RenFont* font, int idx) { GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); unsigned int render_option = font_set_render_options(font), load_option = font_set_load_options(font); @@ -99,7 +111,7 @@ static GlyphSet* font_load_glyphset(RenFont* font, int idx) { unsigned int byte_width = font->subpixel ? 3 : 1; for (int i = 0; i < 256; ++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 | FT_LOAD_BITMAP_METRICS_ONLY) || FT_Render_Glyph(font->face->glyph, render_option)) + if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, 0, font->style) || FT_Render_Glyph(font->face->glyph, render_option)) continue; FT_GlyphSlot slot = font->face->glyph; int glyph_width = slot->bitmap.width / byte_width; @@ -107,7 +119,7 @@ static GlyphSet* font_load_glyphset(RenFont* font, int idx) { pen_x += glyph_width; font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; // xadvance for some reason should be computed like this. - if (FT_Load_Glyph(font->face, glyph_index, FT_LOAD_BITMAP_METRICS_ONLY) || FT_Render_Glyph(font->face->glyph, render_option)) + if (FT_Load_Glyph(font->face, glyph_index, FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&slot->outline, 0, font->style) || FT_Render_Glyph(font->face->glyph, render_option)) continue; set->metrics[i].xadvance = (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f; @@ -120,7 +132,7 @@ static GlyphSet* font_load_glyphset(RenFont* font, int idx) { if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option)) continue; FT_GlyphSlot slot = font->face->glyph; - FT_Outline_Translate(&slot->outline, (64 / bitmaps_cached) * j, 0 ); + 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) { @@ -148,7 +160,7 @@ static GlyphSet* font_get_glyphset(RenFont* font, int codepoint) { return font->sets[idx]; } -RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting) { +RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting, unsigned char style) { FT_Face face; if (FT_New_Face( library, path, 0, &face)) return NULL; @@ -161,6 +173,7 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha font->size = size; font->subpixel = subpixel; font->hinting = hinting; + font->style = style; GlyphSet* set = font_get_glyphset(font, ' '); font->space_advance = (int)set->metrics[' '].xadvance; font->tab_advance = font->space_advance * 2; @@ -171,7 +184,7 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha } RenFont* ren_font_copy(RenFont* font, float size) { - return ren_font_load(font->path, size, font->subpixel, font->hinting); + return ren_font_load(font->path, size, font->subpixel, font->hinting, font->style); } void ren_font_free(RenFont* font) { @@ -225,17 +238,17 @@ int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor colo int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; unsigned char* source_pixels = set->surface[bitmap_index]->pixels; int start_x = pen_x + metric->bitmap_left, endX = metric->x1 - metric->x0 + pen_x; - int glyph_end = metric->x1, glyphStart = metric->x0; + int glyph_end = metric->x1, glyph_start = metric->x0; if (color.a > 0 && endX >= clip.x && start_x <= clip_end_x) { for (int line = metric->y0; line < metric->y1; ++line) { - int targetY = line + y - metric->y0 - metric->bitmap_top + font->size; - if (targetY < clip.y) + int target_y = line + y - metric->y0 - metric->bitmap_top + font->size; + if (target_y < clip.y) continue; - if (targetY >= clip_end_y) + if (target_y >= clip_end_y) break; - unsigned int* destination_pixel = (unsigned int*)&destination_pixels[surface->pitch * targetY + start_x * bytes_per_pixel]; + 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[bitmap_index]->pitch + metric->x0 * (font->subpixel ? 3 : 1)]; - for (int x = glyphStart; x < glyph_end; ++x) { + for (int x = glyph_start; x < glyph_end; ++x) { unsigned int destination_color = *destination_pixel; SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; if (font->subpixel) { diff --git a/src/renderer.h b/src/renderer.h index dfc21c06..f07f4411 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -7,10 +7,11 @@ typedef struct RenFont RenFont; typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting; +typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2 } ERenFontStyle; typedef struct { uint8_t b, g, r, a; } RenColor; typedef struct { int x, y, width, height; } RenRect; -RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting); +RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting, unsigned char style); RenFont* ren_font_copy(RenFont* font, float size); void ren_font_free(RenFont *font); void ren_font_set_tab_size(RenFont *font, int n); From 940db0f9c78ecb8f8b0b29196a742980b76ac253 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 16 Sep 2021 19:00:56 -0400 Subject: [PATCH 110/165] Added in underline as well. --- src/api/renderer.c | 6 ++++-- src/renderer.c | 6 ++++-- src/renderer.h | 2 +- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/api/renderer.c b/src/api/renderer.c index 3d89f041..be06bce0 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -21,7 +21,6 @@ static int f_font_load(lua_State *L) { } } } - lua_pop(L, 1); lua_getfield(L, 3, "hinting"); if (lua_isstring(L, -1)) { const char *hinting = lua_tostring(L, -1); @@ -43,7 +42,10 @@ static int f_font_load(lua_State *L) { lua_getfield(L, 3, "bold"); if (lua_toboolean(L, -1)) font_style |= FONT_STYLE_BOLD; - lua_pop(L, 1); + lua_getfield(L, 3, "underline"); + if (lua_toboolean(L, -1)) + font_style |= FONT_STYLE_UNDERLINE; + lua_pop(L, 5); } RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); *font = ren_font_load(filename, size, subpixel, font_hinting, font_style); diff --git a/src/renderer.c b/src/renderer.c index 435fdeb9..91cd82fc 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -237,9 +237,9 @@ int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor colo GlyphMetric* metric = &set->metrics[codepoint % 256]; int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; unsigned char* source_pixels = set->surface[bitmap_index]->pixels; - int start_x = pen_x + metric->bitmap_left, endX = metric->x1 - metric->x0 + pen_x; + int start_x = pen_x + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; int glyph_end = metric->x1, glyph_start = metric->x0; - if (color.a > 0 && endX >= clip.x && start_x <= clip_end_x) { + if (color.a > 0 && end_x >= clip.x && start_x <= clip_end_x) { for (int line = metric->y0; line < metric->y1; ++line) { int target_y = line + y - metric->y0 - metric->bitmap_top + font->size; if (target_y < clip.y) @@ -274,6 +274,8 @@ int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor colo } pen_x += metric->xadvance ? metric->xadvance : font->space_advance; } + if (font->style & FONT_STYLE_UNDERLINE) + ren_draw_rect((RenRect){ x, y + ren_font_get_height(font) - 1, pen_x - x, 1 }, color); return pen_x; } diff --git a/src/renderer.h b/src/renderer.h index f07f4411..b798c2b5 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -7,7 +7,7 @@ typedef struct RenFont RenFont; typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting; -typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2 } ERenFontStyle; +typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2, FONT_STYLE_UNDERLINE = 4 } ERenFontStyle; typedef struct { uint8_t b, g, r, a; } RenColor; typedef struct { int x, y, width, height; } RenRect; From c5fda5237f92435cb7492b93654ed30616a9058f Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 21 Sep 2021 21:49:32 -0400 Subject: [PATCH 111/165] Added in correcion calculations for surface_scale. --- src/renderer.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 91cd82fc..4b4dd58e 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -164,7 +164,9 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha FT_Face face; if (FT_New_Face( library, path, 0, &face)) return NULL; - if (FT_Set_Pixel_Sizes(face, 0, (int)size)) + + const int surface_scale = renwin_surface_scale(&window_renderer); + if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale))) goto failure; int len = strlen(path); RenFont* font = check_alloc(calloc(1, sizeof(RenFont) + len + 1)); @@ -212,7 +214,8 @@ int ren_font_get_width(RenFont *font, const char *text) { text = utf8_to_codepoint(text, &codepoint); width += font_get_glyphset(font, codepoint)->metrics[codepoint % 256].xadvance; } - return width; + const int surface_scale = renwin_surface_scale(&window_renderer); + return width / surface_scale; } float ren_font_get_size(RenFont *font) { @@ -225,7 +228,10 @@ int ren_font_get_height(RenFont *font) { int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) { SDL_Surface *surface = renwin_get_surface(&window_renderer); const RenRect clip = window_renderer.clip; - float pen_x = x; + + const int surface_scale = renwin_surface_scale(&window_renderer); + float pen_x = x * surface_scale; + y *= surface_scale; int bytes_per_pixel = surface->format->BytesPerPixel; const char* end = text + strlen(text); unsigned char* destination_pixels = surface->pixels; @@ -241,7 +247,7 @@ int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor colo int glyph_end = metric->x1, glyph_start = metric->x0; if (color.a > 0 && end_x >= clip.x && start_x <= clip_end_x) { for (int line = metric->y0; line < metric->y1; ++line) { - int target_y = line + y - metric->y0 - metric->bitmap_top + font->size; + int target_y = line + y - metric->y0 - metric->bitmap_top + font->size * surface_scale; if (target_y < clip.y) continue; if (target_y >= clip_end_y) @@ -275,8 +281,8 @@ int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor colo pen_x += metric->xadvance ? metric->xadvance : font->space_advance; } if (font->style & FONT_STYLE_UNDERLINE) - ren_draw_rect((RenRect){ x, y + ren_font_get_height(font) - 1, pen_x - x, 1 }, color); - return pen_x; + ren_draw_rect((RenRect){ x, y / surface_scale + ren_font_get_height(font) - 1, (pen_x - x) / surface_scale, 1 }, color); + return pen_x / surface_scale; } /******************* Rectangles **********************/ From 27fe185ed44b89a2c36b2036ec5f27da995e632f Mon Sep 17 00:00:00 2001 From: takase1121 <20792268+takase1121@users.noreply.github.com> Date: Sat, 25 Sep 2021 10:31:15 +0800 Subject: [PATCH 112/165] fix unable to load any native library something went wrong in snprintf that it skips the first character of the library name. Not only that, the signature is actually luaopen and not lua_open. --- src/api/system.c | 49 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/api/system.c b/src/api/system.c index 0cd64271..bcd1b997 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -695,39 +695,40 @@ static void* api_require(const char* symbol) { return NULL; } -static int f_load_native_plugin(lua_State* L) { - size_t sname, namelen, pathlen; - int results; - char olib[512]; olib[sizeof(olib)-1] = 0; - const char* name = luaL_checklstring(L, -2, &namelen); - const char* path = luaL_checklstring(L, -1, &pathlen); - void* library = SDL_LoadObject(path); - if (name == 0 || !library) +static int f_load_native_plugin(lua_State *L) { + char entrypoint_name[512]; entrypoint_name[sizeof(entrypoint_name) - 1] = '\0'; + int result; + + const char *name = luaL_checkstring(L, 1); + const char *path = luaL_checkstring(L, 2); + void *library = SDL_LoadObject(path); + if (!library) return luaL_error(L, "Unable to load %s: %s", name, SDL_GetError()); + lua_getglobal(L, "package"); lua_getfield(L, -1, "native_plugins"); lua_pushlightuserdata(L, library); lua_setfield(L, -2, name); lua_pop(L, 1); - for (sname = namelen - 1; sname > 0 && name[sname] != '.'; --sname); - snprintf(olib, sizeof(olib), "lua_open_lite_xl_%s", &name[sname+1]); - int (*ext_entrypoint)(lua_State* L, void*) = SDL_LoadFunction(library, olib); + + 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); if (!ext_entrypoint) { - snprintf(olib, sizeof(olib), "lua_open_%s", &name[sname+1]); - int (*entrypoint)(lua_State* L) = SDL_LoadFunction(library, olib); - if (!entrypoint) { - return luaL_error(L, "Unable to load %s: Can't find entrypoint. Requires a " - "function defined as int lua_open_lite_xl_%s(lua_State* L, void* XL)", - name, &name[sname+1]); - } - results = entrypoint(L); + snprintf(entrypoint_name, sizeof(entrypoint_name), "luaopen_%s", basename); + int (*entrypoint)(lua_State *L) = 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); } else { - results = ext_entrypoint(L, api_require); + result = ext_entrypoint(L, api_require); } - if (!results) - return luaL_error(L, "Unable to load %s: Your entrypoint must return at " - " least one value.", name); - return results; + + if (!result) + return luaL_error(L, "Unable to load %s: entrypoint must return a value", name); + + return result; } From d07d0e6d22bd03ed37624a93f2460d9c78ea3d25 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 24 Sep 2021 23:34:19 -0400 Subject: [PATCH 113/165] Made width return a floating point. --- src/renderer.c | 4 ++-- src/renderer.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 4b4dd58e..6d557966 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -206,8 +206,8 @@ int ren_font_get_tab_size(RenFont *font) { return font_get_glyphset(font, '\t')->metrics['\t'].xadvance / font->space_advance; } -int ren_font_get_width(RenFont *font, const char *text) { - int width = 0; +float ren_font_get_width(RenFont *font, const char *text) { + float width = 0; const char* end = text + strlen(text); while (text < end) { unsigned int codepoint; diff --git a/src/renderer.h b/src/renderer.h index b798c2b5..052a5c89 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -16,7 +16,7 @@ RenFont* ren_font_copy(RenFont* font, float size); void ren_font_free(RenFont *font); void ren_font_set_tab_size(RenFont *font, int n); int ren_font_get_tab_size(RenFont *font); -int ren_font_get_width(RenFont *font, const char *text); +float ren_font_get_width(RenFont *font, const char *text); int ren_font_get_height(RenFont *font); float ren_font_get_size(RenFont *font); int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color); From 806e4bc9702916a9c4e429d3a5a8e70148fb556f Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 25 Sep 2021 00:35:55 -0400 Subject: [PATCH 114/165] Converted all ints to floats for x coordinate purposes. --- src/api/renderer.c | 6 +++--- src/rencache.c | 7 ++++--- src/rencache.h | 16 ++++++++-------- src/renderer.c | 9 +++++---- src/renderer.h | 2 +- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/api/renderer.c b/src/api/renderer.c index be06bce0..a8475cbf 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -168,11 +168,11 @@ static int f_draw_rect(lua_State *L) { static int f_draw_text(lua_State *L) { RenFont** font = luaL_checkudata(L, 1, API_TYPE_FONT); const char *text = luaL_checkstring(L, 2); - int x_subpixel = luaL_checknumber(L, 3); + float x = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - x_subpixel = rencache_draw_text(L, *font, text, x_subpixel, y, color); - lua_pushnumber(L, x_subpixel); + x = rencache_draw_text(L, *font, text, x, y, color); + lua_pushnumber(L, x); return 1; } diff --git a/src/rencache.c b/src/rencache.c index 7bbb9df6..c886f362 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -127,9 +127,10 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } -int rencache_draw_text(lua_State *L, RenFont *font, const char *text, int x, int y, RenColor color) +float rencache_draw_text(lua_State *L, RenFont *font, const char *text, int x, int y, RenColor color) { - RenRect rect = { x, y, ren_font_get_width(font, text), ren_font_get_height(font) }; + float width = ren_font_get_width(font, text); + RenRect rect = { x, y, (int)width, ren_font_get_height(font) }; if (rects_overlap(screen_rect, rect)) { int sz = strlen(text) + 1; Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz); @@ -141,7 +142,7 @@ int rencache_draw_text(lua_State *L, RenFont *font, const char *text, int x, int cmd->tab_size = ren_font_get_tab_size(font); } } - return x + rect.width; + return x + width; } diff --git a/src/rencache.h b/src/rencache.h index 7a7f80f5..034d2001 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -5,13 +5,13 @@ #include #include "renderer.h" -void rencache_show_debug(bool enable); -void rencache_set_clip_rect(RenRect rect); -void rencache_draw_rect(RenRect rect, RenColor color); -int rencache_draw_text(lua_State *L, RenFont *font, - const char *text, int 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_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); +void rencache_invalidate(void); +void rencache_begin_frame(lua_State *L); +void rencache_end_frame(lua_State *L); #endif diff --git a/src/renderer.c b/src/renderer.c index 6d557966..e174981b 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -42,8 +42,8 @@ typedef struct { typedef struct RenFont { FT_Face face; GlyphSet* sets[MAX_GLYPHSET]; - float size; - short max_height, space_advance, tab_advance; + float size, space_advance, tab_advance; + short max_height; bool subpixel; ERenFontHinting hinting; unsigned char style; @@ -212,7 +212,8 @@ float ren_font_get_width(RenFont *font, const char *text) { while (text < end) { unsigned int codepoint; text = utf8_to_codepoint(text, &codepoint); - width += font_get_glyphset(font, codepoint)->metrics[codepoint % 256].xadvance; + float advance = font_get_glyphset(font, codepoint)->metrics[codepoint % 256].xadvance; + width += advance ? advance : font->space_advance; } const int surface_scale = renwin_surface_scale(&window_renderer); return width / surface_scale; @@ -225,7 +226,7 @@ int ren_font_get_height(RenFont *font) { return font->size + 3; } -int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) { +float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) { SDL_Surface *surface = renwin_get_surface(&window_renderer); const RenRect clip = window_renderer.clip; diff --git a/src/renderer.h b/src/renderer.h index 052a5c89..4eff61a5 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -19,7 +19,7 @@ int ren_font_get_tab_size(RenFont *font); float ren_font_get_width(RenFont *font, const char *text); int ren_font_get_height(RenFont *font); float ren_font_get_size(RenFont *font); -int ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color); +float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color); void ren_draw_rect(RenRect rect, RenColor color); From 42d72cc29652108e9c0f02fee7cd4e343f9446b8 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 25 Sep 2021 00:37:08 -0400 Subject: [PATCH 115/165] Missed a float. --- src/rencache.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rencache.c b/src/rencache.c index c886f362..fff6c584 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -127,7 +127,7 @@ void rencache_draw_rect(RenRect rect, RenColor color) { } } -float rencache_draw_text(lua_State *L, RenFont *font, const char *text, int x, int y, RenColor color) +float rencache_draw_text(lua_State *L, RenFont *font, const char *text, float x, int y, RenColor color) { float width = ren_font_get_width(font, text); RenRect rect = { x, y, (int)width, ren_font_get_height(font) }; From b17aa3b0686d113b3cd4f5765f26710e872c19c6 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 25 Sep 2021 00:45:19 -0400 Subject: [PATCH 116/165] Addressed issue where glyphs would continue to draw past their clip. --- src/renderer.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index e174981b..2b53e7ba 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -246,13 +246,15 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co unsigned char* source_pixels = set->surface[bitmap_index]->pixels; int start_x = pen_x + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; int glyph_end = metric->x1, glyph_start = metric->x0; - if (color.a > 0 && end_x >= clip.x && start_x <= clip_end_x) { + if (color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { for (int line = metric->y0; line < metric->y1; ++line) { int target_y = line + y - metric->y0 - metric->bitmap_top + font->size * surface_scale; if (target_y < clip.y) continue; if (target_y >= clip_end_y) break; + if (start_x + (glyph_end - glyph_start) >= clip_end_x) + glyph_end = glyph_start + (clip_end_x - start_x); 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[bitmap_index]->pitch + metric->x0 * (font->subpixel ? 3 : 1)]; for (int x = glyph_start; x < glyph_end; ++x) { From 8816131780e53e2e3b2ca1096e35d57eb9cf5281 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 25 Sep 2021 12:55:20 -0400 Subject: [PATCH 117/165] Added in a float for rencache. --- src/rencache.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/rencache.c b/src/rencache.c index fff6c584..ebb17da1 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -25,6 +25,7 @@ typedef struct { RenRect rect; RenColor color; RenFont *font; + float text_x; char text[0]; } Command; @@ -139,6 +140,7 @@ float rencache_draw_text(lua_State *L, RenFont *font, const char *text, float x, cmd->color = color; cmd->font = font; cmd->rect = rect; + cmd->text_x = x; cmd->tab_size = ren_font_get_tab_size(font); } } @@ -247,7 +249,7 @@ void rencache_end_frame(lua_State *L) { break; case DRAW_TEXT: ren_font_set_tab_size(cmd->font, cmd->tab_size); - ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); + ren_draw_text(cmd->font, cmd->text, cmd->text_x, cmd->rect.y, cmd->color); break; } } From ecbdb7a945cc392bfb0c9c7a04228e3eb729e5ab Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 25 Sep 2021 13:01:01 -0400 Subject: [PATCH 118/165] Reverted bledthrough changes. --- src/api/api.h | 2 -- src/api/regex.c | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/src/api/api.h b/src/api/api.h index e7bc57ea..2e9bdb2e 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -8,8 +8,6 @@ #define API_TYPE_FONT "Font" #define API_TYPE_PROCESS "Process" -#define API_CONSTANT_DEFINE(L, idx, key, n) (lua_pushnumber(L, n), lua_setfield(L, idx - 1, key)) - void api_load_libs(lua_State *L); #endif diff --git a/src/api/regex.c b/src/api/regex.c index e0f94056..1043b1c5 100644 --- a/src/api/regex.c +++ b/src/api/regex.c @@ -101,11 +101,17 @@ int luaopen_regex(lua_State *L) { lua_setfield(L, -2, "__name"); lua_pushvalue(L, -1); lua_setfield(L, LUA_REGISTRYINDEX, "regex"); - API_CONSTANT_DEFINE(L, -1, "ANCHORED", PCRE2_ANCHORED); - API_CONSTANT_DEFINE(L, -1, "ENDANCHORED", PCRE2_ENDANCHORED); - API_CONSTANT_DEFINE(L, -1, "NOTBOL", PCRE2_NOTBOL); - API_CONSTANT_DEFINE(L, -1, "NOTEOL", PCRE2_NOTEOL); - API_CONSTANT_DEFINE(L, -1, "NOTEMPTY", PCRE2_NOTEMPTY); - API_CONSTANT_DEFINE(L, -1, "NOTEMPTY_ATSTART", PCRE2_NOTEMPTY_ATSTART); + lua_pushnumber(L, PCRE2_ANCHORED); + lua_setfield(L, -2, "ANCHORED"); + lua_pushnumber(L, PCRE2_ANCHORED) ; + lua_setfield(L, -2, "ENDANCHORED"); + lua_pushnumber(L, PCRE2_NOTBOL); + lua_setfield(L, -2, "NOTBOL"); + lua_pushnumber(L, PCRE2_NOTEOL); + lua_setfield(L, -2, "NOTEOL"); + lua_pushnumber(L, PCRE2_NOTEMPTY); + lua_setfield(L, -2, "NOTEMPTY"); + lua_pushnumber(L, PCRE2_NOTEMPTY_ATSTART); + lua_setfield(L, -2, "NOTEMPTY_ATSTART"); return 1; } From 84622a0009deb57dec47ebedaada846ffb2bf7fe Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 26 Sep 2021 10:18:13 -0400 Subject: [PATCH 119/165] Potentially fixing issue with cache not invalidating on restart. (#548) --- src/main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main.c b/src/main.c index 6275be74..a7147e76 100644 --- a/src/main.c +++ b/src/main.c @@ -184,6 +184,7 @@ init_lua: lua_pcall(L, 0, 1, 0); if (lua_toboolean(L, -1)) { lua_close(L); + rencache_invalidate(); goto init_lua; } From 8f8af19cbe480cef85c2fdc24f06b4d4cc37077f Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 26 Sep 2021 10:21:57 -0400 Subject: [PATCH 120/165] Rearranged DPI calc so that on calc failure, returns 1. (#547) --- src/main.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main.c b/src/main.c index a7147e76..4eb1ec03 100644 --- a/src/main.c +++ b/src/main.c @@ -18,13 +18,12 @@ SDL_Window *window; static double get_scale(void) { -#ifdef __APPLE__ - return 1.0; -#else - float dpi = 96.0; - SDL_GetDisplayDPI(0, NULL, &dpi, NULL); - return dpi / 96.0; +#ifndef __APPLE__ + float dpi; + if (SDL_GetDisplayDPI(0, NULL, &dpi, NULL) == 0) + return dpi / 96.0; #endif + return 1.0; } From 4690459a13f09c8ed11d3d216c753d9a23a5fdeb Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 26 Sep 2021 19:46:32 -0400 Subject: [PATCH 121/165] Used different weights. --- src/renderer.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 2b53e7ba..5dc966f3 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -77,10 +77,11 @@ static int font_set_load_options(RenFont* font) { static int font_set_render_options(RenFont* font) { if (font->subpixel) { + unsigned char weights[] = { 0x10, 0x40, 0x70, 0x40, 0x10 } ; switch (font->hinting) { case FONT_HINTING_NONE: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE); break; - case FONT_HINTING_SLIGHT: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_LIGHT); break; - case FONT_HINTING_FULL: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_DEFAULT); break; + case FONT_HINTING_SLIGHT: + case FONT_HINTING_FULL: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_LIGHT); FT_Library_SetLcdFilterWeights(library, weights); break; } return FT_RENDER_MODE_LCD; } else { From 7a21ec382fb3a0b2d06a4814a3b62f85463553f4 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 26 Sep 2021 20:09:51 -0400 Subject: [PATCH 122/165] Unecessary call.. --- src/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index 5dc966f3..08552350 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -81,7 +81,7 @@ static int font_set_render_options(RenFont* font) { switch (font->hinting) { case FONT_HINTING_NONE: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE); break; case FONT_HINTING_SLIGHT: - case FONT_HINTING_FULL: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_LIGHT); FT_Library_SetLcdFilterWeights(library, weights); break; + case FONT_HINTING_FULL: FT_Library_SetLcdFilterWeights(library, weights); break; } return FT_RENDER_MODE_LCD; } else { From 86bf023c90ffcb5d806c190d2bb3de43839fa58e Mon Sep 17 00:00:00 2001 From: Not-a-web-Developer <47886897+Not-a-web-Developer@users.noreply.github.com> Date: Mon, 27 Sep 2021 19:29:25 +0530 Subject: [PATCH 123/165] fixed the build link in readme.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 73204c57..71f856a1 100644 --- a/README.md +++ b/README.md @@ -100,7 +100,7 @@ See the [licenses] file for details on licenses used by the required dependencie [screenshot-dark]: https://user-images.githubusercontent.com/433545/111063905-66943980-84b1-11eb-9040-3876f1133b20.png [lite]: https://github.com/rxi/lite [website]: https://lite-xl.github.io -[build]: https://lite-xl.github.io/en/build +[build]: https://lite-xl.github.io/en/documentation/build/ [Get Lite XL]: https://github.com/franko/lite-xl/releases/latest [Get plugins]: https://github.com/franko/lite-plugins [Get color themes]: https://github.com/rxi/lite-colors From eb73ad3f8aa8d31f458a0e63989cd533ec3522ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Andr=C3=A9=20Santoni?= Date: Thu, 30 Sep 2021 03:22:00 +0700 Subject: [PATCH 124/165] Fix the size and blurriness of the icon on OSX (#553) * Fix the size and blurriness of the icon on OSX * Don't nest ifndef * Fix --- resources/icons/icon.icns | Bin 37249 -> 44037 bytes src/main.c | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/icons/icon.icns b/resources/icons/icon.icns index 128045d82aaa1c8a01ec0df88e95b851ed9cb259..71fea32f04f3bbc83d1101c8c046eb7b9d3031dd 100644 GIT binary patch literal 44037 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 Twx>Yvu$Kq+OZs#1^?mZc#fzXH literal 37249 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 Date: Thu, 30 Sep 2021 19:38:32 +0800 Subject: [PATCH 125/165] Remove duplicate command declaration (#565) --- data/core/commands/doc.lua | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index fb17e674..b8ce2cb5 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -168,16 +168,6 @@ local commands = { doc():set_selection(line, col) end, - - ["doc:indent"] = function() - for idx, line1, col1, line2, col2 in doc_multiline_selections(true) do - local l1, c1, l2, c2 = doc():indent_text(false, line1, col1, line2, col2) - if l1 then - doc():set_selections(idx, l1, c1, l2, c2) - end - end - end, - ["doc:select-lines"] = function() for idx, line1, _, line2 in doc():get_selections(true) do append_line_if_last_line(line2) From ab0ca031fae17ca6c18a472558adb0944d089293 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Thu, 30 Sep 2021 22:10:38 +0200 Subject: [PATCH 126/165] Add option to disable scrolling past the end (#566) --- data/core/config.lua | 1 + data/core/docview.lua | 3 +++ 2 files changed, 4 insertions(+) diff --git a/data/core/config.lua b/data/core/config.lua index caecdfcd..f11c8959 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -5,6 +5,7 @@ config.fps = 60 config.max_log_items = 80 config.message_timeout = 5 config.mouse_wheel_scroll = 50 * SCALE +config.scroll_past_end = true config.file_size_limit = 10 config.ignore_files = "^%." config.symbol_pattern = "[%a_][%w_]*" diff --git a/data/core/docview.lua b/data/core/docview.lua index 161eac47..c26393a1 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -98,6 +98,9 @@ end function DocView:get_scrollable_size() + if not config.scroll_past_end then + return self:get_line_height() * (#self.doc.lines) + style.padding.y * 2 + end return self:get_line_height() * (#self.doc.lines - 1) + self.size.y end From 531cd3bedb43b2d5c545ab2d29de26f09bec17a1 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 1 Oct 2021 21:20:44 -0400 Subject: [PATCH 127/165] Fixed issue with metrics. --- src/renderer.c | 93 +++++++++++++++++++++++--------------------------- 1 file changed, 43 insertions(+), 50 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 08552350..8d0bc239 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -35,13 +35,13 @@ typedef struct { } GlyphMetric; typedef struct { - SDL_Surface* surface[SUBPIXEL_BITMAPS_CACHED]; + SDL_Surface* surface; GlyphMetric metrics[256]; } GlyphSet; typedef struct RenFont { FT_Face face; - GlyphSet* sets[MAX_GLYPHSET]; + GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_GLYPHSET]; float size, space_advance, tab_advance; short max_height; bool subpixel; @@ -105,29 +105,24 @@ static int font_set_style(FT_Outline* outline, int x_translation, unsigned char return 0; } -static GlyphSet* font_load_glyphset(RenFont* font, int idx) { - GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); +void font_load_glyphset(RenFont* font, int idx) { unsigned int render_option = font_set_render_options(font), load_option = font_set_load_options(font); - int pen_x = 0, bitmaps_cached = font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1; + int bitmaps_cached = font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1; unsigned int byte_width = font->subpixel ? 3 : 1; - for (int i = 0; i < 256; ++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 | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, 0, font->style) || FT_Render_Glyph(font->face->glyph, render_option)) - continue; - FT_GlyphSlot slot = font->face->glyph; - int glyph_width = slot->bitmap.width / byte_width; - set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; - pen_x += glyph_width; - font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; - // xadvance for some reason should be computed like this. - if (FT_Load_Glyph(font->face, glyph_index, FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&slot->outline, 0, font->style) || FT_Render_Glyph(font->face->glyph, render_option)) - continue; - set->metrics[i].xadvance = (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f; - - } - for (int j = 0; j < bitmaps_cached; ++j) { - set->surface[j] = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0)); - unsigned char* pixels = set->surface[j]->pixels; + for (int j = 0, pen_x = 0; j < bitmaps_cached; ++j) { + GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); + for (int i = 0; i < 256; ++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 | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) + continue; + FT_GlyphSlot slot = font->face->glyph; + int glyph_width = slot->bitmap.width / byte_width; + set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; + pen_x += glyph_width; + font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; + } + set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0)); + unsigned char* pixels = set->surface->pixels; for (int i = 0; i < 256; ++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)) @@ -137,28 +132,20 @@ static GlyphSet* font_load_glyphset(RenFont* font, int idx) { if (FT_Render_Glyph(slot, render_option)) continue; for (int line = 0; line < slot->bitmap.rows; ++line) { - int target_offset = set->surface[j]->pitch * line + set->metrics[i].x0 * byte_width; + int target_offset = set->surface->pitch * line + set->metrics[i].x0 * byte_width; int source_offset = line * slot->bitmap.pitch; memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width); } } + font->sets[j][idx] = set; } - return set; } -static void font_free_glyphset(GlyphSet* set) { - for (int i = 0; i < SUBPIXEL_BITMAPS_CACHED; ++i) { - if (set->surface[i]) - SDL_FreeSurface(set->surface[i]); - } - free(set); -} - -static GlyphSet* font_get_glyphset(RenFont* font, int codepoint) { +static GlyphSet* font_get_glyphset(RenFont* font, int codepoint, int subpixel_idx) { int idx = (codepoint >> 8) % MAX_GLYPHSET; - if (!font->sets[idx]) - font->sets[idx] = font_load_glyphset(font, idx); - return font->sets[idx]; + if (!font->sets[subpixel_idx][idx]) + font_load_glyphset(font, idx); + return font->sets[subpixel_idx][idx]; } RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting, unsigned char style) { @@ -177,7 +164,7 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha font->subpixel = subpixel; font->hinting = hinting; font->style = style; - GlyphSet* set = font_get_glyphset(font, ' '); + GlyphSet* set = font_get_glyphset(font, ' ', 0); font->space_advance = (int)set->metrics[' '].xadvance; font->tab_advance = font->space_advance * 2; return font; @@ -191,20 +178,26 @@ RenFont* ren_font_copy(RenFont* font, float size) { } void ren_font_free(RenFont* font) { - for (int i = 0; i < MAX_GLYPHSET; ++i) { - if (font->sets[i]) - font_free_glyphset(font->sets[i]); + for (int i = 0; i < SUBPIXEL_BITMAPS_CACHED; ++i) { + for (int j = 0; j < MAX_GLYPHSET; ++j) { + if (font->sets[i][j]) { + if (font->sets[i][j]->surface) + SDL_FreeSurface(font->sets[i][j]->surface); + free(font->sets[i][j]); + } + } } FT_Done_Face(font->face); free(font); } void ren_font_set_tab_size(RenFont *font, int n) { - font_get_glyphset(font, '\t')->metrics['\t'].xadvance = font->space_advance * n; + for (int i = 0; i < (font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1); ++i) + font_get_glyphset(font, '\t', i)->metrics['\t'].xadvance = font->space_advance * n; } int ren_font_get_tab_size(RenFont *font) { - return font_get_glyphset(font, '\t')->metrics['\t'].xadvance / font->space_advance; + return font_get_glyphset(font, '\t', 0)->metrics['\t'].xadvance / font->space_advance; } float ren_font_get_width(RenFont *font, const char *text) { @@ -213,7 +206,7 @@ float ren_font_get_width(RenFont *font, const char *text) { while (text < end) { unsigned int codepoint; text = utf8_to_codepoint(text, &codepoint); - float advance = font_get_glyphset(font, codepoint)->metrics[codepoint % 256].xadvance; + float advance = font_get_glyphset(font, codepoint, 0)->metrics[codepoint % 256].xadvance; width += advance ? advance : font->space_advance; } const int surface_scale = renwin_surface_scale(&window_renderer); @@ -241,11 +234,11 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co while (text < end) { unsigned int codepoint, r, g, b; text = utf8_to_codepoint(text, &codepoint); - GlyphSet* set = font_get_glyphset(font, codepoint); - GlyphMetric* metric = &set->metrics[codepoint % 256]; int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; - unsigned char* source_pixels = set->surface[bitmap_index]->pixels; - int start_x = pen_x + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; + GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index); + GlyphMetric* metric = &set->metrics[codepoint % 256]; + unsigned char* source_pixels = set->surface->pixels; + int start_x = floor(pen_x) + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; int glyph_end = metric->x1, glyph_start = metric->x0; if (color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { for (int line = metric->y0; line < metric->y1; ++line) { @@ -257,7 +250,7 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co if (start_x + (glyph_end - glyph_start) >= clip_end_x) glyph_end = glyph_start + (clip_end_x - start_x); 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[bitmap_index]->pitch + metric->x0 * (font->subpixel ? 3 : 1)]; + unsigned char* source_pixel = &source_pixels[line * set->surface->pitch + metric->x0 * (font->subpixel ? 3 : 1)]; for (int x = glyph_start; x < glyph_end; ++x) { unsigned int destination_color = *destination_pixel; SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; @@ -282,7 +275,7 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co } } } - pen_x += metric->xadvance ? metric->xadvance : font->space_advance; + pen_x += metric->xadvance ? metric->xadvance : font->space_advance; } if (font->style & FONT_STYLE_UNDERLINE) ren_draw_rect((RenRect){ x, y / surface_scale + ren_font_get_height(font) - 1, (pen_x - x) / surface_scale, 1 }, color); From 3b280401e433d7b8f7a679e58c44b618d68707a2 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sat, 2 Oct 2021 03:24:35 +0200 Subject: [PATCH 128/165] Small cleanup of `scale` plugin --- data/plugins/scale.lua | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/data/plugins/scale.lua b/data/plugins/scale.lua index acf3c7bb..37d4cac9 100644 --- a/data/plugins/scale.lua +++ b/data/plugins/scale.lua @@ -13,9 +13,6 @@ config.plugins.scale = { use_mousewheel = true } -local MINIMUM_SCALE = 0.25; - -local scale_level = 0 local scale_steps = 0.05 local current_scale = SCALE @@ -36,9 +33,6 @@ local function set_scale(scale) local s = scale / current_scale current_scale = scale - -- we set scale_level in case this was called by user - scale_level = (scale - default_scale) / scale_steps - if config.plugins.scale.mode == "ui" then SCALE = scale @@ -85,18 +79,15 @@ function RootView:on_mouse_wheel(d, ...) end local function res_scale() - scale_level = 0 set_scale(default_scale) end local function inc_scale() - scale_level = scale_level + 1 - set_scale(default_scale + scale_level * scale_steps) + set_scale(current_scale + scale_steps) end -local function dec_scale() - scale_level = scale_level - 1 - set_scale(math.max(default_scale + scale_level * scale_steps), MINIMUM_SCALE) +local function dec_scale() + set_scale(current_scale - scale_steps) end From c5f60a78651e2185d551abcea79a36ac1ba55b8d Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Fri, 1 Oct 2021 23:56:36 -0400 Subject: [PATCH 129/165] Fixed issues if one got into high codepoint ranges. --- src/renderer.c | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 8d0bc239..df04246f 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -111,6 +111,7 @@ void font_load_glyphset(RenFont* font, int idx) { unsigned int byte_width = font->subpixel ? 3 : 1; for (int j = 0, pen_x = 0; j < bitmaps_cached; ++j) { GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); + font->sets[j][idx] = set; for (int i = 0; i < 256; ++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 | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) @@ -121,6 +122,8 @@ void font_load_glyphset(RenFont* font, int idx) { pen_x += glyph_width; font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; } + if (pen_x == 0) + continue; set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0)); unsigned char* pixels = set->surface->pixels; for (int i = 0; i < 256; ++i) { @@ -137,11 +140,10 @@ void font_load_glyphset(RenFont* font, int idx) { memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width); } } - font->sets[j][idx] = set; } } -static GlyphSet* font_get_glyphset(RenFont* font, int codepoint, int subpixel_idx) { +static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) { int idx = (codepoint >> 8) % MAX_GLYPHSET; if (!font->sets[subpixel_idx][idx]) font_load_glyphset(font, idx); @@ -164,8 +166,7 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha font->subpixel = subpixel; font->hinting = hinting; font->style = style; - GlyphSet* set = font_get_glyphset(font, ' ', 0); - font->space_advance = (int)set->metrics[' '].xadvance; + font->space_advance = (int)font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance; font->tab_advance = font->space_advance * 2; return font; failure: @@ -206,8 +207,8 @@ float ren_font_get_width(RenFont *font, const char *text) { while (text < end) { unsigned int codepoint; text = utf8_to_codepoint(text, &codepoint); - float advance = font_get_glyphset(font, codepoint, 0)->metrics[codepoint % 256].xadvance; - width += advance ? advance : font->space_advance; + GlyphMetric* metric = &font_get_glyphset(font, codepoint, 0)->metrics[codepoint % 256]; + width += metric->xadvance ? metric->xadvance : font->space_advance; } const int surface_scale = renwin_surface_scale(&window_renderer); return width / surface_scale; @@ -237,10 +238,10 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index); GlyphMetric* metric = &set->metrics[codepoint % 256]; - unsigned char* source_pixels = set->surface->pixels; int start_x = floor(pen_x) + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; int glyph_end = metric->x1, glyph_start = metric->x0; - if (color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { + if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { + unsigned char* source_pixels = set->surface->pixels; for (int line = metric->y0; line < metric->y1; ++line) { int target_y = line + y - metric->y0 - metric->bitmap_top + font->size * surface_scale; if (target_y < clip.y) @@ -291,16 +292,6 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) { return dst; } - -#define rect_draw_loop(expr) \ - for (int j = y1; j < y2; j++) { \ - for (int i = x1; i < x2; i++) { \ - *d = expr; \ - d++; \ - } \ - d += dr; \ - } - void ren_draw_rect(RenRect rect, RenColor color) { if (color.a == 0) { return; } @@ -329,7 +320,11 @@ void ren_draw_rect(RenRect rect, RenColor color) { SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 }; SDL_FillRect(surface, &rect, SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a)); } else { - rect_draw_loop(blend_pixel(*d, color)); + for (int j = y1; j < y2; j++) { + for (int i = x1; i < x2; i++, d++) + *d = blend_pixel(*d, color); + d += dr; + } } } From b5f0b340d2d74db6556b6b3b9189f0840b667ff2 Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sat, 2 Oct 2021 16:38:10 +0200 Subject: [PATCH 130/165] Add option to disable caret blinking (#572) --- data/core/config.lua | 1 + data/core/docview.lua | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/data/core/config.lua b/data/core/config.lua index f11c8959..eb4dcdf6 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -24,6 +24,7 @@ config.max_project_files = 2000 config.transitions = true config.animation_rate = 1.0 config.blink_period = 0.8 +config.disable_blink = false config.draw_whitespace = false config.borderless = false config.tab_close_button = true diff --git a/data/core/docview.lua b/data/core/docview.lua index c26393a1..9a2972dc 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -411,10 +411,12 @@ function DocView:draw_overlay() local T = config.blink_period for _, line, col in self.doc:get_selections() do if line >= minline and line <= maxline - and (core.blink_timer - core.blink_start) % T < T / 2 and system.window_has_focus() then - local x, y = self:get_line_screen_position(line) - self:draw_caret(x + self:get_col_x_offset(line, col), y) + if config.disable_blink + or (core.blink_timer - core.blink_start) % T < T / 2 then + local x, y = self:get_line_screen_position(line) + self:draw_caret(x + self:get_col_x_offset(line, col), y) + end end end end From bf06aa1c4df906d88f162505a329c645f9b1ea5d Mon Sep 17 00:00:00 2001 From: Guldoman Date: Sat, 2 Oct 2021 16:38:45 +0200 Subject: [PATCH 131/165] Load project module on project change (#571) --- data/core/init.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/data/core/init.lua b/data/core/init.lua index af291767..7ab4629d 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -79,6 +79,9 @@ function core.open_folder_project(dir_path_abs) if core.set_project_dir(dir_path_abs, core.on_quit_project) then core.root_view:close_all_docviews() update_recents_project("add", dir_path_abs) + if not core.load_project_module() then + command.perform("core:open-log") + end core.on_enter_project(dir_path_abs) end end From ee61b3408423dd85650458147ea3a481a36e4cb0 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 2 Oct 2021 12:23:32 -0400 Subject: [PATCH 132/165] Turned off whitespace by default. --- data/core/config.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/data/core/config.lua b/data/core/config.lua index caecdfcd..53ad4ec2 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -33,5 +33,6 @@ config.plugins = {} config.plugins.trimwhitespace = false config.plugins.lineguide = false +config.plugins.drawwhitespace = false return config From f80f920bdabd6cfdf3d7935e563ed8a3f7cf43ad Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 2 Oct 2021 12:25:52 -0400 Subject: [PATCH 133/165] Added in suggested changes. --- data/core/style.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/data/core/style.lua b/data/core/style.lua index 9a6efb50..3b0d9e35 100644 --- a/data/core/style.lua +++ b/data/core/style.lua @@ -21,11 +21,11 @@ style.tab_width = common.round(170 * SCALE) -- -- On High DPI monitor or non RGB monitor you may consider using antialiasing grayscale instead. -- The antialiasing grayscale with full hinting is interesting for crisp font rendering. -style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 14 * SCALE) -style.big_font = style.font:copy(40 * SCALE) +style.font = renderer.font.load(DATADIR .. "/fonts/FiraSans-Regular.ttf", 15 * SCALE) +style.big_font = style.font:copy(46 * SCALE) style.icon_font = renderer.font.load(DATADIR .. "/fonts/icons.ttf", 16 * SCALE, {antialiasing="grayscale", hinting="full"}) -style.icon_big_font = style.icon_font:copy(24 * SCALE) -style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 14 * SCALE) +style.icon_big_font = style.icon_font:copy(23 * SCALE) +style.code_font = renderer.font.load(DATADIR .. "/fonts/JetBrainsMono-Regular.ttf", 15 * SCALE) style.background = { common.color "#2e2e32" } style.background2 = { common.color "#252529" } From 26ec2d70902f576c1da94e59e828847ba86ef231 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 2 Oct 2021 14:13:39 -0400 Subject: [PATCH 134/165] Fixed negative pens. --- src/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index df04246f..4768606c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -236,7 +236,7 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co unsigned int codepoint, r, g, b; text = utf8_to_codepoint(text, &codepoint); int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; - GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index); + GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); GlyphMetric* metric = &set->metrics[codepoint % 256]; int start_x = floor(pen_x) + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; int glyph_end = metric->x1, glyph_start = metric->x0; From 8477818c96a4de092353488ca005c263c581138d Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 7 Oct 2021 19:03:16 +0200 Subject: [PATCH 135/165] Fix error in incremental syntax highlighter In the highlither thread We should accept a previously generated line tokenization past first_invalid_line only if the text is the same. The text can change because of insert or remove operations. Close #573. --- data/core/doc/highlighter.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/doc/highlighter.lua b/data/core/doc/highlighter.lua index e7650d01..72fba793 100644 --- a/data/core/doc/highlighter.lua +++ b/data/core/doc/highlighter.lua @@ -24,7 +24,7 @@ function Highlighter:new(doc) for i = self.first_invalid_line, max do local state = (i > 1) and self.lines[i - 1].state local line = self.lines[i] - if not (line and line.init_state == state) then + if not (line and line.init_state == state and line.text == self.doc.lines[i]) then self.lines[i] = self:tokenize_line(i, state) end end From 44d7f3738f618d3f8e0d47ac5fead147f4516d83 Mon Sep 17 00:00:00 2001 From: Francesco Abbate Date: Thu, 7 Oct 2021 19:19:03 +0200 Subject: [PATCH 136/165] Improve highlither for document edits The syntax highlighter keep a cache of the documents like tokenization. In order to minimize the amount of tokenize re-computations we insert some emtty lines or remove some lines in the highlither lines corresponding to the lines added or removed to the document. --- data/core/doc/highlighter.lua | 15 ++++++++++++++- data/core/doc/init.lua | 4 ++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/data/core/doc/highlighter.lua b/data/core/doc/highlighter.lua index 72fba793..4cb703da 100644 --- a/data/core/doc/highlighter.lua +++ b/data/core/doc/highlighter.lua @@ -44,12 +44,25 @@ function Highlighter:reset() self.max_wanted_line = 0 end - function Highlighter:invalidate(idx) self.first_invalid_line = math.min(self.first_invalid_line, idx) self.max_wanted_line = math.min(self.max_wanted_line, #self.doc.lines) end +function Highlighter:insert_notify(line, n) + self:invalidate(line) + for i = 1, n do + table.insert(self.lines, line, nil) + end +end + +function Highlighter:remove_notify(line, n) + self:invalidate(line) + for i = 1, n do + table.remove(self.lines, line) + end +end + function Highlighter:tokenize_line(idx, state) local res = {} diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index aff31e94..d53a5c96 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -348,7 +348,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time) push_undo(undo_stack, time, "remove", line, col, line2, col2) -- update highlighter and assure selection is in bounds - self.highlighter:invalidate(line) + self.highlighter:insert_notify(line, #lines - 1) self:sanitize_selection() end @@ -375,7 +375,7 @@ function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) end -- update highlighter and assure selection is in bounds - self.highlighter:invalidate(line1) + self.highlighter:remove_notify(line1, line2 - line1) self:sanitize_selection() end From fe787de97aac342dfc1a1d7d14b98856ee42be88 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Thu, 7 Oct 2021 18:54:23 -0400 Subject: [PATCH 137/165] Fixed clip boundaries. --- src/renderer.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index 4768606c..629c2cff 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -238,7 +238,8 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); GlyphMetric* metric = &set->metrics[codepoint % 256]; - int start_x = floor(pen_x) + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x; + 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 (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) { unsigned char* source_pixels = set->surface->pixels; From d2e16ce0b57a18cb1fc9592cf79ede1e54f6be80 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Mon, 11 Oct 2021 22:29:53 -0400 Subject: [PATCH 138/165] Fixed clip issues if glyph exists before clip.x --- src/renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer.c b/src/renderer.c index 629c2cff..15dd4f1c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -256,6 +256,8 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co for (int x = glyph_start; x < glyph_end; ++x) { unsigned int destination_color = *destination_pixel; SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; + if (start_x + (x - glyph_start) < clip.x) + continue; if (font->subpixel) { SDL_Color src = { *source_pixel++, *source_pixel++, *source_pixel++ }; r = color.r * src.r + dst.r * (255 - src.r) + 127; From d3fa64ce5947f13688bb07257008bfc847da5b0b Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Tue, 12 Oct 2021 09:06:37 +0200 Subject: [PATCH 139/165] rely on /proc/self --- src/main.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.c b/src/main.c index 182511d0..99aa580f 100644 --- a/src/main.c +++ b/src/main.c @@ -32,8 +32,7 @@ static void get_exe_filename(char *buf, int sz) { int len = GetModuleFileName(NULL, buf, sz - 1); buf[len] = '\0'; #elif __linux__ - char path[512]; - sprintf(path, "/proc/%d/exe", getpid()); + char path[] = "/proc/self/exe"; int len = readlink(path, buf, sz - 1); buf[len] = '\0'; #elif __APPLE__ From 7c1ff0f3d872327c21874c850707a3034fddf22b Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 12 Oct 2021 21:22:02 -0400 Subject: [PATCH 140/165] Fixed writing before clip. --- src/renderer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 15dd4f1c..40fde76a 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -251,13 +251,16 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co break; if (start_x + (glyph_end - glyph_start) >= clip_end_x) glyph_end = glyph_start + (clip_end_x - start_x); + else if (start_x < clip.x) { + int offset = clip.x - start_x; + 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 + metric->x0 * (font->subpixel ? 3 : 1)]; + unsigned char* source_pixel = &source_pixels[line * set->surface->pitch + glyph_start * (font->subpixel ? 3 : 1)]; for (int x = glyph_start; x < glyph_end; ++x) { unsigned int destination_color = *destination_pixel; SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; - if (start_x + (x - glyph_start) < clip.x) - continue; if (font->subpixel) { SDL_Color src = { *source_pixel++, *source_pixel++, *source_pixel++ }; r = color.r * src.r + dst.r * (255 - src.r) + 127; From f2488fdd8dcc20cc1cfdc1ec7a36e01c127fba26 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Tue, 12 Oct 2021 23:24:52 -0400 Subject: [PATCH 141/165] Added in support for font groupings. --- data/core/statusview.lua | 3 +- src/api/renderer.c | 69 ++++++++++++++++++++++++++++++---------- src/rencache.c | 16 +++++----- src/rencache.h | 2 +- src/renderer.c | 66 +++++++++++++++++++++++++------------- src/renderer.h | 13 ++++---- 6 files changed, 115 insertions(+), 54 deletions(-) diff --git a/data/core/statusview.lua b/data/core/statusview.lua index 58421c31..3342bdb9 100644 --- a/data/core/statusview.lua +++ b/data/core/statusview.lua @@ -6,6 +6,7 @@ local style = require "core.style" local DocView = require "core.docview" local LogView = require "core.logview" local View = require "core.view" +local Object = require "core.object" local StatusView = View:extend() @@ -70,7 +71,7 @@ local function draw_items(self, items, x, y, draw_fn) local color = style.text for _, item in ipairs(items) do - if type(item) == "userdata" then + if Object.is(item, renderer.font) then font = item elseif type(item) == "table" then color = item diff --git a/src/api/renderer.c b/src/api/renderer.c index a8475cbf..e1f4f9bb 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -55,21 +55,55 @@ static int f_font_load(lua_State *L) { return 1; } +static bool font_retrieve(lua_State* L, RenFont** fonts, int idx) { + memset(fonts, 0, sizeof(fonts)*FONT_FALLBACK_MAX); + if (lua_type(L, 1) == LUA_TTABLE) { + for (int i = 0; i < FONT_FALLBACK_MAX; ++i) { + lua_rawgeti(L, 1, i+1); + bool nil = lua_isnil(L, -1); + if (!nil) + fonts[i] = *(RenFont**)luaL_checkudata(L, -1, API_TYPE_FONT); + lua_pop(L, 1); + if (nil) + break; + } + return true; + } else { + fonts[0] = *(RenFont**)luaL_checkudata(L, 1, API_TYPE_FONT); + return false; + } +} + static int f_font_copy(lua_State *L) { - RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); - float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_get_height(*self); - RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); - *font = ren_font_copy(*self, size); - if (!*font) - return luaL_error(L, "failed to copy font"); + RenFont* fonts[FONT_FALLBACK_MAX]; + float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_group_get_height(fonts); + bool table = font_retrieve(L, fonts, 1); + if (table) { + lua_newtable(L); + luaL_setmetatable(L, API_TYPE_FONT); + } + for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { + RenFont** font = lua_newuserdata(L, sizeof(RenFont*)); + *font = ren_font_copy(fonts[i], size); + if (!*font) + return luaL_error(L, "failed to copy font"); + luaL_setmetatable(L, API_TYPE_FONT); + if (table) + lua_rawseti(L, -2, i+1); + } + return 1; +} + +static int f_font_group(lua_State* L) { + luaL_checktype(L, 1, LUA_TTABLE); luaL_setmetatable(L, API_TYPE_FONT); return 1; } static int f_font_set_tab_size(lua_State *L) { - RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); + RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); int n = luaL_checknumber(L, 2); - ren_font_set_tab_size(*self, n); + ren_font_group_set_tab_size(fonts, n); return 0; } @@ -79,21 +113,22 @@ static int f_font_gc(lua_State *L) { return 0; } + static int f_font_get_width(lua_State *L) { - RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_font_get_width(*self, luaL_checkstring(L, 2))); + RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); + lua_pushnumber(L, ren_font_group_get_width(fonts, luaL_checkstring(L, 2))); return 1; } static int f_font_get_height(lua_State *L) { - RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_font_get_height(*self)); + RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); + lua_pushnumber(L, ren_font_group_get_height(fonts)); return 1; } static int f_font_get_size(lua_State *L) { - RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT); - lua_pushnumber(L, ren_font_get_size(*self)); + RenFont* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); + lua_pushnumber(L, ren_font_group_get_size(fonts)); return 1; } @@ -166,12 +201,13 @@ static int f_draw_rect(lua_State *L) { } static int f_draw_text(lua_State *L) { - RenFont** font = luaL_checkudata(L, 1, API_TYPE_FONT); + RenFont* fonts[FONT_FALLBACK_MAX]; + font_retrieve(L, fonts, 1); const char *text = luaL_checkstring(L, 2); float x = luaL_checknumber(L, 3); int y = luaL_checknumber(L, 4); RenColor color = checkcolor(L, 5, 255); - x = rencache_draw_text(L, *font, text, x, y, color); + x = rencache_draw_text(L, fonts, text, x, y, color); lua_pushnumber(L, x); return 1; } @@ -191,6 +227,7 @@ static const luaL_Reg fontLib[] = { { "__gc", f_font_gc }, { "load", f_font_load }, { "copy", f_font_copy }, + { "group", f_font_group }, { "set_tab_size", f_font_set_tab_size }, { "get_width", f_font_get_width }, { "get_height", f_font_get_height }, diff --git a/src/rencache.c b/src/rencache.c index ebb17da1..4997687c 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -24,7 +24,7 @@ typedef struct { int32_t size; RenRect rect; RenColor color; - RenFont *font; + RenFont *fonts[FONT_FALLBACK_MAX]; float text_x; char text[0]; } Command; @@ -128,20 +128,20 @@ 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(lua_State *L, RenFont **fonts, const char *text, float x, int y, RenColor color) { - float width = ren_font_get_width(font, text); - RenRect rect = { x, y, (int)width, ren_font_get_height(font) }; + float width = ren_font_group_get_width(fonts, text); + RenRect rect = { x, y, (int)width, ren_font_group_get_height(fonts) }; if (rects_overlap(screen_rect, rect)) { int sz = strlen(text) + 1; Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz); if (cmd) { memcpy(cmd->text, text, sz); cmd->color = color; - cmd->font = font; + memcpy(cmd->fonts, fonts, sizeof(RenFont*)*FONT_FALLBACK_MAX); cmd->rect = rect; cmd->text_x = x; - cmd->tab_size = ren_font_get_tab_size(font); + cmd->tab_size = ren_font_group_get_tab_size(fonts); } } return x + width; @@ -248,8 +248,8 @@ void rencache_end_frame(lua_State *L) { ren_draw_rect(cmd->rect, cmd->color); break; case DRAW_TEXT: - ren_font_set_tab_size(cmd->font, cmd->tab_size); - ren_draw_text(cmd->font, cmd->text, cmd->text_x, cmd->rect.y, cmd->color); + ren_font_group_set_tab_size(cmd->fonts, cmd->tab_size); + ren_draw_text(cmd->fonts, cmd->text, cmd->text_x, cmd->rect.y, cmd->color); break; } } diff --git a/src/rencache.h b/src/rencache.h index 034d2001..75bb5051 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -8,7 +8,7 @@ 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, +float rencache_draw_text(lua_State *L, RenFont **font, const char *text, float x, int y, RenColor color); void rencache_invalidate(void); void rencache_begin_frame(lua_State *L); diff --git a/src/renderer.c b/src/renderer.c index 40fde76a..c406510d 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -29,7 +29,7 @@ static void* check_alloc(void *ptr) { /************************* Fonts *************************/ typedef struct { - unsigned short x0, x1, y0, y1; + unsigned short x0, x1, y0, y1, loaded; short bitmap_left, bitmap_top; float xadvance; } GlyphMetric; @@ -113,12 +113,12 @@ void font_load_glyphset(RenFont* font, int idx) { GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); font->sets[j][idx] = set; for (int i = 0; i < 256; ++i) { - int glyph_index = FT_Get_Char_Index( font->face, i + idx * MAX_GLYPHSET); + 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 | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) continue; FT_GlyphSlot slot = font->face->glyph; int glyph_width = slot->bitmap.width / byte_width; - set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; + set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, true, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f}; pen_x += glyph_width; font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height; } @@ -192,39 +192,49 @@ void ren_font_free(RenFont* font) { free(font); } -void ren_font_set_tab_size(RenFont *font, int n) { - for (int i = 0; i < (font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1); ++i) - font_get_glyphset(font, '\t', i)->metrics['\t'].xadvance = font->space_advance * n; +void ren_font_group_set_tab_size(RenFont **fonts, int n) { + for (int j = 0; j < FONT_FALLBACK_MAX && fonts[j]; ++j) { + for (int i = 0; i < (fonts[j]->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1); ++i) + font_get_glyphset(fonts[j], '\t', i)->metrics['\t'].xadvance = fonts[j]->space_advance * n; + } } -int ren_font_get_tab_size(RenFont *font) { - return font_get_glyphset(font, '\t', 0)->metrics['\t'].xadvance / font->space_advance; +int ren_font_group_get_tab_size(RenFont **fonts) { + return font_get_glyphset(fonts[0], '\t', 0)->metrics['\t'].xadvance / fonts[0]->space_advance; } -float ren_font_get_width(RenFont *font, const char *text) { +float ren_font_group_get_width(RenFont **fonts, const char *text) { float width = 0; const char* end = text + strlen(text); + GlyphMetric* metric; while (text < end) { unsigned int codepoint; text = utf8_to_codepoint(text, &codepoint); - GlyphMetric* metric = &font_get_glyphset(font, codepoint, 0)->metrics[codepoint % 256]; - width += metric->xadvance ? metric->xadvance : font->space_advance; + for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { + metric = &font_get_glyphset(fonts[i], codepoint, 0)->metrics[codepoint % 256]; + if (metric->loaded || codepoint < 0xFF) + break; + } + if (!metric->loaded && codepoint > 0xFF) + metric = &font_get_glyphset(fonts[0], 0x25A1, 0)->metrics[0x25A1 % 256]; + width += metric->xadvance ? metric->xadvance : fonts[0]->space_advance; } const int surface_scale = renwin_surface_scale(&window_renderer); return width / surface_scale; } -float ren_font_get_size(RenFont *font) { - return font->size; +float ren_font_group_get_size(RenFont **fonts) { + return fonts[0]->size; } -int ren_font_get_height(RenFont *font) { - return font->size + 3; +int ren_font_group_get_height(RenFont **fonts) { + return fonts[0]->size + 3; } -float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) { +float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor color) { SDL_Surface *surface = renwin_get_surface(&window_renderer); const RenRect clip = window_renderer.clip; + RenFont* font; const int surface_scale = renwin_surface_scale(&window_renderer); float pen_x = x * surface_scale; y *= surface_scale; @@ -235,9 +245,21 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co while (text < end) { unsigned int codepoint, r, g, b; text = utf8_to_codepoint(text, &codepoint); - int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; - GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); - GlyphMetric* metric = &set->metrics[codepoint % 256]; + GlyphSet* set; GlyphMetric* metric; + for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { + font = fonts[i]; + int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; + set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); + metric = &set->metrics[codepoint % 256]; + if (metric->loaded || codepoint < 0xFF) + break; + } + if (!metric->loaded && codepoint > 0xFF) { + codepoint = 0x25A1; font = fonts[0]; + int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; + set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); + metric = &set->metrics[codepoint % 256]; + } 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; @@ -282,10 +304,10 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co } } } - pen_x += metric->xadvance ? metric->xadvance : font->space_advance; + pen_x += metric->xadvance ? metric->xadvance : font->space_advance; } - if (font->style & FONT_STYLE_UNDERLINE) - ren_draw_rect((RenRect){ x, y / surface_scale + ren_font_get_height(font) - 1, (pen_x - x) / surface_scale, 1 }, color); + if (fonts[0]->style & FONT_STYLE_UNDERLINE) + ren_draw_rect((RenRect){ x, y / surface_scale + ren_font_group_get_height(fonts) - 1, (pen_x - x) / surface_scale, 1 }, color); return pen_x / surface_scale; } diff --git a/src/renderer.h b/src/renderer.h index 4eff61a5..6058583e 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -5,6 +5,7 @@ #include #include +#define FONT_FALLBACK_MAX 4 typedef struct RenFont RenFont; typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting; typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2, FONT_STYLE_UNDERLINE = 4 } ERenFontStyle; @@ -14,12 +15,12 @@ typedef struct { int x, y, width, height; } RenRect; RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting, unsigned char style); RenFont* ren_font_copy(RenFont* font, float size); void ren_font_free(RenFont *font); -void ren_font_set_tab_size(RenFont *font, int n); -int ren_font_get_tab_size(RenFont *font); -float ren_font_get_width(RenFont *font, const char *text); -int ren_font_get_height(RenFont *font); -float ren_font_get_size(RenFont *font); -float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color); +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_tab_size(RenFont **font, int n); +float ren_font_group_get_width(RenFont **font, const char *text); +float ren_draw_text(RenFont **font, const char *text, float x, int y, RenColor color); void ren_draw_rect(RenRect rect, RenColor color); From 3092dca91912b48252b2b595bf326a5e17243616 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 16 Oct 2021 22:59:41 -0400 Subject: [PATCH 142/165] Changed computation to take into account alpha blending. --- src/renderer.c | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 40fde76a..3b4bef7c 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -10,8 +10,6 @@ #include "renderer.h" #include "renwindow.h" -#define DIVIDE_BY_255_SIGNED(x, sign_val) (((x) + (sign_val) + ((x)>>8)) >> 8) -#define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1) #define MAX_GLYPHSET 256 #define SUBPIXEL_BITMAPS_CACHED 3 @@ -154,7 +152,6 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha FT_Face face; if (FT_New_Face( library, path, 0, &face)) return NULL; - const int surface_scale = renwin_surface_scale(&window_renderer); if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale))) goto failure; @@ -261,23 +258,10 @@ float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor co for (int x = glyph_start; x < glyph_end; ++x) { unsigned int destination_color = *destination_pixel; SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; - if (font->subpixel) { - SDL_Color src = { *source_pixel++, *source_pixel++, *source_pixel++ }; - r = color.r * src.r + dst.r * (255 - src.r) + 127; - r = DIVIDE_BY_255(r); - g = color.g * src.g + dst.g * (255 - src.g) + 127; - g = DIVIDE_BY_255(g); - b = color.b * src.b + dst.b * (255 - src.b) + 127; - b = DIVIDE_BY_255(b); - } else { - unsigned char intensity = *source_pixel++; - r = color.r * intensity + dst.r * (255 - intensity) + 127; - r = DIVIDE_BY_255(r); - g = color.g * intensity + dst.g * (255 - intensity) + 127; - g = DIVIDE_BY_255(g); - b = color.b * intensity + dst.b * (255 - intensity) + 127; - b = DIVIDE_BY_255(b); - } + SDL_Color src = { *(font->subpixel ? source_pixel++ : source_pixel), *(font->subpixel ? source_pixel++ : source_pixel), *source_pixel++ }; + 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; *destination_pixel++ = dst.a << 24 | r << 16 | g << 8 | b; } } From 7575d2eee68a43301e84ca158c30c48b5cb9d21b Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 16 Oct 2021 23:32:17 -0400 Subject: [PATCH 143/165] Fixed minor issue. --- src/api/renderer.c | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/api/renderer.c b/src/api/renderer.c index e1f4f9bb..60256118 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -56,28 +56,24 @@ static int f_font_load(lua_State *L) { } static bool font_retrieve(lua_State* L, RenFont** fonts, int idx) { - memset(fonts, 0, sizeof(fonts)*FONT_FALLBACK_MAX); - if (lua_type(L, 1) == LUA_TTABLE) { - for (int i = 0; i < FONT_FALLBACK_MAX; ++i) { - lua_rawgeti(L, 1, i+1); - bool nil = lua_isnil(L, -1); - if (!nil) - fonts[i] = *(RenFont**)luaL_checkudata(L, -1, API_TYPE_FONT); - lua_pop(L, 1); - if (nil) - break; - } - return true; - } else { - fonts[0] = *(RenFont**)luaL_checkudata(L, 1, API_TYPE_FONT); + memset(fonts, 0, sizeof(RenFont*)*FONT_FALLBACK_MAX); + if (lua_type(L, 1) != LUA_TTABLE) { + fonts[0] = *(RenFont**)luaL_checkudata(L, idx, API_TYPE_FONT); return false; } + int i = 0; + do { + lua_rawgeti(L, idx, i+1); + fonts[i] = !lua_isnil(L, -1) ? *(RenFont**)luaL_checkudata(L, -1, API_TYPE_FONT) : NULL; + lua_pop(L, 1); + } while (fonts[i] && i++ < FONT_FALLBACK_MAX); + return true; } static int f_font_copy(lua_State *L) { RenFont* fonts[FONT_FALLBACK_MAX]; - float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_group_get_height(fonts); bool table = font_retrieve(L, fonts, 1); + float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_group_get_height(fonts); if (table) { lua_newtable(L); luaL_setmetatable(L, API_TYPE_FONT); From d1fcdacacd137f461795e1c318e22efe55f5ecfd Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sat, 16 Oct 2021 23:49:42 -0400 Subject: [PATCH 144/165] Broke out font groupings. --- src/renderer.c | 70 ++++++++++++++++++++++++-------------------------- 1 file changed, 34 insertions(+), 36 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index c406510d..fb955b3e 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -145,9 +145,9 @@ void font_load_glyphset(RenFont* font, int idx) { static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) { int idx = (codepoint >> 8) % MAX_GLYPHSET; - if (!font->sets[subpixel_idx][idx]) + if (!font->sets[font->subpixel ? subpixel_idx : 0][idx]) font_load_glyphset(font, idx); - return font->sets[subpixel_idx][idx]; + return font->sets[font->subpixel ? subpixel_idx : 0][idx]; } RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting, unsigned char style) { @@ -203,26 +203,6 @@ int ren_font_group_get_tab_size(RenFont **fonts) { return font_get_glyphset(fonts[0], '\t', 0)->metrics['\t'].xadvance / fonts[0]->space_advance; } -float ren_font_group_get_width(RenFont **fonts, const char *text) { - float width = 0; - const char* end = text + strlen(text); - GlyphMetric* metric; - while (text < end) { - unsigned int codepoint; - text = utf8_to_codepoint(text, &codepoint); - for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { - metric = &font_get_glyphset(fonts[i], codepoint, 0)->metrics[codepoint % 256]; - if (metric->loaded || codepoint < 0xFF) - break; - } - if (!metric->loaded && codepoint > 0xFF) - metric = &font_get_glyphset(fonts[0], 0x25A1, 0)->metrics[0x25A1 % 256]; - width += metric->xadvance ? metric->xadvance : fonts[0]->space_advance; - } - const int surface_scale = renwin_surface_scale(&window_renderer); - return width / surface_scale; -} - float ren_font_group_get_size(RenFont **fonts) { return fonts[0]->size; } @@ -230,6 +210,35 @@ int ren_font_group_get_height(RenFont **fonts) { return fonts[0]->size + 3; } +static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFont** fonts, unsigned int codepoint, int bitmap_index) { + for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { + *set = font_get_glyphset(fonts[i], codepoint, bitmap_index); + *metric = &(*set)->metrics[codepoint % 256]; + if ((*metric)->loaded || codepoint < 0xFF) + return fonts[i]; + } + if (!(*metric)->loaded && codepoint > 0xFF) { + codepoint = 0x25A1; + *set = font_get_glyphset(fonts[0], codepoint, bitmap_index); + *metric = &(*set)->metrics[codepoint % 256]; + } + return fonts[0]; +} + +float ren_font_group_get_width(RenFont **fonts, const char *text) { + float width = 0; + const char* end = text + strlen(text); + GlyphMetric* metric; GlyphSet* set; + while (text < end) { + unsigned int codepoint; + text = utf8_to_codepoint(text, &codepoint); + font_group_get_glyph(&set, &metric, fonts, codepoint, 0); + width += metric->xadvance ? metric->xadvance : fonts[0]->space_advance; + } + const int surface_scale = renwin_surface_scale(&window_renderer); + return width / surface_scale; +} + float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor color) { SDL_Surface *surface = renwin_get_surface(&window_renderer); const RenRect clip = window_renderer.clip; @@ -246,20 +255,9 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor unsigned int codepoint, r, g, b; text = utf8_to_codepoint(text, &codepoint); GlyphSet* set; GlyphMetric* metric; - for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { - font = fonts[i]; - int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; - set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); - metric = &set->metrics[codepoint % 256]; - if (metric->loaded || codepoint < 0xFF) - break; - } - if (!metric->loaded && codepoint > 0xFF) { - codepoint = 0x25A1; font = fonts[0]; - int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0; - set = font_get_glyphset(font, codepoint, bitmap_index + (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0)); - metric = &set->metrics[codepoint % 256]; - } + int bitmap_index = (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED); + bitmap_index += (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0); + font = font_group_get_glyph(&set, &metric, fonts, codepoint, bitmap_index); 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; From c7c4a3c5286826785de5ea483c1f16802ea79875 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 17 Oct 2021 00:10:40 -0400 Subject: [PATCH 145/165] Clarified. --- src/renderer.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index fb955b3e..a70111fa 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -211,6 +211,8 @@ int ren_font_group_get_height(RenFont **fonts) { } static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFont** fonts, unsigned int codepoint, int bitmap_index) { + if (bitmap_index < 0) + bitmap_index += SUBPIXEL_BITMAPS_CACHED; for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { *set = font_get_glyphset(fonts[i], codepoint, bitmap_index); *metric = &(*set)->metrics[codepoint % 256]; @@ -218,9 +220,8 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo return fonts[i]; } if (!(*metric)->loaded && codepoint > 0xFF) { - codepoint = 0x25A1; - *set = font_get_glyphset(fonts[0], codepoint, bitmap_index); - *metric = &(*set)->metrics[codepoint % 256]; + *set = font_get_glyphset(fonts[0], 0x25A1, bitmap_index); + *metric = &(*set)->metrics[0x25A1 % 256]; } return fonts[0]; } @@ -228,7 +229,7 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo float ren_font_group_get_width(RenFont **fonts, const char *text) { float width = 0; const char* end = text + strlen(text); - GlyphMetric* metric; GlyphSet* set; + GlyphMetric* metric = NULL; GlyphSet* set = NULL; while (text < end) { unsigned int codepoint; text = utf8_to_codepoint(text, &codepoint); @@ -243,7 +244,6 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor SDL_Surface *surface = renwin_get_surface(&window_renderer); const RenRect clip = window_renderer.clip; - RenFont* font; const int surface_scale = renwin_surface_scale(&window_renderer); float pen_x = x * surface_scale; y *= surface_scale; @@ -254,10 +254,8 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor while (text < end) { unsigned int codepoint, r, g, b; text = utf8_to_codepoint(text, &codepoint); - GlyphSet* set; GlyphMetric* metric; - int bitmap_index = (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED); - bitmap_index += (bitmap_index < 0 ? SUBPIXEL_BITMAPS_CACHED : 0); - font = font_group_get_glyph(&set, &metric, fonts, codepoint, bitmap_index); + 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)); 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; From cab315bed1521f1c2dd1318be75e261d8ac65ad0 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 17 Oct 2021 00:22:27 -0400 Subject: [PATCH 146/165] Added in a rectdraw when a fallback glyph isn't present. --- src/renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/renderer.c b/src/renderer.c index a70111fa..ade84066 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -259,6 +259,8 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor 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; for (int line = metric->y0; line < metric->y1; ++line) { From 16fc15daee9b61366eced6d6dc4208ce2e8a0475 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 17 Oct 2021 00:26:20 -0400 Subject: [PATCH 147/165] Allowed for a white square as part of the other groups. --- src/renderer.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index ade84066..7035914d 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -219,10 +219,8 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo if ((*metric)->loaded || codepoint < 0xFF) return fonts[i]; } - if (!(*metric)->loaded && codepoint > 0xFF) { - *set = font_get_glyphset(fonts[0], 0x25A1, bitmap_index); - *metric = &(*set)->metrics[0x25A1 % 256]; - } + if (!(*metric)->loaded && codepoint > 0xFF && codepoint != 0x25A1) + return font_group_get_glyph(set, metric, fonts, 0x25A1, bitmap_index); return fonts[0]; } From b816a04d2725e7835c838664af084073353b2a49 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 17 Oct 2021 00:39:08 -0400 Subject: [PATCH 148/165] Added in a missing static. --- src/renderer.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 7035914d..385f8303 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -105,7 +105,7 @@ static int font_set_style(FT_Outline* outline, int x_translation, unsigned char return 0; } -void font_load_glyphset(RenFont* font, int idx) { +static void font_load_glyphset(RenFont* font, int idx) { unsigned int render_option = font_set_render_options(font), load_option = font_set_load_options(font); int bitmaps_cached = font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1; unsigned int byte_width = font->subpixel ? 3 : 1; @@ -150,6 +150,20 @@ static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int su return font->sets[font->subpixel ? subpixel_idx : 0][idx]; } +static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFont** fonts, unsigned int codepoint, int bitmap_index) { + if (bitmap_index < 0) + bitmap_index += SUBPIXEL_BITMAPS_CACHED; + for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { + *set = font_get_glyphset(fonts[i], codepoint, bitmap_index); + *metric = &(*set)->metrics[codepoint % 256]; + if ((*metric)->loaded || codepoint < 0xFF) + return fonts[i]; + } + if (!(*metric)->loaded && codepoint > 0xFF && codepoint != 0x25A1) + return font_group_get_glyph(set, metric, fonts, 0x25A1, bitmap_index); + return fonts[0]; +} + RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting, unsigned char style) { FT_Face face; if (FT_New_Face( library, path, 0, &face)) @@ -210,20 +224,6 @@ int ren_font_group_get_height(RenFont **fonts) { return fonts[0]->size + 3; } -static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFont** fonts, unsigned int codepoint, int bitmap_index) { - if (bitmap_index < 0) - bitmap_index += SUBPIXEL_BITMAPS_CACHED; - for (int i = 0; i < FONT_FALLBACK_MAX && fonts[i]; ++i) { - *set = font_get_glyphset(fonts[i], codepoint, bitmap_index); - *metric = &(*set)->metrics[codepoint % 256]; - if ((*metric)->loaded || codepoint < 0xFF) - return fonts[i]; - } - if (!(*metric)->loaded && codepoint > 0xFF && codepoint != 0x25A1) - return font_group_get_glyph(set, metric, fonts, 0x25A1, bitmap_index); - return fonts[0]; -} - float ren_font_group_get_width(RenFont **fonts, const char *text) { float width = 0; const char* end = text + strlen(text); From 461533eabf0195394208b3ecc0562eb471c05358 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Wed, 20 Oct 2021 18:43:22 -0400 Subject: [PATCH 149/165] Handles occasions where our color bytes aren't in the order we expected. --- src/renderer.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 49261e19..aec945e0 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -246,6 +246,7 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor const char* end = text + strlen(text); unsigned char* destination_pixels = surface->pixels; int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height; + while (text < end) { unsigned int codepoint, r, g, b; text = utf8_to_codepoint(text, &codepoint); @@ -275,12 +276,12 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor unsigned char* source_pixel = &source_pixels[line * set->surface->pitch + glyph_start * (font->subpixel ? 3 : 1)]; for (int x = glyph_start; x < glyph_end; ++x) { unsigned int destination_color = *destination_pixel; - SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF }; + 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->subpixel ? source_pixel++ : source_pixel), *(font->subpixel ? source_pixel++ : source_pixel), *source_pixel++ }; 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; - *destination_pixel++ = dst.a << 24 | r << 16 | g << 8 | b; + *destination_pixel++ = dst.a << surface->format->Ashift | r << surface->format->Rshift | g << surface->format->Gshift | b << surface->format->Bshift; } } } @@ -323,14 +324,15 @@ void ren_draw_rect(RenRect rect, RenColor color) { RenColor *d = (RenColor*) surface->pixels; d += x1 + y1 * surface->w; int dr = surface->w - (x2 - x1); - + unsigned int translated = SDL_MapRGB(surface->format, color.r, color.g, color.b); if (color.a == 0xff) { SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 }; - SDL_FillRect(surface, &rect, SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a)); + SDL_FillRect(surface, &rect, translated); } else { + RenColor translated_color = (RenColor){ translated & 0xFF, (translated >> 8) & 0xFF, (translated >> 16) & 0xFF, color.a }; for (int j = y1; j < y2; j++) { for (int i = x1; i < x2; i++, d++) - *d = blend_pixel(*d, color); + *d = blend_pixel(*d, translated_color); d += dr; } } From 065fe0769682e16e146849b0d98de4b31376af9e Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Mon, 25 Oct 2021 19:29:31 +0200 Subject: [PATCH 150/165] Add auto labeler workflow --- .github/labeler.yml | 35 +++++++++++++++++++++++++++ .github/workflows/auto_labeler_pr.yml | 16 ++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 .github/labeler.yml create mode 100644 .github/workflows/auto_labeler_pr.yml diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..a59bd3ec --- /dev/null +++ b/.github/labeler.yml @@ -0,0 +1,35 @@ +"Category: CI": + - .github/workflows/* + +"Category: Meta": + - ./* + - .github/* + - .github/ISSUE_TEMPLATE/* + - .github/PULL_REQUEST_TEMPLATE/* + - .gitignore + +"Category: Build System": + - meson.build + - meson_options.txt + - subprojects/* + +"Category: Documentation": + - docs/* + +"Category: Resources": + - resources/* + +"Category: Themes": + - data/colors/* + +"Category: Lua Core": + - data/core/* + +"Category: Fonts": + - data/fonts/* + +"Category: Plugins": + - data/plugins/* + +"Category: C Core": + - src/* diff --git a/.github/workflows/auto_labeler_pr.yml b/.github/workflows/auto_labeler_pr.yml new file mode 100644 index 00000000..48ea1eeb --- /dev/null +++ b/.github/workflows/auto_labeler_pr.yml @@ -0,0 +1,16 @@ +name: "Pull Request Labeler" +on: +- pull_request_target + +permissions: + pull-requests: write + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Apply Type Label + uses: actions/labeler@v3 + with: + repo-token: "${{ secrets.GITHUB_TOKEN }}" + sync-labels: "" # works around actions/labeler#104 From e313eb0e2e226c26665eda615b19a42e85bab6c8 Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Thu, 28 Oct 2021 08:40:18 +0200 Subject: [PATCH 151/165] ensure command alignment is correct --- src/rencache.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/rencache.c b/src/rencache.c index 4997687c..c3254cd0 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "rencache.h" @@ -84,6 +85,8 @@ static RenRect merge_rects(RenRect a, RenRect b) { static Command* push_command(int type, int size) { + size_t alignment = alignof(max_align_t) - 1; + size = (size + alignment) & ~alignment; Command *cmd = (Command*) (command_buf + command_buf_idx); int n = command_buf_idx + size; if (n > COMMAND_BUF_SIZE) { From 9e67995feb4b5a3deacc04af715efd1cadb60e5b Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 31 Oct 2021 13:27:51 -0400 Subject: [PATCH 152/165] Expand glyphsets to accomodate emojis. --- src/renderer.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 49261e19..79d0a372 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -11,6 +11,7 @@ #include "renwindow.h" #define MAX_GLYPHSET 256 +#define LOADABLE_GLYPHSETS 512 #define SUBPIXEL_BITMAPS_CACHED 3 static RenWindow window_renderer = {0}; @@ -34,12 +35,12 @@ typedef struct { typedef struct { SDL_Surface* surface; - GlyphMetric metrics[256]; + GlyphMetric metrics[MAX_GLYPHSET]; } GlyphSet; typedef struct RenFont { FT_Face face; - GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_GLYPHSET]; + GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][LOADABLE_GLYPHSETS]; float size, space_advance, tab_advance; short max_height; bool subpixel; @@ -110,7 +111,7 @@ static void font_load_glyphset(RenFont* font, int idx) { for (int j = 0, pen_x = 0; j < bitmaps_cached; ++j) { GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet))); font->sets[j][idx] = set; - for (int i = 0; i < 256; ++i) { + 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 | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option)) continue; @@ -124,7 +125,7 @@ static void font_load_glyphset(RenFont* font, int idx) { continue; set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0)); unsigned char* pixels = set->surface->pixels; - for (int i = 0; i < 256; ++i) { + 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)) continue; @@ -142,7 +143,7 @@ static void font_load_glyphset(RenFont* font, int idx) { } static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) { - int idx = (codepoint >> 8) % MAX_GLYPHSET; + int idx = (codepoint >> 8) % LOADABLE_GLYPHSETS; if (!font->sets[font->subpixel ? subpixel_idx : 0][idx]) font_load_glyphset(font, idx); return font->sets[font->subpixel ? subpixel_idx : 0][idx]; From b7cb7e2b67c77178677e9e3b4f705601ada1b7f0 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 31 Oct 2021 13:34:46 -0400 Subject: [PATCH 153/165] Just added MAX. More inline with other constant. --- src/renderer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/renderer.c b/src/renderer.c index 79d0a372..c5d6b217 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -11,7 +11,7 @@ #include "renwindow.h" #define MAX_GLYPHSET 256 -#define LOADABLE_GLYPHSETS 512 +#define MAX_LOADABLE_GLYPHSETS 512 #define SUBPIXEL_BITMAPS_CACHED 3 static RenWindow window_renderer = {0}; @@ -40,7 +40,7 @@ typedef struct { typedef struct RenFont { FT_Face face; - GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][LOADABLE_GLYPHSETS]; + GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS]; float size, space_advance, tab_advance; short max_height; bool subpixel; @@ -143,7 +143,7 @@ static void font_load_glyphset(RenFont* font, int idx) { } static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) { - int idx = (codepoint >> 8) % LOADABLE_GLYPHSETS; + int idx = (codepoint >> 8) % MAX_LOADABLE_GLYPHSETS; if (!font->sets[font->subpixel ? subpixel_idx : 0][idx]) font_load_glyphset(font, idx); return font->sets[font->subpixel ? subpixel_idx : 0][idx]; From 90714c48e1b1990c61208c6b542a326eafa11ed6 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 1 Nov 2021 10:03:36 -0400 Subject: [PATCH 154/165] Update renderer.c Upped limit to 1024. --- src/renderer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer.c b/src/renderer.c index c5d6b217..7cd88237 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -11,7 +11,7 @@ #include "renwindow.h" #define MAX_GLYPHSET 256 -#define MAX_LOADABLE_GLYPHSETS 512 +#define MAX_LOADABLE_GLYPHSETS 1024 #define SUBPIXEL_BITMAPS_CACHED 3 static RenWindow window_renderer = {0}; From cd10497b495f42f5cb102aac93af3e9280e65619 Mon Sep 17 00:00:00 2001 From: PIESEL <50019824+piotrek94692@users.noreply.github.com> Date: Mon, 1 Nov 2021 16:30:15 +0100 Subject: [PATCH 155/165] Use Python syntax highlighting for Ren'Py scripts. Ren'Py is a very popular Python visual novel engine, which has it's own "coding language" (even GitHub shows it as a separate language) and it's own file extension. Generally Python syntax highlighting works for the Ren'Py language. --- data/plugins/language_python.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/plugins/language_python.lua b/data/plugins/language_python.lua index e19caa63..252a0d14 100644 --- a/data/plugins/language_python.lua +++ b/data/plugins/language_python.lua @@ -2,7 +2,7 @@ local syntax = require "core.syntax" syntax.add { - files = { "%.py$", "%.pyw$" }, + files = { "%.py$", "%.pyw$", "%.rpy$" }, headers = "^#!.*[ /]python", comment = "#", patterns = { From d0ece3570580195dd3c5128d129056901537b54e Mon Sep 17 00:00:00 2001 From: obtusedev <66740598+obtusedev@users.noreply.github.com> Date: Tue, 2 Nov 2021 15:14:13 -0400 Subject: [PATCH 156/165] Add install instructions for prebuilt binaries --- README.md | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/README.md b/README.md index 71f856a1..6ec3a73b 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,53 @@ DESTDIR="$(pwd)/Lite XL.app" meson install --skip-subprojects -C build Please note that the package is relocatable to any prefix and the option prefix affects only the place where the application is actually installed. +## Installing Prebuilt + +Head over to [releases](https://github.com/lite-xl/lite-xl/releases) and download the version for your operating system. + +### Ubuntu + +Unzip the file and `cd` into the `lite-xl` directory: + +```sh +tar -xzf +cd lite-xl +``` + +Copy files over into appropriate directories: + +```sh +mkdir -p $HOME/.local/bin && cp bin/lite-xl $HOME/.local/bin +cp -r share $HOME/.local +``` + +If `$HOME/.local/bin` is not in PATH: + +```sh +echo -e 'export PATH=$PATH:$HOME/.local/bin' >> $HOME/.bashrc +``` + +To get the icon to show up in app launcher: + +```sh +xdg-desktop-menu forceupdate +``` + +You may need to logout and login again to see icon in app launcher. + +To uninstall just run: + +```sh +rm -f $HOME/.local/bin/lite-xl +rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \ + $HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \ + $HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \ + $HOME/.local/share/lite-xl \ + $HOME/.local/share/doc/lite-xl +``` + +You may need to `Alt + F2` and enter 'r' to see changes. + ## Contributing Any additional functionality that can be added through a plugin should be done From 3867ff2be7302c3a584bea2c0f4a2f8537ac0a4b Mon Sep 17 00:00:00 2001 From: Cukmekerb Date: Fri, 5 Nov 2021 14:35:58 -0700 Subject: [PATCH 157/165] Document bold, italic, and underlined font rendering --- docs/api/renderer.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/api/renderer.lua b/docs/api/renderer.lua index bb622131..6820a14d 100644 --- a/docs/api/renderer.lua +++ b/docs/api/renderer.lua @@ -19,6 +19,9 @@ renderer.color = {} ---@class renderer.fontoptions ---@field public antialiasing "'grayscale'" | "'subpixel'" ---@field public hinting "'slight'" | "'none'" | '"full"' +-- @field public bold boolean +-- @field public italic boolean +-- @field public underline boolean renderer.fontoptions = {} --- From 05dcddaeece0ca39cad2b5e093de4a9317b77923 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 7 Nov 2021 13:14:48 -0500 Subject: [PATCH 158/165] Made plugin load order deterministic. --- data/core/init.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/data/core/init.lua b/data/core/init.lua index 6ef0ab7e..6dd54cd4 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -675,16 +675,18 @@ function core.load_plugins() userdir = {dir = USERDIR, plugins = {}}, datadir = {dir = DATADIR, plugins = {}}, } - local files = {} + local files, ordered = {}, {} for _, root_dir in ipairs {DATADIR, USERDIR} do local plugin_dir = root_dir .. "/plugins" for _, filename in ipairs(system.list_dir(plugin_dir) or {}) do + if not files[filename] then table.insert(ordered, filename) end files[filename] = plugin_dir -- user plugins will always replace system plugins end end + table.sort(ordered) - for filename, plugin_dir in pairs(files) do - local basename = filename:match("(.-)%.lua$") or filename + for i, filename in ipairs(ordered) do + local plugin_dir, basename = files[filename], filename:match("(.-)%.lua$") or filename local is_lua_file, version_match = check_plugin_version(plugin_dir .. '/' .. filename) if is_lua_file then if not version_match then From a184ec9cc326d3ced15b080d2d38ff8d2aed2a7b Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 7 Nov 2021 14:04:42 -0500 Subject: [PATCH 159/165] Improved heuristic to pay more attention to string length. --- src/api/system.c | 60 +++++++++++++++--------------------------------- 1 file changed, 18 insertions(+), 42 deletions(-) diff --git a/src/api/system.c b/src/api/system.c index bcd1b997..85d0db2a 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -591,56 +591,32 @@ static int f_exec(lua_State *L) { return 0; } - static int f_fuzzy_match(lua_State *L) { size_t strLen, ptnLen; const char *str = luaL_checklstring(L, 1, &strLen); const char *ptn = luaL_checklstring(L, 2, &ptnLen); - bool files = false; - if (lua_gettop(L) > 2 && lua_isboolean(L,3)) - files = lua_toboolean(L, 3); - - int score = 0; - int run = 0; - - // Match things *backwards*. This allows for better matching on filenames than the above + // If true match things *backwards*. This allows for better matching on filenames than the above // function. For example, in the lite project, opening "renderer" has lib/font_render/build.sh // as the first result, rather than src/renderer.c. Clearly that's wrong. - if (files) { - const char* strEnd = str + strLen - 1; - const char* ptnEnd = ptn + ptnLen - 1; - while (strEnd >= str && ptnEnd >= ptn) { - while (*strEnd == ' ') { strEnd--; } - while (*ptnEnd == ' ') { ptnEnd--; } - if (tolower(*strEnd) == tolower(*ptnEnd)) { - score += run * 10 - (*strEnd != *ptnEnd); - run++; - ptnEnd--; - } else { - score -= 10; - run = 0; - } - strEnd--; + bool files = lua_gettop(L) > 2 && lua_isboolean(L,3) && lua_toboolean(L, 3); + int score = 0, run = 0, increment = files ? -1 : 1; + const char* strTarget = files ? str + strLen - 1 : str; + const char* ptnTarget = files ? ptn + ptnLen - 1 : ptn; + while (*strTarget && *ptnTarget) { + while (*strTarget == ' ') { strTarget += increment; } + while (*ptnTarget == ' ') { ptnTarget += increment; } + if (tolower(*strTarget) == tolower(*ptnTarget)) { + score += run * 10 - (*strTarget != *ptnTarget); + run++; + ptnTarget += increment; + } else { + score -= 10; + run = 0; } - if (ptnEnd >= ptn) { return 0; } - } else { - while (*str && *ptn) { - while (*str == ' ') { str++; } - while (*ptn == ' ') { ptn++; } - if (tolower(*str) == tolower(*ptn)) { - score += run * 10 - (*str != *ptn); - run++; - ptn++; - } else { - score -= 10; - run = 0; - } - str++; - } - if (*ptn) { return 0; } + strTarget += increment; } - - lua_pushnumber(L, score - (int)strLen); + if (*ptnTarget) { return 0; } + lua_pushnumber(L, score - (int)strLen * 10); return 1; } From 934f9c05d42255f604af9e969001056c1a9e8a86 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 7 Nov 2021 15:01:03 -0500 Subject: [PATCH 160/165] Screwed up checks. --- src/api/system.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/api/system.c b/src/api/system.c index 85d0db2a..c998fa05 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -602,9 +602,9 @@ static int f_fuzzy_match(lua_State *L) { int score = 0, run = 0, increment = files ? -1 : 1; const char* strTarget = files ? str + strLen - 1 : str; const char* ptnTarget = files ? ptn + ptnLen - 1 : ptn; - while (*strTarget && *ptnTarget) { - while (*strTarget == ' ') { strTarget += increment; } - while (*ptnTarget == ' ') { ptnTarget += increment; } + while (strTarget >= str && ptnTarget >= ptn && *strTarget && *ptnTarget) { + while (strTarget >= str && *strTarget == ' ') { strTarget += increment; } + while (ptnTarget >= ptn && *ptnTarget == ' ') { ptnTarget += increment; } if (tolower(*strTarget) == tolower(*ptnTarget)) { score += run * 10 - (*strTarget != *ptnTarget); run++; @@ -615,7 +615,7 @@ static int f_fuzzy_match(lua_State *L) { } strTarget += increment; } - if (*ptnTarget) { return 0; } + if (ptnTarget >= ptn && *ptnTarget) { return 0; } lua_pushnumber(L, score - (int)strLen * 10); return 1; } From d22bf520edb9738cab266b9f8e438e396b69eb35 Mon Sep 17 00:00:00 2001 From: Takase <20792268+takase1121@users.noreply.github.com> Date: Mon, 8 Nov 2021 12:44:09 +0800 Subject: [PATCH 161/165] Keymap generator (#503) add keymap generator --- scripts/README.md | 1 + scripts/keymap-generator/dkjson.lua | 714 ++++++++++++++++++ scripts/keymap-generator/keymap-generator.lua | 75 ++ 3 files changed, 790 insertions(+) create mode 100644 scripts/keymap-generator/dkjson.lua create mode 100644 scripts/keymap-generator/keymap-generator.lua diff --git a/scripts/README.md b/scripts/README.md index a236599c..7d66fbca 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -22,6 +22,7 @@ Various scripts and configurations used to configure, build, and package Lite XL and run Lite XL, mainly useful for CI and documentation purpose. Preferably not to be used in user systems. - **fontello-config.json**: Used by the icons generator. +- **keymap-generator**: Generates a JSON file containing the keymap [1]: https://github.com/LinusU/node-appdmg [2]: https://docs.appimage.org/ diff --git a/scripts/keymap-generator/dkjson.lua b/scripts/keymap-generator/dkjson.lua new file mode 100644 index 00000000..fa50b9fa --- /dev/null +++ b/scripts/keymap-generator/dkjson.lua @@ -0,0 +1,714 @@ +-- Module options: +local always_try_using_lpeg = true +local register_global_module_table = false +local global_module_name = 'json' + +--[==[ + +David Kolf's JSON module for Lua 5.1/5.2 + +Version 2.5 + + +For the documentation see the corresponding readme.txt or visit +. + +You can contact the author by sending an e-mail to 'david' at the +domain 'dkolf.de'. + + +Copyright (C) 2010-2013 David Heiko Kolf + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--]==] + +-- global dependencies: +local pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset = + pairs, type, tostring, tonumber, getmetatable, setmetatable, rawset +local error, require, pcall, select = error, require, pcall, select +local floor, huge = math.floor, math.huge +local strrep, gsub, strsub, strbyte, strchar, strfind, strlen, strformat = + string.rep, string.gsub, string.sub, string.byte, string.char, + string.find, string.len, string.format +local strmatch = string.match +local concat = table.concat + +local json = { version = "dkjson 2.5" } + +if register_global_module_table then + _G[global_module_name] = json +end + +local _ENV = nil -- blocking globals in Lua 5.2 + +pcall (function() + -- Enable access to blocked metatables. + -- Don't worry, this module doesn't change anything in them. + local debmeta = require "debug".getmetatable + if debmeta then getmetatable = debmeta end +end) + +json.null = setmetatable ({}, { + __tojson = function () return "null" end +}) + +local function isarray (tbl) + local max, n, arraylen = 0, 0, 0 + for k,v in pairs (tbl) do + if k == 'n' and type(v) == 'number' then + arraylen = v + if v > max then + max = v + end + else + if type(k) ~= 'number' or k < 1 or floor(k) ~= k then + return false + end + if k > max then + max = k + end + n = n + 1 + end + end + if max > 10 and max > arraylen and max > n * 2 then + return false -- don't create an array with too many holes + end + return true, max +end + +local escapecodes = { + ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b", ["\f"] = "\\f", + ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t" +} + +local function escapeutf8 (uchar) + local value = escapecodes[uchar] + if value then + return value + end + local a, b, c, d = strbyte (uchar, 1, 4) + a, b, c, d = a or 0, b or 0, c or 0, d or 0 + if a <= 0x7f then + value = a + elseif 0xc0 <= a and a <= 0xdf and b >= 0x80 then + value = (a - 0xc0) * 0x40 + b - 0x80 + elseif 0xe0 <= a and a <= 0xef and b >= 0x80 and c >= 0x80 then + value = ((a - 0xe0) * 0x40 + b - 0x80) * 0x40 + c - 0x80 + elseif 0xf0 <= a and a <= 0xf7 and b >= 0x80 and c >= 0x80 and d >= 0x80 then + value = (((a - 0xf0) * 0x40 + b - 0x80) * 0x40 + c - 0x80) * 0x40 + d - 0x80 + else + return "" + end + if value <= 0xffff then + return strformat ("\\u%.4x", value) + elseif value <= 0x10ffff then + -- encode as UTF-16 surrogate pair + value = value - 0x10000 + local highsur, lowsur = 0xD800 + floor (value/0x400), 0xDC00 + (value % 0x400) + return strformat ("\\u%.4x\\u%.4x", highsur, lowsur) + else + return "" + end +end + +local function fsub (str, pattern, repl) + -- gsub always builds a new string in a buffer, even when no match + -- exists. First using find should be more efficient when most strings + -- don't contain the pattern. + if strfind (str, pattern) then + return gsub (str, pattern, repl) + else + return str + end +end + +local function quotestring (value) + -- based on the regexp "escapable" in https://github.com/douglascrockford/JSON-js + value = fsub (value, "[%z\1-\31\"\\\127]", escapeutf8) + if strfind (value, "[\194\216\220\225\226\239]") then + value = fsub (value, "\194[\128-\159\173]", escapeutf8) + value = fsub (value, "\216[\128-\132]", escapeutf8) + value = fsub (value, "\220\143", escapeutf8) + value = fsub (value, "\225\158[\180\181]", escapeutf8) + value = fsub (value, "\226\128[\140-\143\168-\175]", escapeutf8) + value = fsub (value, "\226\129[\160-\175]", escapeutf8) + value = fsub (value, "\239\187\191", escapeutf8) + value = fsub (value, "\239\191[\176-\191]", escapeutf8) + end + return "\"" .. value .. "\"" +end +json.quotestring = quotestring + +local function replace(str, o, n) + local i, j = strfind (str, o, 1, true) + if i then + return strsub(str, 1, i-1) .. n .. strsub(str, j+1, -1) + else + return str + end +end + +-- locale independent num2str and str2num functions +local decpoint, numfilter + +local function updatedecpoint () + decpoint = strmatch(tostring(0.5), "([^05+])") + -- build a filter that can be used to remove group separators + numfilter = "[^0-9%-%+eE" .. gsub(decpoint, "[%^%$%(%)%%%.%[%]%*%+%-%?]", "%%%0") .. "]+" +end + +updatedecpoint() + +local function num2str (num) + return replace(fsub(tostring(num), numfilter, ""), decpoint, ".") +end + +local function str2num (str) + local num = tonumber(replace(str, ".", decpoint)) + if not num then + updatedecpoint() + num = tonumber(replace(str, ".", decpoint)) + end + return num +end + +local function addnewline2 (level, buffer, buflen) + buffer[buflen+1] = "\n" + buffer[buflen+2] = strrep (" ", level) + buflen = buflen + 2 + return buflen +end + +function json.addnewline (state) + if state.indent then + state.bufferlen = addnewline2 (state.level or 0, + state.buffer, state.bufferlen or #(state.buffer)) + end +end + +local encode2 -- forward declaration + +local function addpair (key, value, prev, indent, level, buffer, buflen, tables, globalorder, state) + local kt = type (key) + if kt ~= 'string' and kt ~= 'number' then + return nil, "type '" .. kt .. "' is not supported as a key by JSON." + end + if prev then + buflen = buflen + 1 + buffer[buflen] = "," + end + if indent then + buflen = addnewline2 (level, buffer, buflen) + end + buffer[buflen+1] = quotestring (key) + buffer[buflen+2] = ":" + return encode2 (value, indent, level, buffer, buflen + 2, tables, globalorder, state) +end + +local function appendcustom(res, buffer, state) + local buflen = state.bufferlen + if type (res) == 'string' then + buflen = buflen + 1 + buffer[buflen] = res + end + return buflen +end + +local function exception(reason, value, state, buffer, buflen, defaultmessage) + defaultmessage = defaultmessage or reason + local handler = state.exception + if not handler then + return nil, defaultmessage + else + state.bufferlen = buflen + local ret, msg = handler (reason, value, state, defaultmessage) + if not ret then return nil, msg or defaultmessage end + return appendcustom(ret, buffer, state) + end +end + +function json.encodeexception(reason, value, state, defaultmessage) + return quotestring("<" .. defaultmessage .. ">") +end + +encode2 = function (value, indent, level, buffer, buflen, tables, globalorder, state) + local valtype = type (value) + local valmeta = getmetatable (value) + valmeta = type (valmeta) == 'table' and valmeta -- only tables + local valtojson = valmeta and valmeta.__tojson + if valtojson then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + state.bufferlen = buflen + local ret, msg = valtojson (value, state) + if not ret then return exception('custom encoder failed', value, state, buffer, buflen, msg) end + tables[value] = nil + buflen = appendcustom(ret, buffer, state) + elseif value == nil then + buflen = buflen + 1 + buffer[buflen] = "null" + elseif valtype == 'number' then + local s + if value ~= value or value >= huge or -value >= huge then + -- This is the behaviour of the original JSON implementation. + s = "null" + else + s = num2str (value) + end + buflen = buflen + 1 + buffer[buflen] = s + elseif valtype == 'boolean' then + buflen = buflen + 1 + buffer[buflen] = value and "true" or "false" + elseif valtype == 'string' then + buflen = buflen + 1 + buffer[buflen] = quotestring (value) + elseif valtype == 'table' then + if tables[value] then + return exception('reference cycle', value, state, buffer, buflen) + end + tables[value] = true + level = level + 1 + local isa, n = isarray (value) + if n == 0 and valmeta and valmeta.__jsontype == 'object' then + isa = false + end + local msg + if isa then -- JSON array + buflen = buflen + 1 + buffer[buflen] = "[" + for i = 1, n do + buflen, msg = encode2 (value[i], indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + if i < n then + buflen = buflen + 1 + buffer[buflen] = "," + end + end + buflen = buflen + 1 + buffer[buflen] = "]" + else -- JSON object + local prev = false + buflen = buflen + 1 + buffer[buflen] = "{" + local order = valmeta and valmeta.__jsonorder or globalorder + if order then + local used = {} + n = #order + for i = 1, n do + local k = order[i] + local v = value[k] + if v then + used[k] = true + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + prev = true -- add a seperator before the next element + end + end + for k,v in pairs (value) do + if not used[k] then + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + else -- unordered + for k,v in pairs (value) do + buflen, msg = addpair (k, v, prev, indent, level, buffer, buflen, tables, globalorder, state) + if not buflen then return nil, msg end + prev = true -- add a seperator before the next element + end + end + if indent then + buflen = addnewline2 (level - 1, buffer, buflen) + end + buflen = buflen + 1 + buffer[buflen] = "}" + end + tables[value] = nil + else + return exception ('unsupported type', value, state, buffer, buflen, + "type '" .. valtype .. "' is not supported by JSON.") + end + return buflen +end + +function json.encode (value, state) + state = state or {} + local oldbuffer = state.buffer + local buffer = oldbuffer or {} + state.buffer = buffer + updatedecpoint() + local ret, msg = encode2 (value, state.indent, state.level or 0, + buffer, state.bufferlen or 0, state.tables or {}, state.keyorder, state) + if not ret then + error (msg, 2) + elseif oldbuffer == buffer then + state.bufferlen = ret + return true + else + state.bufferlen = nil + state.buffer = nil + return concat (buffer) + end +end + +local function loc (str, where) + local line, pos, linepos = 1, 1, 0 + while true do + pos = strfind (str, "\n", pos, true) + if pos and pos < where then + line = line + 1 + linepos = pos + pos = pos + 1 + else + break + end + end + return "line " .. line .. ", column " .. (where - linepos) +end + +local function unterminated (str, what, where) + return nil, strlen (str) + 1, "unterminated " .. what .. " at " .. loc (str, where) +end + +local function scanwhite (str, pos) + while true do + pos = strfind (str, "%S", pos) + if not pos then return nil end + local sub2 = strsub (str, pos, pos + 1) + if sub2 == "\239\187" and strsub (str, pos + 2, pos + 2) == "\191" then + -- UTF-8 Byte Order Mark + pos = pos + 3 + elseif sub2 == "//" then + pos = strfind (str, "[\n\r]", pos + 2) + if not pos then return nil end + elseif sub2 == "/*" then + pos = strfind (str, "*/", pos + 2) + if not pos then return nil end + pos = pos + 2 + else + return pos + end + end +end + +local escapechars = { + ["\""] = "\"", ["\\"] = "\\", ["/"] = "/", ["b"] = "\b", ["f"] = "\f", + ["n"] = "\n", ["r"] = "\r", ["t"] = "\t" +} + +local function unichar (value) + if value < 0 then + return nil + elseif value <= 0x007f then + return strchar (value) + elseif value <= 0x07ff then + return strchar (0xc0 + floor(value/0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0xffff then + return strchar (0xe0 + floor(value/0x1000), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + elseif value <= 0x10ffff then + return strchar (0xf0 + floor(value/0x40000), + 0x80 + (floor(value/0x1000) % 0x40), + 0x80 + (floor(value/0x40) % 0x40), + 0x80 + (floor(value) % 0x40)) + else + return nil + end +end + +local function scanstring (str, pos) + local lastpos = pos + 1 + local buffer, n = {}, 0 + while true do + local nextpos = strfind (str, "[\"\\]", lastpos) + if not nextpos then + return unterminated (str, "string", pos) + end + if nextpos > lastpos then + n = n + 1 + buffer[n] = strsub (str, lastpos, nextpos - 1) + end + if strsub (str, nextpos, nextpos) == "\"" then + lastpos = nextpos + 1 + break + else + local escchar = strsub (str, nextpos + 1, nextpos + 1) + local value + if escchar == "u" then + value = tonumber (strsub (str, nextpos + 2, nextpos + 5), 16) + if value then + local value2 + if 0xD800 <= value and value <= 0xDBff then + -- we have the high surrogate of UTF-16. Check if there is a + -- low surrogate escaped nearby to combine them. + if strsub (str, nextpos + 6, nextpos + 7) == "\\u" then + value2 = tonumber (strsub (str, nextpos + 8, nextpos + 11), 16) + if value2 and 0xDC00 <= value2 and value2 <= 0xDFFF then + value = (value - 0xD800) * 0x400 + (value2 - 0xDC00) + 0x10000 + else + value2 = nil -- in case it was out of range for a low surrogate + end + end + end + value = value and unichar (value) + if value then + if value2 then + lastpos = nextpos + 12 + else + lastpos = nextpos + 6 + end + end + end + end + if not value then + value = escapechars[escchar] or escchar + lastpos = nextpos + 2 + end + n = n + 1 + buffer[n] = value + end + end + if n == 1 then + return buffer[1], lastpos + elseif n > 1 then + return concat (buffer), lastpos + else + return "", lastpos + end +end + +local scanvalue -- forward declaration + +local function scantable (what, closechar, str, startpos, nullval, objectmeta, arraymeta) + local len = strlen (str) + local tbl, n = {}, 0 + local pos = startpos + 1 + if what == 'object' then + setmetatable (tbl, objectmeta) + else + setmetatable (tbl, arraymeta) + end + while true do + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + local char = strsub (str, pos, pos) + if char == closechar then + return tbl, pos + 1 + end + local val1, err + val1, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + if char == ":" then + if val1 == nil then + return nil, pos, "cannot use nil as table index (at " .. loc (str, pos) .. ")" + end + pos = scanwhite (str, pos + 1) + if not pos then return unterminated (str, what, startpos) end + local val2 + val2, pos, err = scanvalue (str, pos, nullval, objectmeta, arraymeta) + if err then return nil, pos, err end + tbl[val1] = val2 + pos = scanwhite (str, pos) + if not pos then return unterminated (str, what, startpos) end + char = strsub (str, pos, pos) + else + n = n + 1 + tbl[n] = val1 + end + if char == "," then + pos = pos + 1 + end + end +end + +scanvalue = function (str, pos, nullval, objectmeta, arraymeta) + pos = pos or 1 + pos = scanwhite (str, pos) + if not pos then + return nil, strlen (str) + 1, "no valid JSON value (reached the end)" + end + local char = strsub (str, pos, pos) + if char == "{" then + return scantable ('object', "}", str, pos, nullval, objectmeta, arraymeta) + elseif char == "[" then + return scantable ('array', "]", str, pos, nullval, objectmeta, arraymeta) + elseif char == "\"" then + return scanstring (str, pos) + else + local pstart, pend = strfind (str, "^%-?[%d%.]+[eE]?[%+%-]?%d*", pos) + if pstart then + local number = str2num (strsub (str, pstart, pend)) + if number then + return number, pend + 1 + end + end + pstart, pend = strfind (str, "^%a%w*", pos) + if pstart then + local name = strsub (str, pstart, pend) + if name == "true" then + return true, pend + 1 + elseif name == "false" then + return false, pend + 1 + elseif name == "null" then + return nullval, pend + 1 + end + end + return nil, pos, "no valid JSON value at " .. loc (str, pos) + end +end + +local function optionalmetatables(...) + if select("#", ...) > 0 then + return ... + else + return {__jsontype = 'object'}, {__jsontype = 'array'} + end +end + +function json.decode (str, pos, nullval, ...) + local objectmeta, arraymeta = optionalmetatables(...) + return scanvalue (str, pos, nullval, objectmeta, arraymeta) +end + +function json.use_lpeg () + local g = require ("lpeg") + + if g.version() == "0.11" then + error "due to a bug in LPeg 0.11, it cannot be used for JSON matching" + end + + local pegmatch = g.match + local P, S, R = g.P, g.S, g.R + + local function ErrorCall (str, pos, msg, state) + if not state.msg then + state.msg = msg .. " at " .. loc (str, pos) + state.pos = pos + end + return false + end + + local function Err (msg) + return g.Cmt (g.Cc (msg) * g.Carg (2), ErrorCall) + end + + local SingleLineComment = P"//" * (1 - S"\n\r")^0 + local MultiLineComment = P"/*" * (1 - P"*/")^0 * P"*/" + local Space = (S" \n\r\t" + P"\239\187\191" + SingleLineComment + MultiLineComment)^0 + + local PlainChar = 1 - S"\"\\\n\r" + local EscapeSequence = (P"\\" * g.C (S"\"\\/bfnrt" + Err "unsupported escape sequence")) / escapechars + local HexDigit = R("09", "af", "AF") + local function UTF16Surrogate (match, pos, high, low) + high, low = tonumber (high, 16), tonumber (low, 16) + if 0xD800 <= high and high <= 0xDBff and 0xDC00 <= low and low <= 0xDFFF then + return true, unichar ((high - 0xD800) * 0x400 + (low - 0xDC00) + 0x10000) + else + return false + end + end + local function UTF16BMP (hex) + return unichar (tonumber (hex, 16)) + end + local U16Sequence = (P"\\u" * g.C (HexDigit * HexDigit * HexDigit * HexDigit)) + local UnicodeEscape = g.Cmt (U16Sequence * U16Sequence, UTF16Surrogate) + U16Sequence/UTF16BMP + local Char = UnicodeEscape + EscapeSequence + PlainChar + local String = P"\"" * g.Cs (Char ^ 0) * (P"\"" + Err "unterminated string") + local Integer = P"-"^(-1) * (P"0" + (R"19" * R"09"^0)) + local Fractal = P"." * R"09"^0 + local Exponent = (S"eE") * (S"+-")^(-1) * R"09"^1 + local Number = (Integer * Fractal^(-1) * Exponent^(-1))/str2num + local Constant = P"true" * g.Cc (true) + P"false" * g.Cc (false) + P"null" * g.Carg (1) + local SimpleValue = Number + String + Constant + local ArrayContent, ObjectContent + + -- The functions parsearray and parseobject parse only a single value/pair + -- at a time and store them directly to avoid hitting the LPeg limits. + local function parsearray (str, pos, nullval, state) + local obj, cont + local npos + local t, nt = {}, 0 + repeat + obj, cont, npos = pegmatch (ArrayContent, str, pos, nullval, state) + if not npos then break end + pos = npos + nt = nt + 1 + t[nt] = obj + until cont == 'last' + return pos, setmetatable (t, state.arraymeta) + end + + local function parseobject (str, pos, nullval, state) + local obj, key, cont + local npos + local t = {} + repeat + key, obj, cont, npos = pegmatch (ObjectContent, str, pos, nullval, state) + if not npos then break end + pos = npos + t[key] = obj + until cont == 'last' + return pos, setmetatable (t, state.objectmeta) + end + + local Array = P"[" * g.Cmt (g.Carg(1) * g.Carg(2), parsearray) * Space * (P"]" + Err "']' expected") + local Object = P"{" * g.Cmt (g.Carg(1) * g.Carg(2), parseobject) * Space * (P"}" + Err "'}' expected") + local Value = Space * (Array + Object + SimpleValue) + local ExpectedValue = Value + Space * Err "value expected" + ArrayContent = Value * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local Pair = g.Cg (Space * String * Space * (P":" + Err "colon expected") * ExpectedValue) + ObjectContent = Pair * Space * (P"," * g.Cc'cont' + g.Cc'last') * g.Cp() + local DecodeValue = ExpectedValue * g.Cp () + + function json.decode (str, pos, nullval, ...) + local state = {} + state.objectmeta, state.arraymeta = optionalmetatables(...) + local obj, retpos = pegmatch (DecodeValue, str, pos, nullval, state) + if state.msg then + return nil, state.pos, state.msg + else + return obj, retpos + end + end + + -- use this function only once: + json.use_lpeg = function () return json end + + json.using_lpeg = true + + return json -- so you can get the module using json = require "dkjson".use_lpeg() +end + +if always_try_using_lpeg then + pcall (json.use_lpeg) +end + +return json + diff --git a/scripts/keymap-generator/keymap-generator.lua b/scripts/keymap-generator/keymap-generator.lua new file mode 100644 index 00000000..235d48c2 --- /dev/null +++ b/scripts/keymap-generator/keymap-generator.lua @@ -0,0 +1,75 @@ +#!/usr/bin/env lua +local dkjson = require "dkjson" + + +local function load_keymap(target, target_map, macos) + _G.MACOS = macos + package.loaded["core.keymap"] = nil + local keymap = require "core.keymap" + + if target then + keymap.map = {} + require(target) + end + + target_map = target_map or {} + -- keymap.reverse_map does not do this? + for key, actions in pairs(keymap.map) do + for _, action in ipairs(actions) do + target_map[action] = target_map[action] or {} + table.insert(target_map[action], key) + end + end + + return target_map +end + + +local function normalize(map) + local result = {} + for action, keys in pairs(map) do + local uniq = {} + local r = { combination = {}, action = action } + for _, v in ipairs(keys) do + if not uniq[v] then + uniq[v] = true + r.combination[#r.combination+1] = v + end + end + result[#result+1] = r + end + table.sort(result, function(a, b) return a.action < b.action end) + return result +end + + +local function process_module(mod, filename) + local map = {} + load_keymap(mod, map) + load_keymap(mod, map, true) + map = normalize(map) + local f = assert(io.open(filename, "wb")) + f:write(dkjson.encode(map, { indent = true })) + f:close() +end + + +print("Warning: this is not guaranteed to work outside lite-xl's own keymap. Proceed with caution") +local LITE_ROOT = arg[1] +if not LITE_ROOT then + error("LITE_ROOT is not given") +end +package.path = package.path .. ";" .. LITE_ROOT .. "/?.lua;" .. LITE_ROOT .. "/?/init.lua" + +-- fix core.command (because we don't want load the entire thing) +package.loaded["core.command"] = {} + +if #arg > 1 then + for i = 2, #arg do + process_module(arg[i], arg[i] .. ".json") + print(string.format("Exported keymap in %q.", arg[i])) + end +else + process_module(nil, "core.keymap.json") + print("Exported the default keymap.") +end From b3eef15e7ae5eab34e0e2fc731ddd17f9214149b Mon Sep 17 00:00:00 2001 From: Guldoman Date: Mon, 8 Nov 2021 16:00:24 +0100 Subject: [PATCH 162/165] Highlight any line that contains a caret Now lines with selections can be highlighted if they contain a caret. --- data/core/docview.lua | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/data/core/docview.lua b/data/core/docview.lua index 4e95c359..6621ef72 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -354,6 +354,18 @@ function DocView:draw_caret(x, y) end function DocView:draw_line_body(idx, x, y) + -- draw highlight if any selection ends on this line + local draw_highlight = false + for lidx, line1, col1, line2, col2 in self.doc:get_selections(false) do + if line1 == idx then + draw_highlight = true + break + end + end + if draw_highlight and config.highlight_current_line and core.active_view == self then + self:draw_line_highlight(x + self.scroll.x, y) + end + -- draw selection if it overlaps this line for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do if idx >= line1 and idx <= line2 then @@ -368,15 +380,6 @@ function DocView:draw_line_body(idx, x, y) end end end - local draw_highlight = nil - for lidx, line1, col1, line2, col2 in self.doc:get_selections(true) do - -- draw line highlight if caret is on this line - if draw_highlight ~= false and config.highlight_current_line - and line1 == idx and core.active_view == self then - draw_highlight = (line1 == line2 and col1 == col2) - end - end - if draw_highlight then self:draw_line_highlight(x + self.scroll.x, y) end -- draw line's text self:draw_line_text(idx, x, y) From 2033b67daee1452005d384ad235c618b2567c7b3 Mon Sep 17 00:00:00 2001 From: Takase <20792268+takase1121@users.noreply.github.com> Date: Wed, 10 Nov 2021 08:12:27 +0800 Subject: [PATCH 163/165] make labeler recursive this is mentioned by Jan in Discord. --- .github/labeler.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/labeler.yml b/.github/labeler.yml index a59bd3ec..41f66c7c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -14,16 +14,16 @@ - subprojects/* "Category: Documentation": - - docs/* + - docs/**/* "Category: Resources": - - resources/* + - resources/**/* "Category: Themes": - data/colors/* "Category: Lua Core": - - data/core/* + - data/core/**/* "Category: Fonts": - data/fonts/* @@ -32,4 +32,4 @@ - data/plugins/* "Category: C Core": - - src/* + - src/**/* From dfa48ddb514e14c154202615ec2bf406b7178a94 Mon Sep 17 00:00:00 2001 From: obtusedev <66740598+obtusedev@users.noreply.github.com> Date: Wed, 10 Nov 2021 18:56:32 -0500 Subject: [PATCH 164/165] Add install instructions for prebuilt binaries --- README.md | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6ec3a73b..e6cd18dc 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ affects only the place where the application is actually installed. Head over to [releases](https://github.com/lite-xl/lite-xl/releases) and download the version for your operating system. -### Ubuntu +### Linux Unzip the file and `cd` into the `lite-xl` directory: @@ -90,7 +90,13 @@ tar -xzf cd lite-xl ``` -Copy files over into appropriate directories: +To run lite-xl without installing: +```sh +cd bin +./lite-xl +``` + +To install lite-xl copy files over into appropriate directories: ```sh mkdir -p $HOME/.local/bin && cp bin/lite-xl $HOME/.local/bin @@ -118,11 +124,9 @@ rm -f $HOME/.local/bin/lite-xl rm -rf $HOME/.local/share/icons/hicolor/scalable/apps/lite-xl.svg \ $HOME/.local/share/applications/org.lite_xl.lite_xl.desktop \ $HOME/.local/share/metainfo/org.lite_xl.lite_xl.appdata.xml \ - $HOME/.local/share/lite-xl \ - $HOME/.local/share/doc/lite-xl + $HOME/.local/share/lite-xl ``` -You may need to `Alt + F2` and enter 'r' to see changes. ## Contributing From 1376eaf54d3462a7dd144c1c8ca44ed48aaa07f9 Mon Sep 17 00:00:00 2001 From: Adam Harrison Date: Sun, 14 Nov 2021 15:40:23 -0500 Subject: [PATCH 165/165] Made varaible anonymous. --- data/core/init.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/core/init.lua b/data/core/init.lua index 6dd54cd4..13d0907b 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -685,7 +685,7 @@ function core.load_plugins() end table.sort(ordered) - for i, filename in ipairs(ordered) do + for _, filename in ipairs(ordered) do local plugin_dir, basename = files[filename], filename:match("(.-)%.lua$") or filename local is_lua_file, version_match = check_plugin_version(plugin_dir .. '/' .. filename) if is_lua_file then