Customized fork of github.com/rxi/lite

Reformat

+8
.editorconfig
···
··· 1 + root = true 2 + 3 + [*.{c,h,lua}] 4 + charset = utf-8 5 + end_of_line = lf 6 + indent_style = tab 7 + insert_final_newline = true 8 + trim_trailing_whitespace = true
+2
.gitignore
···
··· 1 + /lite 2 + /*.o
-19
LICENSE
··· 1 - Copyright (c) 2020 rxi 2 - 3 - Permission is hereby granted, free of charge, to any person obtaining a copy of 4 - this software and associated documentation files (the "Software"), to deal in 5 - the Software without restriction, including without limitation the rights to 6 - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 - of the Software, and to permit persons to whom the Software is furnished to do 8 - so, subject to the following conditions: 9 - 10 - The above copyright notice and this permission notice shall be included in all 11 - copies or substantial portions of the Software. 12 - 13 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 - SOFTWARE.
···
-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 ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 15
··· 1 # lite 2 ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 3
+4
compile_flags.txt
···
··· 1 + -Wall 2 + -Isrc 3 + -fno-strict-aliasing 4 + -DLUA_USE_POSIX
+1 -1
data/user/init.lua
··· 9 config.indent_size = 8 10 11 -- light theme: 12 - -- require "user.colors.summer" 13 14 -- key binding: 15 -- keymap.add { ["ctrl+escape"] = "core:quit" }
··· 9 config.indent_size = 8 10 11 -- light theme: 12 + require "user.colors.fall" 13 14 -- key binding: 15 -- keymap.add { ["ctrl+escape"] = "core:quit" }
+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
···
··· 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
··· 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 - for (int i = 0; libs[i].name; i++) { 16 - luaL_requiref(L, libs[i].name, libs[i].func, 1); 17 - } 18 }
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 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
··· 8 typedef struct RenImage RenImage; 9 typedef struct RenFont RenFont; 10 11 - typedef struct { uint8_t b, g, r, a; } RenColor; 12 - typedef struct { int x, y, width, height; } RenRect; 13 14 15 void ren_init(SDL_Window *win);
··· 8 typedef struct RenImage RenImage; 9 typedef struct RenFont RenFont; 10 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; 20 21 22 void ren_init(SDL_Window *win);