+8
.editorconfig
+8
.editorconfig
-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.
···
-12
README.md
doc/original-readme.md
-12
README.md
doc/original-readme.md
···
1
-
# lite (emmeline's fork)
2
-
3
-
A fork of the [lite](https://github.com/rxi/lite) editor by rxi.
4
-
5
-
## Changes
6
-
7
-
- [ ] Reformat to my code style :p
8
-
- [/] Update to SDL 3
9
-
- [ ] Use LuaJIT
10
-
11
-
---
12
-
13
# lite
14

15
+1
-1
data/user/init.lua
+1
-1
data/user/init.lua
+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.
+29
readme
+29
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
+
-------
+9
-7
src/api/api.c
+9
-7
src/api/api.c
···
6
7
8
static const luaL_Reg libs[] = {
9
+
{ "system", luaopen_system },
10
+
{ "renderer", luaopen_renderer },
11
+
{ NULL, NULL }
12
};
13
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
+
}
20
}
+78
-70
src/api/renderer.c
+78
-70
src/api/renderer.c
···
3
#include "rencache.h"
4
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;
21
}
22
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;
28
}
29
30
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;
37
}
38
39
40
-
static int f_begin_frame(lua_State *L) {
41
-
rencache_begin_frame();
42
-
return 0;
43
}
44
45
46
-
static int f_end_frame(lua_State *L) {
47
-
rencache_end_frame();
48
-
return 0;
49
}
50
51
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;
60
}
61
62
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;
72
}
73
74
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;
84
}
85
86
87
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 }
96
};
97
98
99
int luaopen_renderer_font(lua_State *L);
100
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;
106
}
···
3
#include "rencache.h"
4
5
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
}
22
23
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;
29
}
30
31
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;
39
}
40
41
42
+
static int f_begin_frame(lua_State *L)
43
+
{
44
+
rencache_begin_frame();
45
+
return 0;
46
}
47
48
49
+
static int f_end_frame(lua_State *L)
50
+
{
51
+
rencache_end_frame();
52
+
return 0;
53
}
54
55
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;
65
}
66
67
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;
78
}
79
80
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;
91
}
92
93
94
static const luaL_Reg lib[] = {
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 }
103
};
104
105
106
int luaopen_renderer_font(lua_State *L);
107
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;
114
}
+46
-38
src/api/renderer_font.c
+46
-38
src/api/renderer_font.c
···
3
#include "rencache.h"
4
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;
14
}
15
16
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;
22
}
23
24
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;
29
}
30
31
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
}
38
39
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;
44
}
45
46
47
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 }
54
};
55
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;
62
}
···
3
#include "rencache.h"
4
5
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;
16
}
17
18
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;
25
}
26
27
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;
34
}
35
36
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;
43
}
44
45
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;
51
}
52
53
54
static const luaL_Reg lib[] = {
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 }
61
};
62
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;
70
}
+328
-286
src/api/system.c
+328
-286
src/api/system.c
···
9
#include "api.h"
10
#include "rencache.h"
11
#ifdef _WIN32
12
-
#include <windows.h>
13
#endif
14
15
extern SDL_Window *window;
16
17
18
-
static const char* button_name(int button) {
19
-
switch (button) {
20
-
case 1 : return "left";
21
-
case 2 : return "middle";
22
-
case 3 : return "right";
23
-
default : return "?";
24
-
}
25
}
26
27
28
-
static char* key_name(char *dst, int sym) {
29
-
strcpy(dst, SDL_GetKeyName(sym));
30
-
char *p = dst;
31
-
while (*p) {
32
-
*p = tolower(*p);
33
-
p++;
34
-
}
35
-
return dst;
36
}
37
38
39
-
static int f_poll_event(lua_State *L) {
40
-
char buf[16];
41
-
float mx, my;
42
-
int wx, wy;
43
-
SDL_Event e;
44
45
top:
46
-
if ( !SDL_PollEvent(&e) ) {
47
-
return 0;
48
-
}
49
50
-
switch (e.type) {
51
-
case SDL_EVENT_QUIT :
52
-
lua_pushstring(L, "quit");
53
-
return 1;
54
55
-
case SDL_EVENT_WINDOW_RESIZED:
56
-
lua_pushstring(L, "resized");
57
-
lua_pushnumber(L, e.window.data1);
58
-
lua_pushnumber(L, e.window.data2);
59
-
return 3;
60
61
-
case SDL_EVENT_WINDOW_EXPOSED:
62
-
rencache_invalidate();
63
-
lua_pushstring(L, "exposed");
64
-
return 1;
65
66
-
case SDL_EVENT_WINDOW_FOCUS_GAINED:
67
-
/* on some systems, when alt-tabbing to the window SDL will queue up
68
-
** several KEYDOWN events for the `tab` key; we flush all keydown
69
-
** events on focus so these are discarded */
70
-
SDL_FlushEvent(SDL_EVENT_KEY_DOWN);
71
-
goto top;
72
73
-
case SDL_EVENT_DROP_FILE :
74
-
SDL_GetGlobalMouseState(&mx, &my);
75
-
SDL_GetWindowPosition(window, &wx, &wy);
76
-
lua_pushstring(L, "filedropped");
77
-
lua_pushstring(L, e.drop.data);
78
-
lua_pushnumber(L, mx - wx);
79
-
lua_pushnumber(L, my - wy);
80
-
return 4;
81
82
-
case SDL_EVENT_KEY_DOWN :
83
-
lua_pushstring(L, "keypressed");
84
-
lua_pushstring(L, key_name(buf, e.key.key));
85
-
return 2;
86
87
-
case SDL_EVENT_KEY_UP :
88
-
lua_pushstring(L, "keyreleased");
89
-
lua_pushstring(L, key_name(buf, e.key.key));
90
-
return 2;
91
92
-
case SDL_EVENT_TEXT_INPUT :
93
-
lua_pushstring(L, "textinput");
94
-
lua_pushstring(L, e.text.text);
95
-
return 2;
96
97
-
case SDL_EVENT_MOUSE_BUTTON_DOWN :
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;
105
106
-
case SDL_EVENT_MOUSE_BUTTON_UP :
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;
113
114
-
case SDL_EVENT_MOUSE_MOTION :
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
122
-
case SDL_EVENT_MOUSE_WHEEL :
123
-
lua_pushstring(L, "mousewheel");
124
-
lua_pushnumber(L, e.wheel.y);
125
-
return 2;
126
127
-
default:
128
-
goto top;
129
-
}
130
131
-
return 0;
132
}
133
134
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;
139
}
140
141
142
static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_POINTER + 1];
143
144
static const char *cursor_opts[] = {
145
-
"arrow",
146
-
"ibeam",
147
-
"sizeh",
148
-
"sizev",
149
-
"hand",
150
-
NULL
151
};
152
153
static const int cursor_enums[] = {
154
-
SDL_SYSTEM_CURSOR_DEFAULT,
155
-
SDL_SYSTEM_CURSOR_TEXT,
156
-
SDL_SYSTEM_CURSOR_EW_RESIZE,
157
-
SDL_SYSTEM_CURSOR_NS_RESIZE,
158
-
SDL_SYSTEM_CURSOR_POINTER
159
};
160
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;
171
}
172
173
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;
178
}
179
180
181
static const char *window_opts[] = { "normal", "maximized", "fullscreen", 0 };
182
enum { WIN_NORMAL, WIN_MAXIMIZED, WIN_FULLSCREEN };
183
184
-
static int f_set_window_mode(lua_State *L) {
185
-
int n = luaL_checkoption(L, 1, "normal", window_opts);
186
-
SDL_SetWindowFullscreen(window, n == WIN_FULLSCREEN ? SDL_GetWindowFullscreenMode(window) : 0);
187
-
if (n == WIN_NORMAL) { SDL_RestoreWindow(window); }
188
-
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); }
189
-
return 0;
190
}
191
192
193
-
static int f_window_has_focus(lua_State *L) {
194
-
unsigned flags = SDL_GetWindowFlags(window);
195
-
lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS);
196
-
return 1;
197
}
198
199
200
-
static int f_show_confirm_dialog(lua_State *L) {
201
-
const char *title = luaL_checkstring(L, 1);
202
-
const char *msg = luaL_checkstring(L, 2);
203
204
-
#if _WIN32
205
-
int id = MessageBox(0, msg, title, MB_YESNO | MB_ICONWARNING);
206
-
lua_pushboolean(L, id == IDYES);
207
208
-
#else
209
-
SDL_MessageBoxButtonData buttons[] = {
210
-
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" },
211
-
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" },
212
-
};
213
-
SDL_MessageBoxData data = {
214
-
.title = title,
215
-
.message = msg,
216
-
.numbuttons = 2,
217
-
.buttons = buttons,
218
-
};
219
-
int buttonid;
220
-
SDL_ShowMessageBox(&data, &buttonid);
221
-
lua_pushboolean(L, buttonid == 1);
222
-
#endif
223
-
return 1;
224
}
225
226
227
-
static int f_chdir(lua_State *L) {
228
-
const char *path = luaL_checkstring(L, 1);
229
-
int err = chdir(path);
230
-
if (err) { luaL_error(L, "chdir() failed"); }
231
-
return 0;
232
}
233
234
235
-
static int f_list_dir(lua_State *L) {
236
-
const char *path = luaL_checkstring(L, 1);
237
238
-
DIR *dir = opendir(path);
239
-
if (!dir) {
240
-
lua_pushnil(L);
241
-
lua_pushstring(L, strerror(errno));
242
-
return 2;
243
-
}
244
245
-
lua_newtable(L);
246
-
int i = 1;
247
-
struct dirent *entry;
248
-
while ( (entry = readdir(dir)) ) {
249
-
if (strcmp(entry->d_name, "." ) == 0) { continue; }
250
-
if (strcmp(entry->d_name, "..") == 0) { continue; }
251
-
lua_pushstring(L, entry->d_name);
252
-
lua_rawseti(L, -2, i);
253
-
i++;
254
-
}
255
256
-
closedir(dir);
257
-
return 1;
258
}
259
260
261
#ifdef _WIN32
262
-
#include <windows.h>
263
-
#define realpath(x, y) _fullpath(y, x, MAX_PATH)
264
#endif
265
266
-
static int f_absolute_path(lua_State *L) {
267
-
const char *path = luaL_checkstring(L, 1);
268
-
char *res = realpath(path, NULL);
269
-
if (!res) { return 0; }
270
-
lua_pushstring(L, res);
271
-
free(res);
272
-
return 1;
273
}
274
275
276
-
static int f_get_file_info(lua_State *L) {
277
-
const char *path = luaL_checkstring(L, 1);
278
279
-
struct stat s;
280
-
int err = stat(path, &s);
281
-
if (err < 0) {
282
-
lua_pushnil(L);
283
-
lua_pushstring(L, strerror(errno));
284
-
return 2;
285
-
}
286
287
-
lua_newtable(L);
288
-
lua_pushnumber(L, s.st_mtime);
289
-
lua_setfield(L, -2, "modified");
290
291
-
lua_pushnumber(L, s.st_size);
292
-
lua_setfield(L, -2, "size");
293
294
-
if (S_ISREG(s.st_mode)) {
295
-
lua_pushstring(L, "file");
296
-
} else if (S_ISDIR(s.st_mode)) {
297
-
lua_pushstring(L, "dir");
298
-
} else {
299
-
lua_pushnil(L);
300
-
}
301
-
lua_setfield(L, -2, "type");
302
303
-
return 1;
304
}
305
306
307
-
static int f_get_clipboard(lua_State *L) {
308
-
char *text = SDL_GetClipboardText();
309
-
if (!text) { return 0; }
310
-
lua_pushstring(L, text);
311
-
SDL_free(text);
312
-
return 1;
313
}
314
315
316
-
static int f_set_clipboard(lua_State *L) {
317
-
const char *text = luaL_checkstring(L, 1);
318
-
SDL_SetClipboardText(text);
319
-
return 0;
320
}
321
322
323
-
static int f_get_time(lua_State *L) {
324
-
double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency();
325
-
lua_pushnumber(L, n);
326
-
return 1;
327
}
328
329
330
-
static int f_sleep(lua_State *L) {
331
-
double n = luaL_checknumber(L, 1);
332
-
SDL_Delay(n * 1000);
333
-
return 0;
334
}
335
336
337
-
static int f_exec(lua_State *L) {
338
-
size_t len;
339
-
const char *cmd = luaL_checklstring(L, 1, &len);
340
-
char *buf = malloc(len + 32);
341
-
if (!buf) { luaL_error(L, "buffer allocation failed"); }
342
-
#if _WIN32
343
-
sprintf(buf, "cmd /c \"%s\"", cmd);
344
-
WinExec(buf, SW_HIDE);
345
-
#else
346
-
sprintf(buf, "%s &", cmd);
347
-
int res = system(buf);
348
-
(void) res;
349
-
#endif
350
-
free(buf);
351
-
return 0;
352
}
353
354
355
-
static int f_fuzzy_match(lua_State *L) {
356
-
const char *str = luaL_checkstring(L, 1);
357
-
const char *ptn = luaL_checkstring(L, 2);
358
-
int score = 0;
359
-
int run = 0;
360
361
-
while (*str && *ptn) {
362
-
while (*str == ' ') { str++; }
363
-
while (*ptn == ' ') { ptn++; }
364
-
if (tolower(*str) == tolower(*ptn)) {
365
-
score += run * 10 - (*str != *ptn);
366
-
run++;
367
-
ptn++;
368
-
} else {
369
-
score -= 10;
370
-
run = 0;
371
-
}
372
-
str++;
373
-
}
374
-
if (*ptn) { return 0; }
375
376
-
lua_pushnumber(L, score - (int) strlen(str));
377
-
return 1;
378
}
379
380
381
static const luaL_Reg lib[] = {
382
-
{ "poll_event", f_poll_event },
383
-
{ "wait_event", f_wait_event },
384
-
{ "set_cursor", f_set_cursor },
385
-
{ "set_window_title", f_set_window_title },
386
-
{ "set_window_mode", f_set_window_mode },
387
-
{ "window_has_focus", f_window_has_focus },
388
-
{ "show_confirm_dialog", f_show_confirm_dialog },
389
-
{ "chdir", f_chdir },
390
-
{ "list_dir", f_list_dir },
391
-
{ "absolute_path", f_absolute_path },
392
-
{ "get_file_info", f_get_file_info },
393
-
{ "get_clipboard", f_get_clipboard },
394
-
{ "set_clipboard", f_set_clipboard },
395
-
{ "get_time", f_get_time },
396
-
{ "sleep", f_sleep },
397
-
{ "exec", f_exec },
398
-
{ "fuzzy_match", f_fuzzy_match },
399
-
{ NULL, NULL }
400
};
401
402
403
-
int luaopen_system(lua_State *L) {
404
-
luaL_newlib(L, lib);
405
-
return 1;
406
}
···
9
#include "api.h"
10
#include "rencache.h"
11
#ifdef _WIN32
12
+
# include <windows.h>
13
#endif
14
15
extern SDL_Window *window;
16
17
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
+
}
27
}
28
29
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;
40
}
41
42
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;
49
50
top:
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;
65
66
+
case SDL_EVENT_WINDOW_EXPOSED:
67
+
rencache_invalidate();
68
+
lua_pushstring(L, "exposed");
69
+
return 1;
70
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;
77
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;
86
87
+
case SDL_EVENT_KEY_DOWN :
88
+
lua_pushstring(L, "keypressed");
89
+
lua_pushstring(L, key_name(buf, e.key.key));
90
+
return 2;
91
92
+
case SDL_EVENT_KEY_UP :
93
+
lua_pushstring(L, "keyreleased");
94
+
lua_pushstring(L, key_name(buf, e.key.key));
95
+
return 2;
96
97
+
case SDL_EVENT_TEXT_INPUT :
98
+
lua_pushstring(L, "textinput");
99
+
lua_pushstring(L, e.text.text);
100
+
return 2;
101
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;
111
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;
120
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;
128
129
+
case SDL_EVENT_MOUSE_WHEEL :
130
+
lua_pushstring(L, "mousewheel");
131
+
lua_pushnumber(L, e.wheel.y);
132
+
return 2;
133
134
+
default:
135
+
goto top;
136
+
}
137
138
+
return 0;
139
}
140
141
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;
147
}
148
149
150
static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_POINTER + 1];
151
152
static const char *cursor_opts[] = {
153
+
"arrow",
154
+
"ibeam",
155
+
"sizeh",
156
+
"sizev",
157
+
"hand",
158
+
NULL
159
};
160
161
static const int cursor_enums[] = {
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
167
};
168
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;
181
}
182
183
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;
189
}
190
191
192
static const char *window_opts[] = { "normal", "maximized", "fullscreen", 0 };
193
enum { WIN_NORMAL, WIN_MAXIMIZED, WIN_FULLSCREEN };
194
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;
204
}
205
206
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;
212
}
213
214
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);
219
220
+
# if _WIN32
221
+
int id = MessageBox(0, msg, title, MB_YESNO | MB_ICONWARNING);
222
+
lua_pushboolean(L, id == IDYES);
223
224
+
# else
225
+
SDL_MessageBoxButtonData buttons[] = {
226
+
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" },
227
+
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" },
228
+
};
229
+
SDL_MessageBoxData data = {
230
+
.title = title,
231
+
.message = msg,
232
+
.numbuttons = 2,
233
+
.buttons = buttons,
234
+
};
235
+
int buttonid;
236
+
SDL_ShowMessageBox(&data, &buttonid);
237
+
lua_pushboolean(L, buttonid == 1);
238
+
# endif
239
+
return 1;
240
}
241
242
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;
250
}
251
252
253
+
static int f_list_dir(lua_State *L)
254
+
{
255
+
const char *path = luaL_checkstring(L, 1);
256
257
+
DIR *dir = opendir(path);
258
+
if (!dir)
259
+
{
260
+
lua_pushnil(L);
261
+
lua_pushstring(L, strerror(errno));
262
+
return 2;
263
+
}
264
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
+
}
276
277
+
closedir(dir);
278
+
return 1;
279
}
280
281
282
#ifdef _WIN32
283
+
# include <windows.h>
284
+
# define realpath(x, y) _fullpath(y, x, MAX_PATH)
285
#endif
286
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;
296
}
297
298
299
+
static int f_get_file_info(lua_State *L)
300
+
{
301
+
const char *path = luaL_checkstring(L, 1);
302
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
+
}
311
312
+
lua_newtable(L);
313
+
lua_pushnumber(L, s.st_mtime);
314
+
lua_setfield(L, -2, "modified");
315
316
+
lua_pushnumber(L, s.st_size);
317
+
lua_setfield(L, -2, "size");
318
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");
326
327
+
return 1;
328
}
329
330
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;
339
}
340
341
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;
347
}
348
349
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;
355
}
356
357
358
+
static int f_sleep(lua_State *L)
359
+
{
360
+
double n = luaL_checknumber(L, 1);
361
+
SDL_Delay(n * 1000);
362
+
return 0;
363
}
364
365
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;
385
}
386
387
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;
394
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;
416
417
+
lua_pushnumber(L, score - (int) strlen(str));
418
+
return 1;
419
}
420
421
422
static const luaL_Reg lib[] = {
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 }
441
};
442
443
444
+
int luaopen_system(lua_State *L)
445
+
{
446
+
luaL_newlib(L, lib);
447
+
return 1;
448
}
+101
-99
src/main.c
+101
-99
src/main.c
···
5
#include "renderer.h"
6
7
#ifdef _WIN32
8
-
#include <windows.h>
9
#elif __linux__
10
-
#include <unistd.h>
11
#elif __APPLE__
12
-
#include <mach-o/dyld.h>
13
#endif
14
15
16
SDL_Window *window;
17
18
19
-
static double get_scale(void) {
20
-
return (double)SDL_min(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()), 1.0);
21
}
22
23
-
24
-
static void get_exe_filename(char *buf, int sz) {
25
-
#if _WIN32
26
-
int len = GetModuleFileName(NULL, buf, sz - 1);
27
-
buf[len] = '\0';
28
-
#elif __linux__
29
-
char path[512];
30
-
sprintf(path, "/proc/%d/exe", getpid());
31
-
int len = readlink(path, buf, sz - 1);
32
-
buf[len] = '\0';
33
-
#elif __APPLE__
34
-
unsigned size = sz;
35
-
_NSGetExecutablePath(buf, &size);
36
-
#else
37
-
strcpy(buf, "./lite");
38
-
#endif
39
}
40
41
42
-
static void init_window_icon(void) {
43
-
#ifndef _WIN32
44
-
#include "../icon.inl"
45
-
(void) icon_rgba_len; /* unused */
46
-
SDL_Surface *surf = SDL_CreateSurfaceFrom(64, 64, SDL_PIXELFORMAT_ABGR8888, icon_rgba, 64 * 4);
47
-
SDL_SetWindowIcon(window, surf);
48
-
SDL_DestroySurface(surf);
49
-
#endif
50
}
51
52
53
-
int main(int argc, char **argv) {
54
-
#ifdef _WIN32
55
-
HINSTANCE lib = LoadLibrary("user32.dll");
56
-
int (*SetProcessDPIAware)() = (void*) GetProcAddress(lib, "SetProcessDPIAware");
57
-
SetProcessDPIAware();
58
-
#endif
59
-
60
-
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
61
-
SDL_EnableScreenSaver();
62
-
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, true);
63
-
atexit(SDL_Quit);
64
-
65
-
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
66
-
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
67
68
-
const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay());
69
-
window = SDL_CreateWindow("", (int)(dm->w * 0.8), (int)(dm->h * 0.8), SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN);
70
-
if (!window)
71
-
{
72
-
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window: %s\n", SDL_GetError());
73
-
return EXIT_FAILURE;
74
-
}
75
-
init_window_icon();
76
-
ren_init(window);
77
78
-
if (!SDL_StartTextInput(window))
79
-
{
80
-
#ifndef _WIN32
81
-
SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR: failed SDL_StartTextInput() - text entry may not work");
82
-
#endif
83
-
}
84
85
86
-
lua_State *L = luaL_newstate();
87
-
luaL_openlibs(L);
88
-
api_load_libs(L);
89
90
91
-
lua_newtable(L);
92
-
for (int i = 0; i < argc; i++) {
93
-
lua_pushstring(L, argv[i]);
94
-
lua_rawseti(L, -2, i + 1);
95
-
}
96
-
lua_setglobal(L, "ARGS");
97
98
-
lua_pushstring(L, "1.11");
99
-
lua_setglobal(L, "VERSION");
100
101
-
lua_pushstring(L, SDL_GetPlatform());
102
-
lua_setglobal(L, "PLATFORM");
103
104
-
lua_pushnumber(L, get_scale());
105
-
lua_setglobal(L, "SCALE");
106
107
-
char exename[2048];
108
-
get_exe_filename(exename, sizeof(exename));
109
-
lua_pushstring(L, exename);
110
-
lua_setglobal(L, "EXEFILE");
111
112
113
-
(void) luaL_dostring(L,
114
-
"local core\n"
115
-
"xpcall(function()\n"
116
-
" SCALE = tonumber(os.getenv(\"LITE_SCALE\")) or SCALE\n"
117
-
" PATHSEP = package.config:sub(1, 1)\n"
118
-
" EXEDIR = EXEFILE:match(\"^(.+)[/\\\\].*$\")\n"
119
-
" package.path = EXEDIR .. '/data/?.lua;' .. package.path\n"
120
-
" package.path = EXEDIR .. '/data/?/init.lua;' .. package.path\n"
121
-
" core = require('core')\n"
122
-
" core.init()\n"
123
-
" core.run()\n"
124
-
"end, function(err)\n"
125
-
" print('Error: ' .. tostring(err))\n"
126
-
" print(debug.traceback(nil, 2))\n"
127
-
" if core and core.on_error then\n"
128
-
" pcall(core.on_error, err)\n"
129
-
" end\n"
130
-
" os.exit(1)\n"
131
-
"end)");
132
133
134
-
lua_close(L);
135
-
SDL_DestroyWindow(window);
136
137
-
return EXIT_SUCCESS;
138
}
···
5
#include "renderer.h"
6
7
#ifdef _WIN32
8
+
# include <windows.h>
9
#elif __linux__
10
+
# include <unistd.h>
11
#elif __APPLE__
12
+
# include <mach-o/dyld.h>
13
#endif
14
15
16
SDL_Window *window;
17
18
19
+
static double get_scale(void)
20
+
{
21
+
return (double)SDL_min(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()), 1.0);
22
}
23
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
40
}
41
42
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
52
}
53
54
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
62
63
+
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS);
64
+
SDL_EnableScreenSaver();
65
+
SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, true);
66
+
atexit(SDL_Quit);
67
68
+
SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
69
+
SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
70
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);
80
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
+
}
87
88
89
+
lua_State *L = luaL_newstate();
90
+
luaL_openlibs(L);
91
+
api_load_libs(L);
92
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");
100
101
+
lua_pushstring(L, "1.11");
102
+
lua_setglobal(L, "VERSION");
103
104
+
lua_pushstring(L, SDL_GetPlatform());
105
+
lua_setglobal(L, "PLATFORM");
106
107
+
lua_pushnumber(L, get_scale());
108
+
lua_setglobal(L, "SCALE");
109
110
+
char exename[2048];
111
+
get_exe_filename(exename, sizeof(exename));
112
+
lua_pushstring(L, exename);
113
+
lua_setglobal(L, "EXEFILE");
114
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)");
134
135
136
+
lua_close(L);
137
+
SDL_DestroyWindow(window);
138
139
+
return EXIT_SUCCESS;
140
}
+265
-197
src/rencache.c
+265
-197
src/rencache.c
···
11
#define CELL_SIZE 96
12
#define COMMAND_BUF_SIZE (1024 * 512)
13
14
-
enum { FREE_FONT, SET_CLIP, DRAW_TEXT, DRAW_RECT };
15
16
-
typedef struct {
17
-
int type, size;
18
-
RenRect rect;
19
-
RenColor color;
20
-
RenFont *font;
21
-
int tab_width;
22
-
char text[0];
23
} Command;
24
25
···
40
/* 32bit fnv-1a hash */
41
#define HASH_INITIAL 2166136261
42
43
-
static void hash(unsigned *h, const void *data, int size) {
44
-
const unsigned char *p = data;
45
-
while (size--) {
46
-
*h = (*h ^ *p++) * 16777619;
47
-
}
48
}
49
50
51
-
static inline int cell_idx(int x, int y) {
52
-
return x + y * CELLS_X;
53
}
54
55
56
-
static inline bool rects_overlap(RenRect a, RenRect b) {
57
-
return b.x + b.width >= a.x && b.x <= a.x + a.width
58
-
&& b.y + b.height >= a.y && b.y <= a.y + a.height;
59
}
60
61
62
-
static RenRect intersect_rects(RenRect a, RenRect b) {
63
-
int x1 = max(a.x, b.x);
64
-
int y1 = max(a.y, b.y);
65
-
int x2 = min(a.x + a.width, b.x + b.width);
66
-
int y2 = min(a.y + a.height, b.y + b.height);
67
-
return (RenRect) { x1, y1, max(0, x2 - x1), max(0, y2 - y1) };
68
}
69
70
71
-
static RenRect merge_rects(RenRect a, RenRect b) {
72
-
int x1 = min(a.x, b.x);
73
-
int y1 = min(a.y, b.y);
74
-
int x2 = max(a.x + a.width, b.x + b.width);
75
-
int y2 = max(a.y + a.height, b.y + b.height);
76
-
return (RenRect) { x1, y1, x2 - x1, y2 - y1 };
77
}
78
79
80
-
static Command* push_command(int type, int size) {
81
-
Command *cmd = (Command*) (command_buf + command_buf_idx);
82
-
int n = command_buf_idx + size;
83
-
if (n > COMMAND_BUF_SIZE) {
84
-
fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n");
85
-
return NULL;
86
-
}
87
-
command_buf_idx = n;
88
-
memset(cmd, 0, sizeof(Command));
89
-
cmd->type = type;
90
-
cmd->size = size;
91
-
return cmd;
92
}
93
94
95
-
static bool next_command(Command **prev) {
96
-
if (*prev == NULL) {
97
-
*prev = (Command*) command_buf;
98
-
} else {
99
-
*prev = (Command*) (((char*) *prev) + (*prev)->size);
100
-
}
101
-
return *prev != ((Command*) (command_buf + command_buf_idx));
102
}
103
104
105
-
void rencache_show_debug(bool enable) {
106
-
show_debug = enable;
107
}
108
109
110
-
void rencache_free_font(RenFont *font) {
111
-
Command *cmd = push_command(FREE_FONT, sizeof(Command));
112
-
if (cmd) { cmd->font = font; }
113
}
114
115
116
-
void rencache_set_clip_rect(RenRect rect) {
117
-
Command *cmd = push_command(SET_CLIP, sizeof(Command));
118
-
if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); }
119
}
120
121
122
-
void rencache_draw_rect(RenRect rect, RenColor color) {
123
-
if (!rects_overlap(screen_rect, rect)) { return; }
124
-
Command *cmd = push_command(DRAW_RECT, sizeof(Command));
125
-
if (cmd) {
126
-
cmd->rect = rect;
127
-
cmd->color = color;
128
-
}
129
}
130
131
132
-
int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) {
133
-
RenRect rect;
134
-
rect.x = x;
135
-
rect.y = y;
136
-
rect.width = ren_get_font_width(font, text);
137
-
rect.height = ren_get_font_height(font);
138
139
-
if (rects_overlap(screen_rect, rect)) {
140
-
int sz = strlen(text) + 1;
141
-
Command *cmd = push_command(DRAW_TEXT, sizeof(Command) + sz);
142
-
if (cmd) {
143
-
memcpy(cmd->text, text, sz);
144
-
cmd->color = color;
145
-
cmd->font = font;
146
-
cmd->rect = rect;
147
-
cmd->tab_width = ren_get_font_tab_width(font);
148
-
}
149
-
}
150
151
-
return x + rect.width;
152
}
153
154
155
-
void rencache_invalidate(void) {
156
-
memset(cells_prev, 0xff, sizeof(cells_buf1));
157
}
158
159
160
-
void rencache_begin_frame(void) {
161
-
/* reset all cells if the screen width/height has changed */
162
-
int w, h;
163
-
ren_get_size(&w, &h);
164
-
if (screen_rect.width != w || h != screen_rect.height) {
165
-
screen_rect.width = w;
166
-
screen_rect.height = h;
167
-
rencache_invalidate();
168
-
}
169
}
170
171
172
-
static void update_overlapping_cells(RenRect r, unsigned h) {
173
-
int x1 = r.x / CELL_SIZE;
174
-
int y1 = r.y / CELL_SIZE;
175
-
int x2 = (r.x + r.width) / CELL_SIZE;
176
-
int y2 = (r.y + r.height) / CELL_SIZE;
177
178
-
for (int y = y1; y <= y2; y++) {
179
-
for (int x = x1; x <= x2; x++) {
180
-
int idx = cell_idx(x, y);
181
-
hash(&cells[idx], &h, sizeof(h));
182
-
}
183
-
}
184
}
185
186
187
-
static void push_rect(RenRect r, int *count) {
188
-
/* try to merge with existing rectangle */
189
-
for (int i = *count - 1; i >= 0; i--) {
190
-
RenRect *rp = &rect_buf[i];
191
-
if (rects_overlap(*rp, r)) {
192
-
*rp = merge_rects(*rp, r);
193
-
return;
194
-
}
195
-
}
196
-
/* couldn't merge with previous rectangle: push */
197
-
rect_buf[(*count)++] = r;
198
}
199
200
201
-
void rencache_end_frame(void) {
202
-
/* update cells from commands */
203
-
Command *cmd = NULL;
204
-
RenRect cr = screen_rect;
205
-
while (next_command(&cmd)) {
206
-
if (cmd->type == SET_CLIP) { cr = cmd->rect; }
207
-
RenRect r = intersect_rects(cmd->rect, cr);
208
-
if (r.width == 0 || r.height == 0) { continue; }
209
-
unsigned h = HASH_INITIAL;
210
-
hash(&h, cmd, cmd->size);
211
-
update_overlapping_cells(r, h);
212
-
}
213
214
-
/* push rects for all cells changed from last frame, reset cells */
215
-
int rect_count = 0;
216
-
int max_x = screen_rect.width / CELL_SIZE + 1;
217
-
int max_y = screen_rect.height / CELL_SIZE + 1;
218
-
for (int y = 0; y < max_y; y++) {
219
-
for (int x = 0; x < max_x; x++) {
220
-
/* compare previous and current cell for change */
221
-
int idx = cell_idx(x, y);
222
-
if (cells[idx] != cells_prev[idx]) {
223
-
push_rect((RenRect) { x, y, 1, 1 }, &rect_count);
224
-
}
225
-
cells_prev[idx] = HASH_INITIAL;
226
-
}
227
-
}
228
229
-
/* expand rects from cells to pixels */
230
-
for (int i = 0; i < rect_count; i++) {
231
-
RenRect *r = &rect_buf[i];
232
-
r->x *= CELL_SIZE;
233
-
r->y *= CELL_SIZE;
234
-
r->width *= CELL_SIZE;
235
-
r->height *= CELL_SIZE;
236
-
*r = intersect_rects(*r, screen_rect);
237
-
}
238
239
-
/* redraw updated regions */
240
-
bool has_free_commands = false;
241
-
for (int i = 0; i < rect_count; i++) {
242
-
/* draw */
243
-
RenRect r = rect_buf[i];
244
-
ren_set_clip_rect(r);
245
246
-
cmd = NULL;
247
-
while (next_command(&cmd)) {
248
-
switch (cmd->type) {
249
-
case FREE_FONT:
250
-
has_free_commands = true;
251
-
break;
252
-
case SET_CLIP:
253
-
ren_set_clip_rect(intersect_rects(cmd->rect, r));
254
-
break;
255
-
case DRAW_RECT:
256
-
ren_draw_rect(cmd->rect, cmd->color);
257
-
break;
258
-
case DRAW_TEXT:
259
-
ren_set_font_tab_width(cmd->font, cmd->tab_width);
260
-
ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color);
261
-
break;
262
-
}
263
-
}
264
265
-
if (show_debug) {
266
-
RenColor color = { rand(), rand(), rand(), 50 };
267
-
ren_draw_rect(r, color);
268
-
}
269
-
}
270
271
-
/* update dirty rects */
272
-
if (rect_count > 0) {
273
-
ren_update_rects(rect_buf, rect_count);
274
-
}
275
276
-
/* free fonts */
277
-
if (has_free_commands) {
278
-
cmd = NULL;
279
-
while (next_command(&cmd)) {
280
-
if (cmd->type == FREE_FONT) {
281
-
ren_free_font(cmd->font);
282
-
}
283
-
}
284
-
}
285
286
-
/* swap cell buffer and reset */
287
-
unsigned *tmp = cells;
288
-
cells = cells_prev;
289
-
cells_prev = tmp;
290
-
command_buf_idx = 0;
291
}
···
11
#define CELL_SIZE 96
12
#define COMMAND_BUF_SIZE (1024 * 512)
13
14
+
enum
15
+
{
16
+
FREE_FONT,
17
+
SET_CLIP,
18
+
DRAW_TEXT,
19
+
DRAW_RECT
20
+
};
21
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];
30
} Command;
31
32
···
47
/* 32bit fnv-1a hash */
48
#define HASH_INITIAL 2166136261
49
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
+
}
57
}
58
59
60
+
static inline int cell_idx(int x, int y)
61
+
{
62
+
return x + y * CELLS_X;
63
}
64
65
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;
72
}
73
74
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) };
82
}
83
84
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 };
92
}
93
94
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;
109
}
110
111
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));
124
}
125
126
127
+
void rencache_show_debug(bool enable)
128
+
{
129
+
show_debug = enable;
130
}
131
132
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
+
}
140
}
141
142
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
+
}
150
}
151
152
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
+
}
165
}
166
167
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);
175
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
+
}
189
190
+
return x + rect.width;
191
}
192
193
194
+
void rencache_invalidate(void)
195
+
{
196
+
memset(cells_prev, 0xff, sizeof(cells_buf1));
197
}
198
199
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
+
}
211
}
212
213
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;
220
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
+
}
229
}
230
231
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;
246
}
247
248
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
+
}
269
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
+
}
287
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
+
}
298
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);
306
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
+
}
327
328
+
if (show_debug)
329
+
{
330
+
RenColor color = { rand(), rand(), rand(), 50 };
331
+
ren_draw_rect(r, color);
332
+
}
333
+
}
334
335
+
/* update dirty rects */
336
+
if (rect_count > 0)
337
+
{
338
+
ren_update_rects(rect_buf, rect_count);
339
+
}
340
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
+
}
353
354
+
/* swap cell buffer and reset */
355
+
unsigned *tmp = cells;
356
+
cells = cells_prev;
357
+
cells_prev = tmp;
358
+
command_buf_idx = 0;
359
}
+299
-249
src/renderer.c
+299
-249
src/renderer.c
···
7
8
#define MAX_GLYPHSET 256
9
10
-
struct RenImage {
11
-
RenColor *pixels;
12
-
int width, height;
13
};
14
15
-
typedef struct {
16
-
RenImage *image;
17
-
stbtt_bakedchar glyphs[256];
18
} GlyphSet;
19
20
-
struct RenFont {
21
-
void *data;
22
-
stbtt_fontinfo stbfont;
23
-
GlyphSet *sets[MAX_GLYPHSET];
24
-
float size;
25
-
int height;
26
};
27
28
29
static SDL_Window *window;
30
-
static struct { int left, top, right, bottom; } clip;
31
32
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
}
40
41
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;
56
}
57
58
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 } );
64
}
65
66
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
-
}
74
}
75
76
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;
82
}
83
84
85
-
void ren_get_size(int *x, int *y) {
86
-
SDL_Surface *surf = SDL_GetWindowSurface(window);
87
-
*x = surf->w;
88
-
*y = surf->h;
89
}
90
91
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;
100
}
101
102
103
-
void ren_free_image(RenImage *image) {
104
-
free(image);
105
}
106
107
108
-
static GlyphSet* load_glyphset(RenFont *font, int idx) {
109
-
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
110
111
-
/* init image */
112
-
int width = 128;
113
-
int height = 128;
114
retry:
115
-
set->image = ren_new_image(width, height);
116
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);
124
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
-
}
132
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
-
}
142
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
-
}
148
149
-
return set;
150
}
151
152
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];
159
}
160
161
162
-
RenFont* ren_load_font(const char *filename, float size) {
163
-
RenFont *font = NULL;
164
-
FILE *fp = NULL;
165
166
-
/* init font */
167
-
font = check_alloc(calloc(1, sizeof(RenFont)));
168
-
font->size = size;
169
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;
180
181
-
/* init stbfont */
182
-
int ok = stbtt_InitFont(&font->stbfont, font->data, 0);
183
-
if (!ok) { goto fail; }
184
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;
190
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;
195
196
-
return font;
197
198
fail:
199
-
if (fp) { fclose(fp); }
200
-
if (font) { free(font->data); }
201
-
free(font);
202
-
return NULL;
203
}
204
205
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);
216
}
217
218
219
-
void ren_set_font_tab_width(RenFont *font, int n) {
220
-
GlyphSet *set = get_glyphset(font, '\t');
221
-
set->glyphs['\t'].xadvance = n;
222
}
223
224
225
-
int ren_get_font_tab_width(RenFont *font) {
226
-
GlyphSet *set = get_glyphset(font, '\t');
227
-
return set->glyphs['\t'].xadvance;
228
}
229
230
231
-
int ren_get_font_width(RenFont *font, const char *text) {
232
-
int x = 0;
233
-
const char *p = text;
234
-
unsigned codepoint;
235
-
while (*p) {
236
-
p = utf8_to_codepoint(p, &codepoint);
237
-
GlyphSet *set = get_glyphset(font, codepoint);
238
-
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
239
-
x += g->xadvance;
240
-
}
241
-
return x;
242
}
243
244
245
-
int ren_get_font_height(RenFont *font) {
246
-
return font->height;
247
}
248
249
250
-
static inline RenColor blend_pixel(RenColor dst, RenColor src) {
251
-
int ia = 0xff - src.a;
252
-
dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8;
253
-
dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8;
254
-
dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8;
255
-
return dst;
256
}
257
258
259
-
static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) {
260
-
src.a = (src.a * color.a) >> 8;
261
-
int ia = 0xff - src.a;
262
-
dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8);
263
-
dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8);
264
-
dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8);
265
-
return dst;
266
}
267
268
269
-
#define rect_draw_loop(expr) \
270
-
for (int j = y1; j < y2; j++) { \
271
-
for (int i = x1; i < x2; i++) { \
272
-
*d = expr; \
273
-
d++; \
274
-
} \
275
-
d += dr; \
276
-
}
277
278
-
void ren_draw_rect(RenRect rect, RenColor color) {
279
-
if (color.a == 0) { return; }
280
281
-
int x1 = rect.x < clip.left ? clip.left : rect.x;
282
-
int y1 = rect.y < clip.top ? clip.top : rect.y;
283
-
int x2 = rect.x + rect.width;
284
-
int y2 = rect.y + rect.height;
285
-
x2 = x2 > clip.right ? clip.right : x2;
286
-
y2 = y2 > clip.bottom ? clip.bottom : y2;
287
288
-
SDL_Surface *surf = SDL_GetWindowSurface(window);
289
-
RenColor *d = (RenColor*) surf->pixels;
290
-
d += x1 + y1 * surf->w;
291
-
int dr = surf->w - (x2 - x1);
292
293
-
if (color.a == 0xff) {
294
-
rect_draw_loop(color);
295
-
} else {
296
-
rect_draw_loop(blend_pixel(*d, color));
297
-
}
298
}
299
300
301
-
void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) {
302
-
if (color.a == 0) { return; }
303
304
-
/* clip */
305
-
int n;
306
-
if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; }
307
-
if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; }
308
-
if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; }
309
-
if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; }
310
311
-
if (sub->width <= 0 || sub->height <= 0) {
312
-
return;
313
-
}
314
315
-
/* draw */
316
-
SDL_Surface *surf = SDL_GetWindowSurface(window);
317
-
RenColor *s = image->pixels;
318
-
RenColor *d = (RenColor*) surf->pixels;
319
-
s += sub->x + sub->y * image->width;
320
-
d += x + y * surf->w;
321
-
int sr = image->width - sub->width;
322
-
int dr = surf->w - sub->width;
323
324
-
for (int j = 0; j < sub->height; j++) {
325
-
for (int i = 0; i < sub->width; i++) {
326
-
*d = blend_pixel2(*d, *s, color);
327
-
d++;
328
-
s++;
329
-
}
330
-
d += dr;
331
-
s += sr;
332
-
}
333
}
334
335
336
-
int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) {
337
-
RenRect rect;
338
-
const char *p = text;
339
-
unsigned codepoint;
340
-
while (*p) {
341
-
p = utf8_to_codepoint(p, &codepoint);
342
-
GlyphSet *set = get_glyphset(font, codepoint);
343
-
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
344
-
rect.x = g->x0;
345
-
rect.y = g->y0;
346
-
rect.width = g->x1 - g->x0;
347
-
rect.height = g->y1 - g->y0;
348
-
ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color);
349
-
x += g->xadvance;
350
-
}
351
-
return x;
352
}
···
7
8
#define MAX_GLYPHSET 256
9
10
+
struct RenImage
11
+
{
12
+
RenColor *pixels;
13
+
int width, height;
14
};
15
16
+
typedef struct
17
+
{
18
+
RenImage *image;
19
+
stbtt_bakedchar glyphs[256];
20
} GlyphSet;
21
22
+
struct RenFont
23
+
{
24
+
void *data;
25
+
stbtt_fontinfo stbfont;
26
+
GlyphSet *sets[MAX_GLYPHSET];
27
+
float size;
28
+
int height;
29
};
30
31
32
static SDL_Window *window;
33
+
static struct
34
+
{
35
+
int left, top, right, bottom;
36
+
} clip;
37
38
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;
47
}
48
49
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;
65
}
66
67
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 });
74
}
75
76
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
+
}
86
}
87
88
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;
95
}
96
97
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;
103
}
104
105
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;
115
}
116
117
118
+
void ren_free_image(RenImage *image)
119
+
{
120
+
free(image);
121
}
122
123
124
+
static GlyphSet* load_glyphset(RenFont *font, int idx)
125
+
{
126
+
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
127
128
+
/* init image */
129
+
int width = 128;
130
+
int height = 128;
131
retry:
132
+
set->image = ren_new_image(width, height);
133
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);
141
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
+
}
150
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
+
}
161
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
+
}
168
169
+
return set;
170
}
171
172
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];
179
}
180
181
182
+
RenFont* ren_load_font(const char *filename, float size)
183
+
{
184
+
RenFont *font = NULL;
185
+
FILE *fp = NULL;
186
187
+
/* init font */
188
+
font = check_alloc(calloc(1, sizeof(RenFont)));
189
+
font->size = size;
190
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;
204
205
+
/* init stbfont */
206
+
int ok = stbtt_InitFont(&font->stbfont, font->data, 0);
207
+
if (!ok)
208
+
goto fail;
209
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;
215
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;
220
221
+
return font;
222
223
fail:
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);
246
}
247
248
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;
253
}
254
255
256
+
int ren_get_font_tab_width(RenFont *font)
257
+
{
258
+
GlyphSet *set = get_glyphset(font, '\t');
259
+
return set->glyphs['\t'].xadvance;
260
}
261
262
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;
276
}
277
278
279
+
int ren_get_font_height(RenFont *font)
280
+
{
281
+
return font->height;
282
}
283
284
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;
292
}
293
294
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;
303
}
304
305
306
+
#define rect_draw_loop(expr) do { \
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
+
} \
316
+
} while (0)
317
318
+
void ren_draw_rect(RenRect rect, RenColor color)
319
+
{
320
+
if (color.a == 0)
321
+
return;
322
323
+
int x1 = rect.x < clip.left ? clip.left : rect.x;
324
+
int y1 = rect.y < clip.top ? clip.top : rect.y;
325
+
int x2 = rect.x + rect.width;
326
+
int y2 = rect.y + rect.height;
327
+
x2 = x2 > clip.right ? clip.right : x2;
328
+
y2 = y2 > clip.bottom ? clip.bottom : y2;
329
330
+
SDL_Surface *surf = SDL_GetWindowSurface(window);
331
+
RenColor *d = (RenColor *)surf->pixels;
332
+
d += x1 + y1 * surf->w;
333
+
int dr = surf->w - (x2 - x1);
334
335
+
if (color.a == 0xff)
336
+
rect_draw_loop(color);
337
+
else
338
+
rect_draw_loop(blend_pixel(*d, color));
339
}
340
341
342
+
void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color)
343
+
{
344
+
if (color.a == 0)
345
+
return;
346
347
+
/* clip */
348
+
int n;
349
+
if ((n = clip.left - x) > 0)
350
+
sub->width -= n; sub->x += n; x += n;
351
+
if ((n = clip.top - y) > 0)
352
+
sub->height -= n; sub->y += n; y += n;
353
+
if ((n = x + sub->width - clip.right ) > 0)
354
+
sub->width -= n;
355
+
if ((n = y + sub->height - clip.bottom) > 0)
356
+
sub->height -= n;
357
358
+
if (sub->width <= 0 || sub->height <= 0)
359
+
return;
360
361
+
/* draw */
362
+
SDL_Surface *surf = SDL_GetWindowSurface(window);
363
+
RenColor *s = image->pixels;
364
+
RenColor *d = (RenColor *)surf->pixels;
365
+
s += sub->x + sub->y * image->width;
366
+
d += x + y * surf->w;
367
+
int sr = image->width - sub->width;
368
+
int dr = surf->w - sub->width;
369
370
+
for (int j = 0; j < sub->height; j++)
371
+
{
372
+
for (int i = 0; i < sub->width; i++)
373
+
{
374
+
*d = blend_pixel2(*d, *s, color);
375
+
d++;
376
+
s++;
377
+
}
378
+
d += dr;
379
+
s += sr;
380
+
}
381
}
382
383
384
+
int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color)
385
+
{
386
+
RenRect rect;
387
+
const char *p = text;
388
+
unsigned codepoint;
389
+
while (*p)
390
+
{
391
+
p = utf8_to_codepoint(p, &codepoint);
392
+
GlyphSet *set = get_glyphset(font, codepoint);
393
+
stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff];
394
+
rect.x = g->x0;
395
+
rect.y = g->y0;
396
+
rect.width = g->x1 - g->x0;
397
+
rect.height = g->y1 - g->y0;
398
+
ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color);
399
+
x += g->xadvance;
400
+
}
401
+
return x;
402
}
+9
-2
src/renderer.h
+9
-2
src/renderer.h