2020-06-29 15:24:08 +02:00
|
|
|
#include <SDL.h>
|
2022-01-15 00:53:46 +01:00
|
|
|
#include <string.h>
|
2020-04-22 00:33:04 +02:00
|
|
|
#include <stdbool.h>
|
2019-12-28 12:16:32 +01:00
|
|
|
#include <ctype.h>
|
2020-04-22 00:33:04 +02:00
|
|
|
#include <errno.h>
|
2022-06-17 15:31:52 +02:00
|
|
|
#include <sys/types.h>
|
2019-12-28 12:16:32 +01:00
|
|
|
#include <sys/stat.h>
|
|
|
|
#include "api.h"
|
2021-12-16 02:31:24 +01:00
|
|
|
#include "../rencache.h"
|
2019-12-28 12:16:32 +01:00
|
|
|
#ifdef _WIN32
|
2020-12-02 16:03:31 +01:00
|
|
|
#include <direct.h>
|
2019-12-28 12:16:32 +01:00
|
|
|
#include <windows.h>
|
2021-06-24 19:54:22 +02:00
|
|
|
#include <fileapi.h>
|
2022-06-17 15:31:52 +02:00
|
|
|
#include "../utfconv.h"
|
2022-11-16 04:23:45 +01:00
|
|
|
|
|
|
|
// Windows does not define the S_ISREG and S_ISDIR macros in stat.h, so we do.
|
|
|
|
// We have to define _CRT_INTERNAL_NONSTDC_NAMES 1 before #including sys/stat.h
|
|
|
|
// in order for Microsoft's stat.h to define names like S_IFMT, S_IFREG, and S_IFDIR,
|
|
|
|
// rather than just defining _S_IFMT, _S_IFREG, and _S_IFDIR as it normally does.
|
|
|
|
#define _CRT_INTERNAL_NONSTDC_NAMES 1
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#if !defined(S_ISREG) && defined(S_IFMT) && defined(S_IFREG)
|
|
|
|
#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
|
|
|
|
#endif
|
|
|
|
#if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR)
|
|
|
|
#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
|
|
|
|
#endif
|
2022-06-17 15:31:52 +02:00
|
|
|
#else
|
|
|
|
|
|
|
|
#include <dirent.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#ifdef __linux__
|
2021-10-12 22:08:17 +02:00
|
|
|
#include <sys/vfs.h>
|
2019-12-28 12:16:32 +01:00
|
|
|
#endif
|
2022-06-17 15:31:52 +02:00
|
|
|
#endif
|
2019-12-28 12:16:32 +01:00
|
|
|
|
|
|
|
extern SDL_Window *window;
|
|
|
|
|
|
|
|
|
|
|
|
static const char* button_name(int button) {
|
|
|
|
switch (button) {
|
2021-10-06 01:42:04 +02:00
|
|
|
case SDL_BUTTON_LEFT : return "left";
|
|
|
|
case SDL_BUTTON_MIDDLE : return "middle";
|
|
|
|
case SDL_BUTTON_RIGHT : return "right";
|
2021-11-14 21:46:33 +01:00
|
|
|
case SDL_BUTTON_X1 : return "x";
|
|
|
|
case SDL_BUTTON_X2 : return "y";
|
2019-12-28 12:16:32 +01:00
|
|
|
default : return "?";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-09-17 22:38:09 +02:00
|
|
|
static void str_tolower(char *p) {
|
2019-12-28 12:16:32 +01:00
|
|
|
while (*p) {
|
|
|
|
*p = tolower(*p);
|
|
|
|
p++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-11 15:08:25 +02:00
|
|
|
struct HitTestInfo {
|
|
|
|
int title_height;
|
2021-04-11 23:52:31 +02:00
|
|
|
int controls_width;
|
2021-04-11 15:08:25 +02:00
|
|
|
int resize_border;
|
|
|
|
};
|
|
|
|
typedef struct HitTestInfo HitTestInfo;
|
|
|
|
|
2022-04-15 17:34:46 +02:00
|
|
|
static HitTestInfo window_hit_info[1] = {{0, 0, 0}};
|
2021-04-10 19:35:57 +02:00
|
|
|
|
2021-04-12 11:54:52 +02:00
|
|
|
#define RESIZE_FROM_TOP 0
|
|
|
|
#define RESIZE_FROM_RIGHT 0
|
|
|
|
|
2021-04-11 15:08:25 +02:00
|
|
|
static SDL_HitTestResult SDLCALL hit_test(SDL_Window *window, const SDL_Point *pt, void *data) {
|
|
|
|
const HitTestInfo *hit_info = (HitTestInfo *) data;
|
|
|
|
const int resize_border = hit_info->resize_border;
|
2021-04-11 23:52:31 +02:00
|
|
|
const int controls_width = hit_info->controls_width;
|
2021-04-10 19:35:57 +02:00
|
|
|
int w, h;
|
|
|
|
|
|
|
|
SDL_GetWindowSize(window, &w, &h);
|
|
|
|
|
2021-04-12 11:54:52 +02:00
|
|
|
if (pt->y < hit_info->title_height &&
|
|
|
|
#if RESIZE_FROM_TOP
|
|
|
|
pt->y > hit_info->resize_border &&
|
|
|
|
#endif
|
2021-04-11 23:52:31 +02:00
|
|
|
pt->x > resize_border && pt->x < w - controls_width) {
|
2021-04-10 19:35:57 +02:00
|
|
|
return SDL_HITTEST_DRAGGABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define REPORT_RESIZE_HIT(name) { \
|
|
|
|
return SDL_HITTEST_RESIZE_##name; \
|
|
|
|
}
|
|
|
|
|
2021-04-11 15:08:25 +02:00
|
|
|
if (pt->x < resize_border && pt->y < resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(TOPLEFT);
|
2021-04-12 11:54:52 +02:00
|
|
|
#if RESIZE_FROM_TOP
|
2021-04-11 23:52:31 +02:00
|
|
|
} else if (pt->x > resize_border && pt->x < w - controls_width && pt->y < resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(TOP);
|
2021-04-12 11:54:52 +02:00
|
|
|
#endif
|
2021-04-11 15:08:25 +02:00
|
|
|
} else if (pt->x > w - resize_border && pt->y < resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(TOPRIGHT);
|
2021-04-12 11:54:52 +02:00
|
|
|
#if RESIZE_FROM_RIGHT
|
2021-04-11 15:08:25 +02:00
|
|
|
} else if (pt->x > w - resize_border && pt->y > resize_border && pt->y < h - resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(RIGHT);
|
2021-04-12 11:54:52 +02:00
|
|
|
#endif
|
2021-04-11 15:08:25 +02:00
|
|
|
} else if (pt->x > w - resize_border && pt->y > h - resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(BOTTOMRIGHT);
|
2021-04-11 15:08:25 +02:00
|
|
|
} else if (pt->x < w - resize_border && pt->x > resize_border && pt->y > h - resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(BOTTOM);
|
2021-04-11 15:08:25 +02:00
|
|
|
} else if (pt->x < resize_border && pt->y > h - resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(BOTTOMLEFT);
|
2021-04-11 15:08:25 +02:00
|
|
|
} else if (pt->x < resize_border && pt->y < h - resize_border && pt->y > resize_border) {
|
2021-04-10 19:35:57 +02:00
|
|
|
REPORT_RESIZE_HIT(LEFT);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SDL_HITTEST_NORMAL;
|
|
|
|
}
|
2019-12-28 12:16:32 +01:00
|
|
|
|
2021-09-17 22:38:09 +02:00
|
|
|
static const char *numpad[] = { "end", "down", "pagedown", "left", "", "right", "home", "up", "pageup", "ins", "delete" };
|
|
|
|
|
|
|
|
static const char *get_key_name(const SDL_Event *e, char *buf) {
|
|
|
|
SDL_Scancode scancode = e->key.keysym.scancode;
|
|
|
|
/* Is the scancode from the keypad and the number-lock off?
|
|
|
|
** We assume that SDL_SCANCODE_KP_1 up to SDL_SCANCODE_KP_9 and SDL_SCANCODE_KP_0
|
|
|
|
** and SDL_SCANCODE_KP_PERIOD are declared in SDL2 in that order. */
|
|
|
|
if (scancode >= SDL_SCANCODE_KP_1 && scancode <= SDL_SCANCODE_KP_1 + 10 &&
|
2021-09-19 18:42:36 +02:00
|
|
|
!(e->key.keysym.mod & KMOD_NUM)) {
|
2021-09-17 22:38:09 +02:00
|
|
|
return numpad[scancode - SDL_SCANCODE_KP_1];
|
|
|
|
} else {
|
2021-12-18 21:09:00 +01:00
|
|
|
/* We need to correctly handle non-standard layouts such as dvorak.
|
|
|
|
Therefore, if a Latin letter(code<128) is pressed in the current layout,
|
|
|
|
then we transmit it as it is. But we also need to support shortcuts in
|
2021-12-21 21:30:55 +01:00
|
|
|
other languages, so for non-Latin characters(code>128) we pass the
|
|
|
|
scancode based name that matches the letter in the QWERTY layout.
|
|
|
|
|
|
|
|
In SDL, the codes of all special buttons such as control, shift, arrows
|
2021-12-23 00:06:24 +01:00
|
|
|
and others, are masked with SDLK_SCANCODE_MASK, which moves them outside
|
|
|
|
the unicode range (>0x10FFFF). Users can remap these buttons, so we need
|
|
|
|
to return the correct name, not scancode based. */
|
2021-12-21 21:30:55 +01:00
|
|
|
if ((e->key.keysym.sym < 128) || (e->key.keysym.sym & SDLK_SCANCODE_MASK))
|
2021-12-18 20:24:28 +01:00
|
|
|
strcpy(buf, SDL_GetKeyName(e->key.keysym.sym));
|
|
|
|
else
|
|
|
|
strcpy(buf, SDL_GetScancodeName(scancode));
|
2021-09-17 22:38:09 +02:00
|
|
|
str_tolower(buf);
|
|
|
|
return buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-17 15:31:52 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
static char *win32_error(DWORD rc) {
|
|
|
|
LPSTR message;
|
|
|
|
FormatMessage(
|
|
|
|
FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
|
|
|
FORMAT_MESSAGE_FROM_SYSTEM |
|
|
|
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
|
|
NULL,
|
|
|
|
rc,
|
|
|
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
(LPTSTR) &message,
|
|
|
|
0,
|
|
|
|
NULL
|
|
|
|
);
|
|
|
|
|
|
|
|
return message;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void push_win32_error(lua_State *L, DWORD rc) {
|
|
|
|
LPSTR message = win32_error(rc);
|
|
|
|
lua_pushstring(L, message);
|
|
|
|
LocalFree(message);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static int f_poll_event(lua_State *L) {
|
|
|
|
char buf[16];
|
2022-12-20 09:30:58 +01:00
|
|
|
int mx, my, w, h;
|
2019-12-28 12:16:32 +01:00
|
|
|
SDL_Event e;
|
2022-12-20 09:30:58 +01:00
|
|
|
SDL_Event event_plus;
|
2019-12-28 12:16:32 +01:00
|
|
|
|
|
|
|
top:
|
|
|
|
if ( !SDL_PollEvent(&e) ) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (e.type) {
|
|
|
|
case SDL_QUIT:
|
|
|
|
lua_pushstring(L, "quit");
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
case SDL_WINDOWEVENT:
|
|
|
|
if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
|
2021-04-29 14:15:24 +02:00
|
|
|
ren_resize_window();
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_pushstring(L, "resized");
|
2021-04-24 10:21:34 +02:00
|
|
|
/* The size below will be in points. */
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, e.window.data1);
|
|
|
|
lua_pushinteger(L, e.window.data2);
|
2019-12-28 12:16:32 +01:00
|
|
|
return 3;
|
|
|
|
} else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) {
|
2020-05-22 10:00:48 +02:00
|
|
|
rencache_invalidate();
|
|
|
|
lua_pushstring(L, "exposed");
|
|
|
|
return 1;
|
2021-04-12 13:31:32 +02:00
|
|
|
} else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
|
|
|
|
lua_pushstring(L, "minimized");
|
|
|
|
return 1;
|
|
|
|
} else if (e.window.event == SDL_WINDOWEVENT_MAXIMIZED) {
|
|
|
|
lua_pushstring(L, "maximized");
|
|
|
|
return 1;
|
|
|
|
} else if (e.window.event == SDL_WINDOWEVENT_RESTORED) {
|
|
|
|
lua_pushstring(L, "restored");
|
|
|
|
return 1;
|
2022-04-29 03:50:34 +02:00
|
|
|
} else if (e.window.event == SDL_WINDOWEVENT_LEAVE) {
|
|
|
|
lua_pushstring(L, "mouseleft");
|
|
|
|
return 1;
|
2019-12-28 12:16:32 +01:00
|
|
|
}
|
2020-11-21 16:36:13 +01:00
|
|
|
if (e.window.event == SDL_WINDOWEVENT_FOCUS_LOST) {
|
|
|
|
lua_pushstring(L, "focuslost");
|
|
|
|
return 1;
|
|
|
|
}
|
2019-12-28 12:16:32 +01:00
|
|
|
/* on some systems, when alt-tabbing to the window SDL will queue up
|
|
|
|
** several KEYDOWN events for the `tab` key; we flush all keydown
|
|
|
|
** events on focus so these are discarded */
|
|
|
|
if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) {
|
|
|
|
SDL_FlushEvent(SDL_KEYDOWN);
|
|
|
|
}
|
|
|
|
goto top;
|
|
|
|
|
|
|
|
case SDL_DROPFILE:
|
2022-10-10 02:13:51 +02:00
|
|
|
SDL_GetMouseState(&mx, &my);
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_pushstring(L, "filedropped");
|
|
|
|
lua_pushstring(L, e.drop.file);
|
2022-10-10 02:13:51 +02:00
|
|
|
lua_pushinteger(L, mx);
|
|
|
|
lua_pushinteger(L, my);
|
2019-12-28 12:16:32 +01:00
|
|
|
SDL_free(e.drop.file);
|
2020-05-20 11:33:08 +02:00
|
|
|
return 4;
|
2019-12-28 12:16:32 +01:00
|
|
|
|
|
|
|
case SDL_KEYDOWN:
|
2021-06-03 22:14:50 +02:00
|
|
|
#ifdef __APPLE__
|
|
|
|
/* on macos 11.2.3 with sdl 2.0.14 the keyup handler for cmd+w below
|
|
|
|
** was not enough. Maybe the quit event started to be triggered from the
|
|
|
|
** keydown handler? In any case, flushing the quit event here too helped. */
|
|
|
|
if ((e.key.keysym.sym == SDLK_w) && (e.key.keysym.mod & KMOD_GUI)) {
|
|
|
|
SDL_FlushEvent(SDL_QUIT);
|
|
|
|
}
|
|
|
|
#endif
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_pushstring(L, "keypressed");
|
2021-09-17 22:38:09 +02:00
|
|
|
lua_pushstring(L, get_key_name(&e, buf));
|
2019-12-28 12:16:32 +01:00
|
|
|
return 2;
|
|
|
|
|
|
|
|
case SDL_KEYUP:
|
2021-04-21 09:51:40 +02:00
|
|
|
#ifdef __APPLE__
|
|
|
|
/* on macos command+w will close the current window
|
|
|
|
** we want to flush this event and let the keymapper
|
|
|
|
** handle this key combination.
|
|
|
|
** Thanks to mathewmariani, taken from his lite-macos github repository. */
|
2021-06-03 22:14:50 +02:00
|
|
|
if ((e.key.keysym.sym == SDLK_w) && (e.key.keysym.mod & KMOD_GUI)) {
|
2021-04-21 09:51:40 +02:00
|
|
|
SDL_FlushEvent(SDL_QUIT);
|
|
|
|
}
|
|
|
|
#endif
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_pushstring(L, "keyreleased");
|
2021-09-17 22:38:09 +02:00
|
|
|
lua_pushstring(L, get_key_name(&e, buf));
|
2019-12-28 12:16:32 +01:00
|
|
|
return 2;
|
|
|
|
|
|
|
|
case SDL_TEXTINPUT:
|
|
|
|
lua_pushstring(L, "textinput");
|
|
|
|
lua_pushstring(L, e.text.text);
|
|
|
|
return 2;
|
|
|
|
|
2022-10-16 01:58:51 +02:00
|
|
|
case SDL_TEXTEDITING:
|
|
|
|
lua_pushstring(L, "textediting");
|
|
|
|
lua_pushstring(L, e.edit.text);
|
|
|
|
lua_pushinteger(L, e.edit.start);
|
|
|
|
lua_pushinteger(L, e.edit.length);
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 22)
|
|
|
|
case SDL_TEXTEDITING_EXT:
|
|
|
|
lua_pushstring(L, "textediting");
|
|
|
|
lua_pushstring(L, e.editExt.text);
|
|
|
|
lua_pushinteger(L, e.editExt.start);
|
|
|
|
lua_pushinteger(L, e.editExt.length);
|
|
|
|
SDL_free(e.editExt.text);
|
|
|
|
return 4;
|
|
|
|
#endif
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
case SDL_MOUSEBUTTONDOWN:
|
|
|
|
if (e.button.button == 1) { SDL_CaptureMouse(1); }
|
|
|
|
lua_pushstring(L, "mousepressed");
|
|
|
|
lua_pushstring(L, button_name(e.button.button));
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, e.button.x);
|
|
|
|
lua_pushinteger(L, e.button.y);
|
|
|
|
lua_pushinteger(L, e.button.clicks);
|
2019-12-28 12:16:32 +01:00
|
|
|
return 5;
|
|
|
|
|
|
|
|
case SDL_MOUSEBUTTONUP:
|
|
|
|
if (e.button.button == 1) { SDL_CaptureMouse(0); }
|
|
|
|
lua_pushstring(L, "mousereleased");
|
|
|
|
lua_pushstring(L, button_name(e.button.button));
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, e.button.x);
|
|
|
|
lua_pushinteger(L, e.button.y);
|
2019-12-28 12:16:32 +01:00
|
|
|
return 4;
|
|
|
|
|
|
|
|
case SDL_MOUSEMOTION:
|
2021-06-09 23:37:03 +02:00
|
|
|
SDL_PumpEvents();
|
|
|
|
while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_MOUSEMOTION, SDL_MOUSEMOTION) > 0) {
|
|
|
|
e.motion.x = event_plus.motion.x;
|
|
|
|
e.motion.y = event_plus.motion.y;
|
|
|
|
e.motion.xrel += event_plus.motion.xrel;
|
|
|
|
e.motion.yrel += event_plus.motion.yrel;
|
|
|
|
}
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_pushstring(L, "mousemoved");
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, e.motion.x);
|
|
|
|
lua_pushinteger(L, e.motion.y);
|
|
|
|
lua_pushinteger(L, e.motion.xrel);
|
|
|
|
lua_pushinteger(L, e.motion.yrel);
|
2019-12-28 12:16:32 +01:00
|
|
|
return 5;
|
|
|
|
|
|
|
|
case SDL_MOUSEWHEEL:
|
|
|
|
lua_pushstring(L, "mousewheel");
|
2022-10-16 02:12:15 +02:00
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 18)
|
|
|
|
lua_pushnumber(L, e.wheel.preciseY);
|
|
|
|
// Use -x to keep consistency with vertical scrolling values (e.g. shift+scroll)
|
|
|
|
lua_pushnumber(L, -e.wheel.preciseX);
|
|
|
|
#else
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, e.wheel.y);
|
2022-10-16 02:12:15 +02:00
|
|
|
lua_pushinteger(L, -e.wheel.x);
|
|
|
|
#endif
|
|
|
|
return 3;
|
2019-12-28 12:16:32 +01:00
|
|
|
|
2022-12-20 09:30:58 +01:00
|
|
|
case SDL_FINGERDOWN:
|
|
|
|
SDL_GetWindowSize(window, &w, &h);
|
|
|
|
|
|
|
|
lua_pushstring(L, "touchpressed");
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h));
|
|
|
|
lua_pushinteger(L, e.tfinger.fingerId);
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case SDL_FINGERUP:
|
|
|
|
SDL_GetWindowSize(window, &w, &h);
|
|
|
|
|
|
|
|
lua_pushstring(L, "touchreleased");
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h));
|
|
|
|
lua_pushinteger(L, e.tfinger.fingerId);
|
|
|
|
return 4;
|
|
|
|
|
|
|
|
case SDL_FINGERMOTION:
|
|
|
|
SDL_PumpEvents();
|
|
|
|
while (SDL_PeepEvents(&event_plus, 1, SDL_GETEVENT, SDL_FINGERMOTION, SDL_FINGERMOTION) > 0) {
|
|
|
|
e.tfinger.x = event_plus.tfinger.x;
|
|
|
|
e.tfinger.y = event_plus.tfinger.y;
|
|
|
|
e.tfinger.dx += event_plus.tfinger.dx;
|
|
|
|
e.tfinger.dy += event_plus.tfinger.dy;
|
|
|
|
}
|
|
|
|
SDL_GetWindowSize(window, &w, &h);
|
|
|
|
|
|
|
|
lua_pushstring(L, "touchmoved");
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.x * w));
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.y * h));
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.dx * w));
|
|
|
|
lua_pushinteger(L, (lua_Integer)(e.tfinger.dy * h));
|
|
|
|
lua_pushinteger(L, e.tfinger.fingerId);
|
|
|
|
return 6;
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
default:
|
|
|
|
goto top;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-10 22:00:40 +02:00
|
|
|
static int f_wait_event(lua_State *L) {
|
2020-06-16 14:53:01 +02:00
|
|
|
int nargs = lua_gettop(L);
|
|
|
|
if (nargs >= 1) {
|
|
|
|
double n = luaL_checknumber(L, 1);
|
|
|
|
lua_pushboolean(L, SDL_WaitEventTimeout(NULL, n * 1000));
|
|
|
|
} else {
|
|
|
|
lua_pushboolean(L, SDL_WaitEvent(NULL));
|
|
|
|
}
|
2020-05-10 22:00:40 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_HAND + 1];
|
|
|
|
|
|
|
|
static const char *cursor_opts[] = {
|
|
|
|
"arrow",
|
|
|
|
"ibeam",
|
|
|
|
"sizeh",
|
|
|
|
"sizev",
|
|
|
|
"hand",
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int cursor_enums[] = {
|
|
|
|
SDL_SYSTEM_CURSOR_ARROW,
|
|
|
|
SDL_SYSTEM_CURSOR_IBEAM,
|
|
|
|
SDL_SYSTEM_CURSOR_SIZEWE,
|
|
|
|
SDL_SYSTEM_CURSOR_SIZENS,
|
|
|
|
SDL_SYSTEM_CURSOR_HAND
|
|
|
|
};
|
|
|
|
|
|
|
|
static int f_set_cursor(lua_State *L) {
|
|
|
|
int opt = luaL_checkoption(L, 1, "arrow", cursor_opts);
|
|
|
|
int n = cursor_enums[opt];
|
|
|
|
SDL_Cursor *cursor = cursor_cache[n];
|
|
|
|
if (!cursor) {
|
|
|
|
cursor = SDL_CreateSystemCursor(n);
|
|
|
|
cursor_cache[n] = cursor;
|
|
|
|
}
|
|
|
|
SDL_SetCursor(cursor);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int f_set_window_title(lua_State *L) {
|
|
|
|
const char *title = luaL_checkstring(L, 1);
|
|
|
|
SDL_SetWindowTitle(window, title);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-11 23:52:31 +02:00
|
|
|
static const char *window_opts[] = { "normal", "minimized", "maximized", "fullscreen", 0 };
|
|
|
|
enum { WIN_NORMAL, WIN_MINIMIZED, WIN_MAXIMIZED, WIN_FULLSCREEN };
|
2020-04-07 19:48:57 +02:00
|
|
|
|
|
|
|
static int f_set_window_mode(lua_State *L) {
|
|
|
|
int n = luaL_checkoption(L, 1, "normal", window_opts);
|
|
|
|
SDL_SetWindowFullscreen(window,
|
|
|
|
n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0);
|
|
|
|
if (n == WIN_NORMAL) { SDL_RestoreWindow(window); }
|
|
|
|
if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); }
|
2021-04-11 23:52:31 +02:00
|
|
|
if (n == WIN_MINIMIZED) { SDL_MinimizeWindow(window); }
|
2020-03-25 23:44:59 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-10 19:35:57 +02:00
|
|
|
static int f_set_window_bordered(lua_State *L) {
|
|
|
|
int bordered = lua_toboolean(L, 1);
|
|
|
|
SDL_SetWindowBordered(window, bordered);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int f_set_window_hit_test(lua_State *L) {
|
2021-04-12 19:05:30 +02:00
|
|
|
if (lua_gettop(L) == 0) {
|
|
|
|
SDL_SetWindowHitTest(window, NULL, NULL);
|
|
|
|
return 0;
|
|
|
|
}
|
2021-04-11 15:08:25 +02:00
|
|
|
window_hit_info->title_height = luaL_checknumber(L, 1);
|
2021-04-11 23:52:31 +02:00
|
|
|
window_hit_info->controls_width = luaL_checknumber(L, 2);
|
|
|
|
window_hit_info->resize_border = luaL_checknumber(L, 3);
|
2021-04-12 19:05:30 +02:00
|
|
|
SDL_SetWindowHitTest(window, hit_test, window_hit_info);
|
|
|
|
return 0;
|
2021-04-10 19:35:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-12-31 11:25:12 +01:00
|
|
|
static int f_get_window_size(lua_State *L) {
|
|
|
|
int x, y, w, h;
|
|
|
|
SDL_GetWindowSize(window, &w, &h);
|
|
|
|
SDL_GetWindowPosition(window, &x, &y);
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, w);
|
|
|
|
lua_pushinteger(L, h);
|
|
|
|
lua_pushinteger(L, x);
|
|
|
|
lua_pushinteger(L, y);
|
2020-12-31 11:25:12 +01:00
|
|
|
return 4;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int f_set_window_size(lua_State *L) {
|
|
|
|
double w = luaL_checknumber(L, 1);
|
|
|
|
double h = luaL_checknumber(L, 2);
|
|
|
|
double x = luaL_checknumber(L, 3);
|
|
|
|
double y = luaL_checknumber(L, 4);
|
|
|
|
SDL_SetWindowSize(window, w, h);
|
|
|
|
SDL_SetWindowPosition(window, x, y);
|
2021-04-29 14:15:24 +02:00
|
|
|
ren_resize_window();
|
2020-12-31 11:25:12 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static int f_window_has_focus(lua_State *L) {
|
|
|
|
unsigned flags = SDL_GetWindowFlags(window);
|
|
|
|
lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-04-12 11:54:52 +02:00
|
|
|
static int f_get_window_mode(lua_State *L) {
|
|
|
|
unsigned flags = SDL_GetWindowFlags(window);
|
|
|
|
if (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) {
|
|
|
|
lua_pushstring(L, "fullscreen");
|
|
|
|
} else if (flags & SDL_WINDOW_MINIMIZED) {
|
|
|
|
lua_pushstring(L, "minimized");
|
|
|
|
} else if (flags & SDL_WINDOW_MAXIMIZED) {
|
|
|
|
lua_pushstring(L, "maximized");
|
|
|
|
} else {
|
|
|
|
lua_pushstring(L, "normal");
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-10-16 01:58:51 +02:00
|
|
|
static int f_set_text_input_rect(lua_State *L) {
|
|
|
|
SDL_Rect rect;
|
|
|
|
rect.x = luaL_checknumber(L, 1);
|
|
|
|
rect.y = luaL_checknumber(L, 2);
|
|
|
|
rect.w = luaL_checknumber(L, 3);
|
|
|
|
rect.h = luaL_checknumber(L, 4);
|
|
|
|
SDL_SetTextInputRect(&rect);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int f_clear_ime(lua_State *L) {
|
|
|
|
#if SDL_VERSION_ATLEAST(2, 0, 22)
|
|
|
|
SDL_ClearComposition();
|
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-04-12 11:54:52 +02:00
|
|
|
|
2022-09-29 17:31:55 +02:00
|
|
|
static int f_raise_window(lua_State *L) {
|
|
|
|
/*
|
|
|
|
SDL_RaiseWindow should be enough but on some window managers like the
|
|
|
|
one used on Gnome the window needs to first have input focus in order
|
|
|
|
to allow the window to be focused. Also on wayland the raise window event
|
|
|
|
may not always be obeyed.
|
|
|
|
*/
|
|
|
|
SDL_SetWindowInputFocus(window);
|
|
|
|
SDL_RaiseWindow(window);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-05 10:33:50 +01:00
|
|
|
static int f_show_fatal_error(lua_State *L) {
|
|
|
|
const char *title = luaL_checkstring(L, 1);
|
|
|
|
const char *msg = luaL_checkstring(L, 2);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
MessageBox(0, msg, title, MB_OK | MB_ICONERROR);
|
|
|
|
#else
|
2022-12-20 23:36:18 +01:00
|
|
|
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, title, msg, NULL);
|
2021-03-05 10:33:50 +01:00
|
|
|
#endif
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-06-24 19:54:22 +02:00
|
|
|
// removes an empty directory
|
|
|
|
static int f_rmdir(lua_State *L) {
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2022-06-17 15:31:52 +02:00
|
|
|
LPWSTR wpath = utfconv_utf8towc(path);
|
|
|
|
int deleted = RemoveDirectoryW(wpath);
|
|
|
|
free(wpath);
|
|
|
|
if (deleted > 0) {
|
2021-06-24 19:54:22 +02:00
|
|
|
lua_pushboolean(L, 1);
|
|
|
|
} else {
|
|
|
|
lua_pushboolean(L, 0);
|
2022-06-17 15:31:52 +02:00
|
|
|
push_win32_error(L, GetLastError());
|
2021-06-24 19:54:22 +02:00
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
int deleted = remove(path);
|
|
|
|
if(deleted < 0) {
|
|
|
|
lua_pushboolean(L, 0);
|
|
|
|
lua_pushstring(L, strerror(errno));
|
|
|
|
|
|
|
|
return 2;
|
|
|
|
} else {
|
|
|
|
lua_pushboolean(L, 1);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-17 17:59:19 +02:00
|
|
|
static int f_chdir(lua_State *L) {
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
2022-06-17 15:31:52 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
LPWSTR wpath = utfconv_utf8towc(path);
|
|
|
|
if (wpath == NULL) { return luaL_error(L, UTFCONV_ERROR_INVALID_CONVERSION ); }
|
|
|
|
int err = _wchdir(wpath);
|
|
|
|
free(wpath);
|
|
|
|
#else
|
2020-05-17 17:59:19 +02:00
|
|
|
int err = chdir(path);
|
2022-06-17 15:31:52 +02:00
|
|
|
#endif
|
|
|
|
if (err) { luaL_error(L, "chdir() failed: %s", strerror(errno)); }
|
2020-05-17 17:59:19 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static int f_list_dir(lua_State *L) {
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
|
|
|
|
2022-06-17 15:31:52 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
lua_settop(L, 1);
|
2022-10-05 03:06:23 +02:00
|
|
|
if (path[0] == 0 || strchr("\\/", path[strlen(path) - 1]) != NULL)
|
2022-06-17 15:31:52 +02:00
|
|
|
lua_pushstring(L, "*");
|
|
|
|
else
|
|
|
|
lua_pushstring(L, "/*");
|
|
|
|
|
|
|
|
lua_concat(L, 2);
|
|
|
|
path = lua_tostring(L, -1);
|
|
|
|
|
|
|
|
LPWSTR wpath = utfconv_utf8towc(path);
|
|
|
|
if (wpath == NULL) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, UTFCONV_ERROR_INVALID_CONVERSION);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
WIN32_FIND_DATAW fd;
|
|
|
|
HANDLE find_handle = FindFirstFileExW(wpath, FindExInfoBasic, &fd, FindExSearchNameMatch, NULL, 0);
|
|
|
|
free(wpath);
|
|
|
|
if (find_handle == INVALID_HANDLE_VALUE) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
push_win32_error(L, GetLastError());
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
char mbpath[MAX_PATH * 4]; // utf-8 spans 4 bytes at most
|
|
|
|
int len, i = 1;
|
|
|
|
lua_newtable(L);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
if (wcscmp(fd.cFileName, L".") == 0) { continue; }
|
|
|
|
if (wcscmp(fd.cFileName, L"..") == 0) { continue; }
|
|
|
|
|
|
|
|
len = WideCharToMultiByte(CP_UTF8, 0, fd.cFileName, -1, mbpath, MAX_PATH * 4, NULL, NULL);
|
|
|
|
if (len == 0) { break; }
|
|
|
|
lua_pushlstring(L, mbpath, len - 1); // len includes \0
|
|
|
|
lua_rawseti(L, -2, i++);
|
|
|
|
} while (FindNextFileW(find_handle, &fd));
|
|
|
|
|
|
|
|
if (GetLastError() != ERROR_NO_MORE_FILES) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
push_win32_error(L, GetLastError());
|
|
|
|
FindClose(find_handle);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
FindClose(find_handle);
|
|
|
|
return 1;
|
|
|
|
#else
|
2019-12-28 12:16:32 +01:00
|
|
|
DIR *dir = opendir(path);
|
2020-04-22 00:33:04 +02:00
|
|
|
if (!dir) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, strerror(errno));
|
|
|
|
return 2;
|
|
|
|
}
|
2019-12-28 12:16:32 +01:00
|
|
|
|
|
|
|
lua_newtable(L);
|
|
|
|
int i = 1;
|
|
|
|
struct dirent *entry;
|
|
|
|
while ( (entry = readdir(dir)) ) {
|
|
|
|
if (strcmp(entry->d_name, "." ) == 0) { continue; }
|
|
|
|
if (strcmp(entry->d_name, "..") == 0) { continue; }
|
|
|
|
lua_pushstring(L, entry->d_name);
|
|
|
|
lua_rawseti(L, -2, i);
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
closedir(dir);
|
|
|
|
return 1;
|
2022-06-17 15:31:52 +02:00
|
|
|
#endif
|
2019-12-28 12:16:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2022-06-17 15:31:52 +02:00
|
|
|
#define realpath(x, y) _wfullpath(y, x, MAX_PATH)
|
2019-12-28 12:16:32 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
static int f_absolute_path(lua_State *L) {
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
2022-06-17 15:31:52 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
LPWSTR wpath = utfconv_utf8towc(path);
|
|
|
|
if (!wpath) { return 0; }
|
|
|
|
|
|
|
|
LPWSTR wfullpath = realpath(wpath, NULL);
|
|
|
|
free(wpath);
|
|
|
|
if (!wfullpath) { return 0; }
|
|
|
|
|
|
|
|
char *res = utfconv_wctoutf8(wfullpath);
|
|
|
|
free(wfullpath);
|
|
|
|
#else
|
2019-12-28 12:16:32 +01:00
|
|
|
char *res = realpath(path, NULL);
|
2022-06-17 15:31:52 +02:00
|
|
|
#endif
|
2019-12-28 12:16:32 +01:00
|
|
|
if (!res) { return 0; }
|
|
|
|
lua_pushstring(L, res);
|
|
|
|
free(res);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int f_get_file_info(lua_State *L) {
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
|
|
|
|
2022-06-17 15:31:52 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
struct _stat s;
|
|
|
|
LPWSTR wpath = utfconv_utf8towc(path);
|
|
|
|
if (wpath == NULL) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, UTFCONV_ERROR_INVALID_CONVERSION);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
int err = _wstat(wpath, &s);
|
|
|
|
free(wpath);
|
|
|
|
#else
|
2019-12-28 12:16:32 +01:00
|
|
|
struct stat s;
|
|
|
|
int err = stat(path, &s);
|
2022-06-17 15:31:52 +02:00
|
|
|
#endif
|
2020-04-22 00:33:04 +02:00
|
|
|
if (err < 0) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
lua_pushstring(L, strerror(errno));
|
|
|
|
return 2;
|
|
|
|
}
|
2019-12-28 12:16:32 +01:00
|
|
|
|
|
|
|
lua_newtable(L);
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, s.st_mtime);
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_setfield(L, -2, "modified");
|
|
|
|
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, s.st_size);
|
2019-12-28 12:16:32 +01:00
|
|
|
lua_setfield(L, -2, "size");
|
|
|
|
|
|
|
|
if (S_ISREG(s.st_mode)) {
|
|
|
|
lua_pushstring(L, "file");
|
|
|
|
} else if (S_ISDIR(s.st_mode)) {
|
|
|
|
lua_pushstring(L, "dir");
|
|
|
|
} else {
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
lua_setfield(L, -2, "type");
|
|
|
|
|
2022-01-10 16:26:39 +01:00
|
|
|
#if __linux__
|
|
|
|
if (S_ISDIR(s.st_mode)) {
|
|
|
|
if (lstat(path, &s) == 0) {
|
|
|
|
lua_pushboolean(L, S_ISLNK(s.st_mode));
|
|
|
|
lua_setfield(L, -2, "symlink");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2019-12-28 12:16:32 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2021-10-12 22:08:17 +02:00
|
|
|
#if __linux__
|
|
|
|
// https://man7.org/linux/man-pages/man2/statfs.2.html
|
|
|
|
|
|
|
|
struct f_type_names {
|
|
|
|
uint32_t magic;
|
|
|
|
const char *name;
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct f_type_names fs_names[] = {
|
|
|
|
{ 0xef53, "ext2/ext3" },
|
|
|
|
{ 0x6969, "nfs" },
|
|
|
|
{ 0x65735546, "fuse" },
|
|
|
|
{ 0x517b, "smb" },
|
|
|
|
{ 0xfe534d42, "smb2" },
|
|
|
|
{ 0x52654973, "reiserfs" },
|
|
|
|
{ 0x01021994, "tmpfs" },
|
|
|
|
{ 0x858458f6, "ramfs" },
|
|
|
|
{ 0x5346544e, "ntfs" },
|
|
|
|
{ 0x0, NULL },
|
|
|
|
};
|
|
|
|
|
2022-03-06 06:59:22 +01:00
|
|
|
#endif
|
|
|
|
|
2021-10-12 22:08:17 +02:00
|
|
|
static int f_get_fs_type(lua_State *L) {
|
2022-03-06 06:59:22 +01:00
|
|
|
#if __linux__
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
|
|
|
struct statfs buf;
|
|
|
|
int status = statfs(path, &buf);
|
|
|
|
if (status != 0) {
|
|
|
|
return luaL_error(L, "error calling statfs on %s", path);
|
2021-10-12 22:08:17 +02:00
|
|
|
}
|
2022-03-06 06:59:22 +01:00
|
|
|
for (int i = 0; fs_names[i].magic; i++) {
|
|
|
|
if (fs_names[i].magic == buf.f_type) {
|
|
|
|
lua_pushstring(L, fs_names[i].name);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2021-12-29 08:19:44 +01:00
|
|
|
lua_pushstring(L, "unknown");
|
|
|
|
return 1;
|
|
|
|
}
|
2021-10-12 22:08:17 +02:00
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
|
2020-12-02 16:03:31 +01:00
|
|
|
static int f_mkdir(lua_State *L) {
|
|
|
|
const char *path = luaL_checkstring(L, 1);
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
2022-06-17 15:31:52 +02:00
|
|
|
LPWSTR wpath = utfconv_utf8towc(path);
|
|
|
|
if (wpath == NULL) {
|
|
|
|
lua_pushboolean(L, 0);
|
|
|
|
lua_pushstring(L, UTFCONV_ERROR_INVALID_CONVERSION);
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int err = _wmkdir(wpath);
|
|
|
|
free(wpath);
|
2020-12-02 16:03:31 +01:00
|
|
|
#else
|
|
|
|
int err = mkdir(path, S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
|
|
|
|
#endif
|
|
|
|
if (err < 0) {
|
|
|
|
lua_pushboolean(L, 0);
|
|
|
|
lua_pushstring(L, strerror(errno));
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
lua_pushboolean(L, 1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static int f_get_clipboard(lua_State *L) {
|
|
|
|
char *text = SDL_GetClipboardText();
|
|
|
|
if (!text) { return 0; }
|
|
|
|
lua_pushstring(L, text);
|
|
|
|
SDL_free(text);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int f_set_clipboard(lua_State *L) {
|
|
|
|
const char *text = luaL_checkstring(L, 1);
|
|
|
|
SDL_SetClipboardText(text);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-02-04 03:20:42 +01:00
|
|
|
static int f_get_process_id(lua_State *L) {
|
|
|
|
#ifdef _WIN32
|
|
|
|
lua_pushinteger(L, GetCurrentProcessId());
|
|
|
|
#else
|
|
|
|
lua_pushinteger(L, getpid());
|
|
|
|
#endif
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static int f_get_time(lua_State *L) {
|
|
|
|
double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency();
|
2022-01-08 18:59:15 +01:00
|
|
|
lua_pushnumber(L, n);
|
2019-12-28 12:16:32 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int f_sleep(lua_State *L) {
|
|
|
|
double n = luaL_checknumber(L, 1);
|
|
|
|
SDL_Delay(n * 1000);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-05-13 21:32:53 +02:00
|
|
|
static int f_exec(lua_State *L) {
|
|
|
|
size_t len;
|
|
|
|
const char *cmd = luaL_checklstring(L, 1, &len);
|
2020-05-22 09:11:05 +02:00
|
|
|
char *buf = malloc(len + 32);
|
2020-05-13 21:32:53 +02:00
|
|
|
if (!buf) { luaL_error(L, "buffer allocation failed"); }
|
|
|
|
#if _WIN32
|
2020-05-22 09:11:05 +02:00
|
|
|
sprintf(buf, "cmd /c \"%s\"", cmd);
|
2020-05-13 21:32:53 +02:00
|
|
|
WinExec(buf, SW_HIDE);
|
|
|
|
#else
|
|
|
|
sprintf(buf, "%s &", cmd);
|
|
|
|
int res = system(buf);
|
|
|
|
(void) res;
|
|
|
|
#endif
|
|
|
|
free(buf);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
static int f_fuzzy_match(lua_State *L) {
|
2021-05-16 19:23:17 +02:00
|
|
|
size_t strLen, ptnLen;
|
|
|
|
const char *str = luaL_checklstring(L, 1, &strLen);
|
|
|
|
const char *ptn = luaL_checklstring(L, 2, &ptnLen);
|
2021-11-07 20:04:42 +01:00
|
|
|
// If true match things *backwards*. This allows for better matching on filenames than the above
|
2021-05-16 19:23:17 +02:00
|
|
|
// function. For example, in the lite project, opening "renderer" has lib/font_render/build.sh
|
|
|
|
// as the first result, rather than src/renderer.c. Clearly that's wrong.
|
2021-11-07 20:04:42 +01:00
|
|
|
bool files = lua_gettop(L) > 2 && lua_isboolean(L,3) && lua_toboolean(L, 3);
|
|
|
|
int score = 0, run = 0, increment = files ? -1 : 1;
|
|
|
|
const char* strTarget = files ? str + strLen - 1 : str;
|
|
|
|
const char* ptnTarget = files ? ptn + ptnLen - 1 : ptn;
|
2021-11-07 21:01:03 +01:00
|
|
|
while (strTarget >= str && ptnTarget >= ptn && *strTarget && *ptnTarget) {
|
|
|
|
while (strTarget >= str && *strTarget == ' ') { strTarget += increment; }
|
|
|
|
while (ptnTarget >= ptn && *ptnTarget == ' ') { ptnTarget += increment; }
|
2021-11-07 20:04:42 +01:00
|
|
|
if (tolower(*strTarget) == tolower(*ptnTarget)) {
|
|
|
|
score += run * 10 - (*strTarget != *ptnTarget);
|
|
|
|
run++;
|
|
|
|
ptnTarget += increment;
|
|
|
|
} else {
|
|
|
|
score -= 10;
|
|
|
|
run = 0;
|
2019-12-28 12:16:32 +01:00
|
|
|
}
|
2021-11-07 20:04:42 +01:00
|
|
|
strTarget += increment;
|
2019-12-28 12:16:32 +01:00
|
|
|
}
|
2021-11-07 21:01:03 +01:00
|
|
|
if (ptnTarget >= ptn && *ptnTarget) { return 0; }
|
2021-12-31 13:53:01 +01:00
|
|
|
lua_pushinteger(L, score - (int)strLen * 10);
|
2019-12-28 12:16:32 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2020-02-07 14:01:56 +01:00
|
|
|
static int f_set_window_opacity(lua_State *L) {
|
|
|
|
double n = luaL_checknumber(L, 1);
|
|
|
|
int r = SDL_SetWindowOpacity(window, n);
|
|
|
|
lua_pushboolean(L, r > -1);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-04-15 17:34:46 +02:00
|
|
|
typedef void (*fptr)(void);
|
|
|
|
|
2021-11-17 01:58:44 +01:00
|
|
|
typedef struct lua_function_node {
|
|
|
|
const char *symbol;
|
2022-04-15 17:34:46 +02:00
|
|
|
fptr address;
|
2021-11-17 01:58:44 +01:00
|
|
|
} lua_function_node;
|
|
|
|
|
2022-04-15 17:34:46 +02:00
|
|
|
#define P(FUNC) { "lua_" #FUNC, (fptr)(lua_##FUNC) }
|
|
|
|
#define U(FUNC) { "luaL_" #FUNC, (fptr)(luaL_##FUNC) }
|
2021-11-17 01:58:44 +01:00
|
|
|
static void* api_require(const char* symbol) {
|
2023-01-05 21:46:26 +01:00
|
|
|
static const lua_function_node nodes[] = {
|
2021-11-17 01:58:44 +01:00
|
|
|
P(atpanic), P(checkstack),
|
|
|
|
P(close), P(concat), P(copy), P(createtable), P(dump),
|
|
|
|
P(error), P(gc), P(getallocf), P(getfield),
|
|
|
|
P(gethook), P(gethookcount), P(gethookmask), P(getinfo), P(getlocal),
|
|
|
|
P(getmetatable), P(getstack), P(gettable), P(gettop), P(getupvalue),
|
2021-12-31 13:53:01 +01:00
|
|
|
P(isnumber), P(isstring), P(isuserdata),
|
|
|
|
P(load), P(newstate), P(newthread), P(next),
|
2021-11-17 01:58:44 +01:00
|
|
|
P(pushboolean), P(pushcclosure), P(pushfstring), P(pushinteger),
|
|
|
|
P(pushlightuserdata), P(pushlstring), P(pushnil), P(pushnumber),
|
|
|
|
P(pushstring), P(pushthread), P(pushvalue),
|
|
|
|
P(pushvfstring), P(rawequal), P(rawget), P(rawgeti),
|
2021-12-31 13:53:01 +01:00
|
|
|
P(rawset), P(rawseti), P(resume),
|
2021-11-17 01:58:44 +01:00
|
|
|
P(setallocf), P(setfield), P(sethook), P(setlocal),
|
|
|
|
P(setmetatable), P(settable), P(settop), P(setupvalue),
|
|
|
|
P(status), P(tocfunction), P(tointegerx), P(tolstring), P(toboolean),
|
|
|
|
P(tonumberx), P(topointer), P(tothread), P(touserdata),
|
|
|
|
P(type), P(typename), P(upvalueid), P(upvaluejoin), P(version), P(xmove),
|
|
|
|
U(getmetafield), U(callmeta), U(argerror), U(checknumber), U(optnumber),
|
|
|
|
U(checkinteger), U(checkstack), U(checktype), U(checkany),
|
|
|
|
U(newmetatable), U(setmetatable), U(testudata), U(checkudata), U(where),
|
|
|
|
U(error), U(fileresult), U(execresult), U(ref), U(unref), U(loadstring),
|
|
|
|
U(newstate), U(setfuncs), U(buffinit), U(addlstring), U(addstring),
|
2022-11-02 03:38:58 +01:00
|
|
|
U(addvalue), U(pushresult), U(openlibs), {"api_load_libs", (void*)(api_load_libs)},
|
2021-11-17 01:58:44 +01:00
|
|
|
#if LUA_VERSION_NUM >= 502
|
2021-12-31 13:53:01 +01:00
|
|
|
P(absindex), P(arith), P(callk), P(compare), P(getglobal),
|
|
|
|
P(len), P(pcallk), P(rawgetp), P(rawlen), P(rawsetp), P(setglobal),
|
|
|
|
P(iscfunction), P(yieldk),
|
|
|
|
U(checkversion_), U(tolstring), U(len), U(getsubtable), U(prepbuffsize),
|
2021-11-17 01:58:44 +01:00
|
|
|
U(pushresultsize), U(buffinitsize), U(checklstring), U(checkoption), U(gsub), U(loadbufferx),
|
2022-12-28 05:39:28 +01:00
|
|
|
U(loadfilex), U(optinteger), U(optlstring), U(requiref), U(traceback),
|
2021-11-17 01:58:44 +01:00
|
|
|
#else
|
2022-12-28 05:39:28 +01:00
|
|
|
P(objlen),
|
|
|
|
#endif
|
|
|
|
#if LUA_VERSION_NUM >= 504
|
|
|
|
P(newuserdatauv), P(setiuservalue), P(getiuservalue)
|
|
|
|
#else
|
|
|
|
P(newuserdata), P(setuservalue), P(getuservalue)
|
2021-11-17 01:58:44 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
};
|
2022-04-15 17:34:46 +02:00
|
|
|
for (size_t i = 0; i < sizeof(nodes) / sizeof(lua_function_node); ++i) {
|
2021-11-17 01:58:44 +01:00
|
|
|
if (strcmp(nodes[i].symbol, symbol) == 0)
|
2022-04-15 17:34:46 +02:00
|
|
|
return *(void**)(&nodes[i].address);
|
2021-11-17 01:58:44 +01:00
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2022-12-02 23:06:35 +01:00
|
|
|
static int f_library_gc(lua_State *L) {
|
|
|
|
lua_getfield(L, 1, "handle");
|
|
|
|
void* handle = lua_touserdata(L, -1);
|
|
|
|
SDL_UnloadObject(handle);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-09-25 04:31:15 +02:00
|
|
|
static int f_load_native_plugin(lua_State *L) {
|
|
|
|
char entrypoint_name[512]; entrypoint_name[sizeof(entrypoint_name) - 1] = '\0';
|
|
|
|
int result;
|
|
|
|
|
|
|
|
const char *name = luaL_checkstring(L, 1);
|
|
|
|
const char *path = luaL_checkstring(L, 2);
|
|
|
|
void *library = SDL_LoadObject(path);
|
|
|
|
if (!library)
|
2022-06-15 10:05:20 +02:00
|
|
|
return (lua_pushstring(L, SDL_GetError()), lua_error(L));
|
2021-09-25 04:31:15 +02:00
|
|
|
|
2021-09-21 05:42:39 +02:00
|
|
|
lua_getglobal(L, "package");
|
|
|
|
lua_getfield(L, -1, "native_plugins");
|
2022-12-02 23:06:35 +01:00
|
|
|
lua_newtable(L);
|
2021-09-14 05:40:01 +02:00
|
|
|
lua_pushlightuserdata(L, library);
|
2022-12-02 23:06:35 +01:00
|
|
|
lua_setfield(L, -2, "handle");
|
|
|
|
luaL_setmetatable(L, API_TYPE_NATIVE_PLUGIN);
|
2021-09-21 05:38:10 +02:00
|
|
|
lua_setfield(L, -2, name);
|
2022-12-02 23:06:35 +01:00
|
|
|
lua_pop(L, 2);
|
2021-09-25 04:31:15 +02:00
|
|
|
|
|
|
|
const char *basename = strrchr(name, '.');
|
|
|
|
basename = !basename ? name : basename + 1;
|
|
|
|
snprintf(entrypoint_name, sizeof(entrypoint_name), "luaopen_lite_xl_%s", basename);
|
2022-04-15 17:34:46 +02:00
|
|
|
int (*ext_entrypoint) (lua_State *L, void* (*)(const char*));
|
|
|
|
*(void**)(&ext_entrypoint) = SDL_LoadFunction(library, entrypoint_name);
|
2021-09-16 22:55:33 +02:00
|
|
|
if (!ext_entrypoint) {
|
2021-09-25 04:31:15 +02:00
|
|
|
snprintf(entrypoint_name, sizeof(entrypoint_name), "luaopen_%s", basename);
|
2022-04-15 17:34:46 +02:00
|
|
|
int (*entrypoint)(lua_State *L);
|
|
|
|
*(void**)(&entrypoint) = SDL_LoadFunction(library, entrypoint_name);
|
2021-09-25 04:31:15 +02:00
|
|
|
if (!entrypoint)
|
|
|
|
return luaL_error(L, "Unable to load %s: Can't find %s(lua_State *L, void *XL)", name, entrypoint_name);
|
|
|
|
result = entrypoint(L);
|
2021-09-16 22:55:33 +02:00
|
|
|
} else {
|
2021-09-25 04:31:15 +02:00
|
|
|
result = ext_entrypoint(L, api_require);
|
2021-09-16 22:55:33 +02:00
|
|
|
}
|
2021-09-25 04:31:15 +02:00
|
|
|
|
|
|
|
if (!result)
|
|
|
|
return luaL_error(L, "Unable to load %s: entrypoint must return a value", name);
|
|
|
|
|
|
|
|
return result;
|
2021-09-14 05:40:01 +02:00
|
|
|
}
|
|
|
|
|
2021-07-12 18:21:27 +02:00
|
|
|
#ifdef _WIN32
|
|
|
|
#define PATHSEP '\\'
|
|
|
|
#else
|
|
|
|
#define PATHSEP '/'
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Special purpose filepath compare function. Corresponds to the
|
|
|
|
order used in the TreeView view of the project's files. Returns true iff
|
|
|
|
path1 < path2 in the TreeView order. */
|
|
|
|
static int f_path_compare(lua_State *L) {
|
|
|
|
const char *path1 = luaL_checkstring(L, 1);
|
|
|
|
const char *type1_s = luaL_checkstring(L, 2);
|
|
|
|
const char *path2 = luaL_checkstring(L, 3);
|
|
|
|
const char *type2_s = luaL_checkstring(L, 4);
|
|
|
|
const int len1 = strlen(path1), len2 = strlen(path2);
|
|
|
|
int type1 = strcmp(type1_s, "dir") != 0;
|
|
|
|
int type2 = strcmp(type2_s, "dir") != 0;
|
|
|
|
/* Find the index of the common part of the path. */
|
|
|
|
int offset = 0, i;
|
|
|
|
for (i = 0; i < len1 && i < len2; i++) {
|
|
|
|
if (path1[i] != path2[i]) break;
|
|
|
|
if (path1[i] == PATHSEP) {
|
|
|
|
offset = i + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If a path separator is present in the name after the common part we consider
|
|
|
|
the entry like a directory. */
|
|
|
|
if (strchr(path1 + offset, PATHSEP)) {
|
|
|
|
type1 = 0;
|
|
|
|
}
|
|
|
|
if (strchr(path2 + offset, PATHSEP)) {
|
|
|
|
type2 = 0;
|
|
|
|
}
|
|
|
|
/* If types are different "dir" types comes before "file" types. */
|
|
|
|
if (type1 != type2) {
|
|
|
|
lua_pushboolean(L, type1 < type2);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
/* If types are the same compare the files' path alphabetically. */
|
|
|
|
int cfr = 0;
|
|
|
|
int len_min = (len1 < len2 ? len1 : len2);
|
|
|
|
for (int j = offset; j <= len_min; j++) {
|
|
|
|
if (path1[j] == path2[j]) continue;
|
|
|
|
if (path1[j] == 0 || path2[j] == 0) {
|
|
|
|
cfr = (path1[j] == 0);
|
|
|
|
} else if (path1[j] == PATHSEP || path2[j] == PATHSEP) {
|
|
|
|
/* For comparison we treat PATHSEP as if it was the string terminator. */
|
|
|
|
cfr = (path1[j] == PATHSEP);
|
|
|
|
} else {
|
|
|
|
cfr = (path1[j] < path2[j]);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
lua_pushboolean(L, cfr);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-12-28 12:16:32 +01:00
|
|
|
|
|
|
|
static const luaL_Reg lib[] = {
|
|
|
|
{ "poll_event", f_poll_event },
|
2020-05-10 22:00:40 +02:00
|
|
|
{ "wait_event", f_wait_event },
|
2019-12-28 12:16:32 +01:00
|
|
|
{ "set_cursor", f_set_cursor },
|
|
|
|
{ "set_window_title", f_set_window_title },
|
2020-04-07 19:48:57 +02:00
|
|
|
{ "set_window_mode", f_set_window_mode },
|
2021-04-12 11:54:52 +02:00
|
|
|
{ "get_window_mode", f_get_window_mode },
|
2021-04-10 19:35:57 +02:00
|
|
|
{ "set_window_bordered", f_set_window_bordered },
|
|
|
|
{ "set_window_hit_test", f_set_window_hit_test },
|
2020-12-31 11:25:12 +01:00
|
|
|
{ "get_window_size", f_get_window_size },
|
|
|
|
{ "set_window_size", f_set_window_size },
|
2022-10-16 01:58:51 +02:00
|
|
|
{ "set_text_input_rect", f_set_text_input_rect },
|
|
|
|
{ "clear_ime", f_clear_ime },
|
2019-12-28 12:16:32 +01:00
|
|
|
{ "window_has_focus", f_window_has_focus },
|
2022-09-29 17:31:55 +02:00
|
|
|
{ "raise_window", f_raise_window },
|
2021-03-05 10:33:50 +01:00
|
|
|
{ "show_fatal_error", f_show_fatal_error },
|
2021-06-24 19:54:22 +02:00
|
|
|
{ "rmdir", f_rmdir },
|
2020-05-17 17:59:19 +02:00
|
|
|
{ "chdir", f_chdir },
|
2020-12-02 16:03:31 +01:00
|
|
|
{ "mkdir", f_mkdir },
|
2019-12-28 12:16:32 +01:00
|
|
|
{ "list_dir", f_list_dir },
|
|
|
|
{ "absolute_path", f_absolute_path },
|
|
|
|
{ "get_file_info", f_get_file_info },
|
|
|
|
{ "get_clipboard", f_get_clipboard },
|
|
|
|
{ "set_clipboard", f_set_clipboard },
|
2022-02-04 03:20:42 +01:00
|
|
|
{ "get_process_id", f_get_process_id },
|
2019-12-28 12:16:32 +01:00
|
|
|
{ "get_time", f_get_time },
|
|
|
|
{ "sleep", f_sleep },
|
2020-05-13 21:32:53 +02:00
|
|
|
{ "exec", f_exec },
|
2019-12-28 12:16:32 +01:00
|
|
|
{ "fuzzy_match", f_fuzzy_match },
|
2020-02-07 14:01:56 +01:00
|
|
|
{ "set_window_opacity", f_set_window_opacity },
|
2021-09-16 18:37:17 +02:00
|
|
|
{ "load_native_plugin", f_load_native_plugin },
|
2021-07-12 18:21:27 +02:00
|
|
|
{ "path_compare", f_path_compare },
|
2021-10-12 22:08:17 +02:00
|
|
|
{ "get_fs_type", f_get_fs_type },
|
2019-12-28 12:16:32 +01:00
|
|
|
{ NULL, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
int luaopen_system(lua_State *L) {
|
2022-12-02 23:06:35 +01:00
|
|
|
luaL_newmetatable(L, API_TYPE_NATIVE_PLUGIN);
|
|
|
|
lua_pushcfunction(L, f_library_gc);
|
|
|
|
lua_setfield(L, -2, "__gc");
|
2019-12-28 12:16:32 +01:00
|
|
|
luaL_newlib(L, lib);
|
|
|
|
return 1;
|
|
|
|
}
|