diff options
57 files changed, 2092 insertions, 3310 deletions
diff --git a/.github/labeler.yml b/.github/labeler.yml new file mode 100644 index 00000000..41f66c7c --- /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 @@ -3,7 +3,6 @@ build*/ lhelper/ submodules/ subprojects/lua/ -subprojects/libagg/ subprojects/reproc/ /appimage* .ccls-cache @@ -77,6 +77,57 @@ 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. + +### Linux + +Unzip the file and `cd` into the `lite-xl` directory: + +```sh +tar -xzf <file> +cd lite-xl +``` + +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 +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 +``` + + ## Contributing Any additional functionality that can be added through a plugin should be done diff --git a/data/core/command.lua b/data/core/command.lua index 7915e16d..2531fb96 100644 --- a/data/core/command.lua +++ b/data/core/command.lua @@ -42,10 +42,10 @@ function command.get_all_valid() end -local function perform(name) +local function perform(name, ...) local cmd = command.map[name] - if cmd and cmd.predicate() then - cmd.perform() + if cmd and cmd.predicate(...) then + cmd.perform(...) return true end return false diff --git a/data/core/commands/doc.lua b/data/core/commands/doc.lua index b8ce2cb5..fe1fa3b1 100644 --- a/data/core/commands/doc.lua +++ b/data/core/commands/doc.lua @@ -82,6 +82,16 @@ local function split_cursor(direction) core.blink_reset() end +local function set_cursor(x, y, snap_type) + local line, col = dv():resolve_screen_position(x, y) + doc():set_selection(line, col, line, col) + if snap_type == "word" or snap_type == "lines" then + command.perform("doc:select-" .. snap_type) + end + dv().mouse_selecting = { line, col, snap_type } + core.blink_reset() +end + local commands = { ["doc:undo"] = function() doc():undo() @@ -388,6 +398,30 @@ local commands = { os.remove(filename) core.log("Removed \"%s\"", filename) end, + + ["doc:select-to-cursor"] = function(x, y, clicks) + local line1, col1 = select(3, doc():get_selection()) + local line2, col2 = dv():resolve_screen_position(x, y) + dv().mouse_selecting = { line1, col1, nil } + doc():set_selection(line2, col2, line1, col1) + end, + + ["doc:set-cursor"] = function(x, y) + set_cursor(x, y, "set") + end, + + ["doc:set-cursor-word"] = function(x, y) + set_cursor(x, y, "word") + end, + + ["doc:set-cursor-line"] = function(x, y, clicks) + set_cursor(x, y, "lines") + end, + + ["doc:split-cursor"] = function(x, y, clicks) + local line, col = dv():resolve_screen_position(x, y) + doc():add_selection(line, col, line, col) + end, ["doc:create-cursor-previous-line"] = function() split_cursor(-1) diff --git a/data/core/commands/findreplace.lua b/data/core/commands/findreplace.lua index 5d27aa69..8fcb8dbc 100644 --- a/data/core/commands/findreplace.lua +++ b/data/core/commands/findreplace.lua @@ -7,7 +7,8 @@ local DocView = require "core.docview" local CommandView = require "core.commandview" local StatusView = require "core.statusview" -local last_view, last_fn, last_text, last_sel +local max_last_finds = 50 +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 @@ -37,7 +38,7 @@ local function update_preview(sel, search_fn, text) last_view:scroll_to_line(line2, true) found_expression = true else - last_view.doc:set_selection(unpack(sel)) + last_view.doc:set_selection(table.unpack(sel)) found_expression = false end end @@ -53,9 +54,9 @@ end local function find(label, search_fn) - last_view, last_sel = core.active_view, - { core.active_view.doc:get_selection() } - local text = last_view.doc:get_text(unpack(last_sel)) + last_view, last_sel, last_finds = core.active_view, + { core.active_view.doc:get_selection() }, {} + local text = last_view.doc:get_text(table.unpack(last_sel)) found_expression = false core.command_view:set_text(text, true) @@ -69,8 +70,8 @@ local function find(label, search_fn) last_fn, last_text = search_fn, text else core.error("Couldn't find %q", text) - last_view.doc:set_selection(unpack(last_sel)) - last_view:scroll_to_make_visible(unpack(last_sel)) + last_view.doc:set_selection(table.unpack(last_sel)) + last_view:scroll_to_make_visible(table.unpack(last_sel)) end end, function(text) update_preview(last_sel, search_fn, text) @@ -79,8 +80,8 @@ local function find(label, search_fn) end, function(explicit) core.status_view:remove_tooltip() if explicit then - last_view.doc:set_selection(unpack(last_sel)) - last_view:scroll_to_make_visible(unpack(last_sel)) + last_view.doc:set_selection(table.unpack(last_sel)) + last_view:scroll_to_make_visible(table.unpack(last_sel)) end end) end diff --git a/data/core/commands/root.lua b/data/core/commands/root.lua index 49bf774a..5bf18390 100644 --- a/data/core/commands/root.lua +++ b/data/core/commands/root.lua @@ -3,6 +3,7 @@ local style = require "core.style" local DocView = require "core.docview" local command = require "core.command" local common = require "core.common" +local config = require "core.config" local t = { @@ -63,7 +64,7 @@ local t = { table.insert(node.views, idx + 1, core.active_view) end end, - + ["root:shrink"] = function() local node = core.root_view:get_active_node() local parent = node:get_parent_node(core.root_view.root_node) @@ -76,7 +77,7 @@ local t = { local parent = node:get_parent_node(core.root_view.root_node) local n = (parent.a == node) and 0.1 or -0.1 parent.divider = common.clamp(parent.divider + n, 0.1, 0.9) - end, + end } @@ -124,3 +125,14 @@ command.add(function() local sx, sy = node:get_locked_size() return not sx and not sy end, t) + +command.add(nil, { + ["root:scroll"] = function(delta) + local view = (core.root_view.overlapping_node and core.root_view.overlapping_node.active_view) or core.active_view + if view and view.scrollable then + view.scroll.to.y = view.scroll.to.y + delta * -config.mouse_wheel_scroll + return true + end + return false + end +}) diff --git a/data/core/config.lua b/data/core/config.lua index 3de48ee1..71e83994 100644 --- a/data/core/config.lua +++ b/data/core/config.lua @@ -27,6 +27,7 @@ config.disable_blink = false config.draw_whitespace = false config.borderless = false config.tab_close_button = true +config.max_clicks = 3 -- Disable plugin loading setting to false the config entry -- of the same name. @@ -34,5 +35,6 @@ config.plugins = {} config.plugins.trimwhitespace = false config.plugins.lineguide = false +config.plugins.drawwhitespace = false return config diff --git a/data/core/contextmenu.lua b/data/core/contextmenu.lua index 36247597..d6131cdf 100644 --- a/data/core/contextmenu.lua +++ b/data/core/contextmenu.lua @@ -49,7 +49,7 @@ function ContextMenu:register(predicate, items) local width, height = 0, 0 --precalculate the size of context menu for i, item in ipairs(items) do if item ~= DIVIDER then - item.info = keymap.reverse_map[item.command] + item.info = keymap.get_binding(item.command) end local lw, lh = get_item_size(item) width = math.max(width, lw) diff --git a/data/core/doc/init.lua b/data/core/doc/init.lua index 06cde9f9..03dcc31e 100644 --- a/data/core/doc/init.lua +++ b/data/core/doc/init.lua @@ -105,7 +105,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 @@ -201,9 +205,9 @@ local function selection_iterator(invariant, idx) local target = invariant[3] and (idx*4 - 7) or (idx*4 + 1) if target > #invariant[1] or target <= 0 or (type(invariant[3]) == "number" and invariant[3] ~= idx - 1) then return end if invariant[2] then - return idx+(invariant[3] and -1 or 1), sort_positions(unpack(invariant[1], target, target+4)) + return idx+(invariant[3] and -1 or 1), sort_positions(table.unpack(invariant[1], target, target+4)) else - return idx+(invariant[3] and -1 or 1), unpack(invariant[1], target, target+4) + return idx+(invariant[3] and -1 or 1), table.unpack(invariant[1], target, target+4) end end @@ -348,7 +352,7 @@ function Doc:raw_insert(line, col, text, undo_stack, time) -- 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, "selection", table.unpack(self.selections)) push_undo(undo_stack, time, "remove", line, col, line2, col2) -- update highlighter and assure selection is in bounds @@ -360,7 +364,7 @@ end function Doc:raw_remove(line1, col1, line2, col2, undo_stack, time) -- push undo local text = self:get_text(line1, col1, line2, col2) - push_undo(undo_stack, time, "selection", unpack(self.selections)) + push_undo(undo_stack, time, "selection", table.unpack(self.selections)) push_undo(undo_stack, time, "insert", line1, col1, text) -- get line content before/after removed text diff --git a/data/core/docview.lua b/data/core/docview.lua index a73bfc9c..487a7784 100644 --- a/data/core/docview.lua +++ b/data/core/docview.lua @@ -153,14 +153,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 @@ -169,14 +169,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 @@ -227,51 +225,6 @@ function DocView:scroll_to_make_visible(line, col) end -local function mouse_selection(doc, clicks, line1, col1, line2, col2) - local swap = line2 < line1 or line2 == line1 and col2 <= col1 - if swap then - line1, col1, line2, col2 = line2, col2, line1, col1 - end - if clicks % 4 == 2 then - line1, col1 = translate.start_of_word(doc, line1, col1) - line2, col2 = translate.end_of_word(doc, line2, col2) - elseif clicks % 4 == 3 then - if line2 == #doc.lines and doc.lines[#doc.lines] ~= "\n" then - doc:insert(math.huge, math.huge, "\n") - end - line1, col1, line2, col2 = line1, 1, line2 + 1, 1 - end - if swap then - return line2, col2, line1, col1 - end - return line1, col1, line2, col2 -end - - -function DocView:on_mouse_pressed(button, x, y, clicks) - local caught = DocView.super.on_mouse_pressed(self, button, x, y, clicks) - if caught then - return - end - if keymap.modkeys["shift"] then - if clicks % 2 == 1 then - local line1, col1 = select(3, self.doc:get_selection()) - local line2, col2 = self:resolve_screen_position(x, y) - self.doc:set_selection(line2, col2, line1, col1) - end - else - local line, col = self:resolve_screen_position(x, y) - if keymap.modkeys["ctrl"] then - self.doc:add_selection(mouse_selection(self.doc, clicks, line, col, line, col)) - else - self.doc:set_selection(mouse_selection(self.doc, clicks, line, col, line, col)) - end - self.mouse_selecting = { line, col, clicks = clicks } - end - core.blink_reset() -end - - function DocView:on_mouse_moved(x, y, ...) DocView.super.on_mouse_moved(self, x, y, ...) @@ -283,8 +236,7 @@ function DocView:on_mouse_moved(x, y, ...) if self.mouse_selecting then local l1, c1 = self:resolve_screen_position(x, y) - local l2, c2 = table.unpack(self.mouse_selecting) - local clicks = self.mouse_selecting.clicks + local l2, c2, snap_type = table.unpack(self.mouse_selecting) if keymap.modkeys["ctrl"] then if l1 > l2 then l1, l2 = l2, l1 end self.doc.selections = { } @@ -292,12 +244,33 @@ function DocView:on_mouse_moved(x, y, ...) self.doc:set_selections(i - l1 + 1, i, math.min(c1, #self.doc.lines[i]), i, math.min(c2, #self.doc.lines[i])) end else - self.doc:set_selection(mouse_selection(self.doc, clicks, l1, c1, l2, c2)) + if snap_type then + l1, c1, l2, c2 = self:mouse_selection(self.doc, snap_type, l1, c1, l2, c2) + end + self.doc:set_selection(l1, c1, l2, c2) end end end +function DocView:mouse_selection(doc, snap_type, line1, col1, line2, col2) + local swap = line2 < line1 or line2 == line1 and col2 <= col1 + if swap then + line1, col1, line2, col2 = line2, col2, line1, col1 + end + if snap_type == "word" then + line1, col1 = translate.start_of_word(doc, line1, col1) + line2, col2 = translate.end_of_word(doc, line2, col2) + elseif snap_type == "lines" then + col1, col2 = 1, math.huge + end + if swap then + return line2, col2, line1, col1 + end + return line1, col1, line2, col2 +end + + function DocView:on_mouse_released(button) DocView.super.on_mouse_released(self, button) self.mouse_selecting = nil @@ -342,16 +315,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 @@ -361,6 +329,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 @@ -375,15 +355,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) diff --git a/data/core/init.lua b/data/core/init.lua index 4a16e6b7..daec49a6 100644 --- a/data/core/init.lua +++ b/data/core/init.lua @@ -577,15 +577,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 @@ -683,7 +674,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() @@ -871,16 +861,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 _, 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 @@ -1194,11 +1186,15 @@ function core.on_event(type, ...) elseif type == "mousemoved" then core.root_view:on_mouse_moved(...) elseif type == "mousepressed" then - core.root_view:on_mouse_pressed(...) + if not core.root_view:on_mouse_pressed(...) then + did_keymap = keymap.on_mouse_pressed(...) + end elseif type == "mousereleased" then core.root_view:on_mouse_released(...) elseif type == "mousewheel" then - core.root_view:on_mouse_wheel(...) + if not core.root_view:on_mouse_wheel(...) then + did_keymap = keymap.on_mouse_wheel(...) + end elseif type == "resized" then core.window_mode = system.get_window_mode() elseif type == "minimized" or type == "maximized" or type == "restored" then diff --git a/data/core/keymap-macos.lua b/data/core/keymap-macos.lua index 53a20468..b0bd41a5 100644 --- a/data/core/keymap-macos.lua +++ b/data/core/keymap-macos.lua @@ -32,6 +32,8 @@ local function keymap_macos(keymap) ["cmd+7"] = "root:switch-to-tab-7", ["cmd+8"] = "root:switch-to-tab-8", ["cmd+9"] = "root:switch-to-tab-9", + ["wheel"] = "root:scroll", + ["cmd+f"] = "find-replace:find", ["cmd+r"] = "find-replace:replace", ["f3"] = "find-replace:repeat-find", @@ -93,6 +95,11 @@ local function keymap_macos(keymap) ["pageup"] = "doc:move-to-previous-page", ["pagedown"] = "doc:move-to-next-page", + ["shift+1lclick"] = "doc:select-to-cursor", + ["ctrl+1lclick"] = "doc:split-cursor", + ["1lclick"] = "doc:set-cursor", + ["2lclick"] = "doc:set-cursor-word", + ["3lclick"] = "doc:set-cursor-line", ["shift+left"] = "doc:select-to-previous-char", ["shift+right"] = "doc:select-to-next-char", ["shift+up"] = "doc:select-to-previous-line", diff --git a/data/core/keymap.lua b/data/core/keymap.lua index 0c2dd863..b076629b 100644 --- a/data/core/keymap.lua +++ b/data/core/keymap.lua @@ -1,4 +1,5 @@ local command = require "core.command" +local config = require "core.config" local keymap = {} keymap.modkeys = {} @@ -30,7 +31,8 @@ function keymap.add_direct(map) end keymap.map[stroke] = commands for _, cmd in ipairs(commands) do - keymap.reverse_map[cmd] = stroke + keymap.reverse_map[cmd] = keymap.reverse_map[cmd] or {} + table.insert(keymap.reverse_map[cmd], stroke) end end end @@ -52,18 +54,43 @@ function keymap.add(map, overwrite) end end for _, cmd in ipairs(commands) do - keymap.reverse_map[cmd] = stroke + keymap.reverse_map[cmd] = keymap.reverse_map[cmd] or {} + table.insert(keymap.reverse_map[cmd], stroke) end end end +local function remove_only(tbl, k, v) + for key, values in pairs(tbl) do + if key == k then + if v then + for i, value in ipairs(values) do + if value == v then + table.remove(values, i) + end + end + else + tbl[key] = nil + end + break + end + end +end + + +function keymap.unbind(key, cmd) + remove_only(keymap.map, key, cmd) + remove_only(keymap.reverse_map, cmd, key) +end + + function keymap.get_binding(cmd) - return keymap.reverse_map[cmd] + return table.unpack(keymap.reverse_map[cmd] or {}) end -function keymap.on_key_pressed(k) +function keymap.on_key_pressed(k, ...) local mk = modkey_map[k] if mk then keymap.modkeys[mk] = true @@ -73,18 +100,30 @@ function keymap.on_key_pressed(k) end else local stroke = key_to_stroke(k) - local commands = keymap.map[stroke] + local commands, performed = keymap.map[stroke] if commands then for _, cmd in ipairs(commands) do - local performed = command.perform(cmd) + performed = command.perform(cmd, ...) if performed then break end end - return true + return performed end end return false end +function keymap.on_mouse_wheel(delta, ...) + return not (keymap.on_key_pressed("wheel" .. (delta > 0 and "up" or "down"), delta, ...) + or keymap.on_key_pressed("wheel", delta, ...)) +end + +function keymap.on_mouse_pressed(button, x, y, clicks) + local click_number = (((clicks - 1) % config.max_clicks) + 1) + return not (keymap.on_key_pressed(click_number .. button:sub(1,1) .. "click", x, y, clicks) or + keymap.on_key_pressed(button:sub(1,1) .. "click", x, y, clicks) or + keymap.on_key_pressed(click_number .. "click", x, y, clicks) or + keymap.on_key_pressed("click", x, y, clicks)) +end function keymap.on_key_released(k) local mk = modkey_map[k] @@ -133,6 +172,7 @@ keymap.add_direct { ["alt+7"] = "root:switch-to-tab-7", ["alt+8"] = "root:switch-to-tab-8", ["alt+9"] = "root:switch-to-tab-9", + ["wheel"] = "root:scroll", ["ctrl+f"] = "find-replace:find", ["ctrl+r"] = "find-replace:replace", @@ -194,6 +234,11 @@ keymap.add_direct { ["pageup"] = "doc:move-to-previous-page", ["pagedown"] = "doc:move-to-next-page", + ["shift+1lclick"] = "doc:select-to-cursor", + ["ctrl+1lclick"] = "doc:split-cursor", + ["1lclick"] = "doc:set-cursor", + ["2lclick"] = "doc:set-cursor-word", + ["3lclick"] = "doc:set-cursor-line", ["shift+left"] = "doc:select-to-previous-char", ["shift+right"] = "doc:select-to-next-char", ["shift+up"] = "doc:select-to-previous-line", diff --git a/data/core/rootview.lua b/data/core/rootview.lua index 51eedc10..49da2923 100644 --- a/data/core/rootview.lua +++ b/data/core/rootview.lua @@ -879,27 +879,29 @@ function RootView:on_mouse_pressed(button, x, y, clicks) local div = self.root_node:get_divider_overlapping_point(x, y) if div then self.dragged_divider = div - return + return true end local node = self.root_node:get_child_overlapping_point(x, y) if node.hovered_scroll_button > 0 then node:scroll_tabs(node.hovered_scroll_button) - return + return true end local idx = node:get_tab_overlapping_point(x, y) if idx then if button == "middle" or node.hovered_close == idx then node:close_view(self.root_node, node.views[idx]) + return true else 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]) + return true end 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) + return node.active_view:on_mouse_pressed(button, x, y, clicks) end end end @@ -1022,17 +1024,18 @@ function RootView:on_mouse_moved(x, y, dx, dy) self.root_node:on_mouse_moved(x, y, dx, dy) - local node = self.root_node:get_child_overlapping_point(x, y) + self.overlapping_node = self.root_node:get_child_overlapping_point(x, y) + local div = self.root_node:get_divider_overlapping_point(x, y) - local tab_index = node and node:get_tab_overlapping_point(x, y) - if node and node:get_scroll_button_index(x, y) then + local tab_index = self.overlapping_node and self.overlapping_node:get_tab_overlapping_point(x, y) + if self.overlapping_node and self.overlapping_node:get_scroll_button_index(x, y) then core.request_cursor("arrow") elseif div then core.request_cursor(div.type == "hsplit" and "sizeh" or "sizev") elseif tab_index then core.request_cursor("arrow") - elseif node then - core.request_cursor(node.active_view.cursor) + elseif self.overlapping_node then + core.request_cursor(self.overlapping_node.active_view.cursor) end end @@ -1040,7 +1043,7 @@ end function RootView:on_mouse_wheel(...) local x, y = self.mouse.x, self.mouse.y local node = self.root_node:get_child_overlapping_point(x, y) - node.active_view:on_mouse_wheel(...) + return node.active_view:on_mouse_wheel(...) end diff --git a/data/core/start.lua b/data/core/start.lua index 24b6d978..31fed147 100644 --- a/data/core/start.lua +++ b/data/core/start.lua @@ -20,3 +20,14 @@ package.path = DATADIR .. '/?/init.lua;' .. package.path package.path = USERDIR .. '/?.lua;' .. package.path 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.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 +end } + +table.pack = table.pack or pack or function(...) return {...} end +table.unpack = table.unpack or unpack 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/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" } diff --git a/data/core/tokenizer.lua b/data/core/tokenizer.lua index d95baeb1..f77fed44 100644 --- a/data/core/tokenizer.lua +++ b/data/core/tokenizer.lua @@ -161,7 +161,7 @@ function tokenizer.tokenize(incoming_syntax, text, state) if count % 2 == 0 then break end end until not res[1] or not close or not target[3] - return unpack(res) + return table.unpack(res) end while i <= #text do diff --git a/data/core/view.lua b/data/core/view.lua index 5b4b3228..4b787d46 100644 --- a/data/core/view.lua +++ b/data/core/view.lua @@ -102,13 +102,9 @@ function View:on_text_input(text) -- no-op end - function View:on_mouse_wheel(y) - if self.scrollable then - self.scroll.to.y = self.scroll.to.y + y * -config.mouse_wheel_scroll - end -end +end function View:get_content_bounds() local x = self.scroll.x diff --git a/data/plugins/drawwhitespace.lua b/data/plugins/drawwhitespace.lua new file mode 100644 index 00000000..7b7fa011 --- /dev/null +++ b/data/plugins/drawwhitespace.lua @@ -0,0 +1,36 @@ +-- 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 = y + self:get_line_text_y_offset() + local tx + local text, offset, s, e = self.doc.lines[idx], 1 + local x1, _, x2, _ = self:get_content_bounds() + local _offset = self:get_x_offset_col(idx, x1) + offset = _offset + 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) + if tx > x + x2 then break end + offset = e + 1 + end + offset = _offset + 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) + if tx > x + x2 then break end + offset = e + 1 + end + draw_line_text(self, idx, x, y) +end diff --git a/data/plugins/language_js.lua b/data/plugins/language_js.lua index 291c1287..d9515d52 100644 --- a/data/plugins/language_js.lua +++ b/data/plugins/language_js.lua @@ -12,8 +12,8 @@ syntax.add { { pattern = { '"', '"', '\\' }, type = "string" }, { pattern = { "'", "'", '\\' }, type = "string" }, { pattern = { "`", "`", '\\' }, type = "string" }, - { pattern = "0x[%da-fA-F]+", type = "number" }, - { pattern = "-?%d+[%d%.eE]*", type = "number" }, + { pattern = "0x[%da-fA-F_]+n?", type = "number" }, + { pattern = "-?%d+[%d%.eE_n]*", type = "number" }, { pattern = "-?%.?%d+", type = "number" }, { pattern = "[%+%-=/%*%^%%<>!~|&]", type = "operator" }, { pattern = "[%a_][%w_]*%f[(]", type = "function" }, diff --git a/data/plugins/lineguide.lua b/data/plugins/lineguide.lua index 61debbff..9f2fca4a 100644 --- a/data/plugins/lineguide.lua +++ b/data/plugins/lineguide.lua @@ -6,9 +6,7 @@ local DocView = require "core.docview" 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("n") * config.line_limit local x = self:get_line_screen_position(1) + offset local y = self.position.y local w = math.ceil(SCALE * 1) diff --git a/data/plugins/projectsearch.lua b/data/plugins/projectsearch.lua index dda3a2d0..d0d75d7f 100644 --- a/data/plugins/projectsearch.lua +++ b/data/plugins/projectsearch.lua @@ -92,7 +92,7 @@ end function ResultsView:on_mouse_pressed(...) local caught = ResultsView.super.on_mouse_pressed(self, ...) if not caught then - self:open_selected_result() + return self:open_selected_result() end end @@ -108,6 +108,7 @@ function ResultsView:open_selected_result() dv.doc:set_selection(res.line, res.col) dv:scroll_to_line(res.line, false, true) end) + return true end diff --git a/data/plugins/scale.lua b/data/plugins/scale.lua index 37d4cac9..616ee40b 100644 --- a/data/plugins/scale.lua +++ b/data/plugins/scale.lua @@ -44,10 +44,14 @@ 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 + renderer.font.set_size(font, s * font:get_size()) end for _, font in pairs(style.syntax_fonts) do @@ -67,17 +71,6 @@ local function get_scale() return current_scale end -local on_mouse_wheel = RootView.on_mouse_wheel - -function RootView:on_mouse_wheel(d, ...) - if keymap.modkeys["ctrl"] and config.plugins.scale.use_mousewheel then - if d < 0 then command.perform "scale:decrease" end - if d > 0 then command.perform "scale:increase" end - else - return on_mouse_wheel(self, d, ...) - end -end - local function res_scale() set_scale(default_scale) end @@ -101,6 +94,8 @@ keymap.add { ["ctrl+0"] = "scale:reset", ["ctrl+-"] = "scale:decrease", ["ctrl+="] = "scale:increase", + ["ctrl+wheelup"] = "scale:increase", + ["ctrl+wheeldown"] = "scale:decrease" } return { diff --git a/data/plugins/treeview.lua b/data/plugins/treeview.lua index 77b6732f..659393ec 100644 --- a/data/plugins/treeview.lua +++ b/data/plugins/treeview.lua @@ -280,6 +280,12 @@ function TreeView:draw_tooltip() end +function TreeView:color_for_item(abs_filename) + -- other plugins can override this to customize the color of each icon + return nil +end + + function TreeView:draw() self:draw_background(style.background2) @@ -303,6 +309,9 @@ function TreeView:draw() color = style.accent end + -- allow for color overrides + local icon_color = self:color_for_item(item.abs_filename) or color + -- icons x = x + item.depth * style.padding.x + style.padding.x if item.type == "dir" then @@ -310,11 +319,11 @@ function TreeView:draw() local icon2 = item.expanded and "D" or "d" common.draw_text(style.icon_font, color, icon1, nil, x, y, 0, h) x = x + style.padding.x - common.draw_text(style.icon_font, color, icon2, nil, x, y, 0, h) + common.draw_text(style.icon_font, icon_color, icon2, nil, x, y, 0, h) x = x + icon_width else x = x + style.padding.x - common.draw_text(style.icon_font, color, "f", nil, x, y, 0, h) + common.draw_text(style.icon_font, icon_color, "f", nil, x, y, 0, h) x = x + icon_width end 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 = {} --- 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 <stdio.h> -#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 <srg@quick.com> - // - - // 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<class PathStorage> - 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<class Scanline, class ScanlineStorage> - 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<class Rasterizer, class Scanline, class ScanlineStorage> - 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 <ft2build.h> -#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<int8u> 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<class GammaF> 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<int16, 6> m_path16; - path_storage_integer<int32, 6> m_path32; - conv_curve<path_storage_integer<int16, 6> > m_curves16; - conv_curve<path_storage_integer<int32, 6> > 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<int16, 6> 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<int32, 6> 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 <cstdlib> - -#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 <string.h> -#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 <typename Order> -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 <typename Order> -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<agg::order_bgra>(dst_ren_buf, x, y, glyph_width, color_a, covers); - } else { - blend_solid_hspan_subpixel<agg::order_bgra>(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 <stdint.h> - -#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<pixfmt_type> base_ren_type; - typedef agg::renderer_scanline_aa_solid<base_ren_type> renderer_solid; - typedef agg::font_engine_freetype_int32 font_engine_type; - typedef agg::font_cache_manager<font_engine_type> 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<font_manager_type::path_adaptor_type> m_curves; - agg::conv_transform<agg::conv_curve<font_manager_type::path_adaptor_type> > 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<class Rasterizer, class Scanline, class RenSolid> - 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 569de826..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'] - -lite_includes += 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/meson.build b/meson.build index 613204b7..8456fe60 100644 --- a/meson.build +++ b/meson.build @@ -1,9 +1,9 @@ project('lite-xl', - ['c', 'cpp'], + ['c'], version : '2.0.3', license : 'MIT', meson_version : '>= 0.54', - default_options : ['c_std=gnu11', 'cpp_std=c++03'] + default_options : ['c_std=gnu11'] ) #=============================================================================== @@ -34,7 +34,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' @@ -51,6 +51,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: [ @@ -59,12 +60,12 @@ if not get_option('source-only') ] ) - lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl, threads_dep] + lite_deps = [lua_dep, sdl_dep, reproc_dep, pcre2_dep, libm, libdl, freetype_dep, threads_dep] 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 #=============================================================================== @@ -120,7 +121,6 @@ configure_file( ) if not get_option('source-only') - subdir('lib/font_renderer') subdir('lib/dmon') subdir('src') subdir('scripts') diff --git a/resources/lite_xl_plugin_api.h b/resources/lite_xl_plugin_api.h new file mode 100644 index 00000000..44c1b4d3 --- /dev/null +++ b/resources/lite_xl_plugin_api.h @@ -0,0 +1,360 @@ +#ifndef LITE_XL_PLUGIN_API +#define LITE_XL_PLUGIN_API +/* +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_lite_xl_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 "/* " +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_lite_xl_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 <stddef.h>" +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 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;" +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 <stddef.h> +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_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 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; + 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); +} +#endif 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 +<http://dkolf.de/src/dkjson-lua.fsl/>. + +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 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..2e9bdb2e 100644 --- a/src/api/api.h +++ b/src/api/api.h @@ -1,12 +1,11 @@ #ifndef API_H #define API_H -#include "lua.h" -#include "lauxlib.h" -#include "lualib.h" +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> #define API_TYPE_FONT "Font" -#define API_TYPE_REPLACE "Replace" #define API_TYPE_PROCESS "Process" void api_load_libs(lua_State *L); 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/renderer.c b/src/api/renderer.c index ec3bc3ba..61057f78 100644 --- a/src/api/renderer.c +++ b/src/api/renderer.c @@ -2,6 +2,131 @@ #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, font_style = 0; + 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_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_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_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); + if (!*font) + return luaL_error(L, "failed to load font"); + luaL_setmetatable(L, API_TYPE_FONT); + return 1; +} + +static bool font_retrieve(lua_State* L, RenFont** fonts, int idx) { + 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]; + 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); + } + 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* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); + int n = luaL_checknumber(L, 2); + ren_font_group_set_tab_size(fonts, 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* 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* 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* fonts[FONT_FALLBACK_MAX]; font_retrieve(L, fonts, 1); + lua_pushnumber(L, ren_font_group_get_size(fonts)); + return 1; +} static RenColor checkcolor(lua_State *L, int idx, int def) { RenColor color; @@ -78,40 +203,18 @@ 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* fonts[FONT_FALLBACK_MAX]; + font_retrieve(L, fonts, 1); 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); + float x = 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); - lua_pushnumber(L, x_subpixel); + x = rencache_draw_text(L, fonts, text, x, y, color); + lua_pushnumber(L, x); 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 }, @@ -120,19 +223,27 @@ 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 }, + { "group", f_font_group }, + { "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 <lua.h> -#include <lauxlib.h> - -#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/api/system.c b/src/api/system.c index 0ebf78f1..c68caa3e 100644 --- a/src/api/system.c +++ b/src/api/system.c @@ -21,9 +21,11 @@ extern SDL_Window *window; static const char* button_name(int button) { switch (button) { - case 1 : return "left"; - case 2 : return "middle"; - case 3 : return "right"; + case SDL_BUTTON_LEFT : return "left"; + case SDL_BUTTON_MIDDLE : return "middle"; + case SDL_BUTTON_RIGHT : return "right"; + case SDL_BUTTON_X1 : return "x"; + case SDL_BUTTON_X2 : return "y"; default : return "?"; } } @@ -653,56 +655,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--; - } - 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++; + 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 >= 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++; + ptnTarget += increment; + } else { + score -= 10; + run = 0; } - if (*ptn) { return 0; } + strTarget += increment; } - - lua_pushnumber(L, score - (int)strLen); + if (ptnTarget >= ptn && *ptnTarget) { return 0; } + lua_pushnumber(L, score - (int)strLen * 10); return 1; } @@ -713,6 +691,86 @@ 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; +} 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), + 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), 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) + return nodes[i].address; + } + return NULL; +} + +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); + + 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(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 { + result = ext_entrypoint(L, api_require); + } + + if (!result) + return luaL_error(L, "Unable to load %s: entrypoint must return a value", name); + + return result; +} + static int f_watch_dir(lua_State *L) { const char *path = luaL_checkstring(L, 1); const int recursive = lua_toboolean(L, 2); @@ -825,6 +883,7 @@ static const luaL_Reg lib[] = { { "exec", f_exec }, { "fuzzy_match", f_fuzzy_match }, { "set_window_opacity", f_set_window_opacity }, + { "load_native_plugin", f_load_native_plugin }, { "watch_dir", f_watch_dir }, { "path_compare", f_path_compare }, #if __linux__ 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 <stddef.h> -#include <stdlib.h> - -#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 - @@ -34,8 +34,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__ diff --git a/src/meson.build b/src/meson.build index 503fdc48..1eaf87fd 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,15 +1,12 @@ 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', 'dirmonitor.c', 'renderer.c', 'renwindow.c', - 'fontdesc.c', 'rencache.c', 'main.c', ] @@ -30,7 +27,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, diff --git a/src/rencache.c b/src/rencache.c index 5a64a5ea..e9339ecb 100644 --- a/src/rencache.c +++ b/src/rencache.c @@ -1,6 +1,7 @@ #include <stddef.h> #include <stdint.h> #include <stdio.h> +#include <stdalign.h> #include <lauxlib.h> #include "rencache.h" @@ -16,32 +17,19 @@ #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 *fonts[FONT_FALLBACK_MAX]; + float text_x; 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 +40,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 @@ -124,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) { @@ -170,35 +133,23 @@ 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) +float rencache_draw_text(lua_State *L, RenFont **fonts, const char *text, float 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) { + 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_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; + memcpy(cmd->fonts, fonts, sizeof(RenFont*)*FONT_FALLBACK_MAX); 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->text_x = x; + cmd->tab_size = ren_font_group_get_tab_size(fonts); } } - - return x + (draw_subpixel ? w_subpixel : rect.width); + return x + width; } @@ -216,7 +167,6 @@ void rencache_begin_frame(lua_State *L) { screen_rect.height = h; rencache_invalidate(); } - font_refs_clear(L); } @@ -303,15 +253,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_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 1d0f45a6..75bb5051 100644 --- a/src/rencache.h +++ b/src/rencache.h @@ -5,13 +5,13 @@ #include <lua.h> #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, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color, - bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_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 cd269687..5aa90ff1 100644 --- a/src/renderer.c +++ b/src/renderer.c @@ -2,37 +2,20 @@ #include <stdbool.h> #include <assert.h> #include <math.h> -#include "font_renderer.h" +#include <ft2build.h> +#include <freetype/ftlcdfil.h> +#include <freetype/ftoutln.h> +#include FT_FREETYPE_H + #include "renderer.h" #include "renwindow.h" #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 MAX_LOADABLE_GLYPHSETS 1024 +#define SUBPIXEL_BITMAPS_CACHED 3 static RenWindow window_renderer = {0}; +static FT_Library library; static void* check_alloc(void *ptr) { if (!ptr) { @@ -42,6 +25,29 @@ static void* check_alloc(void *ptr) { return ptr; } +/************************* Fonts *************************/ + +typedef struct { + unsigned short x0, x1, y0, y1, loaded; + short bitmap_left, bitmap_top; + float xadvance; +} GlyphMetric; + +typedef struct { + SDL_Surface* surface; + GlyphMetric metrics[MAX_GLYPHSET]; +} GlyphSet; + +typedef struct RenFont { + FT_Face face; + GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS]; + float size, space_advance, tab_advance; + short max_height; + bool subpixel; + ERenFontHinting hinting; + unsigned char style; + char path[0]; +} RenFont; static const char* utf8_to_codepoint(const char *p, unsigned *dst) { unsigned res, n; @@ -59,213 +65,235 @@ 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; -} - - -void ren_cp_replace_free(CPReplaceTable *rep_table) { - free(rep_table->replacements); +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_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) { + 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: + case FONT_HINTING_FULL: FT_Library_SetLcdFilterWeights(library, weights); 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); + return 0; } - -void ren_update_rects(RenRect *rects, int count) { - static bool initial_frame = true; - if (initial_frame) { - renwin_show_window(&window_renderer); - initial_frame = false; +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); } - 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; + return 0; } - -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); +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; + 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 < 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; + 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, 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; + } + 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 < 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; + FT_GlyphSlot slot = font->face->glyph; + 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) { + 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); + } + } } - return font->sets[idx]; } +static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) { + 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]; +} -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; +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]; } - FR_Renderer_Free(font->renderer); - return 0; + if (!(*metric)->loaded && codepoint > 0xFF && codepoint != 0x25A1) + return font_group_get_glyph(set, metric, fonts, 0x25A1, bitmap_index); + return fonts[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; - } - 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); +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; - } - 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; - + 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)); + strcpy(font->path, path); + font->face = face; + font->size = size; + font->subpixel = subpixel; + font->hinting = hinting; + font->style = style; + font->space_advance = (int)font_get_glyphset(font, ' ', 0)->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, font->style); +} -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); +void ren_font_free(RenFont* font) { + 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]); + } } } - FR_Renderer_Free(font->renderer); + 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_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_get_font_tab_size(RenFont *font) { - GlyphSet *set = get_glyphset(font, '\t'); - return set->glyphs['\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_group_get_size(RenFont **fonts) { + return fonts[0]->size; +} +int ren_font_group_get_height(RenFont **fonts) { + return fonts[0]->size + 3; +} -/* 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; +float ren_font_group_get_width(RenFont **fonts, const char *text) { + float width = 0; + const char* end = text + strlen(text); + GlyphMetric* metric = NULL; GlyphSet* set = NULL; + 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; } - /* 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 (x + x_scale_to_points / 2) / x_scale_to_points; + 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; -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; + 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; + 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 = 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; + 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) { + 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); + 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 + 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 & 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 << surface->format->Ashift | r << surface->format->Rshift | g << surface->format->Gshift | b << surface->format->Bshift; + } + } + } + pen_x += metric->xadvance ? metric->xadvance : font->space_advance; + } + 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; } - +/******************* 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; @@ -274,16 +302,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; } @@ -307,97 +325,63 @@ 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 { - rect_draw_loop(blend_pixel(*d, color)); + 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, translated_color); + d += dr; + } } } +/*************** 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_set_clip_rect(RenRect rect) { + renwin_set_clip_rect(&window_renderer, rect); } -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_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; } - -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; -} diff --git a/src/renderer.h b/src/renderer.h index bab059bb..6058583e 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -3,37 +3,26 @@ #include <SDL.h> #include <stdint.h> -#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 <stdbool.h> +#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; 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, unsigned char style); +RenFont* ren_font_copy(RenFont* font, float size); +void ren_font_free(RenFont *font); +int ren_font_group_get_tab_size(RenFont **font); +int ren_font_group_get_height(RenFont **font); +float ren_font_group_get_size(RenFont **font); +void ren_font_group_set_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); void ren_init(SDL_Window *win); void ren_resize_window(); @@ -42,27 +31,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 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 |
