Customized fork of github.com/rxi/lite

Compare changes

Choose any two refs to compare.

+16
.editorconfig
··· 1 + root = true 2 + 3 + [*.{c,h}] 4 + charset = utf-8 5 + end_of_line = lf 6 + indent_style = tab 7 + insert_final_newline = true 8 + trim_trailing_whitespace = true 9 + 10 + [*.{lua}] 11 + charset = utf-8 12 + end_of_line = lf 13 + indent_style = space 14 + indent_size = 2 15 + insert_final_newline = true 16 + trim_trailing_whitespace = true
+2
.gitignore
··· 1 + /lite 2 + /*.o
-19
LICENSE
··· 1 - Copyright (c) 2020 rxi 2 - 3 - Permission is hereby granted, free of charge, to any person obtaining a copy of 4 - this software and associated documentation files (the "Software"), to deal in 5 - the Software without restriction, including without limitation the rights to 6 - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 - of the Software, and to permit persons to whom the Software is furnished to do 8 - so, subject to the following conditions: 9 - 10 - The above copyright notice and this permission notice shall be included in all 11 - copies or substantial portions of the Software. 12 - 13 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 - SOFTWARE.
-39
README.md
··· 1 - # lite 2 - ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 3 - 4 - A lightweight text editor written in Lua 5 - 6 - * **[Get lite](https://github.com/rxi/lite/releases/latest)** โ€” Download 7 - for Windows and Linux 8 - * **[Get plugins](https://github.com/rxi/lite-plugins)** โ€” Add additional 9 - functionality 10 - * **[Get color themes](https://github.com/rxi/lite-colors)** โ€” Add additional colors 11 - themes 12 - 13 - ## Overview 14 - lite is a lightweight text editor written mostly in Lua โ€” it aims to provide 15 - something practical, pretty, *small* and fast, implemented as simply as 16 - possible; easy to modify and extend, or to use without doing either. 17 - 18 - ## Customization 19 - Additional functionality can be added through plugins which are available from 20 - the [plugins repository](https://github.com/rxi/lite-plugins); additional color 21 - themes can be found in the [colors repository](https://github.com/rxi/lite-colors). 22 - The editor can be customized by making changes to the 23 - [user module](data/user/init.lua). 24 - 25 - ## Building 26 - You can build the project yourself on Linux using the provided `build.sh` 27 - script. Note that the project does not need to be rebuilt if you are only making 28 - changes to the Lua portion of the code. 29 - 30 - ## Contributing 31 - Any additional functionality that can be added through a plugin should be done 32 - so as a plugin, after which a pull request to the 33 - [plugins repository](https://github.com/rxi/lite-plugins) can be made. In hopes 34 - of remaining lightweight, pull requests adding additional functionality to the 35 - core will likely not be merged. Bug reports and bug fixes are welcome. 36 - 37 - ## License 38 - This project is free software; you can redistribute it and/or modify it under 39 - the terms of the MIT license. See [LICENSE](LICENSE) for details.
+16
build.bat
··· 1 + @echo off 2 + 3 + rem download this: 4 + rem https://nuwen.net/mingw.html 5 + 6 + echo compiling (windows)... 7 + 8 + windres res.rc -O coff -o res.res 9 + gcc src/*.c src/api/*.c src/lib/lua52/*.c src/lib/stb/*.c^ 10 + -O3 -s -std=gnu11 -fno-strict-aliasing -Isrc -DLUA_USE_POPEN^ 11 + -Iwinlib/SDL2-2.0.10/x86_64-w64-mingw32/include^ 12 + -lmingw32 -lm -lSDL2main -lSDL2 -Lwinlib/SDL2-2.0.10/x86_64-w64-mingw32/lib^ 13 + -mwindows res.res^ 14 + -o lite.exe 15 + 16 + echo done
+2 -2
build.sh
··· 1 1 #!/bin/bash 2 2 3 - cflags="-Wall -O3 -g -std=gnu11 -Isrc" 4 - lflags="-lSDL2 -lm" 3 + cflags="-Wall -O3 -g -std=gnu11 -fno-strict-aliasing -Isrc" 4 + lflags="-lSDL3 -lm" 5 5 6 6 if [[ $* == *windows* ]]; then 7 7 platform="windows"
+4
compile_flags.txt
··· 1 + -Wall 2 + -Isrc 3 + -fno-strict-aliasing 4 + -DLUA_USE_POSIX
+2 -2
data/core/commands/core.lua
··· 35 35 end) 36 36 end, 37 37 38 - ["core:command-finder"] = function() 38 + ["core:find-command"] = function() 39 39 local commands = command.get_all_valid() 40 40 core.command_view:enter("Do Command", function(text, item) 41 41 if item then ··· 54 54 end) 55 55 end, 56 56 57 - ["core:file-finder"] = function() 57 + ["core:find-file"] = function() 58 58 core.command_view:enter("Open File From Project", function(text, item) 59 59 text = item and item.text or text 60 60 core.root_view:open_doc(core.open_doc(text))
+7 -5
data/core/commands/doc.lua
··· 160 160 local line1, _, line2 = doc():get_selection(true) 161 161 if line1 == line2 then line2 = line2 + 1 end 162 162 local text = doc():get_text(line1, 1, line2, math.huge) 163 - text = text:gsub("\n[\t ]*", " ") 163 + text = text:gsub("(.-)\n[\t ]*", function(x) 164 + return x:find("^%s*$") and x or x .. " " 165 + end) 164 166 doc():insert(line1, 1, text) 165 167 doc():remove(line1, #text + 1, line2, math.huge) 166 168 if doc():has_selection() then ··· 320 322 local translations = { 321 323 ["previous-char"] = translate.previous_char, 322 324 ["next-char"] = translate.next_char, 323 - ["previous-word-boundary"] = translate.previous_word_boundary, 324 - ["next-word-boundary"] = translate.next_word_boundary, 325 - ["previous-start-of-block"] = translate.previous_start_of_block, 326 - ["next-start-of-block"] = translate.next_start_of_block, 325 + ["previous-word-start"] = translate.previous_word_start, 326 + ["next-word-end"] = translate.next_word_end, 327 + ["previous-block-start"] = translate.previous_block_start, 328 + ["next-block-end"] = translate.next_block_end, 327 329 ["start-of-doc"] = translate.start_of_doc, 328 330 ["end-of-doc"] = translate.end_of_doc, 329 331 ["start-of-line"] = translate.start_of_line,
+4 -3
data/core/commands/findreplace.lua
··· 45 45 else 46 46 core.error("Couldn't find %q", text) 47 47 dv.doc:set_selection(table.unpack(sel)) 48 + dv:scroll_to_make_visible(sel[1], sel[2]) 48 49 end 49 50 50 51 end, function(text) 51 52 local ok, line1, col1, line2, col2 = pcall(search_fn, dv.doc, sel[1], sel[2], text) 52 - if text == "" then 53 - dv.doc:set_selection(table.unpack(sel)) 54 - elseif ok and line1 then 53 + if ok and line1 and text ~= "" then 55 54 dv.doc:set_selection(line2, col2, line1, col1) 56 55 dv:scroll_to_line(line2, true) 57 56 found = true 58 57 else 58 + dv.doc:set_selection(table.unpack(sel)) 59 59 found = false 60 60 end 61 61 62 62 end, function(explicit) 63 63 if explicit then 64 64 dv.doc:set_selection(table.unpack(sel)) 65 + dv:scroll_to_make_visible(sel[1], sel[2]) 65 66 end 66 67 end) 67 68 end
+1 -1
data/core/doc/highlighter.lua
··· 46 46 47 47 48 48 function Highlighter:invalidate(idx) 49 - self.first_invalid_line = idx 49 + self.first_invalid_line = math.min(self.first_invalid_line, idx) 50 50 self.max_wanted_line = math.min(self.max_wanted_line, #self.doc.lines) 51 51 end 52 52
+2 -1
data/core/doc/init.lua
··· 56 56 57 57 58 58 function Doc:reset_syntax() 59 - local syn = syntax.get(self.filename or "") 59 + local header = self:get_text(1, 1, self:position_offset(1, 1, 128)) 60 + local syn = syntax.get(self.filename or "", header) 60 61 if self.syntax ~= syn then 61 62 self.syntax = syn 62 63 self.highlighter:reset()
+27 -28
data/core/doc/translate.lua
··· 28 28 end 29 29 30 30 31 - function translate.previous_word_boundary(doc, line, col) 32 - local char = doc:get_char(doc:position_offset(line, col, -1)) 33 - local inword = not is_non_word(char) 34 - repeat 35 - local line2, col2 = line, col 36 - line, col = doc:position_offset(line, col, -1) 37 - if line == line2 and col == col2 then 31 + function translate.previous_word_start(doc, line, col) 32 + local prev 33 + while line > 1 or col > 1 do 34 + local l, c = doc:position_offset(line, col, -1) 35 + local char = doc:get_char(l, c) 36 + if prev and prev ~= char or not is_non_word(char) then 38 37 break 39 38 end 40 - local c = doc:get_char(doc:position_offset(line, col, -1)) 41 - until inword and is_non_word(c) or not inword and c ~= char 42 - return line, col 39 + prev, line, col = char, l, c 40 + end 41 + return translate.start_of_word(doc, line, col) 43 42 end 44 43 45 44 46 - function translate.next_word_boundary(doc, line, col) 47 - local char = doc:get_char(line, col) 48 - local inword = not is_non_word(char) 49 - repeat 50 - local line2, col2 = line, col 51 - line, col = doc:position_offset(line, col, 1) 52 - if line == line2 and col == col2 then 45 + function translate.next_word_end(doc, line, col) 46 + local prev 47 + local end_line, end_col = translate.end_of_doc(doc, line, col) 48 + while line < end_line or col < end_col do 49 + local char = doc:get_char(line, col) 50 + if prev and prev ~= char or not is_non_word(char) then 53 51 break 54 52 end 55 - local c = doc:get_char(line, col) 56 - until inword and is_non_word(c) or not inword and c ~= char 57 - return line, col 53 + line, col = doc:position_offset(line, col, 1) 54 + prev = char 55 + end 56 + return translate.end_of_word(doc, line, col) 58 57 end 59 58 60 59 ··· 86 85 end 87 86 88 87 89 - function translate.previous_start_of_block(doc, line, col) 88 + function translate.previous_block_start(doc, line, col) 90 89 while true do 91 90 line = line - 1 92 91 if line <= 1 then 93 92 return 1, 1 94 93 end 95 - if doc.lines[line-1]:match("^%s*$") 96 - and not doc.lines[line]:match("^%s*$") then 94 + if doc.lines[line-1]:find("^%s*$") 95 + and not doc.lines[line]:find("^%s*$") then 97 96 return line, (doc.lines[line]:find("%S")) 98 97 end 99 98 end 100 99 end 101 100 102 101 103 - function translate.next_start_of_block(doc, line, col) 102 + function translate.next_block_end(doc, line, col) 104 103 while true do 105 - line = line + 1 106 104 if line >= #doc.lines then 107 105 return #doc.lines, 1 108 106 end 109 - if doc.lines[line-1]:match("^%s*$") 110 - and not doc.lines[line]:match("^%s*$") then 111 - return line, (doc.lines[line]:find("%S")) 107 + if doc.lines[line+1]:find("^%s*$") 108 + and not doc.lines[line]:find("^%s*$") then 109 + return line+1, #doc.lines[line+1] 112 110 end 111 + line = line + 1 113 112 end 114 113 end 115 114
+34 -19
data/core/docview.lua
··· 192 192 end 193 193 194 194 195 + local function mouse_selection(doc, clicks, line1, col1, line2, col2) 196 + local swap = line2 < line1 or line2 == line1 and col2 <= col1 197 + if swap then 198 + line1, col1, line2, col2 = line2, col2, line1, col1 199 + end 200 + if clicks == 2 then 201 + line1, col1 = translate.start_of_word(doc, line1, col1) 202 + line2, col2 = translate.end_of_word(doc, line2, col2) 203 + elseif clicks == 3 then 204 + if line2 == #doc.lines and doc.lines[#doc.lines] ~= "\n" then 205 + doc:insert(math.huge, math.huge, "\n") 206 + end 207 + line1, col1, line2, col2 = line1, 1, line2 + 1, 1 208 + end 209 + if swap then 210 + return line2, col2, line1, col1 211 + end 212 + return line1, col1, line2, col2 213 + end 214 + 215 + 195 216 function DocView:on_mouse_pressed(button, x, y, clicks) 196 217 local caught = DocView.super.on_mouse_pressed(self, button, x, y, clicks) 197 218 if caught then 198 219 return 199 220 end 200 - local line, col = self:resolve_screen_position(x, y) 201 - if clicks == 2 then 202 - local line1, col1 = translate.start_of_word(self.doc, line, col) 203 - local line2, col2 = translate.end_of_word(self.doc, line, col) 204 - self.doc:set_selection(line2, col2, line1, col1) 205 - elseif clicks == 3 then 206 - if line == #self.doc.lines then 207 - self.doc:insert(math.huge, math.huge, "\n") 221 + if keymap.modkeys["shift"] then 222 + if clicks == 1 then 223 + local line1, col1 = select(3, self.doc:get_selection()) 224 + local line2, col2 = self:resolve_screen_position(x, y) 225 + self.doc:set_selection(line2, col2, line1, col1) 208 226 end 209 - self.doc:set_selection(line + 1, 1, line, 1) 210 227 else 211 - local line2, col2 212 - if keymap.modkeys["shift"] then 213 - line2, col2 = select(3, self.doc:get_selection()) 214 - end 215 - self.doc:set_selection(line, col, line2, col2) 216 - self.mouse_selecting = true 228 + local line, col = self:resolve_screen_position(x, y) 229 + self.doc:set_selection(mouse_selection(self.doc, clicks, line, col, line, col)) 230 + self.mouse_selecting = { line, col, clicks = clicks } 217 231 end 218 232 self.blink_timer = 0 219 233 end ··· 229 243 end 230 244 231 245 if self.mouse_selecting then 232 - local _, _, line2, col2 = self.doc:get_selection() 233 - local line1, col1 = self:resolve_screen_position(x, y) 234 - self.doc:set_selection(line1, col1, line2, col2) 246 + local l1, c1 = self:resolve_screen_position(x, y) 247 + local l2, c2 = table.unpack(self.mouse_selecting) 248 + local clicks = self.mouse_selecting.clicks 249 + self.doc:set_selection(mouse_selection(self.doc, clicks, l1, c1, l2, c2)) 235 250 end 236 251 end 237 252 238 253 239 254 function DocView:on_mouse_released(button) 240 255 DocView.super.on_mouse_released(self, button) 241 - self.mouse_selecting = false 256 + self.mouse_selecting = nil 242 257 end 243 258 244 259
+45 -22
data/core/init.lua
··· 12 12 local core = {} 13 13 14 14 15 - -- the following line has been added for temporary backwards-compatibility with 16 - -- plugins. on the 1.05 release this will be removed: 17 - core.project_dir = "." 18 - 19 - 20 15 local function project_scan_thread() 21 16 local function diff_files(a, b) 22 17 if #a ~= #b then return true end ··· 87 82 CommandView = require "core.commandview" 88 83 Doc = require "core.doc" 89 84 85 + local project_dir = EXEDIR 86 + local files = {} 87 + for i = 2, #ARGS do 88 + local info = system.get_file_info(ARGS[i]) or {} 89 + if info.type == "file" then 90 + table.insert(files, system.absolute_path(ARGS[i])) 91 + elseif info.type == "dir" then 92 + project_dir = ARGS[i] 93 + end 94 + end 95 + 96 + system.chdir(project_dir) 97 + 90 98 core.frame_start = 0 91 99 core.clip_rect_stack = {{ 0,0,0,0 }} 92 100 core.log_items = {} 93 101 core.docs = {} 94 102 core.threads = setmetatable({}, { __mode = "k" }) 95 103 core.project_files = {} 104 + core.redraw = true 96 105 97 106 core.root_view = RootView() 98 107 core.command_view = CommandView() ··· 107 116 local got_user_error = not core.try(require, "user") 108 117 local got_project_error = not core.load_project_module() 109 118 110 - for i = 2, #ARGS do 111 - local filename = ARGS[i] 112 - local info = system.get_file_info(filename) 113 - if info and info.type == "file" then 114 - core.root_view:open_doc(core.open_doc(filename)) 115 - end 119 + for _, filename in ipairs(files) do 120 + core.root_view:open_doc(core.open_doc(filename)) 116 121 end 117 122 118 123 if got_plugin_error or got_user_error or got_project_error then 119 124 command.perform("core:open-log") 120 125 end 126 + end 121 127 122 - local info = ARGS[2] and system.get_file_info(ARGS[2]) 123 - system.chdir(info and info.type == "dir" and ARGS[2] or ".") 128 + 129 + local temp_uid = (system.get_time() * 1000) % 0xffffffff 130 + local temp_file_prefix = string.format(".lite_temp_%08x", temp_uid) 131 + local temp_file_counter = 0 132 + 133 + local function delete_temp_files() 134 + for _, filename in ipairs(system.list_dir(EXEDIR)) do 135 + if filename:find(temp_file_prefix, 1, true) == 1 then 136 + os.remove(EXEDIR .. PATHSEP .. filename) 137 + end 138 + end 139 + end 140 + 141 + function core.temp_filename(ext) 142 + temp_file_counter = temp_file_counter + 1 143 + return EXEDIR .. PATHSEP .. temp_file_prefix 144 + .. string.format("%06x", temp_file_counter) .. (ext or "") 124 145 end 125 146 126 147 127 148 function core.quit(force) 128 149 if force then 150 + delete_temp_files() 129 151 os.exit() 130 152 end 131 153 local dirty_count = 0 ··· 364 386 -- update 365 387 core.root_view.size.x, core.root_view.size.y = width, height 366 388 core.root_view:update() 367 - if not core.redraw then 368 - if not system.window_has_focus() then system.wait_event(0.5) end 369 - return 370 - end 389 + if not core.redraw then return false end 371 390 core.redraw = false 372 391 373 392 -- close unreferenced docs ··· 381 400 382 401 -- update window title 383 402 local name = core.active_view:get_name() 384 - if name ~= "---" then 385 - system.set_window_title(name .. " - lite") 386 - else 387 - system.set_window_title("lite") 403 + local title = (name ~= "---") and (name .. " - lite") or "lite" 404 + if title ~= core.window_title then 405 + system.set_window_title(title) 406 + core.window_title = title 388 407 end 389 408 390 409 -- draw ··· 393 412 renderer.set_clip_rect(table.unpack(core.clip_rect_stack[1])) 394 413 core.root_view:draw() 395 414 renderer.end_frame() 415 + return true 396 416 end 397 417 398 418 ··· 431 451 function core.run() 432 452 while true do 433 453 core.frame_start = system.get_time() 434 - core.step() 454 + local did_redraw = core.step() 435 455 run_threads() 456 + if not did_redraw and not system.window_has_focus() then 457 + system.wait_event(0.25) 458 + end 436 459 local elapsed = system.get_time() - core.frame_start 437 460 system.sleep(math.max(0, 1 / config.fps - elapsed)) 438 461 end
+15 -14
data/core/keymap.lua
··· 84 84 85 85 86 86 keymap.add { 87 - ["ctrl+shift+p"] = "core:command-finder", 88 - ["ctrl+p"] = "core:file-finder", 87 + ["ctrl+shift+p"] = "core:find-command", 88 + ["ctrl+p"] = "core:find-file", 89 89 ["ctrl+o"] = "core:open-file", 90 90 ["ctrl+n"] = "core:new-doc", 91 91 ["alt+return"] = "core:toggle-fullscreen", ··· 132 132 ["shift+tab"] = "doc:unindent", 133 133 ["backspace"] = "doc:backspace", 134 134 ["shift+backspace"] = "doc:backspace", 135 - ["ctrl+backspace"] = "doc:delete-to-previous-word-boundary", 136 - ["ctrl+shift+backspace"] = "doc:delete-to-previous-word-boundary", 135 + ["ctrl+backspace"] = "doc:delete-to-previous-word-start", 136 + ["ctrl+shift+backspace"] = "doc:delete-to-previous-word-start", 137 137 ["delete"] = "doc:delete", 138 138 ["shift+delete"] = "doc:delete", 139 - ["ctrl+delete"] = "doc:delete-to-next-word-boundary", 140 - ["ctrl+shift+delete"] = "doc:delete-to-next-word-boundary", 139 + ["ctrl+delete"] = "doc:delete-to-next-word-end", 140 + ["ctrl+shift+delete"] = "doc:delete-to-next-word-end", 141 141 ["return"] = { "command:submit", "doc:newline" }, 142 + ["keypad enter"] = { "command:submit", "doc:newline" }, 142 143 ["ctrl+return"] = "doc:newline-below", 143 144 ["ctrl+shift+return"] = "doc:newline-above", 144 145 ["ctrl+j"] = "doc:join-lines", ··· 155 156 ["right"] = "doc:move-to-next-char", 156 157 ["up"] = { "command:select-previous", "doc:move-to-previous-line" }, 157 158 ["down"] = { "command:select-next", "doc:move-to-next-line" }, 158 - ["ctrl+left"] = "doc:move-to-previous-word-boundary", 159 - ["ctrl+right"] = "doc:move-to-next-word-boundary", 160 - ["ctrl+["] = "doc:move-to-previous-start-of-block", 161 - ["ctrl+]"] = "doc:move-to-next-start-of-block", 159 + ["ctrl+left"] = "doc:move-to-previous-word-start", 160 + ["ctrl+right"] = "doc:move-to-next-word-end", 161 + ["ctrl+["] = "doc:move-to-previous-block-start", 162 + ["ctrl+]"] = "doc:move-to-next-block-end", 162 163 ["home"] = "doc:move-to-start-of-line", 163 164 ["end"] = "doc:move-to-end-of-line", 164 165 ["ctrl+home"] = "doc:move-to-start-of-doc", ··· 170 171 ["shift+right"] = "doc:select-to-next-char", 171 172 ["shift+up"] = "doc:select-to-previous-line", 172 173 ["shift+down"] = "doc:select-to-next-line", 173 - ["ctrl+shift+left"] = "doc:select-to-previous-word-boundary", 174 - ["ctrl+shift+right"] = "doc:select-to-next-word-boundary", 175 - ["ctrl+shift+["] = "doc:select-to-previous-start-of-block", 176 - ["ctrl+shift+]"] = "doc:select-to-next-start-of-block", 174 + ["ctrl+shift+left"] = "doc:select-to-previous-word-start", 175 + ["ctrl+shift+right"] = "doc:select-to-next-word-end", 176 + ["ctrl+shift+["] = "doc:select-to-previous-block-start", 177 + ["ctrl+shift+]"] = "doc:select-to-next-block-end", 177 178 ["shift+home"] = "doc:select-to-start-of-line", 178 179 ["shift+end"] = "doc:select-to-end-of-line", 179 180 ["ctrl+shift+home"] = "doc:select-to-start-of-doc",
+25 -12
data/core/rootview.lua
··· 9 9 10 10 local EmptyView = View:extend() 11 11 12 - function EmptyView:draw() 13 - self:draw_background(style.background) 14 - local pos = self.position 15 - local x, y, w, h = pos.x, pos.y, self.size.x, self.size.y 16 - local _, y = common.draw_text(style.big_font, style.dim, "empty", "center", x, y, w, h) 12 + local function draw_text(x, y, color) 13 + local th = style.big_font:get_height() 14 + local dh = th + style.padding.y * 2 15 + x = renderer.draw_text(style.big_font, "lite", x, y + (dh - th) / 2, color) 16 + x = x + style.padding.x 17 + renderer.draw_rect(x, y, math.ceil(1 * SCALE), dh, color) 17 18 local lines = { 18 - { fmt = "%s to run a command", cmd = "core:command-finder" }, 19 - { fmt = "%s to open a file from the project", cmd = "core:file-finder" }, 19 + { fmt = "%s to run a command", cmd = "core:find-command" }, 20 + { fmt = "%s to open a file from the project", cmd = "core:find-file" }, 20 21 } 21 - local th = style.font:get_height() 22 + th = style.font:get_height() 23 + y = y + (dh - th * 2 - style.padding.y) / 2 24 + local w = 0 22 25 for _, line in ipairs(lines) do 23 26 local text = string.format(line.fmt, keymap.get_binding(line.cmd)) 24 - y = y + style.padding.y 25 - common.draw_text(style.font, style.dim, text, "center", x, y, w, th) 26 - y = y + th 27 + w = math.max(w, renderer.draw_text(style.font, text, x + style.padding.x, y, color)) 28 + y = y + th + style.padding.y 27 29 end 30 + return w, dh 31 + end 32 + 33 + function EmptyView:draw() 34 + self:draw_background(style.background) 35 + local w, h = draw_text(0, 0, { 0, 0, 0, 0 }) 36 + local x = self.position.x + math.max(style.padding.x, (self.size.x - w) / 2) 37 + local y = self.position.y + (self.size.y - h) / 2 38 + draw_text(x, y, style.dim) 28 39 end 29 40 30 41 ··· 338 349 color = style.text 339 350 end 340 351 core.push_clip_rect(x, y, w, h) 341 - common.draw_text(style.font, color, text, "center", x, y, w, h) 352 + x, w = x + style.padding.x, w - style.padding.x * 2 353 + local align = style.font:get_width(text) > w and "left" or "center" 354 + common.draw_text(style.font, color, text, align, x, y, w, h) 342 355 core.pop_clip_rect() 343 356 end 344 357
+8 -3
data/core/syntax.lua
··· 11 11 end 12 12 13 13 14 - function syntax.get(filename) 14 + local function find(string, field) 15 15 for i = #syntax.items, 1, -1 do 16 16 local t = syntax.items[i] 17 - if common.match_pattern(filename, t.files) then 17 + if common.match_pattern(string, t[field] or {}) then 18 18 return t 19 19 end 20 20 end 21 - return plain_text_syntax 21 + end 22 + 23 + function syntax.get(filename, header) 24 + return find(filename, "files") 25 + or find(header, "headers") 26 + or plain_text_syntax 22 27 end 23 28 24 29
+10 -8
data/plugins/language_js.lua
··· 8 8 { pattern = { "/%*", "%*/" }, type = "comment" }, 9 9 { pattern = { '"', '"', '\\' }, type = "string" }, 10 10 { pattern = { "'", "'", '\\' }, type = "string" }, 11 + { pattern = { "`", "`", '\\' }, type = "string" }, 11 12 { pattern = "0x[%da-fA-F]+", type = "number" }, 12 13 { pattern = "-?%d+[%d%.eE]*", type = "number" }, 13 14 { pattern = "-?%.?%d+", type = "number" }, ··· 16 17 { pattern = "[%a_][%w_]*", type = "symbol" }, 17 18 }, 18 19 symbols = { 19 - ["arguments"] = "keyword2", 20 20 ["async"] = "keyword", 21 21 ["await"] = "keyword", 22 22 ["break"] = "keyword", ··· 32 32 ["else"] = "keyword", 33 33 ["export"] = "keyword", 34 34 ["extends"] = "keyword", 35 - ["false"] = "literal", 36 35 ["finally"] = "keyword", 37 36 ["for"] = "keyword", 38 37 ["function"] = "keyword", ··· 40 39 ["if"] = "keyword", 41 40 ["import"] = "keyword", 42 41 ["in"] = "keyword", 43 - ["Infinity"] = "keyword2", 44 42 ["instanceof"] = "keyword", 45 43 ["let"] = "keyword", 46 - ["NaN"] = "keyword2", 47 44 ["new"] = "keyword", 48 - ["null"] = "literal", 49 45 ["return"] = "keyword", 50 46 ["set"] = "keyword", 47 + ["static"] = "keyword", 51 48 ["super"] = "keyword", 52 49 ["switch"] = "keyword", 53 - ["this"] = "keyword2", 54 50 ["throw"] = "keyword", 55 - ["true"] = "literal", 56 51 ["try"] = "keyword", 57 52 ["typeof"] = "keyword", 58 - ["undefined"] = "literal", 59 53 ["var"] = "keyword", 60 54 ["void"] = "keyword", 61 55 ["while"] = "keyword", 62 56 ["with"] = "keyword", 63 57 ["yield"] = "keyword", 58 + ["true"] = "literal", 59 + ["false"] = "literal", 60 + ["null"] = "literal", 61 + ["undefined"] = "literal", 62 + ["arguments"] = "keyword2", 63 + ["Infinity"] = "keyword2", 64 + ["NaN"] = "keyword2", 65 + ["this"] = "keyword2", 64 66 }, 65 67 }
+2
data/plugins/language_lua.lua
··· 2 2 3 3 syntax.add { 4 4 files = "%.lua$", 5 + headers = "^#!.*[ /]lua", 5 6 comment = "--", 6 7 patterns = { 7 8 { pattern = { '"', '"', '\\' }, type = "string" }, ··· 12 13 { pattern = "-?0x%x+", type = "number" }, 13 14 { pattern = "-?%d+[%d%.eE]*", type = "number" }, 14 15 { pattern = "-?%.?%d+", type = "number" }, 16 + { pattern = "<%a+>", type = "keyword2" }, 15 17 { pattern = "%.%.%.?", type = "operator" }, 16 18 { pattern = "[<>~=]=", type = "operator" }, 17 19 { pattern = "[%+%-=/%*%^%%#<>]", type = "operator" },
+2 -1
data/plugins/language_python.lua
··· 1 1 local syntax = require "core.syntax" 2 2 3 3 syntax.add { 4 - files = "%.py$", 4 + files = { "%.py$", "%.pyw$" }, 5 + headers = "^#!.*[ /]python", 5 6 comment = "#", 6 7 patterns = { 7 8 { pattern = { "#", "\n" }, type = "comment" },
+1
data/plugins/language_xml.lua
··· 2 2 3 3 syntax.add { 4 4 files = { "%.xml$", "%.html?$" }, 5 + headers = "<%?xml", 5 6 patterns = { 6 7 { pattern = { "<!%-%-", "%-%->" }, type = "comment" }, 7 8 { pattern = { '%f[^>][^<]', '%f[<]' }, type = "normal" },
+7
data/plugins/trimwhitespace.lua
··· 4 4 5 5 6 6 local function trim_trailing_whitespace(doc) 7 + local cline, ccol = doc:get_selection() 7 8 for i = 1, #doc.lines do 8 9 local old_text = doc:get_text(i, 1, i, math.huge) 9 10 local new_text = old_text:gsub("%s*$", "") 11 + 12 + -- don't remove whitespace which would cause the caret to reposition 13 + if cline == i and ccol > #new_text then 14 + new_text = old_text:sub(1, ccol - 1) 15 + end 16 + 10 17 if old_text ~= new_text then 11 18 doc:insert(i, 1, new_text) 12 19 doc:remove(i, #new_text + 1, i, math.huge)
+1 -1
data/user/colors/summer.lua
··· 5 5 style.background2 = { common.color "#f2f2f2" } 6 6 style.background3 = { common.color "#f2f2f2" } 7 7 style.text = { common.color "#404040" } 8 - style.caret = { common.color "#181818" } 8 + style.caret = { common.color "#fc1785" } 9 9 style.accent = { common.color "#fc1785" } 10 10 style.dim = { common.color "#b0b0b0" } 11 11 style.divider = { common.color "#e8e8e8" }
+4 -1
data/user/init.lua
··· 5 5 local config = require "core.config" 6 6 local style = require "core.style" 7 7 8 + config.tab_type = "hard" 9 + config.indent_size = 8 10 + 8 11 -- light theme: 9 - -- require "user.colors.summer" 12 + require "user.colors.fall" 10 13 11 14 -- key binding: 12 15 -- keymap.add { ["ctrl+escape"] = "core:quit" }
+41
doc/original-readme.md
··· 1 + # lite 2 + ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 3 + 4 + A lightweight text editor written in Lua 5 + 6 + * **[Get lite](https://github.com/rxi/lite/releases/latest)** โ€” Download 7 + for Windows and Linux 8 + * **[Get started](doc/usage.md)** โ€” A quick overview on how to get started 9 + * **[Get plugins](https://github.com/rxi/lite-plugins)** โ€” Add additional 10 + functionality 11 + * **[Get color themes](https://github.com/rxi/lite-colors)** โ€” Add additional colors 12 + themes 13 + 14 + ## Overview 15 + lite is a lightweight text editor written mostly in Lua โ€” it aims to provide 16 + something practical, pretty, *small* and fast, implemented as simply as 17 + possible; easy to modify and extend, or to use without doing either. 18 + 19 + ## Customization 20 + Additional functionality can be added through plugins which are available from 21 + the [plugins repository](https://github.com/rxi/lite-plugins); additional color 22 + themes can be found in the [colors repository](https://github.com/rxi/lite-colors). 23 + The editor can be customized by making changes to the 24 + [user module](data/user/init.lua). 25 + 26 + ## Building 27 + You can build the project yourself on Linux using the `build.sh` script 28 + or on Windows using the `build.bat` script *([MinGW](https://nuwen.net/mingw.html) is required)*. 29 + Note that the project does not need to be rebuilt if you are only making changes 30 + to the Lua portion of the code. 31 + 32 + ## Contributing 33 + Any additional functionality that can be added through a plugin should be done 34 + so as a plugin, after which a pull request to the 35 + [plugins repository](https://github.com/rxi/lite-plugins) can be made. In hopes 36 + of remaining lightweight, pull requests adding additional functionality to the 37 + core will likely not be merged. Bug reports and bug fixes are welcome. 38 + 39 + ## License 40 + This project is free software; you can redistribute it and/or modify it under 41 + the terms of the MIT license. See [LICENSE](LICENSE) for details.
+146
doc/usage.md
··· 1 + # lite 2 + 3 + ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 4 + 5 + ## Overview 6 + lite is a lightweight text editor written mostly in Lua โ€” it aims to provide 7 + something practical, pretty, *small* and fast, implemented as simply as 8 + possible; easy to modify and extend, or to use without doing either. 9 + 10 + 11 + ## Getting Started 12 + When lite is started it's typically opened with a *project directory* โ€” this 13 + is the directory where your project's code and other data resides. The project 14 + directory is set once when lite is started and, for the duration of the 15 + session, cannot be changed. 16 + 17 + To open lite with a specific project directory the directory name can be passed 18 + as a command-line argument *(`.` can be passed to use the current directory)* or 19 + the directory can be dragged onto either the lite executable or a running 20 + instance of lite. 21 + 22 + The main way of opening files in lite is through the `core:find-file` command 23 + โ€” this provides a fuzzy finder over all of the project's files and can be 24 + opened using the **`ctrl+p`** shortcut by default. 25 + 26 + Commands can be run using keyboard shortcuts, or by using the `core:find-command` 27 + command bound to **`ctrl+shift+p`** by default. For example, pressing 28 + `ctrl+shift+p` and typing `newdoc` then pressing `return` would open a new 29 + document. The current keyboard shortcut for a command can be seen to the right 30 + of the command name on the command finder, thus to find the shortcut for a command 31 + `ctrl+shift+p` can be pressed and the command name typed. 32 + 33 + 34 + ## User Module 35 + lite can be configured through use of the user module. The user module can be 36 + used for changing options in the config module, adding additional key bindings, 37 + loading custom color themes, modifying the style or changing any other part of 38 + lite to your personal preference. 39 + 40 + The user module is loaded by lite when the application starts, after the plugins 41 + have been loaded. 42 + 43 + The user module can be modified by running the `core:open-user-module` command 44 + or otherwise directly opening the `data/user/init.lua` file. 45 + 46 + 47 + ## Project Module 48 + The project module is an optional module which is loaded from the current 49 + project's directory when lite is started. Project modules can be useful for 50 + things like adding custom commands for project-specific build systems, or 51 + loading project-specific plugins. 52 + 53 + The project module is loaded by lite when the application starts, after both the 54 + plugins and user module have been loaded. 55 + 56 + The project module can be edited by running the `core:open-project-module` 57 + command โ€” if the module does not exist for the current project when the 58 + command is run it will be created. 59 + 60 + 61 + ## Commands 62 + Commands in lite are used both through the command finder (`ctrl+shift+p`) and 63 + by lite's keyboard shortcut system. Commands consist of 3 components: 64 + * **Name** โ€” The command name in the form of `namespace:action-name`, for 65 + example: `doc:select-all` 66 + * **Predicate** โ€” A function that returns true if the command can be ran, for 67 + example, for any document commands the predicate checks whether the active 68 + view is a document 69 + * **Function** โ€” The function which performs the command itself 70 + 71 + Commands can be added using the `command.add` function provided by the 72 + `core.command` module: 73 + ```lua 74 + local core = require "core" 75 + local command = require "core.command" 76 + 77 + command.add("core.docview", { 78 + ["doc:save"] = function() 79 + core.active_view.doc:save() 80 + core.log("Saved '%s', core.active_view.doc.filename) 81 + end 82 + }) 83 + ``` 84 + 85 + Commands can be performed programatically (eg. from another command or by your 86 + user module) by calling the `command.perform` function after requiring the 87 + `command` module: 88 + ```lua 89 + local command = require "core.command" 90 + command.perform "core:quit" 91 + ``` 92 + 93 + 94 + ## Keymap 95 + All keyboard shortcuts in lite are handled by the `core.keymap` module. A key 96 + binding in lite maps a "stroke" (eg. `ctrl+q`) to one or more commands (eg. 97 + `core:quit`). When the shortcut is pressed lite will iterate each command 98 + assigned to that key and run the *predicate function* for that command โ€” if the 99 + predicate passes it stops iterating and runs the command. 100 + 101 + An example of where this used is the default binding of the `tab` key: 102 + ``` lua 103 + ["tab"] = { "command:complete", "doc:indent" }, 104 + ``` 105 + When tab is pressed the `command:complete` command is attempted which will only 106 + succeed if the command-input at the bottom of the window is active. Otherwise 107 + the `doc:indent` command is attempted which will only succeed if we have a 108 + document as our active view. 109 + 110 + A new mapping can be added by your user module as follows: 111 + ```lua 112 + local keymap = require "core.keymap" 113 + keymap.add { ["ctrl+q"] = "core:quit" } 114 + ``` 115 + 116 + 117 + ## Plugins 118 + Plugins in lite are normal lua modules and are treated as such โ€” no 119 + complicated plugin manager is provided, and, once a plugin is loaded, it is never 120 + expected be to have to unload itself. 121 + 122 + To install a plugin simply drop it in the `data/plugins` directory โ€” installed 123 + plugins will be automatically loaded when lite starts. To uninstall a plugin the 124 + plugin file can be deleted โ€” any plugin (including those included with lite's 125 + default installation) can be deleted to remove its functionality. 126 + 127 + If you want to load a plugin only under a certain circumstance (for example, 128 + only on a given project) the plugin can be placed somewhere other than the 129 + `data/plugins` directory so that it is not automatically loaded. The plugin can 130 + then be loaded manually as needed by using the `require` function. 131 + 132 + Plugins can be downloaded from the [plugins repository](https://github.com/rxi/lite-plugins). 133 + 134 + 135 + ## Color Themes 136 + Colors themes in lite are lua modules which overwrite the color fields of lite's 137 + `core.style` module. Color themes should be placed in the `data/user/colors` 138 + directory. 139 + 140 + A color theme can be set by requiring it in your user module: 141 + ```lua 142 + require "user.colors.winter" 143 + ``` 144 + 145 + Color themes can be downloaded from the [color themes repository](https://github.com/rxi/lite-colors). 146 +
+53
license
··· 1 + === emmeline/lite: MIT License === 2 + 3 + Copyright (c) 2025 emmeline 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy of 6 + this software and associated documentation files (the "Software"), to deal in 7 + the Software without restriction, including without limitation the rights to 8 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 + of the Software, and to permit persons to whom the Software is furnished to do 10 + so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE. 22 + 23 + === rxi/lite: MIT License === 24 + 25 + Copyright (c) 2020 rxi 26 + 27 + Permission is hereby granted, free of charge, to any person obtaining a copy of 28 + this software and associated documentation files (the "Software"), to deal in 29 + the Software without restriction, including without limitation the rights to 30 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 31 + of the Software, and to permit persons to whom the Software is furnished to do 32 + so, subject to the following conditions: 33 + 34 + The above copyright notice and this permission notice shall be included in all 35 + copies or substantial portions of the Software. 36 + 37 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 + SOFTWARE. 44 + 45 + === lua 5.4.8: MIT License === 46 + 47 + Copyright ยฉ 1994โ€“2025 Lua.org, PUC-Rio. 48 + 49 + 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: 50 + 51 + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 52 + 53 + 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.
+31
readme
··· 1 + lite (emmeline's fork) 2 + ====================== 3 + 4 + A fork of the [lite](https://github.com/rxi/lite) editor by rxi. 5 + 6 + A lightweight editor written in Lua. 7 + 8 + Usage: <doc/usage.md> 9 + Plugins: <https://github.com/rxi/lite-plugins> 10 + Themes: <https://github.com/rxi/lite-colors> 11 + 12 + Build 13 + ----- 14 + 15 + $ git clone https://tangled.org/emmeline.girlkisser.top/lite 16 + $ cd lite 17 + $ ./build.sh 18 + $ ln -s $(pwd)/lite ~/.local/bin/ 19 + $ lite . 20 + 21 + Changes 22 + ------- 23 + 24 + - [x] Reformat to my code style :p 25 + - [x] Update to SDL 3 26 + - [ ] Use LuaJIT 27 + 28 + License 29 + ------- 30 + 31 + MIT License
+9 -7
src/api/api.c
··· 6 6 7 7 8 8 static const luaL_Reg libs[] = { 9 - { "system", luaopen_system }, 10 - { "renderer", luaopen_renderer }, 11 - { NULL, NULL } 9 + { "system", luaopen_system }, 10 + { "renderer", luaopen_renderer }, 11 + { NULL, NULL } 12 12 }; 13 13 14 - void api_load_libs(lua_State *L) { 15 - for (int i = 0; libs[i].name; i++) { 16 - luaL_requiref(L, libs[i].name, libs[i].func, 1); 17 - } 14 + void api_load_libs(lua_State *L) 15 + { 16 + for (int i = 0 ; libs[i].name ; i++) 17 + { 18 + luaL_requiref(L, libs[i].name, libs[i].func, 1); 19 + } 18 20 }
+78 -70
src/api/renderer.c
··· 3 3 #include "rencache.h" 4 4 5 5 6 - static RenColor checkcolor(lua_State *L, int idx, int def) { 7 - RenColor color; 8 - if (lua_isnoneornil(L, idx)) { 9 - return (RenColor) { def, def, def, 255 }; 10 - } 11 - lua_rawgeti(L, idx, 1); 12 - lua_rawgeti(L, idx, 2); 13 - lua_rawgeti(L, idx, 3); 14 - lua_rawgeti(L, idx, 4); 15 - color.r = luaL_checknumber(L, -4); 16 - color.g = luaL_checknumber(L, -3); 17 - color.b = luaL_checknumber(L, -2); 18 - color.a = luaL_optnumber(L, -1, 255); 19 - lua_pop(L, 4); 20 - return color; 6 + static RenColor checkcolor(lua_State *L, int idx, int def) 7 + { 8 + RenColor color; 9 + if (lua_isnoneornil(L, idx)) 10 + return (RenColor){ def, def, def, 255 }; 11 + lua_rawgeti(L, idx, 1); 12 + lua_rawgeti(L, idx, 2); 13 + lua_rawgeti(L, idx, 3); 14 + lua_rawgeti(L, idx, 4); 15 + color.r = luaL_checknumber(L, -4); 16 + color.g = luaL_checknumber(L, -3); 17 + color.b = luaL_checknumber(L, -2); 18 + color.a = luaL_optnumber(L, -1, 255); 19 + lua_pop(L, 4); 20 + return color; 21 21 } 22 22 23 23 24 - static int f_show_debug(lua_State *L) { 25 - luaL_checkany(L, 1); 26 - rencache_show_debug(lua_toboolean(L, 1)); 27 - return 0; 24 + static int f_show_debug(lua_State *L) 25 + { 26 + luaL_checkany(L, 1); 27 + rencache_show_debug(lua_toboolean(L, 1)); 28 + return 0; 28 29 } 29 30 30 31 31 - static int f_get_size(lua_State *L) { 32 - int w, h; 33 - ren_get_size(&w, &h); 34 - lua_pushnumber(L, w); 35 - lua_pushnumber(L, h); 36 - return 2; 32 + static int f_get_size(lua_State *L) 33 + { 34 + int w, h; 35 + ren_get_size(&w, &h); 36 + lua_pushnumber(L, w); 37 + lua_pushnumber(L, h); 38 + return 2; 37 39 } 38 40 39 41 40 - static int f_begin_frame(lua_State *L) { 41 - rencache_begin_frame(); 42 - return 0; 42 + static int f_begin_frame(lua_State *L) 43 + { 44 + rencache_begin_frame(); 45 + return 0; 43 46 } 44 47 45 48 46 - static int f_end_frame(lua_State *L) { 47 - rencache_end_frame(); 48 - return 0; 49 + static int f_end_frame(lua_State *L) 50 + { 51 + rencache_end_frame(); 52 + return 0; 49 53 } 50 54 51 55 52 - static int f_set_clip_rect(lua_State *L) { 53 - RenRect rect; 54 - rect.x = luaL_checknumber(L, 1); 55 - rect.y = luaL_checknumber(L, 2); 56 - rect.width = luaL_checknumber(L, 3); 57 - rect.height = luaL_checknumber(L, 4); 58 - rencache_set_clip_rect(rect); 59 - return 0; 56 + static int f_set_clip_rect(lua_State *L) 57 + { 58 + RenRect rect; 59 + rect.x = luaL_checknumber(L, 1); 60 + rect.y = luaL_checknumber(L, 2); 61 + rect.width = luaL_checknumber(L, 3); 62 + rect.height = luaL_checknumber(L, 4); 63 + rencache_set_clip_rect(rect); 64 + return 0; 60 65 } 61 66 62 67 63 - static int f_draw_rect(lua_State *L) { 64 - RenRect rect; 65 - rect.x = luaL_checknumber(L, 1); 66 - rect.y = luaL_checknumber(L, 2); 67 - rect.width = luaL_checknumber(L, 3); 68 - rect.height = luaL_checknumber(L, 4); 69 - RenColor color = checkcolor(L, 5, 255); 70 - rencache_draw_rect(rect, color); 71 - return 0; 68 + static int f_draw_rect(lua_State *L) 69 + { 70 + RenRect rect; 71 + rect.x = luaL_checknumber(L, 1); 72 + rect.y = luaL_checknumber(L, 2); 73 + rect.width = luaL_checknumber(L, 3); 74 + rect.height = luaL_checknumber(L, 4); 75 + RenColor color = checkcolor(L, 5, 255); 76 + rencache_draw_rect(rect, color); 77 + return 0; 72 78 } 73 79 74 80 75 - static int f_draw_text(lua_State *L) { 76 - RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); 77 - const char *text = luaL_checkstring(L, 2); 78 - int x = luaL_checknumber(L, 3); 79 - int y = luaL_checknumber(L, 4); 80 - RenColor color = checkcolor(L, 5, 255); 81 - x = rencache_draw_text(*font, text, x, y, color); 82 - lua_pushnumber(L, x); 83 - return 1; 81 + static int f_draw_text(lua_State *L) 82 + { 83 + RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); 84 + const char *text = luaL_checkstring(L, 2); 85 + int x = luaL_checknumber(L, 3); 86 + int y = luaL_checknumber(L, 4); 87 + RenColor color = checkcolor(L, 5, 255); 88 + x = rencache_draw_text(*font, text, x, y, color); 89 + lua_pushnumber(L, x); 90 + return 1; 84 91 } 85 92 86 93 87 94 static const luaL_Reg lib[] = { 88 - { "show_debug", f_show_debug }, 89 - { "get_size", f_get_size }, 90 - { "begin_frame", f_begin_frame }, 91 - { "end_frame", f_end_frame }, 92 - { "set_clip_rect", f_set_clip_rect }, 93 - { "draw_rect", f_draw_rect }, 94 - { "draw_text", f_draw_text }, 95 - { NULL, NULL } 95 + { "show_debug", f_show_debug }, 96 + { "get_size", f_get_size }, 97 + { "begin_frame", f_begin_frame }, 98 + { "end_frame", f_end_frame }, 99 + { "set_clip_rect", f_set_clip_rect }, 100 + { "draw_rect", f_draw_rect }, 101 + { "draw_text", f_draw_text }, 102 + { NULL, NULL } 96 103 }; 97 104 98 105 99 106 int luaopen_renderer_font(lua_State *L); 100 107 101 - int luaopen_renderer(lua_State *L) { 102 - luaL_newlib(L, lib); 103 - luaopen_renderer_font(L); 104 - lua_setfield(L, -2, "font"); 105 - return 1; 108 + int luaopen_renderer(lua_State *L) 109 + { 110 + luaL_newlib(L, lib); 111 + luaopen_renderer_font(L); 112 + lua_setfield(L, -2, "font"); 113 + return 1; 106 114 }
+46 -38
src/api/renderer_font.c
··· 3 3 #include "rencache.h" 4 4 5 5 6 - static int f_load(lua_State *L) { 7 - const char *filename = luaL_checkstring(L, 1); 8 - float size = luaL_checknumber(L, 2); 9 - RenFont **self = lua_newuserdata(L, sizeof(*self)); 10 - luaL_setmetatable(L, API_TYPE_FONT); 11 - *self = ren_load_font(filename, size); 12 - if (!*self) { luaL_error(L, "failed to load font"); } 13 - return 1; 6 + static int f_load(lua_State *L) 7 + { 8 + const char *filename = luaL_checkstring(L, 1); 9 + float size = luaL_checknumber(L, 2); 10 + RenFont **self = lua_newuserdata(L, sizeof(*self)); 11 + luaL_setmetatable(L, API_TYPE_FONT); 12 + *self = ren_load_font(filename, size); 13 + if (!*self) 14 + luaL_error(L, "failed to load font"); 15 + return 1; 14 16 } 15 17 16 18 17 - static int f_set_tab_width(lua_State *L) { 18 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 19 - int n = luaL_checknumber(L, 2); 20 - ren_set_font_tab_width(*self, n); 21 - return 0; 19 + static int f_set_tab_width(lua_State *L) 20 + { 21 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 22 + int n = luaL_checknumber(L, 2); 23 + ren_set_font_tab_width(*self, n); 24 + return 0; 22 25 } 23 26 24 27 25 - static int f_gc(lua_State *L) { 26 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 27 - if (*self) { rencache_free_font(*self); } 28 - return 0; 28 + static int f_gc(lua_State *L) 29 + { 30 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 31 + if (*self) 32 + rencache_free_font(*self); 33 + return 0; 29 34 } 30 35 31 36 32 - static int f_get_width(lua_State *L) { 33 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 34 - const char *text = luaL_checkstring(L, 2); 35 - lua_pushnumber(L, ren_get_font_width(*self, text) ); 36 - return 1; 37 + static int f_get_width(lua_State *L) 38 + { 39 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 40 + const char *text = luaL_checkstring(L, 2); 41 + lua_pushnumber(L, ren_get_font_width(*self, text) ); 42 + return 1; 37 43 } 38 44 39 45 40 - static int f_get_height(lua_State *L) { 41 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 42 - lua_pushnumber(L, ren_get_font_height(*self) ); 43 - return 1; 46 + static int f_get_height(lua_State *L) 47 + { 48 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 49 + lua_pushnumber(L, ren_get_font_height(*self) ); 50 + return 1; 44 51 } 45 52 46 53 47 54 static const luaL_Reg lib[] = { 48 - { "__gc", f_gc }, 49 - { "load", f_load }, 50 - { "set_tab_width", f_set_tab_width }, 51 - { "get_width", f_get_width }, 52 - { "get_height", f_get_height }, 53 - { NULL, NULL } 55 + { "__gc", f_gc }, 56 + { "load", f_load }, 57 + { "set_tab_width", f_set_tab_width }, 58 + { "get_width", f_get_width }, 59 + { "get_height", f_get_height }, 60 + { NULL, NULL } 54 61 }; 55 62 56 - int luaopen_renderer_font(lua_State *L) { 57 - luaL_newmetatable(L, API_TYPE_FONT); 58 - luaL_setfuncs(L, lib, 0); 59 - lua_pushvalue(L, -1); 60 - lua_setfield(L, -2, "__index"); 61 - return 1; 63 + int luaopen_renderer_font(lua_State *L) 64 + { 65 + luaL_newmetatable(L, API_TYPE_FONT); 66 + luaL_setfuncs(L, lib, 0); 67 + lua_pushvalue(L, -1); 68 + lua_setfield(L, -2, "__index"); 69 + return 1; 62 70 }
+333 -292
src/api/system.c
··· 1 - #include <SDL2/SDL.h> 1 + #include <SDL3/SDL.h> 2 2 #include <stdbool.h> 3 + #include <stdlib.h> 3 4 #include <ctype.h> 4 5 #include <dirent.h> 5 6 #include <unistd.h> ··· 8 9 #include "api.h" 9 10 #include "rencache.h" 10 11 #ifdef _WIN32 11 - #include <windows.h> 12 + # include <windows.h> 12 13 #endif 13 14 14 15 extern SDL_Window *window; 15 16 16 17 17 - static const char* button_name(int button) { 18 - switch (button) { 19 - case 1 : return "left"; 20 - case 2 : return "middle"; 21 - case 3 : return "right"; 22 - default : return "?"; 23 - } 18 + static const char* button_name(int button) 19 + { 20 + switch (button) 21 + { 22 + case 1 : return "left"; 23 + case 2 : return "middle"; 24 + case 3 : return "right"; 25 + default : return "?"; 26 + } 24 27 } 25 28 26 29 27 - static char* key_name(char *dst, int sym) { 28 - strcpy(dst, SDL_GetKeyName(sym)); 29 - char *p = dst; 30 - while (*p) { 31 - *p = tolower(*p); 32 - p++; 33 - } 34 - return dst; 30 + static char* key_name(char *dst, int sym) 31 + { 32 + strcpy(dst, SDL_GetKeyName(sym)); 33 + char *p = dst; 34 + while (*p) 35 + { 36 + *p = tolower(*p); 37 + p++; 38 + } 39 + return dst; 35 40 } 36 41 37 42 38 - static int f_poll_event(lua_State *L) { 39 - char buf[16]; 40 - int mx, my, wx, wy; 41 - SDL_Event e; 43 + static int f_poll_event(lua_State *L) 44 + { 45 + char buf[16]; 46 + float mx, my; 47 + int wx, wy; 48 + SDL_Event e; 42 49 43 50 top: 44 - if ( !SDL_PollEvent(&e) ) { 45 - return 0; 46 - } 51 + if (!SDL_PollEvent(&e)) 52 + return 0; 53 + 54 + switch (e.type) 55 + { 56 + case SDL_EVENT_QUIT : 57 + lua_pushstring(L, "quit"); 58 + return 1; 59 + 60 + case SDL_EVENT_WINDOW_RESIZED: 61 + lua_pushstring(L, "resized"); 62 + lua_pushnumber(L, e.window.data1); 63 + lua_pushnumber(L, e.window.data2); 64 + return 3; 47 65 48 - switch (e.type) { 49 - case SDL_QUIT: 50 - lua_pushstring(L, "quit"); 51 - return 1; 66 + case SDL_EVENT_WINDOW_EXPOSED: 67 + rencache_invalidate(); 68 + lua_pushstring(L, "exposed"); 69 + return 1; 52 70 53 - case SDL_WINDOWEVENT: 54 - if (e.window.event == SDL_WINDOWEVENT_RESIZED) { 55 - lua_pushstring(L, "resized"); 56 - lua_pushnumber(L, e.window.data1); 57 - lua_pushnumber(L, e.window.data2); 58 - return 3; 59 - } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { 60 - rencache_invalidate(); 61 - lua_pushstring(L, "exposed"); 62 - return 1; 63 - } 64 - /* on some systems, when alt-tabbing to the window SDL will queue up 65 - ** several KEYDOWN events for the `tab` key; we flush all keydown 66 - ** events on focus so these are discarded */ 67 - if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { 68 - SDL_FlushEvent(SDL_KEYDOWN); 69 - } 70 - goto top; 71 + case SDL_EVENT_WINDOW_FOCUS_GAINED: 72 + /* on some systems, when alt-tabbing to the window SDL will queue up 73 + ** several KEYDOWN events for the `tab` key; we flush all keydown 74 + ** events on focus so these are discarded */ 75 + SDL_FlushEvent(SDL_EVENT_KEY_DOWN); 76 + goto top; 71 77 72 - case SDL_DROPFILE: 73 - SDL_GetGlobalMouseState(&mx, &my); 74 - SDL_GetWindowPosition(window, &wx, &wy); 75 - lua_pushstring(L, "filedropped"); 76 - lua_pushstring(L, e.drop.file); 77 - lua_pushnumber(L, mx - wx); 78 - lua_pushnumber(L, my - wy); 79 - SDL_free(e.drop.file); 80 - return 4; 78 + case SDL_EVENT_DROP_FILE : 79 + SDL_GetGlobalMouseState(&mx, &my); 80 + SDL_GetWindowPosition(window, &wx, &wy); 81 + lua_pushstring(L, "filedropped"); 82 + lua_pushstring(L, e.drop.data); 83 + lua_pushnumber(L, mx - wx); 84 + lua_pushnumber(L, my - wy); 85 + return 4; 81 86 82 - case SDL_KEYDOWN: 83 - lua_pushstring(L, "keypressed"); 84 - lua_pushstring(L, key_name(buf, e.key.keysym.sym)); 85 - return 2; 87 + case SDL_EVENT_KEY_DOWN : 88 + lua_pushstring(L, "keypressed"); 89 + lua_pushstring(L, key_name(buf, e.key.key)); 90 + return 2; 86 91 87 - case SDL_KEYUP: 88 - lua_pushstring(L, "keyreleased"); 89 - lua_pushstring(L, key_name(buf, e.key.keysym.sym)); 90 - return 2; 92 + case SDL_EVENT_KEY_UP : 93 + lua_pushstring(L, "keyreleased"); 94 + lua_pushstring(L, key_name(buf, e.key.key)); 95 + return 2; 91 96 92 - case SDL_TEXTINPUT: 93 - lua_pushstring(L, "textinput"); 94 - lua_pushstring(L, e.text.text); 95 - return 2; 97 + case SDL_EVENT_TEXT_INPUT : 98 + lua_pushstring(L, "textinput"); 99 + lua_pushstring(L, e.text.text); 100 + return 2; 96 101 97 - case SDL_MOUSEBUTTONDOWN: 98 - if (e.button.button == 1) { SDL_CaptureMouse(1); } 99 - lua_pushstring(L, "mousepressed"); 100 - lua_pushstring(L, button_name(e.button.button)); 101 - lua_pushnumber(L, e.button.x); 102 - lua_pushnumber(L, e.button.y); 103 - lua_pushnumber(L, e.button.clicks); 104 - return 5; 102 + case SDL_EVENT_MOUSE_BUTTON_DOWN : 103 + if (e.button.button == 1) 104 + SDL_CaptureMouse(1); 105 + lua_pushstring(L, "mousepressed"); 106 + lua_pushstring(L, button_name(e.button.button)); 107 + lua_pushnumber(L, e.button.x); 108 + lua_pushnumber(L, e.button.y); 109 + lua_pushnumber(L, e.button.clicks); 110 + return 5; 105 111 106 - case SDL_MOUSEBUTTONUP: 107 - if (e.button.button == 1) { SDL_CaptureMouse(0); } 108 - lua_pushstring(L, "mousereleased"); 109 - lua_pushstring(L, button_name(e.button.button)); 110 - lua_pushnumber(L, e.button.x); 111 - lua_pushnumber(L, e.button.y); 112 - return 4; 112 + case SDL_EVENT_MOUSE_BUTTON_UP : 113 + if (e.button.button == 1) 114 + SDL_CaptureMouse(0); 115 + lua_pushstring(L, "mousereleased"); 116 + lua_pushstring(L, button_name(e.button.button)); 117 + lua_pushnumber(L, e.button.x); 118 + lua_pushnumber(L, e.button.y); 119 + return 4; 113 120 114 - case SDL_MOUSEMOTION: 115 - lua_pushstring(L, "mousemoved"); 116 - lua_pushnumber(L, e.motion.x); 117 - lua_pushnumber(L, e.motion.y); 118 - lua_pushnumber(L, e.motion.xrel); 119 - lua_pushnumber(L, e.motion.yrel); 120 - return 5; 121 + case SDL_EVENT_MOUSE_MOTION : 122 + lua_pushstring(L, "mousemoved"); 123 + lua_pushnumber(L, e.motion.x); 124 + lua_pushnumber(L, e.motion.y); 125 + lua_pushnumber(L, e.motion.xrel); 126 + lua_pushnumber(L, e.motion.yrel); 127 + return 5; 121 128 122 - case SDL_MOUSEWHEEL: 123 - lua_pushstring(L, "mousewheel"); 124 - lua_pushnumber(L, e.wheel.y); 125 - return 2; 129 + case SDL_EVENT_MOUSE_WHEEL : 130 + lua_pushstring(L, "mousewheel"); 131 + lua_pushnumber(L, e.wheel.y); 132 + return 2; 126 133 127 - default: 128 - goto top; 129 - } 134 + default: 135 + goto top; 136 + } 130 137 131 - return 0; 138 + return 0; 132 139 } 133 140 134 141 135 - static int f_wait_event(lua_State *L) { 136 - double n = luaL_checknumber(L, 1); 137 - lua_pushboolean(L, SDL_WaitEventTimeout(NULL, n * 1000)); 138 - return 1; 142 + static int f_wait_event(lua_State *L) 143 + { 144 + double n = luaL_checknumber(L, 1); 145 + lua_pushboolean(L, SDL_WaitEventTimeout(NULL, n * 1000)); 146 + return 1; 139 147 } 140 148 141 149 142 - static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_HAND + 1]; 150 + static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_POINTER + 1]; 143 151 144 152 static const char *cursor_opts[] = { 145 - "arrow", 146 - "ibeam", 147 - "sizeh", 148 - "sizev", 149 - "hand", 150 - NULL 153 + "arrow", 154 + "ibeam", 155 + "sizeh", 156 + "sizev", 157 + "hand", 158 + NULL 151 159 }; 152 160 153 161 static const int cursor_enums[] = { 154 - SDL_SYSTEM_CURSOR_ARROW, 155 - SDL_SYSTEM_CURSOR_IBEAM, 156 - SDL_SYSTEM_CURSOR_SIZEWE, 157 - SDL_SYSTEM_CURSOR_SIZENS, 158 - SDL_SYSTEM_CURSOR_HAND 162 + SDL_SYSTEM_CURSOR_DEFAULT, 163 + SDL_SYSTEM_CURSOR_TEXT, 164 + SDL_SYSTEM_CURSOR_EW_RESIZE, 165 + SDL_SYSTEM_CURSOR_NS_RESIZE, 166 + SDL_SYSTEM_CURSOR_POINTER 159 167 }; 160 168 161 - static int f_set_cursor(lua_State *L) { 162 - int opt = luaL_checkoption(L, 1, "arrow", cursor_opts); 163 - int n = cursor_enums[opt]; 164 - SDL_Cursor *cursor = cursor_cache[n]; 165 - if (!cursor) { 166 - cursor = SDL_CreateSystemCursor(n); 167 - cursor_cache[n] = cursor; 168 - } 169 - SDL_SetCursor(cursor); 170 - return 0; 169 + static int f_set_cursor(lua_State *L) 170 + { 171 + int opt = luaL_checkoption(L, 1, "arrow", cursor_opts); 172 + int n = cursor_enums[opt]; 173 + SDL_Cursor *cursor = cursor_cache[n]; 174 + if (!cursor) 175 + { 176 + cursor = SDL_CreateSystemCursor(n); 177 + cursor_cache[n] = cursor; 178 + } 179 + SDL_SetCursor(cursor); 180 + return 0; 171 181 } 172 182 173 183 174 - static int f_set_window_title(lua_State *L) { 175 - const char *title = luaL_checkstring(L, 1); 176 - SDL_SetWindowTitle(window, title); 177 - return 0; 184 + static int f_set_window_title(lua_State *L) 185 + { 186 + const char *title = luaL_checkstring(L, 1); 187 + SDL_SetWindowTitle(window, title); 188 + return 0; 178 189 } 179 190 180 191 181 192 static const char *window_opts[] = { "normal", "maximized", "fullscreen", 0 }; 182 193 enum { WIN_NORMAL, WIN_MAXIMIZED, WIN_FULLSCREEN }; 183 194 184 - static int f_set_window_mode(lua_State *L) { 185 - int n = luaL_checkoption(L, 1, "normal", window_opts); 186 - SDL_SetWindowFullscreen(window, 187 - n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); 188 - if (n == WIN_NORMAL) { SDL_RestoreWindow(window); } 189 - if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); } 190 - return 0; 195 + static int f_set_window_mode(lua_State *L) 196 + { 197 + int n = luaL_checkoption(L, 1, "normal", window_opts); 198 + SDL_SetWindowFullscreen(window, n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN : 0); 199 + if (n == WIN_NORMAL) 200 + SDL_RestoreWindow(window); 201 + if (n == WIN_MAXIMIZED) 202 + SDL_MaximizeWindow(window); 203 + return 0; 191 204 } 192 205 193 206 194 - static int f_window_has_focus(lua_State *L) { 195 - unsigned flags = SDL_GetWindowFlags(window); 196 - lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS); 197 - return 1; 207 + static int f_window_has_focus(lua_State *L) 208 + { 209 + unsigned flags = SDL_GetWindowFlags(window); 210 + lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS); 211 + return 1; 198 212 } 199 213 200 214 201 - static int f_show_confirm_dialog(lua_State *L) { 202 - const char *title = luaL_checkstring(L, 1); 203 - const char *msg = luaL_checkstring(L, 2); 215 + static int f_show_confirm_dialog(lua_State *L) 216 + { 217 + const char *title = luaL_checkstring(L, 1); 218 + const char *msg = luaL_checkstring(L, 2); 204 219 205 - #if _WIN32 206 - int id = MessageBox(0, msg, title, MB_YESNO | MB_ICONWARNING); 207 - lua_pushboolean(L, id == IDYES); 220 + # if _WIN32 221 + int id = MessageBox(0, msg, title, MB_YESNO | MB_ICONWARNING); 222 + lua_pushboolean(L, id == IDYES); 223 + # else 224 + SDL_MessageBoxButtonData buttons[] = { 225 + { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" }, 226 + { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" }, 227 + }; 228 + SDL_MessageBoxData data = { 229 + .title = title, 230 + .message = msg, 231 + .numbuttons = 2, 232 + .buttons = buttons, 233 + }; 234 + int buttonid; 235 + SDL_ShowMessageBox(&data, &buttonid); 236 + lua_pushboolean(L, buttonid == 1); 237 + # endif 208 238 209 - #else 210 - SDL_MessageBoxButtonData buttons[] = { 211 - { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" }, 212 - { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" }, 213 - }; 214 - SDL_MessageBoxData data = { 215 - .title = title, 216 - .message = msg, 217 - .numbuttons = 2, 218 - .buttons = buttons, 219 - }; 220 - int buttonid; 221 - SDL_ShowMessageBox(&data, &buttonid); 222 - lua_pushboolean(L, buttonid == 1); 223 - #endif 224 - return 1; 239 + return 1; 225 240 } 226 241 227 242 228 - static int f_chdir(lua_State *L) { 229 - const char *path = luaL_checkstring(L, 1); 230 - int err = chdir(path); 231 - if (err) { luaL_error(L, "chdir() failed"); } 232 - return 0; 243 + static int f_chdir(lua_State *L) 244 + { 245 + const char *path = luaL_checkstring(L, 1); 246 + int err = chdir(path); 247 + if (err) 248 + luaL_error(L, "chdir() failed"); 249 + return 0; 233 250 } 234 251 235 252 236 - static int f_list_dir(lua_State *L) { 237 - const char *path = luaL_checkstring(L, 1); 253 + static int f_list_dir(lua_State *L) 254 + { 255 + const char *path = luaL_checkstring(L, 1); 238 256 239 - DIR *dir = opendir(path); 240 - if (!dir) { 241 - lua_pushnil(L); 242 - lua_pushstring(L, strerror(errno)); 243 - return 2; 244 - } 257 + DIR *dir = opendir(path); 258 + if (!dir) 259 + { 260 + lua_pushnil(L); 261 + lua_pushstring(L, strerror(errno)); 262 + return 2; 263 + } 245 264 246 - lua_newtable(L); 247 - int i = 1; 248 - struct dirent *entry; 249 - while ( (entry = readdir(dir)) ) { 250 - if (strcmp(entry->d_name, "." ) == 0) { continue; } 251 - if (strcmp(entry->d_name, "..") == 0) { continue; } 252 - lua_pushstring(L, entry->d_name); 253 - lua_rawseti(L, -2, i); 254 - i++; 255 - } 265 + lua_newtable(L); 266 + int i = 1; 267 + struct dirent *entry; 268 + while ((entry = readdir(dir))) 269 + { 270 + if (strcmp(entry->d_name, "." ) == 0 || strcmp(entry->d_name, "..") == 0) 271 + continue; 272 + lua_pushstring(L, entry->d_name); 273 + lua_rawseti(L, -2, i); 274 + i++; 275 + } 256 276 257 - closedir(dir); 258 - return 1; 277 + closedir(dir); 278 + return 1; 259 279 } 260 280 261 281 262 282 #ifdef _WIN32 263 - #include <windows.h> 264 - #define realpath(x, y) _fullpath(y, x, MAX_PATH) 283 + # include <windows.h> 284 + # define realpath(x, y) _fullpath(y, x, MAX_PATH) 265 285 #endif 266 286 267 - static int f_absolute_path(lua_State *L) { 268 - const char *path = luaL_checkstring(L, 1); 269 - char *res = realpath(path, NULL); 270 - if (!res) { return 0; } 271 - lua_pushstring(L, res); 272 - free(res); 273 - return 1; 287 + static int f_absolute_path(lua_State *L) 288 + { 289 + const char *path = luaL_checkstring(L, 1); 290 + char *res = realpath(path, NULL); 291 + if (!res) 292 + return 0; 293 + lua_pushstring(L, res); 294 + free(res); 295 + return 1; 274 296 } 275 297 276 298 277 - static int f_get_file_info(lua_State *L) { 278 - const char *path = luaL_checkstring(L, 1); 299 + static int f_get_file_info(lua_State *L) 300 + { 301 + const char *path = luaL_checkstring(L, 1); 279 302 280 - struct stat s; 281 - int err = stat(path, &s); 282 - if (err < 0) { 283 - lua_pushnil(L); 284 - lua_pushstring(L, strerror(errno)); 285 - return 2; 286 - } 303 + struct stat s; 304 + int err = stat(path, &s); 305 + if (err < 0) 306 + { 307 + lua_pushnil(L); 308 + lua_pushstring(L, strerror(errno)); 309 + return 2; 310 + } 287 311 288 - lua_newtable(L); 289 - lua_pushnumber(L, s.st_mtime); 290 - lua_setfield(L, -2, "modified"); 312 + lua_newtable(L); 313 + lua_pushnumber(L, s.st_mtime); 314 + lua_setfield(L, -2, "modified"); 291 315 292 - lua_pushnumber(L, s.st_size); 293 - lua_setfield(L, -2, "size"); 316 + lua_pushnumber(L, s.st_size); 317 + lua_setfield(L, -2, "size"); 294 318 295 - if (S_ISREG(s.st_mode)) { 296 - lua_pushstring(L, "file"); 297 - } else if (S_ISDIR(s.st_mode)) { 298 - lua_pushstring(L, "dir"); 299 - } else { 300 - lua_pushnil(L); 301 - } 302 - lua_setfield(L, -2, "type"); 319 + if (S_ISREG(s.st_mode)) 320 + lua_pushstring(L, "file"); 321 + else if (S_ISDIR(s.st_mode)) 322 + lua_pushstring(L, "dir"); 323 + else 324 + lua_pushnil(L); 325 + lua_setfield(L, -2, "type"); 303 326 304 - return 1; 327 + return 1; 305 328 } 306 329 307 330 308 - static int f_get_clipboard(lua_State *L) { 309 - char *text = SDL_GetClipboardText(); 310 - if (!text) { return 0; } 311 - lua_pushstring(L, text); 312 - SDL_free(text); 313 - return 1; 331 + static int f_get_clipboard(lua_State *L) 332 + { 333 + char *text = SDL_GetClipboardText(); 334 + if (!text) 335 + return 0; 336 + lua_pushstring(L, text); 337 + SDL_free(text); 338 + return 1; 314 339 } 315 340 316 341 317 - static int f_set_clipboard(lua_State *L) { 318 - const char *text = luaL_checkstring(L, 1); 319 - SDL_SetClipboardText(text); 320 - return 0; 342 + static int f_set_clipboard(lua_State *L) 343 + { 344 + const char *text = luaL_checkstring(L, 1); 345 + SDL_SetClipboardText(text); 346 + return 0; 321 347 } 322 348 323 349 324 - static int f_get_time(lua_State *L) { 325 - double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency(); 326 - lua_pushnumber(L, n); 327 - return 1; 350 + static int f_get_time(lua_State *L) 351 + { 352 + double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency(); 353 + lua_pushnumber(L, n); 354 + return 1; 328 355 } 329 356 330 357 331 - static int f_sleep(lua_State *L) { 332 - double n = luaL_checknumber(L, 1); 333 - SDL_Delay(n * 1000); 334 - return 0; 358 + static int f_sleep(lua_State *L) 359 + { 360 + double n = luaL_checknumber(L, 1); 361 + SDL_Delay(n * 1000); 362 + return 0; 335 363 } 336 364 337 365 338 - static int f_exec(lua_State *L) { 339 - size_t len; 340 - const char *cmd = luaL_checklstring(L, 1, &len); 341 - char *buf = malloc(len + 32); 342 - if (!buf) { luaL_error(L, "buffer allocation failed"); } 343 - #if _WIN32 344 - sprintf(buf, "cmd /c \"%s\"", cmd); 345 - WinExec(buf, SW_HIDE); 346 - #else 347 - sprintf(buf, "%s &", cmd); 348 - int res = system(buf); 349 - (void) res; 350 - #endif 351 - free(buf); 352 - return 0; 366 + static int f_exec(lua_State *L) 367 + { 368 + size_t len; 369 + const char *cmd = luaL_checklstring(L, 1, &len); 370 + char *buf = malloc(len + 32); 371 + if (!buf) 372 + luaL_error(L, "buffer allocation failed"); 373 + 374 + # if _WIN32 375 + sprintf(buf, "cmd /c \"%s\"", cmd); 376 + WinExec(buf, SW_HIDE); 377 + # else 378 + sprintf(buf, "%s &", cmd); 379 + int res = system(buf); 380 + (void)res; 381 + # endif 382 + 383 + free(buf); 384 + return 0; 353 385 } 354 386 355 387 356 - static int f_fuzzy_match(lua_State *L) { 357 - const char *str = luaL_checkstring(L, 1); 358 - const char *ptn = luaL_checkstring(L, 2); 359 - int score = 0; 360 - int run = 0; 388 + static int f_fuzzy_match(lua_State *L) 389 + { 390 + const char *str = luaL_checkstring(L, 1); 391 + const char *ptn = luaL_checkstring(L, 2); 392 + int score = 0; 393 + int run = 0; 361 394 362 - while (*str && *ptn) { 363 - while (*str == ' ') { str++; } 364 - while (*ptn == ' ') { ptn++; } 365 - if (tolower(*str) == tolower(*ptn)) { 366 - score += run; 367 - run++; 368 - ptn++; 369 - } else { 370 - score--; 371 - run = 0; 372 - } 373 - str++; 374 - } 375 - if (*ptn) { return 0; } 395 + while (*str && *ptn) 396 + { 397 + while (*str == ' ') 398 + str++; 399 + while (*ptn == ' ') 400 + ptn++; 401 + if (tolower(*str) == tolower(*ptn)) 402 + { 403 + score += run * 10 - (*str != *ptn); 404 + run++; 405 + ptn++; 406 + } 407 + else 408 + { 409 + score -= 10; 410 + run = 0; 411 + } 412 + str++; 413 + } 414 + if (*ptn) 415 + return 0; 376 416 377 - lua_pushnumber(L, score - (int) strlen(str)); 378 - return 1; 417 + lua_pushnumber(L, score - (int) strlen(str)); 418 + return 1; 379 419 } 380 420 381 421 382 422 static const luaL_Reg lib[] = { 383 - { "poll_event", f_poll_event }, 384 - { "wait_event", f_wait_event }, 385 - { "set_cursor", f_set_cursor }, 386 - { "set_window_title", f_set_window_title }, 387 - { "set_window_mode", f_set_window_mode }, 388 - { "window_has_focus", f_window_has_focus }, 389 - { "show_confirm_dialog", f_show_confirm_dialog }, 390 - { "chdir", f_chdir }, 391 - { "list_dir", f_list_dir }, 392 - { "absolute_path", f_absolute_path }, 393 - { "get_file_info", f_get_file_info }, 394 - { "get_clipboard", f_get_clipboard }, 395 - { "set_clipboard", f_set_clipboard }, 396 - { "get_time", f_get_time }, 397 - { "sleep", f_sleep }, 398 - { "exec", f_exec }, 399 - { "fuzzy_match", f_fuzzy_match }, 400 - { NULL, NULL } 423 + { "poll_event", f_poll_event }, 424 + { "wait_event", f_wait_event }, 425 + { "set_cursor", f_set_cursor }, 426 + { "set_window_title", f_set_window_title }, 427 + { "set_window_mode", f_set_window_mode }, 428 + { "window_has_focus", f_window_has_focus }, 429 + { "show_confirm_dialog", f_show_confirm_dialog }, 430 + { "chdir", f_chdir }, 431 + { "list_dir", f_list_dir }, 432 + { "absolute_path", f_absolute_path }, 433 + { "get_file_info", f_get_file_info }, 434 + { "get_clipboard", f_get_clipboard }, 435 + { "set_clipboard", f_set_clipboard }, 436 + { "get_time", f_get_time }, 437 + { "sleep", f_sleep }, 438 + { "exec", f_exec }, 439 + { "fuzzy_match", f_fuzzy_match }, 440 + { NULL, NULL } 401 441 }; 402 442 403 443 404 - int luaopen_system(lua_State *L) { 405 - luaL_newlib(L, lib); 406 - return 1; 444 + int luaopen_system(lua_State *L) 445 + { 446 + luaL_newlib(L, lib); 447 + return 1; 407 448 }
+262 -104
src/lib/stb/stb_truetype.h
··· 1 - // stb_truetype.h - v1.19 - public domain 2 - // authored from 2009-2016 by Sean Barrett / RAD Game Tools 1 + // stb_truetype.h - v1.24 - public domain 2 + // authored from 2009-2020 by Sean Barrett / RAD Game Tools 3 + // 4 + // ======================================================================= 5 + // 6 + // NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES 7 + // 8 + // This library does no range checking of the offsets found in the file, 9 + // meaning an attacker can use it to read arbitrary memory. 10 + // 11 + // ======================================================================= 3 12 // 4 13 // This library processes TrueType files: 5 14 // parse files ··· 32 41 // Daniel Ribeiro Maciel 33 42 // 34 43 // Bug/warning reports/fixes: 35 - // "Zer" on mollyrocket Fabian "ryg" Giesen 36 - // Cass Everitt Martins Mozeiko 37 - // stoiko (Haemimont Games) Cap Petschulat 38 - // Brian Hook Omar Cornut 39 - // Walter van Niftrik github:aloucks 44 + // "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe 45 + // Cass Everitt Martins Mozeiko github:aloucks 46 + // stoiko (Haemimont Games) Cap Petschulat github:oyvindjam 47 + // Brian Hook Omar Cornut github:vassvik 48 + // Walter van Niftrik Ryan Griege 40 49 // David Gow Peter LaValle 41 50 // David Given Sergey Popov 42 51 // Ivan-Assen Ivanov Giumo X. Clanjor ··· 44 53 // Johan Duparc Thomas Fields 45 54 // Hou Qiming Derek Vinyard 46 55 // Rob Loach Cort Stratton 47 - // Kenney Phillis Jr. github:oyvindjam 48 - // Brian Costabile github:vassvik 49 - // 56 + // Kenney Phillis Jr. Brian Costabile 57 + // Ken Voskuil (kaesve) 58 + // 50 59 // VERSION HISTORY 51 60 // 61 + // 1.24 (2020-02-05) fix warning 62 + // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) 63 + // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined 64 + // 1.21 (2019-02-25) fix warning 65 + // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() 52 66 // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod 53 67 // 1.18 (2018-01-29) add missing function 54 68 // 1.17 (2017-07-23) make more arguments const; doc fix ··· 75 89 // 76 90 // USAGE 77 91 // 78 - // Include this file in whatever places neeed to refer to it. In ONE C/C++ 92 + // Include this file in whatever places need to refer to it. In ONE C/C++ 79 93 // file, write: 80 94 // #define STB_TRUETYPE_IMPLEMENTATION 81 95 // before the #include of this file. This expands out the actual ··· 206 220 // 207 221 // Advancing for the next character: 208 222 // Call GlyphHMetrics, and compute 'current_point += SF * advance'. 209 - // 223 + // 210 224 // 211 225 // ADVANCED USAGE 212 226 // ··· 242 256 // recommend it. 243 257 // 244 258 // 245 - // SOURCE STATISTICS (based on v0.6c, 2050 LOC) 246 - // 247 - // Documentation & header file 520 LOC \___ 660 LOC documentation 248 - // Sample code 140 LOC / 249 - // Truetype parsing 620 LOC ---- 620 LOC TrueType 250 - // Software rasterization 240 LOC \ . 251 - // Curve tesselation 120 LOC \__ 550 LOC Bitmap creation 252 - // Bitmap management 100 LOC / 253 - // Baked bitmap interface 70 LOC / 254 - // Font name matching & access 150 LOC ---- 150 255 - // C runtime library abstraction 60 LOC ---- 60 256 - // 257 - // 258 259 // PERFORMANCE MEASUREMENTS FOR 1.06: 259 260 // 260 261 // 32-bit 64-bit ··· 344 345 } 345 346 return 0; 346 347 } 347 - #endif 348 + #endif 348 349 // 349 350 // Output: 350 351 // ··· 358 359 // :@@. M@M 359 360 // @@@o@@@@ 360 361 // :M@@V:@@. 361 - // 362 + // 362 363 ////////////////////////////////////////////////////////////////////////////// 363 - // 364 + // 364 365 // Complete program: print "Hello World!" banner, with bugs 365 366 // 366 367 #if 0 ··· 556 557 // 557 558 // It's inefficient; you might want to c&p it and optimize it. 558 559 560 + STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); 561 + // Query the font vertical metrics without having to create a font first. 559 562 560 563 561 564 ////////////////////////////////////////////////////////////////////////////// ··· 641 644 // To use with PackFontRangesGather etc., you must set it before calls 642 645 // call to PackFontRangesGatherRects. 643 646 647 + STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); 648 + // If skip != 0, this tells stb_truetype to skip any codepoints for which 649 + // there is no corresponding glyph. If skip=0, which is the default, then 650 + // codepoints without a glyph recived the font's "missing character" glyph, 651 + // typically an empty box by convention. 652 + 644 653 STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above 645 654 int char_index, // character to display 646 655 float *xpos, float *ypos, // pointers to current position in screen pixel space ··· 653 662 // Calling these functions in sequence is roughly equivalent to calling 654 663 // stbtt_PackFontRanges(). If you more control over the packing of multiple 655 664 // fonts, or if you want to pack custom data into a font texture, take a look 656 - // at the source to of stbtt_PackFontRanges() and create a custom version 665 + // at the source to of stbtt_PackFontRanges() and create a custom version 657 666 // using these functions, e.g. call GatherRects multiple times, 658 667 // building up a single array of rects, then call PackRects once, 659 668 // then call RenderIntoRects repeatedly. This may result in a ··· 669 678 int height; 670 679 int stride_in_bytes; 671 680 int padding; 681 + int skip_missing; 672 682 unsigned int h_oversample, v_oversample; 673 683 unsigned char *pixels; 674 684 void *nodes; ··· 694 704 // file will only define one font and it always be at offset 0, so it will 695 705 // return '0' for index 0, and -1 for all other indices. 696 706 697 - // The following structure is defined publically so you can declare one on 707 + // The following structure is defined publicly so you can declare one on 698 708 // the stack or as a global or etc, but you should treat it as opaque. 699 709 struct stbtt_fontinfo 700 710 { ··· 704 714 705 715 int numGlyphs; // number of glyphs, needed for range checking 706 716 707 - int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf 717 + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf 708 718 int index_map; // a cmap mapping for our chosen character encoding 709 719 int indexToLocFormat; // format needed to map from glyph index to glyph 710 720 ··· 733 743 // and you want a speed-up, call this function with the character you're 734 744 // going to process, then use glyph-based functions instead of the 735 745 // codepoint-based functions. 746 + // Returns 0 if the character codepoint is not defined in the font. 736 747 737 748 738 749 ////////////////////////////////////////////////////////////////////////////// ··· 786 797 STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 787 798 // as above, but takes one or more glyph indices for greater efficiency 788 799 800 + typedef struct stbtt_kerningentry 801 + { 802 + int glyph1; // use stbtt_FindGlyphIndex 803 + int glyph2; 804 + int advance; 805 + } stbtt_kerningentry; 806 + 807 + STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); 808 + STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); 809 + // Retrieves a complete list of all of the kerning pairs provided by the font 810 + // stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. 811 + // The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) 789 812 790 813 ////////////////////////////////////////////////////////////////////////////// 791 814 // ··· 820 843 // returns # of vertices and fills *vertices with the pointer to them 821 844 // these are expressed in "unscaled" coordinates 822 845 // 823 - // The shape is a series of countours. Each one starts with 846 + // The shape is a series of contours. Each one starts with 824 847 // a STBTT_moveto, then consists of a series of mixed 825 848 // STBTT_lineto and STBTT_curveto segments. A lineto 826 849 // draws a line from previous endpoint to its x,y; a curveto ··· 829 852 830 853 STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); 831 854 // frees the data allocated above 855 + 856 + STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); 857 + STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); 858 + // fills svg with the character's SVG data. 859 + // returns data size or 0 if SVG not found. 832 860 833 861 ////////////////////////////////////////////////////////////////////////////// 834 862 // ··· 916 944 STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); 917 945 // These functions compute a discretized SDF field for a single character, suitable for storing 918 946 // in a single-channel texture, sampling with bilinear filtering, and testing against 919 - // larger than some threshhold to produce scalable fonts. 947 + // larger than some threshold to produce scalable fonts. 920 948 // info -- the font 921 949 // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap 922 950 // glyph/codepoint -- the character to generate the SDF for ··· 959 987 // and computing from that can allow drop-out prevention). 960 988 // 961 989 // The algorithm has not been optimized at all, so expect it to be slow 962 - // if computing lots of characters or very large sizes. 990 + // if computing lots of characters or very large sizes. 963 991 964 992 965 993 ··· 1331 1359 return stbtt__cff_get_index(&cff); 1332 1360 } 1333 1361 1362 + // since most people won't use this, find this table the first time it's needed 1363 + static int stbtt__get_svg(stbtt_fontinfo *info) 1364 + { 1365 + stbtt_uint32 t; 1366 + if (info->svg < 0) { 1367 + t = stbtt__find_table(info->data, info->fontstart, "SVG "); 1368 + if (t) { 1369 + stbtt_uint32 offset = ttULONG(info->data + t + 2); 1370 + info->svg = t + offset; 1371 + } else { 1372 + info->svg = 0; 1373 + } 1374 + } 1375 + return info->svg; 1376 + } 1377 + 1334 1378 static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) 1335 1379 { 1336 1380 stbtt_uint32 cmap, t; ··· 1409 1453 info->numGlyphs = ttUSHORT(data+t+4); 1410 1454 else 1411 1455 info->numGlyphs = 0xffff; 1456 + 1457 + info->svg = -1; 1412 1458 1413 1459 // find a cmap encoding table we understand *now* to avoid searching 1414 1460 // later. (todo: could make this installable) ··· 1716 1762 if (i != 0) 1717 1763 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1718 1764 1719 - // now start the new one 1765 + // now start the new one 1720 1766 start_off = !(flags & 1); 1721 1767 if (start_off) { 1722 1768 // if we start off with an off-curve point, then when we need to find a point on the curve ··· 1758 1804 } 1759 1805 } 1760 1806 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1761 - } else if (numberOfContours == -1) { 1807 + } else if (numberOfContours < 0) { 1762 1808 // Compound shapes. 1763 1809 int more = 1; 1764 1810 stbtt_uint8 *comp = data + g + 10; ··· 1769 1815 int comp_num_verts = 0, i; 1770 1816 stbtt_vertex *comp_verts = 0, *tmp = 0; 1771 1817 float mtx[6] = {1,0,0,1,0,0}, m, n; 1772 - 1818 + 1773 1819 flags = ttSHORT(comp); comp+=2; 1774 1820 gidx = ttSHORT(comp); comp+=2; 1775 1821 ··· 1799 1845 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 1800 1846 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1801 1847 } 1802 - 1848 + 1803 1849 // Find transformation scales. 1804 1850 m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 1805 1851 n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); ··· 1835 1881 // More components ? 1836 1882 more = flags & (1<<5); 1837 1883 } 1838 - } else if (numberOfContours < 0) { 1839 - // @TODO other compound variations? 1840 - STBTT_assert(0); 1841 1884 } else { 1842 1885 // numberOfCounters == 0, do nothing 1843 1886 } ··· 2266 2309 } 2267 2310 } 2268 2311 2312 + STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) 2313 + { 2314 + stbtt_uint8 *data = info->data + info->kern; 2315 + 2316 + // we only look at the first table. it must be 'horizontal' and format 0. 2317 + if (!info->kern) 2318 + return 0; 2319 + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 2320 + return 0; 2321 + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 2322 + return 0; 2323 + 2324 + return ttUSHORT(data+10); 2325 + } 2326 + 2327 + STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) 2328 + { 2329 + stbtt_uint8 *data = info->data + info->kern; 2330 + int k, length; 2331 + 2332 + // we only look at the first table. it must be 'horizontal' and format 0. 2333 + if (!info->kern) 2334 + return 0; 2335 + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 2336 + return 0; 2337 + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 2338 + return 0; 2339 + 2340 + length = ttUSHORT(data+10); 2341 + if (table_length < length) 2342 + length = table_length; 2343 + 2344 + for (k = 0; k < length; k++) 2345 + { 2346 + table[k].glyph1 = ttUSHORT(data+18+(k*6)); 2347 + table[k].glyph2 = ttUSHORT(data+20+(k*6)); 2348 + table[k].advance = ttSHORT(data+22+(k*6)); 2349 + } 2350 + 2351 + return length; 2352 + } 2353 + 2269 2354 static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) 2270 2355 { 2271 2356 stbtt_uint8 *data = info->data + info->kern; ··· 2463 2548 if (valueFormat2 != 0) return 0; 2464 2549 2465 2550 STBTT_assert(coverageIndex < pairSetCount); 2551 + STBTT__NOTUSED(pairSetCount); 2466 2552 2467 2553 needle=glyph2; 2468 2554 r=pairValueCount-1; ··· 2540 2626 2541 2627 if (info->gpos) 2542 2628 xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); 2543 - 2544 - if (info->kern) 2629 + else if (info->kern) 2545 2630 xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); 2546 2631 2547 2632 return xAdvance; ··· 2602 2687 STBTT_free(v, info->userdata); 2603 2688 } 2604 2689 2690 + STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) 2691 + { 2692 + int i; 2693 + stbtt_uint8 *data = info->data; 2694 + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); 2695 + 2696 + int numEntries = ttUSHORT(svg_doc_list); 2697 + stbtt_uint8 *svg_docs = svg_doc_list + 2; 2698 + 2699 + for(i=0; i<numEntries; i++) { 2700 + stbtt_uint8 *svg_doc = svg_docs + (12 * i); 2701 + if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) 2702 + return svg_doc; 2703 + } 2704 + return 0; 2705 + } 2706 + 2707 + STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) 2708 + { 2709 + stbtt_uint8 *data = info->data; 2710 + stbtt_uint8 *svg_doc; 2711 + 2712 + if (info->svg == 0) 2713 + return 0; 2714 + 2715 + svg_doc = stbtt_FindSVGDoc(info, gl); 2716 + if (svg_doc != NULL) { 2717 + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); 2718 + return ttULONG(svg_doc + 8); 2719 + } else { 2720 + return 0; 2721 + } 2722 + } 2723 + 2724 + STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) 2725 + { 2726 + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); 2727 + } 2728 + 2605 2729 ////////////////////////////////////////////////////////////////////////////// 2606 2730 // 2607 2731 // antialiasing software rasterizer ··· 2727 2851 float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 2728 2852 STBTT_assert(z != NULL); 2729 2853 if (!z) return z; 2730 - 2854 + 2731 2855 // round dx down to avoid overshooting 2732 2856 if (dxdy < 0) 2733 2857 z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); ··· 2805 2929 } 2806 2930 } 2807 2931 } 2808 - 2932 + 2809 2933 e = e->next; 2810 2934 } 2811 2935 } ··· 3160 3284 if (e->y0 != e->y1) { 3161 3285 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); 3162 3286 if (z != NULL) { 3163 - STBTT_assert(z->ey >= scan_y_top); 3287 + if (j == 0 && off_y != 0) { 3288 + if (z->ey < scan_y_top) { 3289 + // this can happen due to subpixel positioning and some kind of fp rounding error i think 3290 + z->ey = scan_y_top; 3291 + } 3292 + } 3293 + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds 3164 3294 // insert at front 3165 3295 z->next = active; 3166 3296 active = z; ··· 3229 3359 3230 3360 static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) 3231 3361 { 3232 - /* threshhold for transitioning to insertion sort */ 3362 + /* threshold for transitioning to insertion sort */ 3233 3363 while (n > 12) { 3234 3364 stbtt__edge t; 3235 3365 int c01,c12,c,m,i,j; ··· 3364 3494 points[n].y = y; 3365 3495 } 3366 3496 3367 - // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 3497 + // tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching 3368 3498 static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) 3369 3499 { 3370 3500 // midpoint ··· 3527 3657 { 3528 3658 int ix0,iy0,ix1,iy1; 3529 3659 stbtt__bitmap gbm; 3530 - stbtt_vertex *vertices; 3660 + stbtt_vertex *vertices; 3531 3661 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3532 3662 3533 3663 if (scale_x == 0) scale_x = scale_y; ··· 3550 3680 if (height) *height = gbm.h; 3551 3681 if (xoff ) *xoff = ix0; 3552 3682 if (yoff ) *yoff = iy0; 3553 - 3683 + 3554 3684 if (gbm.w && gbm.h) { 3555 3685 gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); 3556 3686 if (gbm.pixels) { ··· 3561 3691 } 3562 3692 STBTT_free(vertices, info->userdata); 3563 3693 return gbm.pixels; 3564 - } 3694 + } 3565 3695 3566 3696 STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) 3567 3697 { ··· 3573 3703 int ix0,iy0; 3574 3704 stbtt_vertex *vertices; 3575 3705 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3576 - stbtt__bitmap gbm; 3706 + stbtt__bitmap gbm; 3577 3707 3578 3708 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); 3579 3709 gbm.pixels = output; ··· 3595 3725 STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3596 3726 { 3597 3727 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 3598 - } 3728 + } 3599 3729 3600 3730 STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) 3601 3731 { ··· 3610 3740 STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3611 3741 { 3612 3742 return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 3613 - } 3743 + } 3614 3744 3615 3745 STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) 3616 3746 { ··· 3735 3865 con->y = 0; 3736 3866 con->bottom_y = 0; 3737 3867 STBTT__NOTUSED(nodes); 3738 - STBTT__NOTUSED(num_nodes); 3868 + STBTT__NOTUSED(num_nodes); 3739 3869 } 3740 3870 3741 3871 static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) ··· 3789 3919 spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 3790 3920 spc->h_oversample = 1; 3791 3921 spc->v_oversample = 1; 3922 + spc->skip_missing = 0; 3792 3923 3793 3924 stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 3794 3925 ··· 3814 3945 spc->v_oversample = v_oversample; 3815 3946 } 3816 3947 3948 + STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) 3949 + { 3950 + spc->skip_missing = skip; 3951 + } 3952 + 3817 3953 #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) 3818 3954 3819 3955 static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) ··· 3956 4092 STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 3957 4093 { 3958 4094 int i,j,k; 4095 + int missing_glyph_added = 0; 3959 4096 3960 4097 k=0; 3961 4098 for (i=0; i < num_ranges; ++i) { ··· 3967 4104 int x0,y0,x1,y1; 3968 4105 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 3969 4106 int glyph = stbtt_FindGlyphIndex(info, codepoint); 3970 - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, 3971 - scale * spc->h_oversample, 3972 - scale * spc->v_oversample, 3973 - 0,0, 3974 - &x0,&y0,&x1,&y1); 3975 - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); 3976 - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); 4107 + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { 4108 + rects[k].w = rects[k].h = 0; 4109 + } else { 4110 + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, 4111 + scale * spc->h_oversample, 4112 + scale * spc->v_oversample, 4113 + 0,0, 4114 + &x0,&y0,&x1,&y1); 4115 + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); 4116 + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); 4117 + if (glyph == 0) 4118 + missing_glyph_added = 1; 4119 + } 3977 4120 ++k; 3978 4121 } 3979 4122 } ··· 4007 4150 // rects array must be big enough to accommodate all characters in the given ranges 4008 4151 STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 4009 4152 { 4010 - int i,j,k, return_value = 1; 4153 + int i,j,k, missing_glyph = -1, return_value = 1; 4011 4154 4012 4155 // save current values 4013 4156 int old_h_over = spc->h_oversample; ··· 4026 4169 sub_y = stbtt__oversample_shift(spc->v_oversample); 4027 4170 for (j=0; j < ranges[i].num_chars; ++j) { 4028 4171 stbrp_rect *r = &rects[k]; 4029 - if (r->was_packed) { 4172 + if (r->was_packed && r->w != 0 && r->h != 0) { 4030 4173 stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; 4031 4174 int advance, lsb, x0,y0,x1,y1; 4032 4175 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; ··· 4072 4215 bc->yoff = (float) y0 * recip_v + sub_y; 4073 4216 bc->xoff2 = (x0 + r->w) * recip_h + sub_x; 4074 4217 bc->yoff2 = (y0 + r->h) * recip_v + sub_y; 4218 + 4219 + if (glyph == 0) 4220 + missing_glyph = j; 4221 + } else if (spc->skip_missing) { 4222 + return_value = 0; 4223 + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { 4224 + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; 4075 4225 } else { 4076 4226 return_value = 0; // if any fail, report failure 4077 4227 } ··· 4110 4260 n = 0; 4111 4261 for (i=0; i < num_ranges; ++i) 4112 4262 n += ranges[i].num_chars; 4113 - 4263 + 4114 4264 rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); 4115 4265 if (rects == NULL) 4116 4266 return 0; ··· 4121 4271 n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); 4122 4272 4123 4273 stbtt_PackFontRangesPackRects(spc, rects, n); 4124 - 4274 + 4125 4275 return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); 4126 4276 4127 4277 STBTT_free(rects, spc->user_allocator_context); ··· 4138 4288 range.chardata_for_range = chardata_for_range; 4139 4289 range.font_size = font_size; 4140 4290 return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 4291 + } 4292 + 4293 + STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) 4294 + { 4295 + int i_ascent, i_descent, i_lineGap; 4296 + float scale; 4297 + stbtt_fontinfo info; 4298 + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); 4299 + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); 4300 + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); 4301 + *ascent = (float) i_ascent * scale; 4302 + *descent = (float) i_descent * scale; 4303 + *lineGap = (float) i_lineGap * scale; 4141 4304 } 4142 4305 4143 4306 STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) ··· 4269 4432 int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; 4270 4433 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4271 4434 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4272 - if (x_inter < x) 4435 + if (x_inter < x) 4273 4436 winding += (y0 < y1) ? 1 : -1; 4274 4437 } 4275 4438 } ··· 4295 4458 y1 = (int)verts[i ].y; 4296 4459 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4297 4460 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4298 - if (x_inter < x) 4461 + if (x_inter < x) 4299 4462 winding += (y0 < y1) ? 1 : -1; 4300 4463 } 4301 4464 } else { ··· 4307 4470 if (hits[1][0] < 0) 4308 4471 winding += (hits[1][1] < 0 ? -1 : 1); 4309 4472 } 4310 - } 4473 + } 4311 4474 } 4312 4475 } 4313 4476 return winding; ··· 4360 4523 int w,h; 4361 4524 unsigned char *data; 4362 4525 4363 - // if one scale is 0, use same scale for both 4364 - if (scale_x == 0) scale_x = scale_y; 4365 - if (scale_y == 0) { 4366 - if (scale_x == 0) return NULL; // if both scales are 0, return NULL 4367 - scale_y = scale_x; 4368 - } 4526 + if (scale == 0) return NULL; 4369 4527 4370 4528 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); 4371 4529 ··· 4388 4546 4389 4547 // invert for y-downwards bitmaps 4390 4548 scale_y = -scale_y; 4391 - 4549 + 4392 4550 { 4393 4551 int x,y,i,j; 4394 4552 float *precompute; ··· 4537 4695 STBTT_free(verts, info->userdata); 4538 4696 } 4539 4697 return data; 4540 - } 4698 + } 4541 4699 4542 4700 STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) 4543 4701 { ··· 4555 4713 // 4556 4714 4557 4715 // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 4558 - static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) 4716 + static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) 4559 4717 { 4560 4718 stbtt_int32 i=0; 4561 4719 ··· 4594 4752 return i; 4595 4753 } 4596 4754 4597 - static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) 4755 + static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) 4598 4756 { 4599 4757 return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); 4600 4758 } ··· 4723 4881 4724 4882 STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) 4725 4883 { 4726 - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); 4884 + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); 4727 4885 } 4728 4886 4729 4887 STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) ··· 4816 4974 ------------------------------------------------------------------------------ 4817 4975 ALTERNATIVE A - MIT License 4818 4976 Copyright (c) 2017 Sean Barrett 4819 - Permission is hereby granted, free of charge, to any person obtaining a copy of 4820 - this software and associated documentation files (the "Software"), to deal in 4821 - the Software without restriction, including without limitation the rights to 4822 - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 4823 - of the Software, and to permit persons to whom the Software is furnished to do 4977 + Permission is hereby granted, free of charge, to any person obtaining a copy of 4978 + this software and associated documentation files (the "Software"), to deal in 4979 + the Software without restriction, including without limitation the rights to 4980 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 4981 + of the Software, and to permit persons to whom the Software is furnished to do 4824 4982 so, subject to the following conditions: 4825 - The above copyright notice and this permission notice shall be included in all 4983 + The above copyright notice and this permission notice shall be included in all 4826 4984 copies or substantial portions of the Software. 4827 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4828 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4829 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4830 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 4831 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 4832 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 4985 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4986 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4987 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4988 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 4989 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 4990 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 4833 4991 SOFTWARE. 4834 4992 ------------------------------------------------------------------------------ 4835 4993 ALTERNATIVE B - Public Domain (www.unlicense.org) 4836 4994 This is free and unencumbered software released into the public domain. 4837 - Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 4838 - software, either in source code form or as a compiled binary, for any purpose, 4995 + Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 4996 + software, either in source code form or as a compiled binary, for any purpose, 4839 4997 commercial or non-commercial, and by any means. 4840 - In jurisdictions that recognize copyright laws, the author or authors of this 4841 - software dedicate any and all copyright interest in the software to the public 4842 - domain. We make this dedication for the benefit of the public at large and to 4843 - the detriment of our heirs and successors. We intend this dedication to be an 4844 - overt act of relinquishment in perpetuity of all present and future rights to 4998 + In jurisdictions that recognize copyright laws, the author or authors of this 4999 + software dedicate any and all copyright interest in the software to the public 5000 + domain. We make this dedication for the benefit of the public at large and to 5001 + the detriment of our heirs and successors. We intend this dedication to be an 5002 + overt act of relinquishment in perpetuity of all present and future rights to 4845 5003 this software under copyright law. 4846 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4847 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4848 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4849 - AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 4850 - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 5004 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 5005 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5006 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5007 + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 5008 + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 4851 5009 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 4852 5010 ------------------------------------------------------------------------------ 4853 5011 */
+103 -106
src/main.c
··· 1 1 #include <stdio.h> 2 - #include <SDL2/SDL.h> 2 + #include <stdlib.h> 3 + #include <SDL3/SDL.h> 3 4 #include "api/api.h" 4 5 #include "renderer.h" 5 6 6 7 #ifdef _WIN32 7 - #include <windows.h> 8 + # include <windows.h> 8 9 #elif __linux__ 9 - #include <unistd.h> 10 + # include <unistd.h> 10 11 #elif __APPLE__ 11 - #include <mach-o/dyld.h> 12 + # include <mach-o/dyld.h> 12 13 #endif 13 14 14 15 15 16 SDL_Window *window; 16 17 17 18 18 - static double get_scale(void) { 19 - float dpi; 20 - SDL_GetDisplayDPI(0, NULL, &dpi, NULL); 21 - #if _WIN32 22 - return dpi / 96.0; 23 - #elif __APPLE__ 24 - return 1.0; /* dpi / 72.0; */ 25 - #else 26 - return 1.0; 27 - #endif 19 + static double get_scale(void) 20 + { 21 + return (double)SDL_min(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()), 1.0); 28 22 } 29 23 30 - 31 - static void get_exe_filename(char *buf, int sz) { 32 - #if _WIN32 33 - int len = GetModuleFileName(NULL, buf, sz - 1); 34 - buf[len] = '\0'; 35 - #elif __linux__ 36 - char path[512]; 37 - sprintf(path, "/proc/%d/exe", getpid()); 38 - int len = readlink(path, buf, sz - 1); 39 - buf[len] = '\0'; 40 - #elif __APPLE__ 41 - unsigned size = sz; 42 - _NSGetExecutablePath(buf, &size); 43 - #else 44 - strcpy(buf, "./lite"); 45 - #endif 24 + static void get_exe_filename(char *buf, int sz) 25 + { 26 + # if _WIN32 27 + int len = GetModuleFileName(NULL, buf, sz - 1); 28 + buf[len] = '\0'; 29 + # elif __linux__ 30 + char path[512]; 31 + sprintf(path, "/proc/%d/exe", getpid()); 32 + int len = readlink(path, buf, sz - 1); 33 + buf[len] = '\0'; 34 + # elif __APPLE__ 35 + unsigned size = sz; 36 + _NSGetExecutablePath(buf, &size); 37 + # else 38 + strcpy(buf, "./lite"); 39 + # endif 46 40 } 47 41 48 42 49 - static void init_window_icon(void) { 50 - #ifndef _WIN32 51 - #include "../icon.inl" 52 - (void) icon_rgba_len; /* unused */ 53 - SDL_Surface *surf = SDL_CreateRGBSurfaceFrom( 54 - icon_rgba, 64, 64, 55 - 32, 64 * 4, 56 - 0x000000ff, 57 - 0x0000ff00, 58 - 0x00ff0000, 59 - 0xff000000); 60 - SDL_SetWindowIcon(window, surf); 61 - SDL_FreeSurface(surf); 62 - #endif 43 + static void init_window_icon(void) 44 + { 45 + # ifndef _WIN32 46 + # include "../icon.inl" 47 + (void) icon_rgba_len; /* unused */ 48 + SDL_Surface *surf = SDL_CreateSurfaceFrom(64, 64, SDL_PIXELFORMAT_ABGR8888, icon_rgba, 64 * 4); 49 + SDL_SetWindowIcon(window, surf); 50 + SDL_DestroySurface(surf); 51 + # endif 63 52 } 64 53 65 54 66 - int main(int argc, char **argv) { 67 - #ifdef _WIN32 68 - HINSTANCE lib = LoadLibrary("user32.dll"); 69 - int (*SetProcessDPIAware)() = (void*) GetProcAddress(lib, "SetProcessDPIAware"); 70 - SetProcessDPIAware(); 71 - #endif 55 + int main(int argc, char **argv) 56 + { 57 + # ifdef _WIN32 58 + HINSTANCE lib = LoadLibrary("user32.dll"); 59 + int (*SetProcessDPIAware)() = (void *)GetProcAddress(lib, "SetProcessDPIAware"); 60 + SetProcessDPIAware(); 61 + # endif 72 62 73 - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); 74 - SDL_EnableScreenSaver(); 75 - SDL_EventState(SDL_DROPFILE, SDL_ENABLE); 76 - atexit(SDL_Quit); 77 - #if SDL_VERSION_ATLEAST(2, 0, 5) 78 - SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); 79 - #endif 63 + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); 64 + SDL_EnableScreenSaver(); 65 + SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, true); 66 + atexit(SDL_Quit); 80 67 81 - SDL_DisplayMode dm; 82 - SDL_GetCurrentDisplayMode(0, &dm); 68 + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); 69 + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); 83 70 84 - window = SDL_CreateWindow( 85 - "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8, 86 - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); 87 - init_window_icon(); 88 - ren_init(window); 89 - 71 + const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay()); 72 + window = SDL_CreateWindow("", (int)(dm->w * 0.8), (int)(dm->h * 0.8), SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN); 73 + if (!window) 74 + { 75 + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window: %s\n", SDL_GetError()); 76 + return EXIT_FAILURE; 77 + } 78 + init_window_icon(); 79 + ren_init(window); 90 80 91 - lua_State *L = luaL_newstate(); 92 - luaL_openlibs(L); 93 - api_load_libs(L); 81 + if (!SDL_StartTextInput(window)) 82 + { 83 + # ifndef _WIN32 84 + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR: failed SDL_StartTextInput() - text entry may not work"); 85 + # endif 86 + } 94 87 95 88 96 - lua_newtable(L); 97 - for (int i = 0; i < argc; i++) { 98 - lua_pushstring(L, argv[i]); 99 - lua_rawseti(L, -2, i + 1); 100 - } 101 - lua_setglobal(L, "ARGS"); 89 + lua_State *L = luaL_newstate(); 90 + luaL_openlibs(L); 91 + api_load_libs(L); 102 92 103 - lua_pushstring(L, "1.04"); 104 - lua_setglobal(L, "VERSION"); 93 + lua_newtable(L); 94 + for (int i = 0 ; i < argc ; i++) 95 + { 96 + lua_pushstring(L, argv[i]); 97 + lua_rawseti(L, -2, i + 1); 98 + } 99 + lua_setglobal(L, "ARGS"); 105 100 106 - lua_pushstring(L, SDL_GetPlatform()); 107 - lua_setglobal(L, "PLATFORM"); 101 + lua_pushstring(L, "1.11"); 102 + lua_setglobal(L, "VERSION"); 108 103 109 - lua_pushnumber(L, get_scale()); 110 - lua_setglobal(L, "SCALE"); 104 + lua_pushstring(L, SDL_GetPlatform()); 105 + lua_setglobal(L, "PLATFORM"); 111 106 112 - char exename[2048]; 113 - get_exe_filename(exename, sizeof(exename)); 114 - lua_pushstring(L, exename); 115 - lua_setglobal(L, "EXEFILE"); 107 + lua_pushnumber(L, get_scale()); 108 + lua_setglobal(L, "SCALE"); 116 109 110 + char exename[2048]; 111 + get_exe_filename(exename, sizeof(exename)); 112 + lua_pushstring(L, exename); 113 + lua_setglobal(L, "EXEFILE"); 117 114 118 - (void) luaL_dostring(L, 119 - "local core\n" 120 - "xpcall(function()\n" 121 - " SCALE = tonumber(os.getenv(\"LITE_SCALE\")) or SCALE\n" 122 - " PATHSEP = package.config:sub(1, 1)\n" 123 - " EXEDIR = EXEFILE:match(\"^(.+)[/\\\\].*$\")\n" 124 - " package.path = EXEDIR .. '/data/?.lua;' .. package.path\n" 125 - " package.path = EXEDIR .. '/data/?/init.lua;' .. package.path\n" 126 - " core = require('core')\n" 127 - " core.init()\n" 128 - " core.run()\n" 129 - "end, function(err)\n" 130 - " print('Error: ' .. tostring(err))\n" 131 - " print(debug.traceback(nil, 2))\n" 132 - " if core and core.on_error then\n" 133 - " pcall(core.on_error, err)\n" 134 - " end\n" 135 - " os.exit(1)\n" 136 - "end)"); 115 + (void) luaL_dostring(L, 116 + "local core\n" 117 + "xpcall(function()\n" 118 + " SCALE = tonumber(os.getenv(\"LITE_SCALE\")) or SCALE\n" 119 + " PATHSEP = package.config:sub(1, 1)\n" 120 + " EXEDIR = EXEFILE:match(\"^(.+)[/\\\\].*$\")\n" 121 + " package.path = EXEDIR .. '/data/?.lua;' .. package.path\n" 122 + " package.path = EXEDIR .. '/data/?/init.lua;' .. package.path\n" 123 + " core = require('core')\n" 124 + " core.init()\n" 125 + " core.run()\n" 126 + "end, function(err)\n" 127 + " print('Error: ' .. tostring(err))\n" 128 + " print(debug.traceback(nil, 2))\n" 129 + " if core and core.on_error then\n" 130 + " pcall(core.on_error, err)\n" 131 + " end\n" 132 + " os.exit(1)\n" 133 + "end)"); 137 134 138 135 139 - lua_close(L); 140 - SDL_DestroyWindow(window); 136 + lua_close(L); 137 + SDL_DestroyWindow(window); 141 138 142 - return EXIT_SUCCESS; 139 + return EXIT_SUCCESS; 143 140 }
+265 -194
src/rencache.c
··· 11 11 #define CELL_SIZE 96 12 12 #define COMMAND_BUF_SIZE (1024 * 512) 13 13 14 - enum { FREE_FONT, SET_CLIP, DRAW_TEXT, DRAW_RECT }; 14 + enum 15 + { 16 + FREE_FONT, 17 + SET_CLIP, 18 + DRAW_TEXT, 19 + DRAW_RECT 20 + }; 15 21 16 - typedef struct { 17 - int type, size; 18 - RenRect rect; 19 - RenColor color; 20 - RenFont *font; 21 - char text[0]; 22 + typedef struct 23 + { 24 + int type, size; 25 + RenRect rect; 26 + RenColor color; 27 + RenFont *font; 28 + int tab_width; 29 + char text[0]; 22 30 } Command; 23 31 24 32 ··· 39 47 /* 32bit fnv-1a hash */ 40 48 #define HASH_INITIAL 2166136261 41 49 42 - static void hash(unsigned *h, const void *data, int size) { 43 - const unsigned char *p = data; 44 - while (size--) { 45 - *h = (*h ^ *p++) * 16777619; 46 - } 50 + static void hash(unsigned *h, const void *data, int size) 51 + { 52 + const unsigned char *p = data; 53 + while (size--) 54 + { 55 + *h = (*h ^ *p++) * 16777619; 56 + } 47 57 } 48 58 49 59 50 - static inline int cell_idx(int x, int y) { 51 - return x + y * CELLS_X; 60 + static inline int cell_idx(int x, int y) 61 + { 62 + return x + y * CELLS_X; 52 63 } 53 64 54 65 55 - static inline bool rects_overlap(RenRect a, RenRect b) { 56 - return b.x + b.width >= a.x && b.x <= a.x + a.width 57 - && b.y + b.height >= a.y && b.y <= a.y + a.height; 66 + static inline bool rects_overlap(RenRect a, RenRect b) 67 + { 68 + return b.x + b.width >= a.x && 69 + b.x <= a.x + a.width && 70 + b.y + b.height >= a.y && 71 + b.y <= a.y + a.height; 58 72 } 59 73 60 74 61 - static RenRect intersect_rects(RenRect a, RenRect b) { 62 - int x1 = max(a.x, b.x); 63 - int y1 = max(a.y, b.y); 64 - int x2 = min(a.x + a.width, b.x + b.width); 65 - int y2 = min(a.y + a.height, b.y + b.height); 66 - return (RenRect) { x1, y1, max(0, x2 - x1), max(0, y2 - y1) }; 75 + static RenRect intersect_rects(RenRect a, RenRect b) 76 + { 77 + int x1 = max(a.x, b.x); 78 + int y1 = max(a.y, b.y); 79 + int x2 = min(a.x + a.width, b.x + b.width); 80 + int y2 = min(a.y + a.height, b.y + b.height); 81 + return (RenRect){ x1, y1, max(0, x2 - x1), max(0, y2 - y1) }; 67 82 } 68 83 69 84 70 - static RenRect merge_rects(RenRect a, RenRect b) { 71 - int x1 = min(a.x, b.x); 72 - int y1 = min(a.y, b.y); 73 - int x2 = max(a.x + a.width, b.x + b.width); 74 - int y2 = max(a.y + a.height, b.y + b.height); 75 - return (RenRect) { x1, y1, x2 - x1, y2 - y1 }; 85 + static RenRect merge_rects(RenRect a, RenRect b) 86 + { 87 + int x1 = min(a.x, b.x); 88 + int y1 = min(a.y, b.y); 89 + int x2 = max(a.x + a.width, b.x + b.width); 90 + int y2 = max(a.y + a.height, b.y + b.height); 91 + return (RenRect){ x1, y1, x2 - x1, y2 - y1 }; 76 92 } 77 93 78 94 79 - static Command* push_command(int type, int size) { 80 - Command *cmd = (Command*) (command_buf + command_buf_idx); 81 - int n = command_buf_idx + size; 82 - if (n > COMMAND_BUF_SIZE) { 83 - fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n"); 84 - return NULL; 85 - } 86 - command_buf_idx = n; 87 - memset(cmd, 0, sizeof(Command)); 88 - cmd->type = type; 89 - cmd->size = size; 90 - return cmd; 95 + static Command* push_command(int type, int size) 96 + { 97 + Command *cmd = (Command *)(command_buf + command_buf_idx); 98 + int n = command_buf_idx + size; 99 + if (n > COMMAND_BUF_SIZE) 100 + { 101 + fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n"); 102 + return NULL; 103 + } 104 + command_buf_idx = n; 105 + memset(cmd, 0, sizeof(Command)); 106 + cmd->type = type; 107 + cmd->size = size; 108 + return cmd; 91 109 } 92 110 93 111 94 - static bool next_command(Command **prev) { 95 - if (*prev == NULL) { 96 - *prev = (Command*) command_buf; 97 - } else { 98 - *prev = (Command*) (((char*) *prev) + (*prev)->size); 99 - } 100 - return *prev != ((Command*) (command_buf + command_buf_idx)); 112 + static bool next_command(Command **prev) 113 + { 114 + if (*prev == NULL) 115 + { 116 + *prev = (Command *)command_buf; 117 + } 118 + else 119 + { 120 + *prev = (Command *)(((char *)*prev) + (*prev)->size); 121 + } 122 + 123 + return *prev != ((Command *)(command_buf + command_buf_idx)); 101 124 } 102 125 103 126 104 - void rencache_show_debug(bool enable) { 105 - show_debug = enable; 127 + void rencache_show_debug(bool enable) 128 + { 129 + show_debug = enable; 106 130 } 107 131 108 132 109 - void rencache_free_font(RenFont *font) { 110 - Command *cmd = push_command(FREE_FONT, sizeof(Command)); 111 - if (cmd) { cmd->font = font; } 133 + void rencache_free_font(RenFont *font) 134 + { 135 + Command *cmd = push_command(FREE_FONT, sizeof(Command)); 136 + if (cmd) 137 + { 138 + cmd->font = font; 139 + } 112 140 } 113 141 114 142 115 - void rencache_set_clip_rect(RenRect rect) { 116 - Command *cmd = push_command(SET_CLIP, sizeof(Command)); 117 - if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); } 143 + void rencache_set_clip_rect(RenRect rect) 144 + { 145 + Command *cmd = push_command(SET_CLIP, sizeof(Command)); 146 + if (cmd) 147 + { 148 + cmd->rect = intersect_rects(rect, screen_rect); 149 + } 118 150 } 119 151 120 152 121 - void rencache_draw_rect(RenRect rect, RenColor color) { 122 - if (!rects_overlap(screen_rect, rect)) { return; } 123 - Command *cmd = push_command(DRAW_RECT, sizeof(Command)); 124 - if (cmd) { 125 - cmd->rect = rect; 126 - cmd->color = color; 127 - } 153 + void rencache_draw_rect(RenRect rect, RenColor color) 154 + { 155 + if (!rects_overlap(screen_rect, rect)) 156 + { 157 + return; 158 + } 159 + Command *cmd = push_command(DRAW_RECT, sizeof(Command)); 160 + if (cmd) 161 + { 162 + cmd->rect = rect; 163 + cmd->color = color; 164 + } 128 165 } 129 166 130 167 131 - int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { 132 - RenRect rect; 133 - rect.x = x; 134 - rect.y = y; 135 - rect.width = ren_get_font_width(font, text); 136 - rect.height = ren_get_font_height(font); 168 + int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) 169 + { 170 + RenRect rect; 171 + rect.x = x; 172 + rect.y = y; 173 + rect.width = ren_get_font_width(font, text); 174 + rect.height = ren_get_font_height(font); 137 175 138 - if (rects_overlap(screen_rect, rect)) { 139 - int sz = strlen(text) + 1; 140 - Command *cmd = push_command(DRAW_TEXT, sizeof(Command) + sz); 141 - if (cmd) { 142 - memcpy(cmd->text, text, sz); 143 - cmd->color = color; 144 - cmd->font = font; 145 - cmd->rect = rect; 146 - } 147 - } 176 + if (rects_overlap(screen_rect, rect)) 177 + { 178 + int sz = strlen(text) + 1; 179 + Command *cmd = push_command(DRAW_TEXT, sizeof(Command) + sz); 180 + if (cmd) 181 + { 182 + memcpy(cmd->text, text, sz); 183 + cmd->color = color; 184 + cmd->font = font; 185 + cmd->rect = rect; 186 + cmd->tab_width = ren_get_font_tab_width(font); 187 + } 188 + } 148 189 149 - return x + rect.width; 190 + return x + rect.width; 150 191 } 151 192 152 193 153 - void rencache_invalidate(void) { 154 - memset(cells_prev, 0xff, sizeof(cells_buf1)); 194 + void rencache_invalidate(void) 195 + { 196 + memset(cells_prev, 0xff, sizeof(cells_buf1)); 155 197 } 156 198 157 199 158 - void rencache_begin_frame(void) { 159 - /* reset all cells if the screen width/height has changed */ 160 - int w, h; 161 - ren_get_size(&w, &h); 162 - if (screen_rect.width != w || h != screen_rect.height) { 163 - screen_rect.width = w; 164 - screen_rect.height = h; 165 - rencache_invalidate(); 166 - } 200 + void rencache_begin_frame(void) 201 + { 202 + /* reset all cells if the screen width/height has changed */ 203 + int w, h; 204 + ren_get_size(&w, &h); 205 + if (screen_rect.width != w || h != screen_rect.height) 206 + { 207 + screen_rect.width = w; 208 + screen_rect.height = h; 209 + rencache_invalidate(); 210 + } 167 211 } 168 212 169 213 170 - static void update_overlapping_cells(RenRect r, unsigned h) { 171 - int x1 = r.x / CELL_SIZE; 172 - int y1 = r.y / CELL_SIZE; 173 - int x2 = (r.x + r.width) / CELL_SIZE; 174 - int y2 = (r.y + r.height) / CELL_SIZE; 214 + static void update_overlapping_cells(RenRect r, unsigned h) 215 + { 216 + int x1 = r.x / CELL_SIZE; 217 + int y1 = r.y / CELL_SIZE; 218 + int x2 = (r.x + r.width) / CELL_SIZE; 219 + int y2 = (r.y + r.height) / CELL_SIZE; 175 220 176 - for (int y = y1; y <= y2; y++) { 177 - for (int x = x1; x <= x2; x++) { 178 - int idx = cell_idx(x, y); 179 - hash(&cells[idx], &h, sizeof(h)); 180 - } 181 - } 221 + for (int y = y1 ; y <= y2 ; y++) 222 + { 223 + for (int x = x1 ; x <= x2 ; x++) 224 + { 225 + int idx = cell_idx(x, y); 226 + hash(&cells[idx], &h, sizeof(h)); 227 + } 228 + } 182 229 } 183 230 184 231 185 - static void push_rect(RenRect r, int *count) { 186 - /* try to merge with existing rectangle */ 187 - for (int i = *count - 1; i >= 0; i--) { 188 - RenRect *rp = &rect_buf[i]; 189 - if (rects_overlap(*rp, r)) { 190 - *rp = merge_rects(*rp, r); 191 - return; 192 - } 193 - } 194 - /* couldn't merge with previous rectangle: push */ 195 - rect_buf[(*count)++] = r; 232 + static void push_rect(RenRect r, int *count) 233 + { 234 + /* try to merge with existing rectangle */ 235 + for (int i = *count - 1 ; i >= 0 ; i--) 236 + { 237 + RenRect *rp = &rect_buf[i]; 238 + if (rects_overlap(*rp, r)) 239 + { 240 + *rp = merge_rects(*rp, r); 241 + return; 242 + } 243 + } 244 + /* couldn't merge with previous rectangle: push */ 245 + rect_buf[(*count)++] = r; 196 246 } 197 247 198 248 199 - void rencache_end_frame(void) { 200 - /* update cells from commands */ 201 - Command *cmd = NULL; 202 - RenRect cr = screen_rect; 203 - while (next_command(&cmd)) { 204 - if (cmd->type == SET_CLIP) { cr = cmd->rect; } 205 - RenRect r = intersect_rects(cmd->rect, cr); 206 - if (r.width == 0 || r.height == 0) { continue; } 207 - unsigned h = HASH_INITIAL; 208 - hash(&h, cmd, cmd->size); 209 - update_overlapping_cells(r, h); 210 - } 249 + void rencache_end_frame(void) 250 + { 251 + /* update cells from commands */ 252 + Command *cmd = NULL; 253 + RenRect cr = screen_rect; 254 + while (next_command(&cmd)) 255 + { 256 + if (cmd->type == SET_CLIP) 257 + { 258 + cr = cmd->rect; 259 + } 260 + RenRect r = intersect_rects(cmd->rect, cr); 261 + if (r.width == 0 || r.height == 0) 262 + { 263 + continue; 264 + } 265 + unsigned h = HASH_INITIAL; 266 + hash(&h, cmd, cmd->size); 267 + update_overlapping_cells(r, h); 268 + } 211 269 212 - /* push rects for all cells changed from last frame, reset cells */ 213 - int rect_count = 0; 214 - int max_x = screen_rect.width / CELL_SIZE + 1; 215 - int max_y = screen_rect.height / CELL_SIZE + 1; 216 - for (int y = 0; y < max_y; y++) { 217 - for (int x = 0; x < max_x; x++) { 218 - /* compare previous and current cell for change */ 219 - int idx = cell_idx(x, y); 220 - if (cells[idx] != cells_prev[idx]) { 221 - push_rect((RenRect) { x, y, 1, 1 }, &rect_count); 222 - } 223 - cells_prev[idx] = HASH_INITIAL; 224 - } 225 - } 270 + /* push rects for all cells changed from last frame, reset cells */ 271 + int rect_count = 0; 272 + int max_x = screen_rect.width / CELL_SIZE + 1; 273 + int max_y = screen_rect.height / CELL_SIZE + 1; 274 + for (int y = 0 ; y < max_y; y++) 275 + { 276 + for (int x = 0 ; x < max_x ; x++) 277 + { 278 + /* compare previous and current cell for change */ 279 + int idx = cell_idx(x, y); 280 + if (cells[idx] != cells_prev[idx]) 281 + { 282 + push_rect((RenRect) { x, y, 1, 1 }, &rect_count); 283 + } 284 + cells_prev[idx] = HASH_INITIAL; 285 + } 286 + } 226 287 227 - /* expand rects from cells to pixels */ 228 - for (int i = 0; i < rect_count; i++) { 229 - RenRect *r = &rect_buf[i]; 230 - r->x *= CELL_SIZE; 231 - r->y *= CELL_SIZE; 232 - r->width *= CELL_SIZE; 233 - r->height *= CELL_SIZE; 234 - *r = intersect_rects(*r, screen_rect); 235 - } 288 + /* expand rects from cells to pixels */ 289 + for (int i = 0; i < rect_count; i++) 290 + { 291 + RenRect *r = &rect_buf[i]; 292 + r->x *= CELL_SIZE; 293 + r->y *= CELL_SIZE; 294 + r->width *= CELL_SIZE; 295 + r->height *= CELL_SIZE; 296 + *r = intersect_rects(*r, screen_rect); 297 + } 236 298 237 - /* redraw updated regions */ 238 - bool has_free_commands = false; 239 - for (int i = 0; i < rect_count; i++) { 240 - /* draw */ 241 - RenRect r = rect_buf[i]; 242 - ren_set_clip_rect(r); 299 + /* redraw updated regions */ 300 + bool has_free_commands = false; 301 + for (int i = 0; i < rect_count; i++) 302 + { 303 + /* draw */ 304 + RenRect r = rect_buf[i]; 305 + ren_set_clip_rect(r); 243 306 244 - cmd = NULL; 245 - while (next_command(&cmd)) { 246 - switch (cmd->type) { 247 - case FREE_FONT: 248 - has_free_commands = true; 249 - break; 250 - case SET_CLIP: 251 - ren_set_clip_rect(intersect_rects(cmd->rect, r)); 252 - break; 253 - case DRAW_RECT: 254 - ren_draw_rect(cmd->rect, cmd->color); 255 - break; 256 - case DRAW_TEXT: 257 - ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); 258 - break; 259 - } 260 - } 307 + cmd = NULL; 308 + while (next_command(&cmd)) 309 + { 310 + switch (cmd->type) 311 + { 312 + case FREE_FONT: 313 + has_free_commands = true; 314 + break; 315 + case SET_CLIP: 316 + ren_set_clip_rect(intersect_rects(cmd->rect, r)); 317 + break; 318 + case DRAW_RECT: 319 + ren_draw_rect(cmd->rect, cmd->color); 320 + break; 321 + case DRAW_TEXT: 322 + ren_set_font_tab_width(cmd->font, cmd->tab_width); 323 + ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); 324 + break; 325 + } 326 + } 261 327 262 - if (show_debug) { 263 - RenColor color = { rand(), rand(), rand(), 50 }; 264 - ren_draw_rect(r, color); 265 - } 266 - } 328 + if (show_debug) 329 + { 330 + RenColor color = { rand(), rand(), rand(), 50 }; 331 + ren_draw_rect(r, color); 332 + } 333 + } 267 334 268 - /* update dirty rects */ 269 - if (rect_count > 0) { 270 - ren_update_rects(rect_buf, rect_count); 271 - } 335 + /* update dirty rects */ 336 + if (rect_count > 0) 337 + { 338 + ren_update_rects(rect_buf, rect_count); 339 + } 272 340 273 - /* free fonts */ 274 - if (has_free_commands) { 275 - cmd = NULL; 276 - while (next_command(&cmd)) { 277 - if (cmd->type == FREE_FONT) { 278 - ren_free_font(cmd->font); 279 - } 280 - } 281 - } 341 + /* free fonts */ 342 + if (has_free_commands) 343 + { 344 + cmd = NULL; 345 + while (next_command(&cmd)) 346 + { 347 + if (cmd->type == FREE_FONT) 348 + { 349 + ren_free_font(cmd->font); 350 + } 351 + } 352 + } 282 353 283 - /* swap cell buffer and reset */ 284 - unsigned *tmp = cells; 285 - cells = cells_prev; 286 - cells_prev = tmp; 287 - command_buf_idx = 0; 354 + /* swap cell buffer and reset */ 355 + unsigned *tmp = cells; 356 + cells = cells_prev; 357 + cells_prev = tmp; 358 + command_buf_idx = 0; 288 359 }
+301 -246
src/renderer.c
··· 7 7 8 8 #define MAX_GLYPHSET 256 9 9 10 - struct RenImage { 11 - RenColor *pixels; 12 - int width, height; 10 + struct RenImage 11 + { 12 + RenColor *pixels; 13 + int width, height; 13 14 }; 14 15 15 - typedef struct { 16 - RenImage *image; 17 - stbtt_bakedchar glyphs[256]; 16 + typedef struct 17 + { 18 + RenImage *image; 19 + stbtt_bakedchar glyphs[256]; 18 20 } GlyphSet; 19 21 20 - struct RenFont { 21 - void *data; 22 - stbtt_fontinfo stbfont; 23 - GlyphSet *sets[MAX_GLYPHSET]; 24 - float size; 25 - int height; 22 + struct RenFont 23 + { 24 + void *data; 25 + stbtt_fontinfo stbfont; 26 + GlyphSet *sets[MAX_GLYPHSET]; 27 + float size; 28 + int height; 26 29 }; 27 30 28 31 29 32 static SDL_Window *window; 30 - static struct { int left, top, right, bottom; } clip; 33 + static struct 34 + { 35 + int left, top, right, bottom; 36 + } clip; 31 37 32 38 33 - static void* check_alloc(void *ptr) { 34 - if (!ptr) { 35 - fprintf(stderr, "Fatal error: memory allocation failed\n"); 36 - exit(EXIT_FAILURE); 37 - } 38 - return ptr; 39 + static void* check_alloc(void *ptr) 40 + { 41 + if (!ptr) 42 + { 43 + fprintf(stderr, "Fatal error: memory allocation failed\n"); 44 + exit(EXIT_FAILURE); 45 + } 46 + return ptr; 39 47 } 40 48 41 49 42 - static const char* utf8_to_codepoint(const char *p, unsigned *dst) { 43 - unsigned res, n; 44 - switch (*p & 0xf0) { 45 - case 0xf0 : res = *p & 0x07; n = 3; break; 46 - case 0xe0 : res = *p & 0x0f; n = 2; break; 47 - case 0xd0 : 48 - case 0xc0 : res = *p & 0x1f; n = 1; break; 49 - default : res = *p; n = 0; break; 50 - } 51 - while (n--) { 52 - res = (res << 6) | (*(++p) & 0x3f); 53 - } 54 - *dst = res; 55 - return p + 1; 50 + static const char* utf8_to_codepoint(const char *p, unsigned *dst) 51 + { 52 + unsigned res, n; 53 + switch (*p & 0xf0) 54 + { 55 + case 0xf0 : res = *p & 0x07; n = 3; break; 56 + case 0xe0 : res = *p & 0x0f; n = 2; break; 57 + case 0xd0 : 58 + case 0xc0 : res = *p & 0x1f; n = 1; break; 59 + default : res = *p; n = 0; break; 60 + } 61 + while (n--) 62 + res = (res << 6) | (*(++p) & 0x3f); 63 + *dst = res; 64 + return p + 1; 56 65 } 57 66 58 67 59 - void ren_init(SDL_Window *win) { 60 - assert(win); 61 - window = win; 62 - SDL_Surface *surf = SDL_GetWindowSurface(window); 63 - ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } ); 68 + void ren_init(SDL_Window *win) 69 + { 70 + assert(win); 71 + window = win; 72 + SDL_Surface *surf = SDL_GetWindowSurface(window); 73 + ren_set_clip_rect((RenRect){ 0, 0, surf->w, surf->h }); 64 74 } 65 75 66 76 67 - void ren_update_rects(RenRect *rects, int count) { 68 - SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count); 69 - static bool initial_frame = true; 70 - if (initial_frame) { 71 - SDL_ShowWindow(window); 72 - initial_frame = false; 73 - } 77 + void ren_update_rects(RenRect *rects, int count) 78 + { 79 + SDL_UpdateWindowSurfaceRects(window, (SDL_Rect *)rects, count); 80 + static bool initial_frame = true; 81 + if (initial_frame) 82 + { 83 + SDL_ShowWindow(window); 84 + initial_frame = false; 85 + } 74 86 } 75 87 76 88 77 - void ren_set_clip_rect(RenRect rect) { 78 - clip.left = rect.x; 79 - clip.top = rect.y; 80 - clip.right = rect.x + rect.width; 81 - clip.bottom = rect.y + rect.height; 89 + void ren_set_clip_rect(RenRect rect) 90 + { 91 + clip.left = rect.x; 92 + clip.top = rect.y; 93 + clip.right = rect.x + rect.width; 94 + clip.bottom = rect.y + rect.height; 82 95 } 83 96 84 97 85 - void ren_get_size(int *x, int *y) { 86 - SDL_Surface *surf = SDL_GetWindowSurface(window); 87 - *x = surf->w; 88 - *y = surf->h; 98 + void ren_get_size(int *x, int *y) 99 + { 100 + SDL_Surface *surf = SDL_GetWindowSurface(window); 101 + *x = surf->w; 102 + *y = surf->h; 89 103 } 90 104 91 105 92 - RenImage* ren_new_image(int width, int height) { 93 - assert(width > 0 && height > 0); 94 - RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); 95 - check_alloc(image); 96 - image->pixels = (void*) (image + 1); 97 - image->width = width; 98 - image->height = height; 99 - return image; 106 + RenImage* ren_new_image(int width, int height) 107 + { 108 + assert(width > 0 && height > 0); 109 + RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); 110 + check_alloc(image); 111 + image->pixels = (void *)(image + 1); 112 + image->width = width; 113 + image->height = height; 114 + return image; 100 115 } 101 116 102 117 103 - void ren_free_image(RenImage *image) { 104 - free(image); 118 + void ren_free_image(RenImage *image) 119 + { 120 + free(image); 105 121 } 106 122 107 123 108 - static GlyphSet* load_glyphset(RenFont *font, int idx) { 109 - GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); 124 + static GlyphSet* load_glyphset(RenFont *font, int idx) 125 + { 126 + GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); 110 127 111 - /* init image */ 112 - int width = 128; 113 - int height = 128; 128 + /* init image */ 129 + int width = 128; 130 + int height = 128; 114 131 retry: 115 - set->image = ren_new_image(width, height); 132 + set->image = ren_new_image(width, height); 116 133 117 - /* load glyphs */ 118 - float s = 119 - stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) / 120 - stbtt_ScaleForPixelHeight(&font->stbfont, 1); 121 - int res = stbtt_BakeFontBitmap( 122 - font->data, 0, font->size * s, (void*) set->image->pixels, 123 - width, height, idx * 256, 256, set->glyphs); 134 + /* load glyphs */ 135 + float s = 136 + stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) / 137 + stbtt_ScaleForPixelHeight(&font->stbfont, 1); 138 + int res = stbtt_BakeFontBitmap( 139 + font->data, 0, font->size * s, (void *)set->image->pixels, 140 + width, height, idx * 256, 256, set->glyphs); 124 141 125 - /* retry with a larger image buffer if the buffer wasn't large enough */ 126 - if (res < 0) { 127 - width *= 2; 128 - height *= 2; 129 - ren_free_image(set->image); 130 - goto retry; 131 - } 142 + /* retry with a larger image buffer if the buffer wasn't large enough */ 143 + if (res < 0) 144 + { 145 + width *= 2; 146 + height *= 2; 147 + ren_free_image(set->image); 148 + goto retry; 149 + } 132 150 133 - /* adjust glyph yoffsets and xadvance */ 134 - int ascent, descent, linegap; 135 - stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 136 - float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size); 137 - int scaled_ascent = ascent * scale + 0.5; 138 - for (int i = 0; i < 256; i++) { 139 - set->glyphs[i].yoff += scaled_ascent; 140 - set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); 141 - } 151 + /* adjust glyph yoffsets and xadvance */ 152 + int ascent, descent, linegap; 153 + stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 154 + float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size); 155 + int scaled_ascent = ascent * scale + 0.5; 156 + for (int i = 0; i < 256; i++) 157 + { 158 + set->glyphs[i].yoff += scaled_ascent; 159 + set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); 160 + } 142 161 143 - /* convert 8bit data to 32bit */ 144 - for (int i = width * height - 1; i >= 0; i--) { 145 - uint8_t n = *((uint8_t*) set->image->pixels + i); 146 - set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n }; 147 - } 162 + /* convert 8bit data to 32bit */ 163 + for (int i = width * height - 1; i >= 0; i--) 164 + { 165 + uint8_t n = *((uint8_t *)set->image->pixels + i); 166 + set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n }; 167 + } 148 168 149 - return set; 169 + return set; 150 170 } 151 171 152 172 153 - static GlyphSet* get_glyphset(RenFont *font, int codepoint) { 154 - int idx = (codepoint >> 8) % MAX_GLYPHSET; 155 - if (!font->sets[idx]) { 156 - font->sets[idx] = load_glyphset(font, idx); 157 - } 158 - return font->sets[idx]; 173 + static GlyphSet* get_glyphset(RenFont *font, int codepoint) 174 + { 175 + int idx = (codepoint >> 8) % MAX_GLYPHSET; 176 + if (!font->sets[idx]) 177 + font->sets[idx] = load_glyphset(font, idx); 178 + return font->sets[idx]; 159 179 } 160 180 161 181 162 - RenFont* ren_load_font(const char *filename, float size) { 163 - RenFont *font = NULL; 164 - FILE *fp = NULL; 182 + RenFont* ren_load_font(const char *filename, float size) 183 + { 184 + RenFont *font = NULL; 185 + FILE *fp = NULL; 165 186 166 - /* init font */ 167 - font = check_alloc(calloc(1, sizeof(RenFont))); 168 - font->size = size; 187 + /* init font */ 188 + font = check_alloc(calloc(1, sizeof(RenFont))); 189 + font->size = size; 169 190 170 - /* load font into buffer */ 171 - fp = fopen(filename, "rb"); 172 - if (!fp) { return NULL; } 173 - /* get size */ 174 - fseek(fp, 0, SEEK_END); int buf_size = ftell(fp); fseek(fp, 0, SEEK_SET); 175 - /* load */ 176 - font->data = check_alloc(malloc(buf_size)); 177 - int _ = fread(font->data, 1, buf_size, fp); (void) _; 178 - fclose(fp); 179 - fp = NULL; 191 + /* load font into buffer */ 192 + fp = fopen(filename, "rb"); 193 + if (!fp) 194 + return NULL; 195 + /* get size */ 196 + fseek(fp, 0, SEEK_END); 197 + int buf_size = ftell(fp); 198 + fseek(fp, 0, SEEK_SET); 199 + /* load */ 200 + font->data = check_alloc(malloc(buf_size)); 201 + (void)fread(font->data, 1, buf_size, fp); 202 + fclose(fp); 203 + fp = NULL; 180 204 181 - /* init stbfont */ 182 - int ok = stbtt_InitFont(&font->stbfont, font->data, 0); 183 - if (!ok) { goto fail; } 205 + /* init stbfont */ 206 + int ok = stbtt_InitFont(&font->stbfont, font->data, 0); 207 + if (!ok) 208 + goto fail; 184 209 185 - /* get height and scale */ 186 - int ascent, descent, linegap; 187 - stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 188 - float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size); 189 - font->height = (ascent - descent + linegap) * scale + 0.5; 210 + /* get height and scale */ 211 + int ascent, descent, linegap; 212 + stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 213 + float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size); 214 + font->height = (ascent - descent + linegap) * scale + 0.5; 190 215 191 - /* make tab and newline glyphs invisible */ 192 - stbtt_bakedchar *g = get_glyphset(font, '\n')->glyphs; 193 - g['\t'].x1 = g['\t'].x0; 194 - g['\n'].x1 = g['\n'].x0; 216 + /* make tab and newline glyphs invisible */ 217 + stbtt_bakedchar *g = get_glyphset(font, '\n')->glyphs; 218 + g['\t'].x1 = g['\t'].x0; 219 + g['\n'].x1 = g['\n'].x0; 195 220 196 - return font; 221 + return font; 197 222 198 223 fail: 199 - if (fp) { fclose(fp); } 200 - if (font) { free(font->data); } 201 - free(font); 202 - return NULL; 224 + if (fp) 225 + fclose(fp); 226 + if (font) 227 + free(font->data); 228 + free(font); 229 + return NULL; 230 + } 231 + 232 + 233 + void ren_free_font(RenFont *font) 234 + { 235 + for (int i = 0 ; i < MAX_GLYPHSET ; i++) 236 + { 237 + GlyphSet *set = font->sets[i]; 238 + if (set) 239 + { 240 + ren_free_image(set->image); 241 + free(set); 242 + } 243 + } 244 + free(font->data); 245 + free(font); 203 246 } 204 247 205 248 206 - void ren_free_font(RenFont *font) { 207 - for (int i = 0; i < MAX_GLYPHSET; i++) { 208 - GlyphSet *set = font->sets[i]; 209 - if (set) { 210 - ren_free_image(set->image); 211 - free(set); 212 - } 213 - } 214 - free(font->data); 215 - free(font); 249 + void ren_set_font_tab_width(RenFont *font, int n) 250 + { 251 + GlyphSet *set = get_glyphset(font, '\t'); 252 + set->glyphs['\t'].xadvance = n; 216 253 } 217 254 218 255 219 - void ren_set_font_tab_width(RenFont *font, int n) { 220 - GlyphSet *set = get_glyphset(font, '\t'); 221 - set->glyphs['\t'].xadvance = n; 256 + int ren_get_font_tab_width(RenFont *font) 257 + { 258 + GlyphSet *set = get_glyphset(font, '\t'); 259 + return set->glyphs['\t'].xadvance; 222 260 } 223 261 224 262 225 - int ren_get_font_width(RenFont *font, const char *text) { 226 - int x = 0; 227 - const char *p = text; 228 - unsigned codepoint; 229 - while (*p) { 230 - p = utf8_to_codepoint(p, &codepoint); 231 - GlyphSet *set = get_glyphset(font, codepoint); 232 - stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 233 - x += g->xadvance; 234 - } 235 - return x; 263 + int ren_get_font_width(RenFont *font, const char *text) 264 + { 265 + int x = 0; 266 + const char *p = text; 267 + unsigned codepoint; 268 + while (*p) 269 + { 270 + p = utf8_to_codepoint(p, &codepoint); 271 + GlyphSet *set = get_glyphset(font, codepoint); 272 + stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 273 + x += g->xadvance; 274 + } 275 + return x; 236 276 } 237 277 238 278 239 - int ren_get_font_height(RenFont *font) { 240 - return font->height; 279 + int ren_get_font_height(RenFont *font) 280 + { 281 + return font->height; 241 282 } 242 283 243 284 244 - static inline RenColor blend_pixel(RenColor dst, RenColor src) { 245 - int ia = 0xff - src.a; 246 - dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8; 247 - dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8; 248 - dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8; 249 - return dst; 285 + static inline RenColor blend_pixel(RenColor dst, RenColor src) 286 + { 287 + int ia = 0xff - src.a; 288 + dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8; 289 + dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8; 290 + dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8; 291 + return dst; 250 292 } 251 293 252 294 253 - static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) { 254 - src.a = (src.a * color.a) >> 8; 255 - int ia = 0xff - src.a; 256 - dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8); 257 - dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8); 258 - dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8); 259 - return dst; 295 + static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) 296 + { 297 + src.a = (src.a * color.a) >> 8; 298 + int ia = 0xff - src.a; 299 + dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8); 300 + dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8); 301 + dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8); 302 + return dst; 260 303 } 261 304 262 305 263 - #define rect_draw_loop(expr) \ 264 - for (int j = y1; j < y2; j++) { \ 265 - for (int i = x1; i < x2; i++) { \ 266 - *d = expr; \ 267 - d++; \ 268 - } \ 269 - d += dr; \ 270 - } 306 + #define rect_draw_loop(expr) \ 307 + for (int j = y1 ; j < y2 ; j++) \ 308 + { \ 309 + for (int i = x1 ; i < x2 ; i++) \ 310 + { \ 311 + *d = expr; \ 312 + d++; \ 313 + } \ 314 + d += dr; \ 315 + } 271 316 272 - void ren_draw_rect(RenRect rect, RenColor color) { 273 - if (color.a == 0) { return; } 317 + void ren_draw_rect(RenRect rect, RenColor color) 318 + { 319 + if (color.a == 0) 320 + return; 274 321 275 - int x1 = rect.x < clip.left ? clip.left : rect.x; 276 - int y1 = rect.y < clip.top ? clip.top : rect.y; 277 - int x2 = rect.x + rect.width; 278 - int y2 = rect.y + rect.height; 279 - x2 = x2 > clip.right ? clip.right : x2; 280 - y2 = y2 > clip.bottom ? clip.bottom : y2; 322 + int x1 = rect.x < clip.left ? clip.left : rect.x; 323 + int y1 = rect.y < clip.top ? clip.top : rect.y; 324 + int x2 = rect.x + rect.width; 325 + int y2 = rect.y + rect.height; 326 + x2 = x2 > clip.right ? clip.right : x2; 327 + y2 = y2 > clip.bottom ? clip.bottom : y2; 281 328 282 - SDL_Surface *surf = SDL_GetWindowSurface(window); 283 - RenColor *d = (RenColor*) surf->pixels; 284 - d += x1 + y1 * surf->w; 285 - int dr = surf->w - (x2 - x1); 329 + SDL_Surface *surf = SDL_GetWindowSurface(window); 330 + RenColor *d = (RenColor *)surf->pixels; 331 + d += x1 + y1 * surf->w; 332 + int dr = surf->w - (x2 - x1); 286 333 287 - if (color.a == 0xff) { 288 - rect_draw_loop(color); 289 - } else { 290 - rect_draw_loop(blend_pixel(*d, color)); 291 - } 334 + if (color.a == 0xff) 335 + { 336 + rect_draw_loop(color); 337 + } 338 + else 339 + { 340 + rect_draw_loop(blend_pixel(*d, color)); 341 + } 292 342 } 293 343 294 344 295 - void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) { 296 - if (color.a == 0) { return; } 345 + void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) 346 + { 347 + if (color.a == 0) 348 + return; 297 349 298 - /* clip */ 299 - int n; 300 - if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; } 301 - if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; } 302 - if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; } 303 - if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; } 350 + /* clip */ 351 + int n; 352 + if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; } 353 + if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; } 354 + if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; } 355 + if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; } 304 356 305 - if (sub->width <= 0 || sub->height <= 0) { 306 - return; 307 - } 357 + if (sub->width <= 0 || sub->height <= 0) 358 + return; 308 359 309 - /* draw */ 310 - SDL_Surface *surf = SDL_GetWindowSurface(window); 311 - RenColor *s = image->pixels; 312 - RenColor *d = (RenColor*) surf->pixels; 313 - s += sub->x + sub->y * image->width; 314 - d += x + y * surf->w; 315 - int sr = image->width - sub->width; 316 - int dr = surf->w - sub->width; 360 + /* draw */ 361 + SDL_Surface *surf = SDL_GetWindowSurface(window); 362 + RenColor *s = image->pixels; 363 + RenColor *d = (RenColor *)surf->pixels; 364 + s += sub->x + sub->y * image->width; 365 + d += x + y * surf->w; 366 + int sr = image->width - sub->width; 367 + int dr = surf->w - sub->width; 317 368 318 - for (int j = 0; j < sub->height; j++) { 319 - for (int i = 0; i < sub->width; i++) { 320 - *d = blend_pixel2(*d, *s, color); 321 - d++; 322 - s++; 323 - } 324 - d += dr; 325 - s += sr; 326 - } 369 + for (int j = 0; j < sub->height; j++) 370 + { 371 + for (int i = 0; i < sub->width; i++) 372 + { 373 + *d = blend_pixel2(*d, *s, color); 374 + d++; 375 + s++; 376 + } 377 + d += dr; 378 + s += sr; 379 + } 327 380 } 328 381 329 382 330 - int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { 331 - RenRect rect; 332 - const char *p = text; 333 - unsigned codepoint; 334 - while (*p) { 335 - p = utf8_to_codepoint(p, &codepoint); 336 - GlyphSet *set = get_glyphset(font, codepoint); 337 - stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 338 - rect.x = g->x0; 339 - rect.y = g->y0; 340 - rect.width = g->x1 - g->x0; 341 - rect.height = g->y1 - g->y0; 342 - ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color); 343 - x += g->xadvance; 344 - } 345 - return x; 383 + int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) 384 + { 385 + RenRect rect; 386 + const char *p = text; 387 + unsigned codepoint; 388 + while (*p) 389 + { 390 + p = utf8_to_codepoint(p, &codepoint); 391 + GlyphSet *set = get_glyphset(font, codepoint); 392 + stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 393 + rect.x = g->x0; 394 + rect.y = g->y0; 395 + rect.width = g->x1 - g->x0; 396 + rect.height = g->y1 - g->y0; 397 + ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color); 398 + x += g->xadvance; 399 + } 400 + return x; 346 401 }
+12 -3
src/renderer.h
··· 1 1 #ifndef RENDERER_H 2 2 #define RENDERER_H 3 3 4 - #include <SDL2/SDL.h> 4 + #include <SDL3/SDL.h> 5 + #include <stdlib.h> 5 6 #include <stdint.h> 6 7 7 8 typedef struct RenImage RenImage; 8 9 typedef struct RenFont RenFont; 9 10 10 - typedef struct { uint8_t b, g, r, a; } RenColor; 11 - typedef struct { int x, y, width, height; } RenRect; 11 + typedef struct 12 + { 13 + uint8_t b, g, r, a; 14 + } RenColor; 15 + 16 + typedef struct 17 + { 18 + int x, y, width, height; 19 + } RenRect; 12 20 13 21 14 22 void ren_init(SDL_Window *win); ··· 22 30 RenFont* ren_load_font(const char *filename, float size); 23 31 void ren_free_font(RenFont *font); 24 32 void ren_set_font_tab_width(RenFont *font, int n); 33 + int ren_get_font_tab_width(RenFont *font); 25 34 int ren_get_font_width(RenFont *font, const char *text); 26 35 int ren_get_font_height(RenFont *font); 27 36