diff options
author | Tomasz Kramkowski <tk@the-tk.com> | 2021-02-11 12:15:12 +0000 |
---|---|---|
committer | Tomasz Kramkowski <tk@the-tk.com> | 2021-02-11 14:25:14 +0000 |
commit | bb97e8aaa15217afe1f3fcdc93662ab03b8ae9d9 (patch) | |
tree | 64fa5b5ba419e72bc6acaddadbc51c73646295fa | |
download | luiml-bb97e8aaa15217afe1f3fcdc93662ab03b8ae9d9.tar.gz luiml-bb97e8aaa15217afe1f3fcdc93662ab03b8ae9d9.tar.xz luiml-bb97e8aaa15217afe1f3fcdc93662ab03b8ae9d9.zip |
init commit
-rwxr-xr-x | .compile.do | 13 | ||||
-rw-r--r-- | .gitignore | 8 | ||||
-rwxr-xr-x | .link-library.do | 12 | ||||
-rwxr-xr-x | .parse-depfile | 15 | ||||
-rw-r--r-- | README.md | 44 | ||||
-rwxr-xr-x | all.do | 2 | ||||
-rwxr-xr-x | clean | 4 | ||||
-rwxr-xr-x | configure | 94 | ||||
-rwxr-xr-x | default.o.do | 4 | ||||
-rw-r--r-- | libluiml.c | 18 | ||||
-rwxr-xr-x | libluiml.so.do | 4 | ||||
-rw-r--r-- | sdl.c | 533 | ||||
-rw-r--r-- | sdl.h | 14 | ||||
-rw-r--r-- | window.c | 179 | ||||
-rw-r--r-- | window.h | 6 | ||||
-rw-r--r-- | window_wait_event.c | 102 | ||||
-rw-r--r-- | xlua.c | 56 | ||||
-rw-r--r-- | xlua.h | 31 |
18 files changed, 1139 insertions, 0 deletions
diff --git a/.compile.do b/.compile.do new file mode 100755 index 0000000..310ddd5 --- /dev/null +++ b/.compile.do @@ -0,0 +1,13 @@ +#!/bin/bash -e +redo-ifchange .config.rc +. ./.config.rc +exec >"$3" +echo "# generated by $0" +echo "CC=$CC" +echo "CFLAGS=(${CFLAGS[@]@Q})" +echo "CPPFLAGS=(${CPPFLAGS[@]@Q})" +echo '"$CC" -MMD -MF "${1%.o}.d" "${CFLAGS[@]}" "${CPPFLAGS[@]}" -c -o "$3" "${1%.o}.c"' +command -v redo-stamp >/dev/null 2>&1 && redo-stamp <"$3" +if command -v redo-stamp >/dev/null 2>&1; then + redo-stamp <"$3" +fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..19f2438 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.d +*.o +*.so +.compile +.config.rc +.link-library +.redo +all diff --git a/.link-library.do b/.link-library.do new file mode 100755 index 0000000..4d7abdd --- /dev/null +++ b/.link-library.do @@ -0,0 +1,12 @@ +#!/bin/bash -e +redo-ifchange .config.rc +. ./.config.rc +exec >"$3" +echo "# generated by $0" +echo "CC=$CC" +echo "LDFLAGS=(${LDFLAGS[@]@Q})" +echo "LDLIBS=(${LDLIBS[@]@Q})" +echo '"$CC" "${LDFLAGS[@]}" "${objects[@]}" "${LDLIBS[@]}" -o "$3"' +if command -v redo-stamp >/dev/null 2>&1; then + redo-stamp <"$3" +fi diff --git a/.parse-depfile b/.parse-depfile new file mode 100755 index 0000000..cad20d9 --- /dev/null +++ b/.parse-depfile @@ -0,0 +1,15 @@ +#!/usr/bin/sed -f +# remove target +1s/^[^:]*: // +# collapse lines +:loop +/\\$/ { + N + s/ \\\n// + b loop +} +# split on unescaped spaces +s/\([^\]\) /\1\ +/g +# unescape spaces +s/\\ / /g diff --git a/README.md b/README.md new file mode 100644 index 0000000..a86107a --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# LUIML - Lua User Interface Markup Language + +This project is an early work in progress. + +The goal is to design a Lua library which turns a Lua data structure and +produces a GUI from it. + +This is similar to some ideas behind [IUP][iup] but LUIML aims to be more +functional and data driven. + +## Dependencies + +### Build + +* redo (works with [apenwarr redo][ap-redo] and [JdeBP redo][jdebp-redo]) +* bash +* pkg-config (or equivalent such as pkgconf) +* C compiler with C11 support (works with gcc and clang) +* sed +* xargs + +### Runtime + +* LuaJIT +* SDL2 +* PangoFT2 + +## Building + +```shell-session +$ ./configure +$ redo all +``` + +To avoid the creation of the "all" file when using JdeBP redo during development +run `redo libluiml.so`. + +## Installing + +This is not supported at the moment. + +[IUP]: http://webserver2.tecgraf.puc-rio.br/iup/ +[ap-redo]: https://redo.readthedocs.io/en/latest/ +[jdebp-redo]: https://jdebp.eu/Softwares/redo/ @@ -0,0 +1,2 @@ +#!/bin/sh +redo-ifchange libluiml.so @@ -0,0 +1,4 @@ +#!/bin/sh +find . \( -name '*.d' -o -name '*.o' -o -name '*.so' \) -delete +rm -f .compile +rm -f .link-library diff --git a/configure b/configure new file mode 100755 index 0000000..2fa53e6 --- /dev/null +++ b/configure @@ -0,0 +1,94 @@ +#!/bin/bash + +libs=(luajit sdl2 pangoft2) +CFLAGS+=(-std=c11 -fPIC) +LDFLAGS+=(-shared) + +usage () { + echo "Usage: $0 [-h|options...]" +} +help () { + echo "Options:" + echo " -B ldlib Append ldlib to LDLIBS" + echo " -C cflag Append cflag to CFLAGS" + echo " -L ldflag Append ldflag to LDFLAGS" + echo " -P cppflag Append cppflag to CPPFLAGS" + echo " -c when Enable compiler colours (always|auto|off) [default: auto]" + echo " -d Enable debugging flags" + echo " -e Enable -Werror" + echo " -h Show this help" + echo " -o Enable optimisation flags" + echo " -v Print results of configuration" + echo " -w Enable warning flags" + echo "Environment:" + echo " CC C compiler" + echo " PKG_CONFIG pkg-config" +} + +exists() { + command -v "$1" >/dev/null 2>&1 +} + +check() { + what=$1 + shift + for p do + if exists "$p"; then + echo "$p" + return + fi + done + echo "$what not set or found" >&2 + return 1 +} + +CC=$(check '$CC, cc, gcc or clang' "$CC" cc gcc clang) || exit +pc=$(check '$PKG_CONFIG, pkg-config or pkg-conf' "$PKG_CONFIG" pkg-config pkg-conf) || exit + +for l in "${libs[@]}"; do + "$pc" --print-errors --exists "$l" || exit +done +CFLAGS+=($("$pc" --cflags-only-other "${libs[@]}")) +CPPFLAGS+=($("$pc" --cflags-only-I "${libs[@]}")) +LDFLAGS+=($("$pc" --libs-only-L --libs-only-other "${libs[@]}")) +LDLIBS+=($("$pc" --libs-only-l "${libs[@]}")) + +colour=auto +while getopts B:C:L:P:c:dehovw opt; do + case $opt in + B) LDLIBS+=("$OPTARG");; + C) CFLAGS+=("$OPTARG");; + L) LDFLAGS+=("$OPTARG");; + P) CPPFLAGS+=("$OPTARG");; + c) colour="$OPTARG";; + d) + CFLAGS+=(-Og -g) + LDFLAGS+=(-Og -g) + ;; + e) CFLAGS+=(-Werror);; + h) usage; help; exit;; + o) + CFLAGS+=(-O2 -flto) + LDFLAGS+=(-O2 -flto) + ;; + v) verbose=1;; + w) CFLAGS+=(-Wall -Wextra);; + ?) usage >&2; exit 1;; + esac +done +if [[ $colour == auto ]] && exists tput && (( $(tput colors) >= 8 )); then + colour=always +fi +if [[ $colour == always ]]; then + CFLAGS+=(-fdiagnostics-color) +fi + +{ + echo "# generated using $0 $@" + echo "CC=$CC" + echo "CFLAGS=(${CFLAGS[@]@Q})" + echo "CPPFLAGS=(${CPPFLAGS[@]@Q})" + echo "LDFLAGS=(${LDFLAGS[@]@Q})" + echo "LDLIBS=(${LDLIBS[@]@Q})" +} >.config.rc +[[ $verbose ]] && cat .config.rc diff --git a/default.o.do b/default.o.do new file mode 100755 index 0000000..121c3c0 --- /dev/null +++ b/default.o.do @@ -0,0 +1,4 @@ +#!/bin/bash -e +redo-ifchange .compile .parse-depfile "${1%.o}.c" +. ./.compile +./.parse-depfile "${1%.o}.d" | tr '\n' '\0' | xargs -0 redo-ifchange diff --git a/libluiml.c b/libluiml.c new file mode 100644 index 0000000..468cc60 --- /dev/null +++ b/libluiml.c @@ -0,0 +1,18 @@ +#include <lauxlib.h> +#include <lua.h> + +#include "window.h" + +__attribute__((visibility ("default"))) +int luaopen_libluiml(lua_State *L) +{ + static const struct luaL_Reg libluiml[] = { + {"create_window", create_window}, + {NULL, NULL}, + }; + + lua_createtable(L, 0, 1); + luaL_register(L, NULL, libluiml); + + return 1; +} diff --git a/libluiml.so.do b/libluiml.so.do new file mode 100755 index 0000000..c5daa66 --- /dev/null +++ b/libluiml.so.do @@ -0,0 +1,4 @@ +#!/bin/bash -e +objects=(libluiml.o window.o sdl.o xlua.o) +redo-ifchange .link-library "${objects[@]}" +. ./.link-library @@ -0,0 +1,533 @@ +#include <SDL.h> +#include <lauxlib.h> + +#include "sdl.h" + +static unsigned long refcnt; + +int sdl_error(lua_State *L, const char *what) +{ + return luaL_error(L, "%s failed: %s", what, SDL_GetError()); +} + +void acquire_sdl() +{ + if (refcnt++ != 0) return; + + SDL_Init(SDL_INIT_VIDEO); + SDL_EnableScreenSaver(); +} + +void release_sdl() +{ + if (refcnt == 0 || --refcnt != 0) return; + + SDL_Quit(); +} + +const char *strbutton(Uint8 button) +{ + switch (button) { + case SDL_BUTTON_LEFT: return "LEFT"; + case SDL_BUTTON_MIDDLE: return "MIDDLE"; + case SDL_BUTTON_RIGHT: return "RIGHT"; + case SDL_BUTTON_X1: return "X1"; + case SDL_BUTTON_X2: return "X2"; + default: return "UNK"; + } +} + +const char *strscancode(SDL_Scancode s) +{ + switch (s) { + case SDL_SCANCODE_A: return "A"; + case SDL_SCANCODE_B: return "B"; + case SDL_SCANCODE_C: return "C"; + case SDL_SCANCODE_D: return "D"; + case SDL_SCANCODE_E: return "E"; + case SDL_SCANCODE_F: return "F"; + case SDL_SCANCODE_G: return "G"; + case SDL_SCANCODE_H: return "H"; + case SDL_SCANCODE_I: return "I"; + case SDL_SCANCODE_J: return "J"; + case SDL_SCANCODE_K: return "K"; + case SDL_SCANCODE_L: return "L"; + case SDL_SCANCODE_M: return "M"; + case SDL_SCANCODE_N: return "N"; + case SDL_SCANCODE_O: return "O"; + case SDL_SCANCODE_P: return "P"; + case SDL_SCANCODE_Q: return "Q"; + case SDL_SCANCODE_R: return "R"; + case SDL_SCANCODE_S: return "S"; + case SDL_SCANCODE_T: return "T"; + case SDL_SCANCODE_U: return "U"; + case SDL_SCANCODE_V: return "V"; + case SDL_SCANCODE_W: return "W"; + case SDL_SCANCODE_X: return "X"; + case SDL_SCANCODE_Y: return "Y"; + case SDL_SCANCODE_Z: return "Z"; + case SDL_SCANCODE_1: return "1"; + case SDL_SCANCODE_2: return "2"; + case SDL_SCANCODE_3: return "3"; + case SDL_SCANCODE_4: return "4"; + case SDL_SCANCODE_5: return "5"; + case SDL_SCANCODE_6: return "6"; + case SDL_SCANCODE_7: return "7"; + case SDL_SCANCODE_8: return "8"; + case SDL_SCANCODE_9: return "9"; + case SDL_SCANCODE_0: return "0"; + case SDL_SCANCODE_RETURN: return "RETURN"; + case SDL_SCANCODE_ESCAPE: return "ESCAPE"; + case SDL_SCANCODE_BACKSPACE: return "BACKSPACE"; + case SDL_SCANCODE_TAB: return "TAB"; + case SDL_SCANCODE_SPACE: return "SPACE"; + case SDL_SCANCODE_MINUS: return "MINUS"; + case SDL_SCANCODE_EQUALS: return "EQUALS"; + case SDL_SCANCODE_LEFTBRACKET: return "LEFTBRACKET"; + case SDL_SCANCODE_RIGHTBRACKET: return "RIGHTBRACKET"; + case SDL_SCANCODE_BACKSLASH: return "BACKSLASH"; + case SDL_SCANCODE_NONUSHASH: return "NONUSHASH"; + case SDL_SCANCODE_SEMICOLON: return "SEMICOLON"; + case SDL_SCANCODE_APOSTROPHE: return "APOSTROPHE"; + case SDL_SCANCODE_GRAVE: return "GRAVE"; + case SDL_SCANCODE_COMMA: return "COMMA"; + case SDL_SCANCODE_PERIOD: return "PERIOD"; + case SDL_SCANCODE_SLASH: return "SLASH"; + case SDL_SCANCODE_CAPSLOCK: return "CAPSLOCK"; + case SDL_SCANCODE_F1: return "F1"; + case SDL_SCANCODE_F2: return "F2"; + case SDL_SCANCODE_F3: return "F3"; + case SDL_SCANCODE_F4: return "F4"; + case SDL_SCANCODE_F5: return "F5"; + case SDL_SCANCODE_F6: return "F6"; + case SDL_SCANCODE_F7: return "F7"; + case SDL_SCANCODE_F8: return "F8"; + case SDL_SCANCODE_F9: return "F9"; + case SDL_SCANCODE_F10: return "F10"; + case SDL_SCANCODE_F11: return "F11"; + case SDL_SCANCODE_F12: return "F12"; + case SDL_SCANCODE_PRINTSCREEN: return "PRINTSCREEN"; + case SDL_SCANCODE_SCROLLLOCK: return "SCROLLLOCK"; + case SDL_SCANCODE_PAUSE: return "PAUSE"; + case SDL_SCANCODE_INSERT: return "INSERT"; + case SDL_SCANCODE_HOME: return "HOME"; + case SDL_SCANCODE_PAGEUP: return "PAGEUP"; + case SDL_SCANCODE_DELETE: return "DELETE"; + case SDL_SCANCODE_END: return "END"; + case SDL_SCANCODE_PAGEDOWN: return "PAGEDOWN"; + case SDL_SCANCODE_RIGHT: return "RIGHT"; + case SDL_SCANCODE_LEFT: return "LEFT"; + case SDL_SCANCODE_DOWN: return "DOWN"; + case SDL_SCANCODE_UP: return "UP"; + case SDL_SCANCODE_NUMLOCKCLEAR: return "NUMLOCKCLEAR"; + case SDL_SCANCODE_KP_DIVIDE: return "KP_DIVIDE"; + case SDL_SCANCODE_KP_MULTIPLY: return "KP_MULTIPLY"; + case SDL_SCANCODE_KP_MINUS: return "KP_MINUS"; + case SDL_SCANCODE_KP_PLUS: return "KP_PLUS"; + case SDL_SCANCODE_KP_ENTER: return "KP_ENTER"; + case SDL_SCANCODE_KP_1: return "KP_1"; + case SDL_SCANCODE_KP_2: return "KP_2"; + case SDL_SCANCODE_KP_3: return "KP_3"; + case SDL_SCANCODE_KP_4: return "KP_4"; + case SDL_SCANCODE_KP_5: return "KP_5"; + case SDL_SCANCODE_KP_6: return "KP_6"; + case SDL_SCANCODE_KP_7: return "KP_7"; + case SDL_SCANCODE_KP_8: return "KP_8"; + case SDL_SCANCODE_KP_9: return "KP_9"; + case SDL_SCANCODE_KP_0: return "KP_0"; + case SDL_SCANCODE_KP_PERIOD: return "KP_PERIOD"; + case SDL_SCANCODE_NONUSBACKSLASH: return "NONUSBACKSLASH"; + case SDL_SCANCODE_APPLICATION: return "APPLICATION"; + case SDL_SCANCODE_POWER: return "POWER"; + case SDL_SCANCODE_KP_EQUALS: return "KP_EQUALS"; + case SDL_SCANCODE_F13: return "F13"; + case SDL_SCANCODE_F14: return "F14"; + case SDL_SCANCODE_F15: return "F15"; + case SDL_SCANCODE_F16: return "F16"; + case SDL_SCANCODE_F17: return "F17"; + case SDL_SCANCODE_F18: return "F18"; + case SDL_SCANCODE_F19: return "F19"; + case SDL_SCANCODE_F20: return "F20"; + case SDL_SCANCODE_F21: return "F21"; + case SDL_SCANCODE_F22: return "F22"; + case SDL_SCANCODE_F23: return "F23"; + case SDL_SCANCODE_F24: return "F24"; + case SDL_SCANCODE_EXECUTE: return "EXECUTE"; + case SDL_SCANCODE_HELP: return "HELP"; + case SDL_SCANCODE_MENU: return "MENU"; + case SDL_SCANCODE_SELECT: return "SELECT"; + case SDL_SCANCODE_STOP: return "STOP"; + case SDL_SCANCODE_AGAIN: return "AGAIN"; + case SDL_SCANCODE_UNDO: return "UNDO"; + case SDL_SCANCODE_CUT: return "CUT"; + case SDL_SCANCODE_COPY: return "COPY"; + case SDL_SCANCODE_PASTE: return "PASTE"; + case SDL_SCANCODE_FIND: return "FIND"; + case SDL_SCANCODE_MUTE: return "MUTE"; + case SDL_SCANCODE_VOLUMEUP: return "VOLUMEUP"; + case SDL_SCANCODE_VOLUMEDOWN: return "VOLUMEDOWN"; + case SDL_SCANCODE_KP_COMMA: return "KP_COMMA"; + case SDL_SCANCODE_KP_EQUALSAS400: return "KP_EQUALSAS400"; + case SDL_SCANCODE_INTERNATIONAL1: return "INTERNATIONAL1"; + case SDL_SCANCODE_INTERNATIONAL2: return "INTERNATIONAL2"; + case SDL_SCANCODE_INTERNATIONAL3: return "INTERNATIONAL3"; + case SDL_SCANCODE_INTERNATIONAL4: return "INTERNATIONAL4"; + case SDL_SCANCODE_INTERNATIONAL5: return "INTERNATIONAL5"; + case SDL_SCANCODE_INTERNATIONAL6: return "INTERNATIONAL6"; + case SDL_SCANCODE_INTERNATIONAL7: return "INTERNATIONAL7"; + case SDL_SCANCODE_INTERNATIONAL8: return "INTERNATIONAL8"; + case SDL_SCANCODE_INTERNATIONAL9: return "INTERNATIONAL9"; + case SDL_SCANCODE_LANG1: return "LANG1"; + case SDL_SCANCODE_LANG2: return "LANG2"; + case SDL_SCANCODE_LANG3: return "LANG3"; + case SDL_SCANCODE_LANG4: return "LANG4"; + case SDL_SCANCODE_LANG5: return "LANG5"; + case SDL_SCANCODE_LANG6: return "LANG6"; + case SDL_SCANCODE_LANG7: return "LANG7"; + case SDL_SCANCODE_LANG8: return "LANG8"; + case SDL_SCANCODE_LANG9: return "LANG9"; + case SDL_SCANCODE_ALTERASE: return "ALTERASE"; + case SDL_SCANCODE_SYSREQ: return "SYSREQ"; + case SDL_SCANCODE_CANCEL: return "CANCEL"; + case SDL_SCANCODE_CLEAR: return "CLEAR"; + case SDL_SCANCODE_PRIOR: return "PRIOR"; + case SDL_SCANCODE_RETURN2: return "RETURN2"; + case SDL_SCANCODE_SEPARATOR: return "SEPARATOR"; + case SDL_SCANCODE_OUT: return "OUT"; + case SDL_SCANCODE_OPER: return "OPER"; + case SDL_SCANCODE_CLEARAGAIN: return "CLEARAGAIN"; + case SDL_SCANCODE_CRSEL: return "CRSEL"; + case SDL_SCANCODE_EXSEL: return "EXSEL"; + case SDL_SCANCODE_KP_00: return "KP_00"; + case SDL_SCANCODE_KP_000: return "KP_000"; + case SDL_SCANCODE_THOUSANDSSEPARATOR: return "THOUSANDSSEPARATOR"; + case SDL_SCANCODE_DECIMALSEPARATOR: return "DECIMALSEPARATOR"; + case SDL_SCANCODE_CURRENCYUNIT: return "CURRENCYUNIT"; + case SDL_SCANCODE_CURRENCYSUBUNIT: return "CURRENCYSUBUNIT"; + case SDL_SCANCODE_KP_LEFTPAREN: return "KP_LEFTPAREN"; + case SDL_SCANCODE_KP_RIGHTPAREN: return "KP_RIGHTPAREN"; + case SDL_SCANCODE_KP_LEFTBRACE: return "KP_LEFTBRACE"; + case SDL_SCANCODE_KP_RIGHTBRACE: return "KP_RIGHTBRACE"; + case SDL_SCANCODE_KP_TAB: return "KP_TAB"; + case SDL_SCANCODE_KP_BACKSPACE: return "KP_BACKSPACE"; + case SDL_SCANCODE_KP_A: return "KP_A"; + case SDL_SCANCODE_KP_B: return "KP_B"; + case SDL_SCANCODE_KP_C: return "KP_C"; + case SDL_SCANCODE_KP_D: return "KP_D"; + case SDL_SCANCODE_KP_E: return "KP_E"; + case SDL_SCANCODE_KP_F: return "KP_F"; + case SDL_SCANCODE_KP_XOR: return "KP_XOR"; + case SDL_SCANCODE_KP_POWER: return "KP_POWER"; + case SDL_SCANCODE_KP_PERCENT: return "KP_PERCENT"; + case SDL_SCANCODE_KP_LESS: return "KP_LESS"; + case SDL_SCANCODE_KP_GREATER: return "KP_GREATER"; + case SDL_SCANCODE_KP_AMPERSAND: return "KP_AMPERSAND"; + case SDL_SCANCODE_KP_DBLAMPERSAND: return "KP_DBLAMPERSAND"; + case SDL_SCANCODE_KP_VERTICALBAR: return "KP_VERTICALBAR"; + case SDL_SCANCODE_KP_DBLVERTICALBAR: return "KP_DBLVERTICALBAR"; + case SDL_SCANCODE_KP_COLON: return "KP_COLON"; + case SDL_SCANCODE_KP_HASH: return "KP_HASH"; + case SDL_SCANCODE_KP_SPACE: return "KP_SPACE"; + case SDL_SCANCODE_KP_AT: return "KP_AT"; + case SDL_SCANCODE_KP_EXCLAM: return "KP_EXCLAM"; + case SDL_SCANCODE_KP_MEMSTORE: return "KP_MEMSTORE"; + case SDL_SCANCODE_KP_MEMRECALL: return "KP_MEMRECALL"; + case SDL_SCANCODE_KP_MEMCLEAR: return "KP_MEMCLEAR"; + case SDL_SCANCODE_KP_MEMADD: return "KP_MEMADD"; + case SDL_SCANCODE_KP_MEMSUBTRACT: return "KP_MEMSUBTRACT"; + case SDL_SCANCODE_KP_MEMMULTIPLY: return "KP_MEMMULTIPLY"; + case SDL_SCANCODE_KP_MEMDIVIDE: return "KP_MEMDIVIDE"; + case SDL_SCANCODE_KP_PLUSMINUS: return "KP_PLUSMINUS"; + case SDL_SCANCODE_KP_CLEAR: return "KP_CLEAR"; + case SDL_SCANCODE_KP_CLEARENTRY: return "KP_CLEARENTRY"; + case SDL_SCANCODE_KP_BINARY: return "KP_BINARY"; + case SDL_SCANCODE_KP_OCTAL: return "KP_OCTAL"; + case SDL_SCANCODE_KP_DECIMAL: return "KP_DECIMAL"; + case SDL_SCANCODE_KP_HEXADECIMAL: return "KP_HEXADECIMAL"; + case SDL_SCANCODE_LCTRL: return "LCTRL"; + case SDL_SCANCODE_LSHIFT: return "LSHIFT"; + case SDL_SCANCODE_LALT: return "LALT"; + case SDL_SCANCODE_LGUI: return "LGUI"; + case SDL_SCANCODE_RCTRL: return "RCTRL"; + case SDL_SCANCODE_RSHIFT: return "RSHIFT"; + case SDL_SCANCODE_RALT: return "RALT"; + case SDL_SCANCODE_RGUI: return "RGUI"; + case SDL_SCANCODE_MODE: return "MODE"; + case SDL_SCANCODE_AUDIONEXT: return "AUDIONEXT"; + case SDL_SCANCODE_AUDIOPREV: return "AUDIOPREV"; + case SDL_SCANCODE_AUDIOSTOP: return "AUDIOSTOP"; + case SDL_SCANCODE_AUDIOPLAY: return "AUDIOPLAY"; + case SDL_SCANCODE_AUDIOMUTE: return "AUDIOMUTE"; + case SDL_SCANCODE_MEDIASELECT: return "MEDIASELECT"; + case SDL_SCANCODE_WWW: return "WWW"; + case SDL_SCANCODE_MAIL: return "MAIL"; + case SDL_SCANCODE_CALCULATOR: return "CALCULATOR"; + case SDL_SCANCODE_COMPUTER: return "COMPUTER"; + case SDL_SCANCODE_AC_SEARCH: return "AC_SEARCH"; + case SDL_SCANCODE_AC_HOME: return "AC_HOME"; + case SDL_SCANCODE_AC_BACK: return "AC_BACK"; + case SDL_SCANCODE_AC_FORWARD: return "AC_FORWARD"; + case SDL_SCANCODE_AC_STOP: return "AC_STOP"; + case SDL_SCANCODE_AC_REFRESH: return "AC_REFRESH"; + case SDL_SCANCODE_AC_BOOKMARKS: return "AC_BOOKMARKS"; + case SDL_SCANCODE_BRIGHTNESSDOWN: return "BRIGHTNESSDOWN"; + case SDL_SCANCODE_BRIGHTNESSUP: return "BRIGHTNESSUP"; + case SDL_SCANCODE_DISPLAYSWITCH: return "DISPLAYSWITCH"; + case SDL_SCANCODE_KBDILLUMTOGGLE: return "KBDILLUMTOGGLE"; + case SDL_SCANCODE_KBDILLUMDOWN: return "KBDILLUMDOWN"; + case SDL_SCANCODE_KBDILLUMUP: return "KBDILLUMUP"; + case SDL_SCANCODE_EJECT: return "EJECT"; + case SDL_SCANCODE_SLEEP: return "SLEEP"; + case SDL_SCANCODE_APP1: return "APP1"; + case SDL_SCANCODE_APP2: return "APP2"; + case SDL_SCANCODE_AUDIOREWIND: return "AUDIOREWIND"; + case SDL_SCANCODE_AUDIOFASTFORWARD: return "AUDIOFASTFORWARD"; + default: return "UNKNOWN"; + } +} + +const char *strsym(SDL_Keycode k) +{ + switch (k) { + case SDLK_RETURN: return "RETURN"; + case SDLK_ESCAPE: return "ESCAPE"; + case SDLK_BACKSPACE: return "BACKSPACE"; + case SDLK_TAB: return "TAB"; + case SDLK_SPACE: return "SPACE"; + case SDLK_EXCLAIM: return "EXCLAIM"; + case SDLK_QUOTEDBL: return "QUOTEDBL"; + case SDLK_HASH: return "HASH"; + case SDLK_PERCENT: return "PERCENT"; + case SDLK_DOLLAR: return "DOLLAR"; + case SDLK_AMPERSAND: return "AMPERSAND"; + case SDLK_QUOTE: return "QUOTE"; + case SDLK_LEFTPAREN: return "LEFTPAREN"; + case SDLK_RIGHTPAREN: return "RIGHTPAREN"; + case SDLK_ASTERISK: return "ASTERISK"; + case SDLK_PLUS: return "PLUS"; + case SDLK_COMMA: return "COMMA"; + case SDLK_MINUS: return "MINUS"; + case SDLK_PERIOD: return "PERIOD"; + case SDLK_SLASH: return "SLASH"; + case SDLK_0: return "0"; + case SDLK_1: return "1"; + case SDLK_2: return "2"; + case SDLK_3: return "3"; + case SDLK_4: return "4"; + case SDLK_5: return "5"; + case SDLK_6: return "6"; + case SDLK_7: return "7"; + case SDLK_8: return "8"; + case SDLK_9: return "9"; + case SDLK_COLON: return "COLON"; + case SDLK_SEMICOLON: return "SEMICOLON"; + case SDLK_LESS: return "LESS"; + case SDLK_EQUALS: return "EQUALS"; + case SDLK_GREATER: return "GREATER"; + case SDLK_QUESTION: return "QUESTION"; + case SDLK_AT: return "AT"; + case SDLK_LEFTBRACKET: return "LEFTBRACKET"; + case SDLK_BACKSLASH: return "BACKSLASH"; + case SDLK_RIGHTBRACKET: return "RIGHTBRACKET"; + case SDLK_CARET: return "CARET"; + case SDLK_UNDERSCORE: return "UNDERSCORE"; + case SDLK_BACKQUOTE: return "BACKQUOTE"; + case SDLK_a: return "a"; + case SDLK_b: return "b"; + case SDLK_c: return "c"; + case SDLK_d: return "d"; + case SDLK_e: return "e"; + case SDLK_f: return "f"; + case SDLK_g: return "g"; + case SDLK_h: return "h"; + case SDLK_i: return "i"; + case SDLK_j: return "j"; + case SDLK_k: return "k"; + case SDLK_l: return "l"; + case SDLK_m: return "m"; + case SDLK_n: return "n"; + case SDLK_o: return "o"; + case SDLK_p: return "p"; + case SDLK_q: return "q"; + case SDLK_r: return "r"; + case SDLK_s: return "s"; + case SDLK_t: return "t"; + case SDLK_u: return "u"; + case SDLK_v: return "v"; + case SDLK_w: return "w"; + case SDLK_x: return "x"; + case SDLK_y: return "y"; + case SDLK_z: return "z"; + case SDLK_CAPSLOCK: return "CAPSLOCK"; + case SDLK_F1: return "F1"; + case SDLK_F2: return "F2"; + case SDLK_F3: return "F3"; + case SDLK_F4: return "F4"; + case SDLK_F5: return "F5"; + case SDLK_F6: return "F6"; + case SDLK_F7: return "F7"; + case SDLK_F8: return "F8"; + case SDLK_F9: return "F9"; + case SDLK_F10: return "F10"; + case SDLK_F11: return "F11"; + case SDLK_F12: return "F12"; + case SDLK_PRINTSCREEN: return "PRINTSCREEN"; + case SDLK_SCROLLLOCK: return "SCROLLLOCK"; + case SDLK_PAUSE: return "PAUSE"; + case SDLK_INSERT: return "INSERT"; + case SDLK_HOME: return "HOME"; + case SDLK_PAGEUP: return "PAGEUP"; + case SDLK_DELETE: return "DELETE"; + case SDLK_END: return "END"; + case SDLK_PAGEDOWN: return "PAGEDOWN"; + case SDLK_RIGHT: return "RIGHT"; + case SDLK_LEFT: return "LEFT"; + case SDLK_DOWN: return "DOWN"; + case SDLK_UP: return "UP"; + case SDLK_NUMLOCKCLEAR: return "NUMLOCKCLEAR"; + case SDLK_KP_DIVIDE: return "KP_DIVIDE"; + case SDLK_KP_MULTIPLY: return "KP_MULTIPLY"; + case SDLK_KP_MINUS: return "KP_MINUS"; + case SDLK_KP_PLUS: return "KP_PLUS"; + case SDLK_KP_ENTER: return "KP_ENTER"; + case SDLK_KP_1: return "KP_1"; + case SDLK_KP_2: return "KP_2"; + case SDLK_KP_3: return "KP_3"; + case SDLK_KP_4: return "KP_4"; + case SDLK_KP_5: return "KP_5"; + case SDLK_KP_6: return "KP_6"; + case SDLK_KP_7: return "KP_7"; + case SDLK_KP_8: return "KP_8"; + case SDLK_KP_9: return "KP_9"; + case SDLK_KP_0: return "KP_0"; + case SDLK_KP_PERIOD: return "KP_PERIOD"; + case SDLK_APPLICATION: return "APPLICATION"; + case SDLK_POWER: return "POWER"; + case SDLK_KP_EQUALS: return "KP_EQUALS"; + case SDLK_F13: return "F13"; + case SDLK_F14: return "F14"; + case SDLK_F15: return "F15"; + case SDLK_F16: return "F16"; + case SDLK_F17: return "F17"; + case SDLK_F18: return "F18"; + case SDLK_F19: return "F19"; + case SDLK_F20: return "F20"; + case SDLK_F21: return "F21"; + case SDLK_F22: return "F22"; + case SDLK_F23: return "F23"; + case SDLK_F24: return "F24"; + case SDLK_EXECUTE: return "EXECUTE"; + case SDLK_HELP: return "HELP"; + case SDLK_MENU: return "MENU"; + case SDLK_SELECT: return "SELECT"; + case SDLK_STOP: return "STOP"; + case SDLK_AGAIN: return "AGAIN"; + case SDLK_UNDO: return "UNDO"; + case SDLK_CUT: return "CUT"; + case SDLK_COPY: return "COPY"; + case SDLK_PASTE: return "PASTE"; + case SDLK_FIND: return "FIND"; + case SDLK_MUTE: return "MUTE"; + case SDLK_VOLUMEUP: return "VOLUMEUP"; + case SDLK_VOLUMEDOWN: return "VOLUMEDOWN"; + case SDLK_KP_COMMA: return "KP_COMMA"; + case SDLK_KP_EQUALSAS400: return "KP_EQUALSAS400"; + case SDLK_ALTERASE: return "ALTERASE"; + case SDLK_SYSREQ: return "SYSREQ"; + case SDLK_CANCEL: return "CANCEL"; + case SDLK_CLEAR: return "CLEAR"; + case SDLK_PRIOR: return "PRIOR"; + case SDLK_RETURN2: return "RETURN2"; + case SDLK_SEPARATOR: return "SEPARATOR"; + case SDLK_OUT: return "OUT"; + case SDLK_OPER: return "OPER"; + case SDLK_CLEARAGAIN: return "CLEARAGAIN"; + case SDLK_CRSEL: return "CRSEL"; + case SDLK_EXSEL: return "EXSEL"; + case SDLK_KP_00: return "KP_00"; + case SDLK_KP_000: return "KP_000"; + case SDLK_THOUSANDSSEPARATOR: return "THOUSANDSSEPARATOR"; + case SDLK_DECIMALSEPARATOR: return "DECIMALSEPARATOR"; + case SDLK_CURRENCYUNIT: return "CURRENCYUNIT"; + case SDLK_CURRENCYSUBUNIT: return "CURRENCYSUBUNIT"; + case SDLK_KP_LEFTPAREN: return "KP_LEFTPAREN"; + case SDLK_KP_RIGHTPAREN: return "KP_RIGHTPAREN"; + case SDLK_KP_LEFTBRACE: return "KP_LEFTBRACE"; + case SDLK_KP_RIGHTBRACE: return "KP_RIGHTBRACE"; + case SDLK_KP_TAB: return "KP_TAB"; + case SDLK_KP_BACKSPACE: return "KP_BACKSPACE"; + case SDLK_KP_A: return "KP_A"; + case SDLK_KP_B: return "KP_B"; + case SDLK_KP_C: return "KP_C"; + case SDLK_KP_D: return "KP_D"; + case SDLK_KP_E: return "KP_E"; + case SDLK_KP_F: return "KP_F"; + case SDLK_KP_XOR: return "KP_XOR"; + case SDLK_KP_POWER: return "KP_POWER"; + case SDLK_KP_PERCENT: return "KP_PERCENT"; + case SDLK_KP_LESS: return "KP_LESS"; + case SDLK_KP_GREATER: return "KP_GREATER"; + case SDLK_KP_AMPERSAND: return "KP_AMPERSAND"; + case SDLK_KP_DBLAMPERSAND: return "KP_DBLAMPERSAND"; + case SDLK_KP_VERTICALBAR: return "KP_VERTICALBAR"; + case SDLK_KP_DBLVERTICALBAR: return "KP_DBLVERTICALBAR"; + case SDLK_KP_COLON: return "KP_COLON"; + case SDLK_KP_HASH: return "KP_HASH"; + case SDLK_KP_SPACE: return "KP_SPACE"; + case SDLK_KP_AT: return "KP_AT"; + case SDLK_KP_EXCLAM: return "KP_EXCLAM"; + case SDLK_KP_MEMSTORE: return "KP_MEMSTORE"; + case SDLK_KP_MEMRECALL: return "KP_MEMRECALL"; + case SDLK_KP_MEMCLEAR: return "KP_MEMCLEAR"; + case SDLK_KP_MEMADD: return "KP_MEMADD"; + case SDLK_KP_MEMSUBTRACT: return "KP_MEMSUBTRACT"; + case SDLK_KP_MEMMULTIPLY: return "KP_MEMMULTIPLY"; + case SDLK_KP_MEMDIVIDE: return "KP_MEMDIVIDE"; + case SDLK_KP_PLUSMINUS: return "KP_PLUSMINUS"; + case SDLK_KP_CLEAR: return "KP_CLEAR"; + case SDLK_KP_CLEARENTRY: return "KP_CLEARENTRY"; + case SDLK_KP_BINARY: return "KP_BINARY"; + case SDLK_KP_OCTAL: return "KP_OCTAL"; + case SDLK_KP_DECIMAL: return "KP_DECIMAL"; + case SDLK_KP_HEXADECIMAL: return "KP_HEXADECIMAL"; + case SDLK_LCTRL: return "LCTRL"; + case SDLK_LSHIFT: return "LSHIFT"; + case SDLK_LALT: return "LALT"; + case SDLK_LGUI: return "LGUI"; + case SDLK_RCTRL: return "RCTRL"; + case SDLK_RSHIFT: return "RSHIFT"; + case SDLK_RALT: return "RALT"; + case SDLK_RGUI: return "RGUI"; + case SDLK_MODE: return "MODE"; + case SDLK_AUDIONEXT: return "AUDIONEXT"; + case SDLK_AUDIOPREV: return "AUDIOPREV"; + case SDLK_AUDIOSTOP: return "AUDIOSTOP"; + case SDLK_AUDIOPLAY: return "AUDIOPLAY"; + case SDLK_AUDIOMUTE: return "AUDIOMUTE"; + case SDLK_MEDIASELECT: return "MEDIASELECT"; + case SDLK_WWW: return "WWW"; + case SDLK_MAIL: return "MAIL"; + case SDLK_CALCULATOR: return "CALCULATOR"; + case SDLK_COMPUTER: return "COMPUTER"; + case SDLK_AC_SEARCH: return "AC_SEARCH"; + case SDLK_AC_HOME: return "AC_HOME"; + case SDLK_AC_BACK: return "AC_BACK"; + case SDLK_AC_FORWARD: return "AC_FORWARD"; + case SDLK_AC_STOP: return "AC_STOP"; + case SDLK_AC_REFRESH: return "AC_REFRESH"; + case SDLK_AC_BOOKMARKS: return "AC_BOOKMARKS"; + case SDLK_BRIGHTNESSDOWN: return "BRIGHTNESSDOWN"; + case SDLK_BRIGHTNESSUP: return "BRIGHTNESSUP"; + case SDLK_DISPLAYSWITCH: return "DISPLAYSWITCH"; + case SDLK_KBDILLUMTOGGLE: return "KBDILLUMTOGGLE"; + case SDLK_KBDILLUMDOWN: return "KBDILLUMDOWN"; + case SDLK_KBDILLUMUP: return "KBDILLUMUP"; + case SDLK_EJECT: return "EJECT"; + case SDLK_SLEEP: return "SLEEP"; + case SDLK_APP1: return "APP1"; + case SDLK_APP2: return "APP2"; + case SDLK_AUDIOREWIND: return "AUDIOREWIND"; + case SDLK_AUDIOFASTFORWARD: return "AUDIOFASTFORWARD"; + default: return "UNKNOWN"; + } +} @@ -0,0 +1,14 @@ +#ifndef SDL_H +#define SDL_H + +#include <SDL.h> +#include <lua.h> + +int sdl_error(lua_State *L, const char *what); +void acquire_sdl(void); +void release_sdl(void); +const char *strbutton(Uint8 button); +const char *strscancode(SDL_Scancode s); +const char *strsym(SDL_Keycode k); + +#endif /* !SDL_H */ diff --git a/window.c b/window.c new file mode 100644 index 0000000..314d589 --- /dev/null +++ b/window.c @@ -0,0 +1,179 @@ +#include <SDL.h> +#include <glib-object.h> +#include <lauxlib.h> +#include <lua.h> +#include <pango/pangoft2.h> + +#include "window.h" +#include "sdl.h" + +#define UD_WINDOW "libluiml.window" + +struct window { + SDL_Window *win; + SDL_Renderer *ren; + PangoContext *pc; +}; + +/* window:destroy_window() */ +static int destroy_window(lua_State *L) +{ + struct window *w; + + w = luaL_checkudata(L, 1, UD_WINDOW); + SDL_DestroyWindow(w->win); + g_object_unref(w->pc); + + release_sdl(); + + return 0; +} + +#include "window_wait_event.c" + +/* window:get_dimensions() -> width, height */ +static int get_dimensions(lua_State *L) +{ + struct window *w; + int width, height; + + w = luaL_checkudata(L, 1, UD_WINDOW); + + SDL_GetWindowSize(w->win, &width, &height); + + lua_pushnumber(L, width); + lua_pushnumber(L, height); + + return 2; +} + +double clamp(double v, double min, double max) +{ + if (v < min) return min; + if (v > max) return max; + return v; +} + +/* window:draw_rect(x, y, width, height, r, g, b) */ +static int draw_rect(lua_State *L) +{ + struct window *w; + SDL_Rect r; + struct { + Uint8 r, g, b; + } colour; + + w = luaL_checkudata(L, 1, UD_WINDOW); + r.x = luaL_checknumber(L, 2); + r.y = luaL_checknumber(L, 3); + r.w = luaL_checknumber(L, 4); + r.h = luaL_checknumber(L, 5); + colour.r = round(clamp(luaL_checknumber(L, 6), 0, 1) * 255.0); + colour.g = round(clamp(luaL_checknumber(L, 7), 0, 1) * 255.0); + colour.b = round(clamp(luaL_checknumber(L, 8), 0, 1) * 255.0); + + SDL_SetRenderDrawColor(w->ren, colour.r, colour.g, colour.b, SDL_ALPHA_OPAQUE); + SDL_RenderFillRect(w->ren, &r); + + return 0; +} + +static int draw_text(lua_State *L) +{ + PangoRectangle extents; + PangoLayout *layout; + int width, height; + struct window *w; + const char *s; + SDL_Rect r; + size_t l; + + w = luaL_checkudata(L, 1, UD_WINDOW); + r.x = luaL_checknumber(L, 2); + r.y = luaL_checknumber(L, 3); + r.w = luaL_checknumber(L, 4); + r.h = luaL_checknumber(L, 5); + s = luaL_checklstring(L, 6, &l); + + layout = pango_layout_new(w->pc); + pango_layout_set_width(layout, r.w * PANGO_SCALE); + pango_layout_set_height(layout, r.h * PANGO_SCALE); + pango_layout_set_text(layout, s, l); + pango_layout_get_extents(layout, NULL, &extents); + width = PANGO_PIXELS(extents.width); + height = PANGO_PIXELS(extents.height); + + return 0; +} + +static int show(lua_State *L) +{ + struct window *w; + + w = luaL_checkudata(L, 1, UD_WINDOW); + + SDL_RenderPresent(w->ren); + + return 0; +} + +/* create_window(title) -> window */ +int create_window(lua_State *L) +{ + static const struct luaL_Reg window_metatable[] = { + {"__gc", destroy_window}, + {"wait_event", wait_event}, + {"get_dimensions", get_dimensions}, + {"draw_rect", draw_rect}, + {"draw_text", draw_text}, + {"show", show}, + {NULL, NULL}, + }; + PangoFontMap *fontmap; + const char *title, *failed; + struct window *w; + + if (luaL_newmetatable(L, UD_WINDOW)) { + lua_pushstring(L, "__index"); + lua_pushvalue(L, -2); + lua_settable(L, -3); + luaL_register(L, NULL, window_metatable); + lua_pop(L, 1); + } + + title = luaL_checkstring(L, 1); + + acquire_sdl(); + + w = lua_newuserdata(L, sizeof *w); + w->win = SDL_CreateWindow(title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + 640, 480, SDL_WINDOW_RESIZABLE); + + if (w->win == NULL) { + failed = "SDL_CreateWindow"; + goto fail_win; + } + + w->ren = SDL_CreateRenderer(w->win, 0, SDL_RENDERER_PRESENTVSYNC); + + if (w->ren == NULL) { + failed = "SDL_CreateRenderer"; + goto fail_ren; + } + + + fontmap = pango_ft2_font_map_new(); + w->pc = pango_font_map_create_context(fontmap); + g_object_unref(fontmap); + + luaL_getmetatable(L, UD_WINDOW); + lua_setmetatable(L, -2); + + return 1; + +fail_ren: SDL_DestroyWindow(w->win); +fail_win: release_sdl(); + return sdl_error(L, failed); +} diff --git a/window.h b/window.h new file mode 100644 index 0000000..3cde3cf --- /dev/null +++ b/window.h @@ -0,0 +1,6 @@ +#ifndef WINDOW_H +#define WINDOW_H + +int create_window(lua_State *L); + +#endif /* !WINDOW_H */ diff --git a/window_wait_event.c b/window_wait_event.c new file mode 100644 index 0000000..9c06d72 --- /dev/null +++ b/window_wait_event.c @@ -0,0 +1,102 @@ +#include "xlua.h" + +/* window:wait_event() -> event, data */ +static int wait_event(lua_State *L) +{ + static const struct key mousebutton_keys[] = { + KEY_STR(button, VT_STRING), + KEY_STR(down, VT_BOOLEAN), + KEY_STR(x, VT_NUMBER), + KEY_STR(y, VT_NUMBER), + KEY_END, + }; + static const struct key mousemotion_keys[] = { + KEY_STR(x, VT_NUMBER), + KEY_STR(y, VT_NUMBER), + KEY_STR(xrel, VT_NUMBER), + KEY_STR(yrel, VT_NUMBER), + KEY_END, + }; + static const struct key key_keys[] = { + KEY_STR(down, VT_BOOLEAN), + KEY_STR(repeat, VT_BOOLEAN), + KEY_END, + }; + static const struct key keysym_keys[] = { + KEY_STR(scancode, VT_STRING), + KEY_STR(sym, VT_STRING), + KEY_END, + }; + struct window *w; + SDL_Event e; + + w = luaL_checkudata(L, 1, UD_WINDOW); + + while (1) { + if (SDL_WaitEvent(&e) == 0) return sdl_error(L, "SDL_WaitEvent"); + + switch (e.type) { + case SDL_QUIT: + lua_pushstring(L, "QUIT"); + lua_pushnil(L); + break; + case SDL_WINDOWEVENT: + if (e.window.event != SDL_WINDOWEVENT_EXPOSED) + continue; + lua_pushstring(L, "REDRAW"); + lua_pushnil(L); + break; + case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: + lua_pushstring(L, "MOUSEBUTTON"); + pushtable(L, mousebutton_keys, + strbutton(e.button.button), + e.button.state == SDL_PRESSED, + (lua_Number)e.button.x, + (lua_Number)e.button.y); + break; + case SDL_MOUSEMOTION: + lua_pushstring(L, "MOUSEMOTION"); + pushtable(L, mousemotion_keys, + (lua_Number)e.motion.x, + (lua_Number)e.motion.y, + (lua_Number)e.motion.xrel, + (lua_Number)e.motion.yrel); + lua_pushstring(L, "state"); + lua_createtable(L, 0, 5); + pushsetmember(L, -1, "LEFT", e.motion.state & SDL_BUTTON_LMASK); + pushsetmember(L, -1, "MIDDLE", e.motion.state & SDL_BUTTON_MMASK); + pushsetmember(L, -1, "RIGHT", e.motion.state & SDL_BUTTON_RMASK); + pushsetmember(L, -1, "X1", e.motion.state & SDL_BUTTON_X1MASK); + pushsetmember(L, -1, "X2", e.motion.state & SDL_BUTTON_X2MASK); + lua_settable(L, -3); + break; + case SDL_KEYDOWN: case SDL_KEYUP: + lua_pushstring(L, "KEY"); + pushtable(L, key_keys, + e.key.state == SDL_PRESSED, + e.key.repeat); + lua_pushstring(L, "keysym"); + pushtable(L, keysym_keys, + strscancode(e.key.keysym.scancode), + strsym(e.key.keysym.sym)); + lua_pushstring(L, "mod"); + lua_createtable(L, 0, 11); + pushsetmember(L, -1, "LSHIFT", e.key.keysym.mod & KMOD_LSHIFT); + pushsetmember(L, -1, "RSHIFT", e.key.keysym.mod & KMOD_RSHIFT); + pushsetmember(L, -1, "LCTRL", e.key.keysym.mod & KMOD_LCTRL); + pushsetmember(L, -1, "RCTRL", e.key.keysym.mod & KMOD_RCTRL); + pushsetmember(L, -1, "LALT", e.key.keysym.mod & KMOD_LALT); + pushsetmember(L, -1, "RALT", e.key.keysym.mod & KMOD_RALT); + pushsetmember(L, -1, "LGUI", e.key.keysym.mod & KMOD_LGUI); + pushsetmember(L, -1, "RGUI", e.key.keysym.mod & KMOD_RGUI); + pushsetmember(L, -1, "NUM", e.key.keysym.mod & KMOD_NUM); + pushsetmember(L, -1, "CAPS", e.key.keysym.mod & KMOD_CAPS); + pushsetmember(L, -1, "MODE", e.key.keysym.mod & KMOD_MODE); + lua_settable(L, -3); + lua_settable(L, -3); + break; + default: continue; + } + return 2; + } +} @@ -0,0 +1,56 @@ +#include <lauxlib.h> +#include <lua.h> +#include <stdarg.h> +#include <stdbool.h> + +#include "xlua.h" + +void pushtable(lua_State *L, const struct key *keys, ...) +{ + va_list ap; + int narr = 0, nrec = 0; + + for (const struct key *key = keys; key->type != KT_NULL; key++) { + switch (key->type) { + case KT_STRING: nrec++; break; + case KT_NUMBER: narr++; break; + default: + luaL_error(L, "pusharray failed: key: invalid key type"); + } + } + + lua_createtable(L, narr, nrec); + + va_start(ap, keys); + + for (const struct key *key = keys; key->type != KT_NULL; key++) { + switch (key->type) { + case KT_STRING: lua_pushstring(L, key->key.str); break; + case KT_NUMBER: lua_pushnumber(L, key->key.num); break; + } + switch (key->vtype) { + case VT_STRING: + lua_pushstring(L, va_arg(ap, const char *)); + break; + case VT_NUMBER: + lua_pushnumber(L, va_arg(ap, lua_Number)); + break; + case VT_BOOLEAN: + lua_pushboolean(L, va_arg(ap, int)); + break; + default: + luaL_error(L, "pusharray failed: key: invalid value type"); + } + lua_settable(L, -3); + } + + va_end(ap); +} + +void pushsetmember(lua_State *L, int index, const char *name, bool in_set) +{ + if (!in_set) return; + if (index < 0) index -= 1; + lua_pushboolean(L, 1); + lua_setfield(L, index, name); +} @@ -0,0 +1,31 @@ +#ifndef XLUA_H +#define XLUA_H + +#include <stdbool.h> + +struct key { + enum key_type { + KT_NULL, + KT_STRING, + KT_NUMBER, + } type; + union { + const char *str; + double num; + } key; + enum value_type { + VT_STRING, + VT_NUMBER, + VT_BOOLEAN, + } vtype; +}; +#define _STR(s) #s +#define STR(s) _STR(s) +#define KEY_STR(k, vt) (struct key){ .type = KT_STRING, .key.str = _STR(k), .vtype = (vt)} +#define KEY_NUM(k, vt) (struct key){ .type = KT_NUMBER, .key.mum = (k), .vtype = (vt)} +#define KEY_END (struct key){ .type = KT_NULL } + +void pushtable(lua_State *L, const struct key *keys, ...); +void pushsetmember(lua_State *L, int index, const char *name, bool in_set); + +#endif /* !XLUA_H */ |