1#include <stdio.h>
2#include <stdlib.h>
3#include <SDL.h>
4#include "api/api.h"
5#include "rencache.h"
6#include "renderer.h"
7
8#include <signal.h>
9
10#ifdef _WIN32
11 #include <windows.h>
12#elif defined(__linux__)
13 #include <unistd.h>
14#elif defined(__APPLE__)
15 #include <mach-o/dyld.h>
16#elif defined(__FreeBSD__)
17 #include <sys/sysctl.h>
18#endif
19
20
21static SDL_Window *window;
22
23static double get_scale(void) {
24#ifndef __APPLE__
25 float dpi;
26 if (SDL_GetDisplayDPI(0, NULL, &dpi, NULL) == 0)
27 return dpi / 96.0;
28#endif
29 return 1.0;
30}
31
32
33static void get_exe_filename(char *buf, int sz) {
34#if _WIN32
35 int len;
36 wchar_t *buf_w = malloc(sizeof(wchar_t) * sz);
37 if (buf_w) {
38 len = GetModuleFileNameW(NULL, buf_w, sz - 1);
39 buf_w[len] = L'\0';
40 // if the conversion failed we'll empty the string
41 if (!WideCharToMultiByte(CP_UTF8, 0, buf_w, -1, buf, sz, NULL, NULL))
42 buf[0] = '\0';
43 free(buf_w);
44 } else {
45 buf[0] = '\0';
46 }
47#elif __linux__
48 char path[] = "/proc/self/exe";
49 ssize_t len = readlink(path, buf, sz - 1);
50 if (len > 0)
51 buf[len] = '\0';
52#elif __APPLE__
53 /* use realpath to resolve a symlink if the process was launched from one.
54 ** This happens when Homebrew installs a cack and creates a symlink in
55 ** /usr/loca/bin for launching the executable from the command line. */
56 unsigned size = sz;
57 char exepath[size];
58 _NSGetExecutablePath(exepath, &size);
59 realpath(exepath, buf);
60#elif __FreeBSD__
61 size_t len = sz;
62 const int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 };
63 sysctl(mib, 4, buf, &len, NULL, 0);
64#else
65 *buf = 0;
66#endif
67}
68
69
70static void init_window_icon(void) {
71#if !defined(_WIN32) && !defined(__APPLE__)
72 #include "../resources/icons/icon.inl"
73 (void) icon_rgba_len; /* unused */
74 SDL_Surface *surf = SDL_CreateRGBSurfaceFrom(
75 icon_rgba, 64, 64,
76 32, 64 * 4,
77 0x000000ff,
78 0x0000ff00,
79 0x00ff0000,
80 0xff000000);
81 SDL_SetWindowIcon(window, surf);
82 SDL_FreeSurface(surf);
83#endif
84}
85
86#ifdef _WIN32
87#define LITE_OS_HOME "USERPROFILE"
88#define LITE_PATHSEP_PATTERN "\\\\"
89#define LITE_NONPATHSEP_PATTERN "[^\\\\]+"
90#else
91#define LITE_OS_HOME "HOME"
92#define LITE_PATHSEP_PATTERN "/"
93#define LITE_NONPATHSEP_PATTERN "[^/]+"
94#endif
95
96#ifdef __APPLE__
97void enable_momentum_scroll();
98#ifdef MACOS_USE_BUNDLE
99void set_macos_bundle_resources(lua_State *L);
100#endif
101#endif
102
103#ifndef LITE_ARCH_TUPLE
104 // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-140
105 #if defined(__x86_64__) || defined(_M_AMD64) || defined(__MINGW64__)
106 #define ARCH_PROCESSOR "x86_64"
107 #elif defined(__i386__) || defined(_M_IX86) || defined(__MINGW32__)
108 #define ARCH_PROCESSOR "x86"
109 #elif defined(__aarch64__) || defined(_M_ARM64) || defined (_M_ARM64EC)
110 #define ARCH_PROCESSOR "aarch64"
111 #elif defined(__arm__) || defined(_M_ARM)
112 #define ARCH_PROCESSOR "arm"
113 #endif
114
115 #if _WIN32
116 #define ARCH_PLATFORM "windows"
117 #elif __linux__
118 #define ARCH_PLATFORM "linux"
119 #elif __FreeBSD__
120 #define ARCH_PLATFORM "freebsd"
121 #elif __APPLE__
122 #define ARCH_PLATFORM "darwin"
123 #endif
124
125 #if !defined(ARCH_PROCESSOR) || !defined(ARCH_PLATFORM)
126 #error "Please define -DLITE_ARCH_TUPLE."
127 #endif
128
129 #define LITE_ARCH_TUPLE ARCH_PROCESSOR "-" ARCH_PLATFORM
130#endif
131
132int main(int argc, char **argv) {
133#ifndef _WIN32
134 signal(SIGPIPE, SIG_IGN);
135#endif
136
137 if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS) != 0) {
138 fprintf(stderr, "Error initializing sdl: %s", SDL_GetError());
139 exit(1);
140 }
141 SDL_EnableScreenSaver();
142 SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
143 atexit(SDL_Quit);
144
145#ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* Available since 2.0.8 */
146 SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0");
147#endif
148#if SDL_VERSION_ATLEAST(2, 0, 5)
149 SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1");
150#endif
151#if SDL_VERSION_ATLEAST(2, 0, 18)
152 SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
153#endif
154#if SDL_VERSION_ATLEAST(2, 0, 22)
155 SDL_SetHint(SDL_HINT_IME_SUPPORT_EXTENDED_TEXT, "1");
156#endif
157
158#if SDL_VERSION_ATLEAST(2, 0, 8)
159 /* This hint tells SDL to respect borderless window as a normal window.
160 ** For example, the window will sit right on top of the taskbar instead
161 ** of obscuring it. */
162 SDL_SetHint("SDL_BORDERLESS_WINDOWED_STYLE", "1");
163#endif
164#if SDL_VERSION_ATLEAST(2, 0, 12)
165 /* This hint tells SDL to allow the user to resize a borderless windoow.
166 ** It also enables aero-snap on Windows apparently. */
167 SDL_SetHint("SDL_BORDERLESS_RESIZABLE_STYLE", "1");
168#endif
169#if SDL_VERSION_ATLEAST(2, 0, 9)
170 SDL_SetHint("SDL_MOUSE_DOUBLE_CLICK_RADIUS", "4");
171#endif
172
173 SDL_DisplayMode dm;
174 SDL_GetCurrentDisplayMode(0, &dm);
175
176 window = SDL_CreateWindow(
177 "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
178 SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
179 init_window_icon();
180 if (!window) {
181 fprintf(stderr, "Error creating lite-xl window: %s", SDL_GetError());
182 exit(1);
183 }
184 ren_init(window);
185
186 lua_State *L;
187init_lua:
188 L = luaL_newstate();
189 luaL_openlibs(L);
190 api_load_libs(L);
191
192
193 lua_newtable(L);
194 for (int i = 0; i < argc; i++) {
195 lua_pushstring(L, argv[i]);
196 lua_rawseti(L, -2, i + 1);
197 }
198 lua_setglobal(L, "ARGS");
199
200 lua_pushstring(L, SDL_GetPlatform());
201 lua_setglobal(L, "PLATFORM");
202
203 lua_pushstring(L, LITE_ARCH_TUPLE);
204 lua_setglobal(L, "ARCH");
205
206 lua_pushnumber(L, get_scale());
207 lua_setglobal(L, "SCALE");
208
209 char exename[2048];
210 get_exe_filename(exename, sizeof(exename));
211 if (*exename) {
212 lua_pushstring(L, exename);
213 } else {
214 // get_exe_filename failed
215 lua_pushstring(L, argv[0]);
216 }
217 lua_setglobal(L, "EXEFILE");
218
219#ifdef __APPLE__
220 enable_momentum_scroll();
221 #ifdef MACOS_USE_BUNDLE
222 set_macos_bundle_resources(L);
223 #endif
224#endif
225 SDL_EventState(SDL_TEXTINPUT, SDL_ENABLE);
226 SDL_EventState(SDL_TEXTEDITING, SDL_ENABLE);
227
228 const char *init_lite_code = \
229 "local core\n"
230 "local os_exit = os.exit\n"
231 "os.exit = function(code, close)\n"
232 " os_exit(code, close == nil and true or close)\n"
233 "end\n"
234 "xpcall(function()\n"
235 " local match = require('utf8extra').match\n"
236 " HOME = os.getenv('" LITE_OS_HOME "')\n"
237 " local exedir = match(EXEFILE, '^(.*)" LITE_PATHSEP_PATTERN LITE_NONPATHSEP_PATTERN "$')\n"
238 " local prefix = os.getenv('LITE_PREFIX') or match(exedir, '^(.*)" LITE_PATHSEP_PATTERN "bin$')\n"
239 " dofile((MACOS_RESOURCES or (prefix and prefix .. '/share/lite-xl' or exedir .. '/data')) .. '/core/start.lua')\n"
240 " core = require(os.getenv('LITE_XL_RUNTIME') or 'core')\n"
241 " core.init()\n"
242 " core.run()\n"
243 "end, function(err)\n"
244 " local error_path = 'error.txt'\n"
245 " io.stdout:write('Error: '..tostring(err)..'\\n')\n"
246 " io.stdout:write(debug.traceback(nil, 2)..'\\n')\n"
247 " if core and core.on_error then\n"
248 " error_path = USERDIR .. PATHSEP .. error_path\n"
249 " pcall(core.on_error, err)\n"
250 " else\n"
251 " local fp = io.open(error_path, 'wb')\n"
252 " fp:write('Error: ' .. tostring(err) .. '\\n')\n"
253 " fp:write(debug.traceback(nil, 2)..'\\n')\n"
254 " fp:close()\n"
255 " error_path = system.absolute_path(error_path)\n"
256 " end\n"
257 " system.show_fatal_error('Lite XL internal error',\n"
258 " 'An internal error occurred in a critical part of the application.\\n\\n'..\n"
259 " 'Error: '..tostring(err)..'\\n\\n'..\n"
260 " 'Details can be found in \\\"'..error_path..'\\\"')\n"
261 " os.exit(1)\n"
262 "end)\n"
263 "return core and core.restart_request\n";
264
265 if (luaL_loadstring(L, init_lite_code)) {
266 fprintf(stderr, "internal error when starting the application\n");
267 exit(1);
268 }
269 lua_pcall(L, 0, 1, 0);
270 if (lua_toboolean(L, -1)) {
271 lua_close(L);
272 rencache_invalidate();
273 goto init_lua;
274 }
275
276 // This allows the window to be destroyed before lite-xl is done with
277 // reaping child processes
278 ren_free_window_resources(&window_renderer);
279 lua_close(L);
280
281 return EXIT_SUCCESS;
282}
283