+16
.editorconfig
+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
-19
LICENSE
-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
-39
README.md
···
1
-
# lite
2
-

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
+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
+4
-3
build.sh
+4
-3
build.sh
···
1
1
#!/bin/bash
2
2
3
-
cflags="-Wall -O3 -g -std=gnu11 -Isrc -DLUA_USE_POPEN"
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"
8
8
outfile="lite.exe"
9
9
compiler="x86_64-w64-mingw32-gcc"
10
-
cflags="$cflags -Iwinlib/SDL2-2.0.10/x86_64-w64-mingw32/include"
10
+
cflags="$cflags -DLUA_USE_POPEN -Iwinlib/SDL2-2.0.10/x86_64-w64-mingw32/include"
11
11
lflags="$lflags -Lwinlib/SDL2-2.0.10/x86_64-w64-mingw32/lib"
12
12
lflags="-lmingw32 -lSDL2main $lflags -mwindows -o $outfile res.res"
13
13
x86_64-w64-mingw32-windres res.rc -O coff -o res.res
···
15
15
platform="unix"
16
16
outfile="lite"
17
17
compiler="gcc"
18
+
cflags="$cflags -DLUA_USE_POSIX"
18
19
lflags="$lflags -o $outfile"
19
20
fi
20
21
+2
-2
data/core/commands/core.lua
+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
+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
+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
+1
-1
data/core/doc/highlighter.lua
+2
-1
data/core/doc/init.lua
+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
+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
+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
+55
-27
data/core/init.lua
+55
-27
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
···
318
340
core.root_view:on_mouse_wheel(...)
319
341
elseif type == "filedropped" then
320
342
local filename, mx, my = ...
321
-
local ok, doc = core.try(core.open_doc, filename)
322
-
if ok then
323
-
local node = core.root_view.root_node:get_child_overlapping_point(mx, my)
324
-
node:set_active_view(node.active_view)
325
-
core.root_view:open_doc(doc)
343
+
local info = system.get_file_info(filename)
344
+
if info and info.type == "dir" then
345
+
system.exec(string.format("%q %q", EXEFILE, filename))
346
+
else
347
+
local ok, doc = core.try(core.open_doc, filename)
348
+
if ok then
349
+
local node = core.root_view.root_node:get_child_overlapping_point(mx, my)
350
+
node:set_active_view(node.active_view)
351
+
core.root_view:open_doc(doc)
352
+
end
326
353
end
327
354
elseif type == "quit" then
328
355
core.quit()
···
359
386
-- update
360
387
core.root_view.size.x, core.root_view.size.y = width, height
361
388
core.root_view:update()
362
-
if not core.redraw then
363
-
if not system.window_has_focus() then system.wait_event(0.5) end
364
-
return
365
-
end
389
+
if not core.redraw then return false end
366
390
core.redraw = false
367
391
368
392
-- close unreferenced docs
···
376
400
377
401
-- update window title
378
402
local name = core.active_view:get_name()
379
-
if name ~= "---" then
380
-
system.set_window_title(name .. " - lite")
381
-
else
382
-
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
383
407
end
384
408
385
409
-- draw
···
388
412
renderer.set_clip_rect(table.unpack(core.clip_rect_stack[1]))
389
413
core.root_view:draw()
390
414
renderer.end_frame()
415
+
return true
391
416
end
392
417
393
418
···
426
451
function core.run()
427
452
while true do
428
453
core.frame_start = system.get_time()
429
-
core.step()
454
+
local did_redraw = core.step()
430
455
run_threads()
456
+
if not did_redraw and not system.window_has_focus() then
457
+
system.wait_event(0.25)
458
+
end
431
459
local elapsed = system.get_time() - core.frame_start
432
460
system.sleep(math.max(0, 1 / config.fps - elapsed))
433
461
end
+15
-14
data/core/keymap.lua
+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",
+34
-17
data/core/rootview.lua
+34
-17
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
···
386
399
387
400
function RootView:open_doc(doc)
388
401
local node = self:get_active_node()
402
+
if node.locked and core.last_active_view then
403
+
core.set_active_view(core.last_active_view)
404
+
node = self:get_active_node()
405
+
end
389
406
assert(not node.locked, "Cannot open doc on locked node")
390
407
for i, view in ipairs(node.views) do
391
408
if view.doc == doc then
···
431
448
432
449
function RootView:on_mouse_moved(x, y, dx, dy)
433
450
if self.dragged_divider then
434
-
local div = self.dragged_divider
435
-
if div.type == "hsplit" then
436
-
div.divider = div.divider + dx / div.size.x
451
+
local node = self.dragged_divider
452
+
if node.type == "hsplit" then
453
+
node.divider = node.divider + dx / node.size.x
437
454
else
438
-
div.divider = div.divider + dy / div.size.y
455
+
node.divider = node.divider + dy / node.size.y
439
456
end
440
-
div.divider = common.clamp(div.divider, 0.01, 0.99)
457
+
node.divider = common.clamp(node.divider, 0.01, 0.99)
441
458
return
442
459
end
443
460
+8
-3
data/core/syntax.lua
+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
+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
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
+2
-1
data/plugins/language_python.lua
+1
data/plugins/language_xml.lua
+1
data/plugins/language_xml.lua
-1
data/plugins/treeview.lua
-1
data/plugins/treeview.lua
+7
data/plugins/trimwhitespace.lua
+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
+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
+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
+41
doc/original-readme.md
···
1
+
# lite
2
+

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
+146
doc/usage.md
···
1
+
# lite
2
+
3
+

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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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
+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