diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2c79fdb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,103 @@ +cmake_minimum_required(VERSION 3.10) +project(load81 C) + +# Set CMake to use C99 standard +set(CMAKE_C_STANDARD 99) + +# Find SDL and required SDL extensions +find_package(SDL REQUIRED) +find_library(SDL_IMAGE_LIBRARY SDL_image REQUIRED) +find_library(SDL_GFX_LIBRARY SDL_gfx REQUIRED) + + +# Include directories +include_directories( + ${SDL_INCLUDE_DIR} + ${SDL_GFX_INCLUDE_DIR} + ${SDL_IMAGE_INCLUDE_DIR} + ${SDL_MIXER_INCLUDE_DIR} + ${SDL_NET_INCLUDE_DIR} + ${SDL_RTF_INCLUDE_DIR} + ${SDL_SOUND_INCLUDE_DIR} + ${SDL_TTF_INCLUDE_DIR} + ${CMAKE_SOURCE_DIR}/lua/src +) + +# Platform-specific customizations +if(APPLE) + include_directories( + /usr/local/Cellar/sdl_gfx/2.0.27/include/SDL/ + /usr/local/Cellar/sdl_image/1.2.12_10/include/SDL/ + /usr/local/Cellar/sdl_mixer/1.2.12_6/include/SDL/ + /usr/local/Cellar/sdl_net/1.2.8_1/include/SDL/ + /usr/local/Cellar/sdl_rtf/0.1.0/include/SDL/ + /usr/local/Cellar/sdl_sound/1.0.3_2/include/SDL/ + /usr/local/Cellar/sdl_ttf/include/SDL) +endif() + +# Find SDL dependencies +#find_package(SDL_gfx REQUIRED) +#find_package(SDL_image REQUIRED) + + +# Compiler flags +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -Wall -W") + +# Source files +set(SOURCES + load81.c + editor.c + framebuffer.c +) + +# Lua library target +add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/lua/src/liblua.a + COMMAND ${CMAKE_MAKE_PROGRAM} ansi + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lua + COMMENT "Building Lua library" +) + +add_custom_target(liblua ALL + DEPENDS ${CMAKE_SOURCE_DIR}/lua/src/liblua.a +) + +# Add the library to the linker path +add_library(lua_lib STATIC IMPORTED) +set_target_properties(lua_lib PROPERTIES + IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/lua/src/liblua.a +) + +# Define the executable +add_executable(load81 ${SOURCES}) + +# Custom command to set macOS quarantine attribute +#add_custom_command( +# TARGET load81 POST_BUILD +# COMMAND bash -c "/usr/bin/xattr -w com.apple.quarantine \"0002;\$(printf %x \$(date +%s));load81;\$(/usr/bin/uuidgen)\" load81" +# COMMENT "Setting macOS quarantine attribute for load81" +#) + +# Link the libraries +target_link_libraries(load81 + lua_lib + m + ${SDL_LIBRARY} + ${SDL_GFX_LIBRARY} + ${SDL_IMAGE_LIBRARY} +) + +# Ensure liblua is built before load81 +add_dependencies(load81 liblua) + +# Add custom clean targets +add_custom_target(clean-lua + COMMAND ${CMAKE_MAKE_PROGRAM} clean + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lua +) + +add_custom_target(distclean + COMMAND ${CMAKE_BUILD_TOOL} clean + COMMAND ${CMAKE_MAKE_PROGRAM} clean + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/lua +) diff --git a/Makefile b/Makefile index a8864fc..a8e592b 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,13 @@ + PKGS=sdl SDL_gfx SDL_image -CFLAGS=-O2 -Wall -W -Ilua/src `pkg-config --cflags $(PKGS)` -LDLIBS=lua/src/liblua.a -lm `pkg-config --libs $(PKGS)` +CFLAGS=-O2 -Wall -W -Ilua/src `sdl-config --cflags` +LDLIBS=lua/src/liblua.a -lm `sdl-config --libs` -lSDL_gfx -lSDL_image + +# Customizations per-OS +UNAME := $(shell uname) +ifeq ($(UNAME), Darwin) +CFLAGS+=-I/usr/local/Cellar//sdl_gfx/2.0.25/include/SDL/ -I/usr/local/Cellar//sdl_image/1.2.12_3/include/SDL/ +endif all: load81 @@ -20,3 +27,4 @@ distclean: clean dep: $(CC) -MM *.c + diff --git a/README.md b/README.md index 04dcd67..844c349 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ PROGRAMMING INTERFACE Drawing functions: * fill(r,g,b,alpha): select the drawing color. +* filled(filled): set the filled state (true or false) * background(r,g,b): paint the whole background with the specified color. * rect(x,y,width,height): draw a rectangle at x,y (left-bottom corner). * ellipse(x,y,width,height): draw an ellipse centered at x,y. @@ -57,7 +58,22 @@ Drawing functions: * text(x,y,string): print the specified text at x,y using a bitmap font. * triangle(x1,y1,x2,y2,x3,y3): draw a triangle with the specified vertex. * getpixel(x,y): return the red,gree,blue value of the specified pixel. -* sprite(file,x,y,[rotation],[antialiasing]): draw sprite at coordinates with the specified rotation (in degrees, default 0) and antialiasing (default false). +* polygon(xv, yv): draw a polygon using a table of X values and a table of Y values. + +Sprite functions: + +* sprite(file,[x,y,[rotation],[antialiasing]]): draw sprite at coordinates with the specified rotation (in degrees, default 0) and antialiasing (default false). + +Returns a sprite userdata object, with the following functions + +* getHeight(): returns the height of the sprite. +* getWidth(): returns the height of the sprite. +* getTiles(): returns x,y for the number of tiles horizontally and vertically. +* setTiles(x,y): set the number of tiles horizontally and vertically. +* getTileSize(): return w,h for the size of a tile, calculated from the width and height of the image divided by the number of tiles horizontally and vertically. +* getTileNum(): returns the number of tiles. +* tile(x,y,tileNum,[rotation],[antialiasing]): draw a tile using tileNum at coordinates with the specified rotation (in degrees, default 0) and antialiasing (default: false). +* draw(x,y,[rotation],[antialiasing]): draw sprite at coordinates with the specified rotation (in degrees, default 0) and antialiasing (default: false). Control functions: @@ -106,6 +122,22 @@ if a button is pressed use: Mouse buttons are called '1', '2', '3', ... and so forth. +JOYSTICK EVENTS +=== +Joystick support is available by directly accessing the joystick[] table. +For example: + + joystick[1].x / joystick[1].y = X/Y position of Joystick1 + joystick[1].button = button state of Joystick1 + joystick[1].name = "hardware name of the Joystick1" + +joystick.count contains the number of joysticks detected by LOAD81 on +startup, and will be 0 if no joysticks are available. Max # of Joysticks +is currently set at 8. + +See examples/joysticks.lua and examples/flames.lua for how to use the +joystick. + LICENSE === diff --git a/contrib/Pandora/PND_Resources/defconf/profile.txt b/contrib/Pandora/PND_Resources/defconf/profile.txt index 185dc0b..0183bab 100644 --- a/contrib/Pandora/PND_Resources/defconf/profile.txt +++ b/contrib/Pandora/PND_Resources/defconf/profile.txt @@ -9,6 +9,7 @@ filepath=./examples/ exepath=./load81 --full extarg=--width;0;%na%;800 extarg=--height;0;%na%;480 +extarg=--bpp;0;%na%;0 extarg=;0;%na%;%filename% # Custom Entries Settings diff --git a/contrib/PocketCHIP/runner.sh b/contrib/PocketCHIP/runner.sh new file mode 100755 index 0000000..366f631 --- /dev/null +++ b/contrib/PocketCHIP/runner.sh @@ -0,0 +1,13 @@ +# runs load81, resetting it if any file-changes occur in the +# WATCHDIR, for example, the onboard examples/ .. any changes +# to the dir will re-load load81 .. +#!/bin/bash +export DISPLAY=:0 +WATCHDIR=./examples/ +inotifywait -m -e close_write $WATCHDIR | \ +while read -r notifile event filename ; \ +do \ + echo "notifile: $notifile event: $event filename: $filename" + killall load81 ; \ + ./load81 --width 480 --height 272 $notifile/$filename & \ +done diff --git a/contrib/debian_pocketchip/load81_0.0-2/DEBIAN/control b/contrib/debian_pocketchip/load81_0.0-2/DEBIAN/control new file mode 100644 index 0000000..dd3a844 --- /dev/null +++ b/contrib/debian_pocketchip/load81_0.0-2/DEBIAN/control @@ -0,0 +1,11 @@ +Package: load81 +Version: 0.1-1 +Section: base +Priority: optional +Architecture: armhf +Maintainer: seclorum +Depends: libsdl-gfx1.2-5 (>=1.2), libsdl-image1.2 (>=1.2) +Description: antirez' LOAD81 packaged for PocketCHIP. + antirez (http://github.com/antirez/load81) created the wonderful + load81 development environment, which provides an editor to + create small apps in the Lua programming language. diff --git a/contrib/debian_pocketchip/makedeb.sh b/contrib/debian_pocketchip/makedeb.sh new file mode 100644 index 0000000..656a61c --- /dev/null +++ b/contrib/debian_pocketchip/makedeb.sh @@ -0,0 +1 @@ +dpkg-deb --build load81_0.0-2/ diff --git a/editor.c b/editor.c index c3faf93..cd94343 100644 --- a/editor.c +++ b/editor.c @@ -392,7 +392,7 @@ void editorDrawCursor(void) { y -= E.margin_top; if (!(E.cblink & 0x80)) drawBox(E.fb,x+charmargin,y, x+charmargin+FONT_KERNING-1,y+FONT_HEIGHT-1, - 165,165,255,128); + 165,165,255,128,1); E.cblink += 4; } @@ -430,25 +430,25 @@ void editorDrawChars(void) { } void editorDrawPowerOff(int x, int y) { - drawEllipse(E.fb,x,y,12,12,66,66,231,255); - drawEllipse(E.fb,x,y,7,7,165,165,255,255); - drawBox(E.fb,x-4,y,x+4,y+12,165,165,255,255); - drawBox(E.fb,x-2,y,x+2,y+14,66,66,231,255); + drawEllipse(E.fb,x,y,12,12,66,66,231,255,1); + drawEllipse(E.fb,x,y,7,7,165,165,255,255,1); + drawBox(E.fb,x-4,y,x+4,y+12,165,165,255,255,1); + drawBox(E.fb,x-2,y,x+2,y+14,66,66,231,255,1); } void editorDrawSaveIcon(int x, int y) { - drawBox(E.fb,x-12,y-12,x+12,y+12,66,66,231,255); - drawBox(E.fb,x-1,y+7,x+1,y+11,165,165,255,255); - drawEllipse(E.fb,x,y,4,4,165,165,255,255); + drawBox(E.fb,x-12,y-12,x+12,y+12,66,66,231,255,1); + drawBox(E.fb,x-1,y+7,x+1,y+11,165,165,255,255,1); + drawEllipse(E.fb,x,y,4,4,165,165,255,255,1); } void editorDraw() { - drawBox(E.fb,0,0,E.fb->width-1,E.fb->height-1,165,165,255,255); + drawBox(E.fb,0,0,E.fb->width-1,E.fb->height-1,165,165,255,255,1); drawBox(E.fb, E.margin_left, E.margin_bottom, E.fb->width-1-E.margin_right, - E.fb->height-1-E.margin_top,66,66,231,255); + E.fb->height-1-E.margin_top,66,66,231,255,1); editorDrawChars(); editorDrawCursor(); /* Show buttons */ @@ -491,24 +491,38 @@ void editorMouseClicked(int x, int y, int button) { } else if (x >= E.margin_left && x <= E.fb->width-1-E.margin_right && y >= E.margin_bottom && y <= E.fb->height-1-E.margin_top) { - int realheight = E.fb->height - E.margin_top - E.margin_bottom; - int realy = y - E.margin_bottom; - int row = (realheight-realy)/FONT_HEIGHT; - int col = (x-E.margin_left)/FONT_KERNING; - int filerow = E.rowoff+row; - int filecol = E.coloff+col; - erow *r = (filerow >= E.numrows) ? NULL : &E.row[filerow]; - - E.cblink = 0; - if (filerow == E.numrows) { - E.cx = 0; - E.cy = filerow-E.rowoff; - } else if (r) { - if (filecol >= r->size) - E.cx = r->size-E.coloff; - else - E.cx = filecol-E.coloff; - E.cy = filerow-E.rowoff; + if (button == 4) { + if (E.rowoff) { + E.rowoff--; + if (E.cy < E.screenrows - 1) E.cy++; + } + } + else if (button == 5) { + if (E.rowoff + E.screenrows < E.numrows) { + E.rowoff++; + if (E.cy > 0) E.cy--; + } + } + else { + int realheight = E.fb->height - E.margin_top - E.margin_bottom; + int realy = y - E.margin_bottom; + int row = (realheight-realy)/FONT_HEIGHT; + int col = (x-E.margin_left)/FONT_KERNING; + int filerow = E.rowoff+row; + int filecol = E.coloff+col; + erow *r = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + + E.cblink = 0; + if (filerow == E.numrows) { + E.cx = 0; + E.cy = filerow-E.rowoff; + } else if (r) { + if (filecol >= r->size) + E.cx = r->size-E.coloff; + else + E.cx = filecol-E.coloff; + E.cy = filerow-E.rowoff; + } } } } @@ -518,6 +532,7 @@ void editorMoveCursor(int key) { int filecol = E.coloff+E.cx; int rowlen; erow *row = (filerow >= E.numrows) ? NULL : &E.row[filerow]; + int temp; switch(key) { case SDLK_LEFT: @@ -552,6 +567,61 @@ void editorMoveCursor(int key) { } } break; + case SDLK_PAGEUP: + if (E.rowoff) { + E.rowoff -= E.screenrows - 1; + if (E.rowoff < 0) { + E.rowoff = 0; + E.cy = 0; + } + } + else { + if (E.cy > 0) E.cy = 0; + } + break; + case SDLK_PAGEDOWN: + if (E.rowoff + E.screenrows - 1 < E.numrows) { + E.rowoff += E.screenrows - 1; + if (E.rowoff + E.screenrows - 1 > E.numrows) E.cy = E.numrows - E.rowoff - 1; + } + else { + E.cy = E.numrows - E.rowoff - 1; + } + break; + case SDLK_HOME: + if (E.modifiers & CTRL_MASK) { + E.rowoff = E.coloff = E.cy = E.cx = 0; + } + else { + if (row && filecol != 0) { + temp = getFirstNonSpace(row); + if (temp > -1) { + if (filecol > temp) { + E.cx = temp; + E.coloff = 0; + } + else { + E.cx = E.coloff = 0; + } + } + } + } + break; + case SDLK_END: + if (E.modifiers & CTRL_MASK) { + E.rowoff = E.numrows - E.screenrows; + E.cy = E.screenrows - 1; + E.coloff = E.cx = 0; + } + else { + if (row && filecol < row->size) { + if (row->size - E.screencols + 1 > 0) { + E.coloff = row->size - E.screencols + 1; + } + E.cx = row->size - E.coloff; + } + } + break; } /* Fix cx if the current line has not enough chars. */ filerow = E.rowoff+E.cy; @@ -567,6 +637,16 @@ void editorMoveCursor(int key) { } } +int getFirstNonSpace(erow *row) { + int i; + for (i = 0; i < row->size; i++) { + if (row->chars[i] != ' ' && row->chars[i] != '\t') { + return i; + } + } + return -1; +} + int editorEvents(void) { SDL_Event event; int j, ksym; @@ -596,6 +676,24 @@ int editorEvents(void) { E.key[ksym].counter = 1; E.key[ksym].translation = (event.key.keysym.unicode & 0xff); } + switch(ksym) { + case SDLK_LSHIFT: + case SDLK_RSHIFT: + E.modifiers |= SHIFT_MASK; + break; + case SDLK_LCTRL: + case SDLK_RCTRL: + E.modifiers |= CTRL_MASK; + break; + case SDLK_LALT: + case SDLK_RALT: + E.modifiers |= ALT_MASK; + break; + case SDLK_LMETA: + case SDLK_RMETA: + E.modifiers |= META_MASK; + break; + } break; } break; @@ -604,6 +702,24 @@ int editorEvents(void) { case SDL_KEYUP: ksym = event.key.keysym.sym; if (ksym >= 0 && ksym < KEY_MAX) E.key[ksym].counter = 0; + switch(ksym) { + case SDLK_LSHIFT: + case SDLK_RSHIFT: + E.modifiers &= ~SHIFT_MASK; + break; + case SDLK_LCTRL: + case SDLK_RCTRL: + E.modifiers &= ~CTRL_MASK; + break; + case SDLK_LALT: + case SDLK_RALT: + E.modifiers &= ~ALT_MASK; + break; + case SDLK_LMETA: + case SDLK_RMETA: + E.modifiers &= ~META_MASK; + break; + } break; /* Mouse click */ case SDL_MOUSEBUTTONDOWN: @@ -628,6 +744,10 @@ int editorEvents(void) { case SDLK_RIGHT: case SDLK_UP: case SDLK_DOWN: + case SDLK_PAGEUP: + case SDLK_PAGEDOWN: + case SDLK_HOME: + case SDLK_END: editorMoveCursor(j); break; case SDLK_BACKSPACE: @@ -636,7 +756,6 @@ int editorEvents(void) { case SDLK_RETURN: editorInsertNewline(); break; - case SDLK_HOME: case SDLK_LSHIFT: case SDLK_RSHIFT: case SDLK_LCTRL: @@ -721,4 +840,5 @@ void initEditor(frameBuffer *fb, int mt, int mb, int ml, int mr) { E.dirty = 0; E.filename = NULL; memset(E.key,0,sizeof(E.key)); + E.modifiers = 0; } diff --git a/editor.h b/editor.h index cfa6fe3..739a2fb 100644 --- a/editor.h +++ b/editor.h @@ -28,6 +28,12 @@ #define HL_FUNCDEF_COLOR {255,255,255} #define HL_LIB_COLOR {255,0,255} +/* Key Held Modifier Bit Masks */ +#define CTRL_MASK (1<<0) +#define SHIFT_MASK (1<<1) +#define ALT_MASK (1<<2) +#define META_MASK (1<<3) + typedef struct erow { int size; /* Size of the row, excluding the null term. */ char *chars; /* Row content. */ @@ -56,6 +62,7 @@ struct editorConfig { erow *row; /* Rows */ time_t lastevent; /* Last event time, so we can go standby */ keyState key[KEY_MAX]; /* Remember if a key is pressed / repeated. */ + unsigned int modifiers; /* Key modifiers held. CTRL & SHIFT & ALT & META */ int dirty; /* File modified but not saved. */ char *filename; /* Currently open filename */ frameBuffer *fb; /* Framebuffer */ @@ -76,4 +83,6 @@ void editorClearError(void); int editorFileWasModified(void); void editorRun(void); +int getFirstNonSpace(erow *row); + #endif /* EDITOR_H */ diff --git a/examples/filelist.lua b/examples/filelist.lua new file mode 100644 index 0000000..2a78878 --- /dev/null +++ b/examples/filelist.lua @@ -0,0 +1,36 @@ + +listed = false + + +function setup() + fileList = {} + print("current working dir: " .. lfs.currentdir()) + for file in lfs.dir[[./examples/]] do + if lfs.attributes(file,"mode") == "file" then + print("found file, "..file) + elseif lfs.attributes(file,"mode")== "directory" then + if (file == ".") then + print("found dir, "..file," containing:") + for l in lfs.dir("./examples/"..file) do + if (l~="." and l~="..") then + print("",l) + -- table.insert(fileList, file) + end + end + end + end + end +end + + +function draw() + if (listed == false) then + for iFile,aFile in ipairs(fileList) do + print("a File:"..iFile) + end + listed = true + else print("Listed?") + end +end + + diff --git a/examples/flames.lua b/examples/flames.lua index f2ff89c..a9e752f 100644 --- a/examples/flames.lua +++ b/examples/flames.lua @@ -10,6 +10,7 @@ function setup() refreshCount = 0 skipCount = 0 Flames = { } + filled(false) for i=1,MaxFlames do x = math.random(WIDTH/3) + (WIDTH/3) @@ -20,15 +21,34 @@ function setup() end end +-- can't find this in .math, but lets have it here for later transformation anyway .. +function map(x, in_min, in_max, out_min, out_max) + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +end + function draw() - local i, f, minMove + local i, f, minMove; + -- user can move joystick to get different flame effects + local joyLengths = { 5, 15, 20, 45, 60, 95, 140, 170, 200}; + local joyFlames = { 5, 10, 15, 30, 60, 75, 90, 150 }; background(0,0,0) + + -- ahem .. change properties of the flame according to joystick input + if joystick.count ~= 0 then + FlameSize = joyLengths[math.floor(map (joystick[1].x, 32767, -32767, 1, #joyLengths) + .5)] + FlameLife = joyFlames[math.floor(map (joystick[1].y, 32767, -32767, 1, #joyFlames) + .5)] + end + + ellipse(dot_x, dot_y, 30, 20); + for i,f in pairs(Flames) do if f.l > 35 then + filled(true) fill(255, 255, 255, 0.9) minMove = 0 elseif f.l > 30 then + filled(false) fill(255, 255, 192, 0.8) minMove = 1 elseif f.l > 20 then diff --git a/examples/gridify.lua b/examples/gridify.lua new file mode 100644 index 0000000..8447768 --- /dev/null +++ b/examples/gridify.lua @@ -0,0 +1,170 @@ + -- +-- draw some grids +-- +screen_width = WIDTH +screen_height = HEIGHT +screen_w_center = screen_width / 2 +screen_h_center = screen_height / 2 + +colors = {} +colors = { {r=255, g=0, b=255, a=1}, + {r=0, g=0, b=255, a=1}, + {r=255, g=0, b=0, a=1}, + {r=0, g=255, b=0, a=1}, + {r=27, g=73, b=100, a=1}, + {r=0, g=51, b=75, a=1}, + {r=100, g=35, b=15, a=1}, + {r=200, g=135, b=150, a=1}, + {r=100, g=0, b=0, a=1}, + {r=100, g=50, b=0, a=1}, + {r=153, g=150, b=50, a=1}, + {r=25, g=102, b=0, a=1}, + {r=25, g=102, b=0, a=1}, + {r=80, g=500, b=200, a=1} } + + +-- dunno where we should get this from .. math.?? +function map(x, in_min, in_max, out_min, out_max) + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +end + +function setup() + background(0,0,0,0) + cell_size = HEIGHT / 4 + fill (27,73,100,1) + background(0,0,0,0) + text(10, 10, string.format("gridify, by torpor")) +end + +function draw_XY_grid() + for i = 0, screen_width, 10 do + for screen_w_center = 0, screen_height do + if (((i % 50) == 0) and ((screen_w_center % 50) == 0)) then + + fill(0, 51, 75, 1) + text(i+4, screen_w_center-1, tostring(i)) + + fill(100, 35, 15, 1) + text( i+4, screen_w_center+8, tostring(i)) -- padding for y direction + rect(i, screen_w_center, 4, 4) + + end + + if ((screen_w_center % 10) == 0) then + + fill(200, 135, 150, 1) + rect(i,screen_w_center,1,1) + + end + end + end +end + +function draw_centered_grid() + + fill(100,0,0,1) + rect(screen_w_center, screen_h_center, 4, 4) + + fill (100,50,0,1) + --fill (153,150,50) + line(0, screen_h_center, screen_width, screen_h_center) + line(screen_w_center, 0, screen_w_center, screen_height) + + for z = screen_w_center, screen_width, 10 do + if (((z - screen_w_center) % 50) == 0) then + fill (25,102,0,1) + ellipse(z, screen_h_center, 6, 6) + --text(z - 8, screen_h_center + 16 ,tostring(z - screen_w_center)) -- padding + end + end + + for z = screen_w_center, 0, -10 do + if (((z - screen_w_center) % 50) == 0) then + fill (25,102,0,1) + ellipse(z, screen_h_center, 6, 6) + --text(z - 8, screen_h_center + 16, tostring(z - screen_w_center)) -- padding + end + end + + for z = screen_h_center, screen_height, 10 do + if (((z - screen_h_center) % 50) == 0) then + + ellipse(screen_w_center, z, 6, 6) + --text(screen_w_center - 8, z + 16, tostring(z - screen_h_center)) -- padding + end + end + + for z = screen_h_center, 0, -10 do + if (((z - screen_h_center) % 50) == 0) then + --text(screen_w_center - 8, z + 16, tostring(z - screen_h_center)) -- padding + end + end +end + +--[[ +// This draws the 'percentage' scale for the centered grid, using +// the largest axes as the base to determine the appropriate +// interval +]] +function draw_percentile_grid() + + biggest_axes = math.max (screen_width, screen_height) + smallest_axes = math.min (screen_width, screen_height) + difference_between_axes = ((biggest_axes - smallest_axes) / 2) + difference = 0 + + if screen_width == biggest_axes then + difference = 0 + else + difference = difference_between_axes + end + + fill(80,500,200,1) + + i=0 + for i=0, 100, 5 do + -- map along the x axis first + x_loc = map(i, 0, 100, 0, biggest_axes) - difference + rect(x_loc, screen_h_center, 5, 5) + text(x_loc -25, screen_h_center+6, tostring(i - 50)) + end + + if (screen_height == biggest_axes) then + difference = 0 + else + difference = difference_between_axes + end + + for i=0, 100, 5 do -- map along the y axis next + y_loc = map(i, 0, 100, 0, biggest_axes) - difference + rect(screen_w_center, y_loc, 5, 5) + text(screen_w_center-25, y_loc + 6, tostring((i - 50) * -1) .. "%") + end +end + + +function draw_colors_grids() +print("screen_w_center : ", screen_w_center) +print("screen_width : ", screen_width) + for i = 1, #colors do + for x = screen_w_center, screen_width do + for y = screen_h_center, screen_height do + fill (colors[i].r, colors[i].g, colors[i].b, colors[i].a) + rect (x * 10, y * 10, 5, 5) + end + end + end +end + + +function draw_grids() + -- draw_percentile_grid() + -- draw_centered_grid() + draw_XY_grid() + draw_colors_grids() +end + +function draw() + draw_grids() +end + diff --git a/examples/joysticks.lua b/examples/joysticks.lua new file mode 100644 index 0000000..a6e4424 --- /dev/null +++ b/examples/joysticks.lua @@ -0,0 +1,49 @@ +-- +-- simple demo of joystick input +-- + +colors = {} +colors = { +{r=255,g=0,b=255,a=1}, +{r=0,g=0,b=255,a=1}, +{r=255,g=0,b=0,a=1}, +{r=0,g=255,b=0,a=1} +} + +ui_background = "examples/robotoothi_bg.png" + +function setup() + background(0,0,0,0) + cell_size = HEIGHT / 4; +end + +function map(x, in_min, in_max, out_min, out_max) + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +end + +function draw_joystick_info(joynum) + fill(colors[joynum].r,colors[joynum].g,colors[joynum].b,colors[joynum].a); + text(10, joynum * cell_size, string.format("# %d %s x:%d/y:%d button:%d", + joynum, joystick[joynum].name, + joystick[joynum].x, joystick[joynum].y, + joystick[joynum].button)); + + dot_x, dot_y = 0; + dot_x = map(joystick[joynum].x, 32767, -32767, HEIGHT, 0) + WIDTH / 4; + dot_y = map(joystick[joynum].y, 32767, -32767, 0, HEIGHT); + ellipse(dot_x, dot_y, 30, 20); + +end + +function draw() + fill (0,255,0,1); + background(0,0,0,0); + + if joystick.count == 0 then + text(10, cell_size, string.format("No joysticks detected .. plug one in and try again!")); + end + + for jn = 1, joystick.count, 1 do + draw_joystick_info(jn); + end +end diff --git a/examples/lines.lua b/examples/lines.lua index 353be9b..f0d255d 100644 --- a/examples/lines.lua +++ b/examples/lines.lua @@ -1,4 +1,5 @@ function setup() + --background(255,0,1); background(0,0,0); end diff --git a/examples/timeout.lua b/examples/timeout.lua new file mode 100644 index 0000000..ae4a90c --- /dev/null +++ b/examples/timeout.lua @@ -0,0 +1,173 @@ + -- +-- display time in all its joy +-- +-- setup() and draw() are start and main functions +-- grid is drawn, time is kept on the grid +-- + +screen_width = WIDTH +screen_height = HEIGHT +screen_w_center = screen_width / 2 +screen_h_center = screen_height / 2 +cell_size = HEIGHT / 4 + +colors = {} +colors = { {r=255, g=0, b=255, a=1}, + {r=0, g=0, b=255, a=1}, + {r=255, g=0, b=0, a=1}, + {r=80, g=500, b=200, a=1}, + {r=87, g=133, b=160, a =1} } + +NUM_EVENTS = 20 + +SLICE_MODULO = 1 + +-- present-time countdown +ptc = NUM_EVENTS + +-- current frame values only +tick = 0 +time_str = "time: " +pixlen = 0 + +-- from now until NUM_EVENTS +event_ticks = {} +event_joyheights = {} + +-- dunno where we should get this from .. math.?? +function map(x, in_min, in_max, out_min, out_max) + return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; +end + +function setup() + background(255, 0, 0, 0) + tick = tick + 1 +end + +function draw_centered_grid() + fill(100,0,0,1) + rect(screen_w_center, screen_h_center, 4, 4) + + fill (100,50,0,1) + --fill (153,150,50) + line(0, screen_h_center, screen_width, screen_h_center) + line(screen_w_center, 0, screen_w_center, screen_height) + + for z = screen_w_center, screen_width, 10 do + if (((z - screen_w_center) % 50) == 0) then + fill (25,102,0,1) + ellipse(z, screen_h_center, 6, 6) + text(z - 8, screen_h_center + 16 ,tostring(z - screen_w_center)) -- padding + end + end + + for z = screen_w_center, 0, -10 do + if (((z - screen_w_center) % 50) == 0) then + fill (25,102,0,1) + ellipse(z, screen_h_center, 6, 6) + text(z - 8, screen_h_center + 16, tostring(z - screen_w_center)) -- padding + end + end + + for z = screen_h_center, screen_height, 10 do + if (((z - screen_h_center) % 50) == 0) then + + ellipse(screen_w_center, z, 6, 6) + text(screen_w_center - 8, z + 16, tostring(z - screen_h_center)) -- padding + end + end + + for z = screen_h_center, 0, -10 do + if (((z - screen_h_center) % 50) == 0) then + ellipse(screen_w_center, z, 6, 6) + text(screen_w_center - 8, z + 16, tostring(z - screen_h_center)) -- padding + end + end +end + + +function draw_colors_grids() + for x = 1, screen_width / cell_size do + for y = 1, screen_height / cell_size do + for i = #colors, 1, -1 do + fill (colors[i].r, colors[i].g, colors[i].b, colors[i].a) + --print(" colors: " .. colors[i].r .. " " .. colors[i].g .. " " .. colors[i].b .. " " .. colors[i].a) + --rect (x * (cell_size / 16), y * (cell_size / 16), 15, 15) + rect (x * 16, y * 16, i * 15, i * 15) + end + end + end +end + +function draw_event_labels(i) + fill(colors[2].r, colors[2].g, colors[2].b, colors[2].a) + +-- print(cn) + fill (colors[cn].r, colors[cn].g, colors[cn].b, colors[cn].a) + + if joystick.count ~= 0 then + local cn = math.modf(map (joystick[1].x, -32768, 32768, 1, #colors)) + print(cn) + fill (colors[cn].r, colors[cn].g, colors[cn].b, colors[cn].a) + end + + rect( (i * 48) - 38, + screen_h_center, + (i * 48) - 34, + map(event_joyheights[i], -32768, 32768, cell_size * 2, -cell_size * 2)) + + fill (87,133,160,1) + text((i * 48) - 38, screen_h_center, event_ticks[i] .. " ") +end + +function draw() + if joystick.count == 0 then + fill (240,0,0,1) + text(250, 10, string.format("Joystick not found, but one is required!")) + else + + -- we keep our own count of event_ticks since start + tick = tick + 1 + + -- keep a list of NUM_EVENTS records and therefore need a present counter + if ptc <= 1 then + ptc = NUM_EVENTS + end + + -- decrement + ptc = ptc - 1 + + -- time slice + if ((ptc % SLICE_MODULO) == 0) then + + background(0,0,0,1) + text(10, 10, string.format("timeout, by torpor")) + + --draw_percentile_grid() + draw_centered_grid() + --draw_XY_grid() + + -- remember our current ptc data + table.insert(event_ticks, tick) + + if joystick.count ~= 0 then + table.insert(event_joyheights, joystick[1].y) + end + + for i = #event_ticks,1,-1 do + draw_event_labels(i) + end + + draw_colors_grids() + + -- prune our little stack + if #event_ticks > NUM_EVENTS then + table.remove(event_ticks, 1) + table.remove(event_joyheights, 1) + end + + end + end + +end + diff --git a/framebuffer.c b/framebuffer.c index 65ced77..8163f2f 100644 --- a/framebuffer.c +++ b/framebuffer.c @@ -8,7 +8,7 @@ SDL_Surface *sdlInit(int width, int height, int bpp, int fullscreen) { SDL_Surface *screen; if (fullscreen) flags |= SDL_FULLSCREEN; - if (SDL_Init(SDL_INIT_VIDEO) == -1) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) == -1) { fprintf(stderr, "SDL Init error: %s\n", SDL_GetError()); return NULL; } @@ -31,9 +31,11 @@ frameBuffer *createFrameBuffer(int width, int height, int bpp, int fullscreen) { fb->width = width; fb->height = height; fb->screen = sdlInit(width,height,bpp,fullscreen); + SDL_PumpEvents(); SDL_initFramerate(&fb->fps_mgr); /* Load the bitmap font */ bfLoadFont((char**)BitmapFont); + gfb = fb; return fb; } @@ -51,22 +53,40 @@ void drawHline(frameBuffer *fb, int x1, int x2, int y, int r, int g, int b, int hlineRGBA(fb->screen, x1, x2, fb->height-1-y, r, g, b, alpha); } -void drawEllipse(frameBuffer *fb, int xc, int yc, int radx, int rady, int r, int g, int b, int alpha) { - filledEllipseRGBA(fb->screen, xc, fb->height-1-yc, radx, rady, r, g, b, alpha); +void drawEllipse(frameBuffer *fb, int xc, int yc, int radx, int rady, int r, int g, int b, int alpha, char filled) { + if (filled) + filledEllipseRGBA(fb->screen, xc, fb->height-1-yc, radx, rady, r, g, b, alpha); + else + ellipseRGBA(fb->screen, xc, fb->height-1-yc, radx, rady, r, g, b, alpha); } -void drawBox(frameBuffer *fb, int x1, int y1, int x2, int y2, int r, int g, int b, int alpha) { - boxRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, r, g, b, alpha); +void drawBox(frameBuffer *fb, int x1, int y1, int x2, int y2, int r, int g, int b, int alpha, char filled) { + if (filled) + boxRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, r, g, b, alpha); + else + rectangleRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, r, g, b, alpha); } -void drawTriangle(frameBuffer *fb, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int alpha) { - filledTrigonRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, x3, fb->height-1-y3, r, g, b, alpha); +void drawTriangle(frameBuffer *fb, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int alpha, char filled) { + if (filled) + filledTrigonRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, x3, fb->height-1-y3, r, g, b, alpha); + else + trigonRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, x3, fb->height-1-y3, r, g, b, alpha); } void drawLine(frameBuffer *fb, int x1, int y1, int x2, int y2, int r, int g, int b, int alpha) { lineRGBA(fb->screen, x1, fb->height-1-y1, x2, fb->height-1-y2, r, g, b, alpha); } +void drawPolygon(frameBuffer *fb, Sint16* xv, Sint16* yv, int n, int r, int g, int b, int alpha, char filled) { + int i; + for (i=0; iheight-1-yv[i]; + if (filled) + filledPolygonRGBA(fb->screen, xv, yv, n, r, g, b, alpha); + else + polygonRGBA(fb->screen, xv, yv, n, r, g, b, alpha); +} + /* ============================= Bitmap font =============================== */ void bfLoadFont(char **c) { /* Set all the entries to NULL. */ @@ -105,7 +125,8 @@ void bfWriteString(frameBuffer *fb, int xp, int yp, const char *s, int len, int * the same interface with load81.c. */ #define SPRITE_MT "l81.sprite_mt" -void spriteBlit(frameBuffer *fb, void *sprite, int x, int y, int angle, int aa) { +/* +void spriteBlit(frameBuffer *fb, sprite *sprite, int x, int y, int angle, int aa) { SDL_Surface *s = sprite; if (s == NULL) return; if (angle) s = rotozoomSurface(s,angle,1,aa); @@ -113,24 +134,62 @@ void spriteBlit(frameBuffer *fb, void *sprite, int x, int y, int angle, int aa) SDL_BlitSurface(s, NULL, fb->screen, &dst); if (angle) SDL_FreeSurface(s); } +*/ + +void spriteBlit(frameBuffer *fb, sprite *sp, int x, int y, int tileNum, int angle, int aa) { + SDL_Surface *s = sp->surf; + if (s == NULL) return; + + if (tileNum >= 0) { + SDL_Rect dst = {x, fb->height-1-y - sp->tileH, sp->tileW, sp->tileH}; + SDL_Rect src = {(tileNum%sp->tileY) * sp->tileW, (tileNum / sp->tileY) * sp->tileH, sp->tileW, sp->tileH}; + if (angle) { + SDL_Surface *temp = SDL_CreateRGBSurface(SDL_SWSURFACE, sp->tileW, sp->tileH, fb->screen->format->BitsPerPixel, 0, 0, 0, 0); + SDL_Surface *tempRot; + SDL_BlitSurface(s, &src, temp, NULL); + tempRot = rotozoomSurface(temp,angle,1,aa); + SDL_BlitSurface(tempRot, NULL, fb->screen, &dst); + SDL_FreeSurface(tempRot); + SDL_FreeSurface(temp); + } + else { + SDL_BlitSurface(s, &src, fb->screen, &dst); + } + } + else { + SDL_Rect dst = {x, fb->height-1-y - s->h, s->w, s->h}; + if (angle) s = rotozoomSurface(s,angle,1,aa); + SDL_BlitSurface(s, NULL, fb->screen, &dst); + if (angle) SDL_FreeSurface(s); + } +} + /* Load sprite. Return surface pointer and object on top of stack */ -void *spriteLoad(lua_State *L, const char *filename) { - SDL_Surface **pps; +sprite *spriteLoad(lua_State *L, const char *filename) { + sprite *pps; /* check if image was already loaded and cached */ lua_getglobal(L, "sprites"); lua_getfield(L, -1, filename); if (lua_isnil(L, -1)) { /* load image into surface */ - SDL_Surface *ps = IMG_Load(filename); - if (ps == NULL) { + sprite ps; + ps.surf = IMG_Load(filename); + if (ps.surf == NULL) { luaL_error(L, "failed to load sprite %s", filename); return NULL; } + ps.w = ps.surf->w; + ps.h = ps.surf->h; + ps.tileX = 0; + ps.tileY = 0; + ps.tileW = ps.w; + ps.tileH = ps.h; + /* box the surface pointer in a userdata */ - pps = (SDL_Surface **)lua_newuserdata(L, sizeof(SDL_Surface *)); + pps = (sprite*)lua_newuserdata(L, sizeof(sprite)); *pps = ps; /* set sprite metatable */ @@ -142,34 +201,92 @@ void *spriteLoad(lua_State *L, const char *filename) { lua_setfield(L, -4, filename); } else { /* unbox surface pointer */ - pps = (SDL_Surface **)luaL_checkudata(L, -1, SPRITE_MT); + pps = (sprite *)luaL_checkudata(L, -1, SPRITE_MT); } - return *pps; + return pps; } int spriteGC(lua_State *L) { - SDL_Surface **pps = (SDL_Surface **)luaL_checkudata(L, 1, SPRITE_MT); - if (pps) SDL_FreeSurface(*pps); + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + if (pps) SDL_FreeSurface(pps->surf); return 0; } int spriteGetHeight(lua_State *L) { - SDL_Surface **pps = (SDL_Surface **)luaL_checkudata(L, 1, SPRITE_MT); - lua_pushnumber(L, (*pps)->h); + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + lua_pushnumber(L, pps->h); return 1; } int spriteGetWidth(lua_State *L) { - SDL_Surface **pps = (SDL_Surface **)luaL_checkudata(L, 1, SPRITE_MT); - lua_pushnumber(L, (*pps)->w); + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + lua_pushnumber(L, pps->w); return 1; } +int spriteGetTiles(lua_State *L) { + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + lua_pushnumber(L, pps->tileX); + lua_pushnumber(L, pps->tileY); + return 2; +} + +int spriteSetTiles(lua_State *L) { + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + pps->tileX = lua_tonumber(L, 2); + pps->tileY = lua_tonumber(L, 3); + pps->tileW = pps->w / pps->tileX; + pps->tileH = pps->h / pps->tileY; + return 0; +} + +int spriteGetTileSize(lua_State *L) { + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + lua_pushnumber(L, pps->tileW); + lua_pushnumber(L, pps->tileH); + return 2; +} + +int spriteGetTileNum(lua_State *L) { + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + lua_pushnumber(L, pps->tileX * pps->tileY); + return 1; +} + +int spriteDrawTile(lua_State *L) { + int x, y, tileNum, angle, antialiasing; + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + x = lua_tonumber(L, 2); + y = lua_tonumber(L, 3); + tileNum = lua_tonumber(L,4); + angle = luaL_optnumber(L,5,0); + antialiasing = lua_toboolean(L,6); + spriteBlit(gfb, pps, x, y, tileNum, angle, antialiasing); + return 0; +} + +int spriteDraw(lua_State *L) { + int x, y, angle, antialiasing; + sprite *pps = (sprite *)luaL_checkudata(L, 1, SPRITE_MT); + x = lua_tonumber(L, 2); + y = lua_tonumber(L, 3); + angle = luaL_optnumber(L,4,0); + antialiasing = lua_toboolean(L,5); + spriteBlit(gfb, pps, x, y, -1, angle, antialiasing); + return 0; +} + static const struct luaL_Reg sprite_m[] = { - { "__gc", spriteGC }, - { "getHeight", spriteGetHeight }, - { "getWidth", spriteGetWidth }, - { NULL, NULL } + { "__gc", spriteGC }, + { "getHeight", spriteGetHeight }, + { "getWidth", spriteGetWidth }, + { "getTiles", spriteGetTiles }, + { "setTiles", spriteSetTiles }, + { "getTileSize", spriteGetTileSize }, + { "getTileNum", spriteGetTileNum }, + { "tile", spriteDrawTile }, + { "draw", spriteDraw }, + { NULL, NULL } }; void initSpriteEngine(lua_State *L) { diff --git a/framebuffer.h b/framebuffer.h index 0a20367..3b8bd5c 100644 --- a/framebuffer.h +++ b/framebuffer.h @@ -14,13 +14,19 @@ #define FONT_HEIGHT 16 #define FONT_KERNING 10 +/* This is the maximum number of joysticks we will allow LOAD81 to deal with */ +#define MAX_JOYSTICKS 8 + typedef struct frameBuffer { int width; int height; SDL_Surface *screen; + SDL_Joystick *joysticks[MAX_JOYSTICKS]; /* Joystick handles are held per-screen */ FPSmanager fps_mgr; } frameBuffer; +frameBuffer *gfb; + /* Frame buffer */ frameBuffer *createFrameBuffer(int width, int height, int bpp, int fullscreen); @@ -28,19 +34,30 @@ frameBuffer *createFrameBuffer(int width, int height, int bpp, int fullscreen); void setPixelWithAlpha(frameBuffer *fb, int x, int y, int r, int g, int b, int alpha); void fillBackground(frameBuffer *fb, int r, int g, int b); void drawHline(frameBuffer *fb, int x1, int x2, int y, int r, int g, int b, int alpha); -void drawEllipse(frameBuffer *fb, int xc, int yc, int radx, int rady, int r, int g, int b, int alpha); -void drawBox(frameBuffer *fb, int x1, int y1, int x2, int y2, int r, int g, int b, int alpha); -void drawTriangle(frameBuffer *fb, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int alpha); +void drawEllipse(frameBuffer *fb, int xc, int yc, int radx, int rady, int r, int g, int b, int alpha, char filled); +void drawBox(frameBuffer *fb, int x1, int y1, int x2, int y2, int r, int g, int b, int alpha, char filled); +void drawTriangle(frameBuffer *fb, int x1, int y1, int x2, int y2, int x3, int y3, int r, int g, int b, int alpha, char filled); void drawLine(frameBuffer *fb, int x1, int y1, int x2, int y2, int r, int g, int b, int alpha); +void drawPolygon(frameBuffer *fb, Sint16* xv, Sint16* yv, int n, int r, int g, int b, int alpha, char filled); /* Bitmap font */ void bfLoadFont(char **c); void bfWriteChar(frameBuffer *fb, int xp, int yp, int c, int r, int g, int b, int alpha); void bfWriteString(frameBuffer *fb, int xp, int yp, const char *s, int len, int r, int g, int b, int alpha); +typedef struct sprite { + int w; + int h; + int tileX; + int tileY; + int tileW; + int tileH; + SDL_Surface *surf; +} sprite; + /* Sprites */ -void spriteBlit(frameBuffer *fb, void *sprite, int x, int y, int angle, int aa); -void *spriteLoad(lua_State *L, const char *filename); +void spriteBlit(frameBuffer *fb, sprite *sp, int x, int y, int tileNum, int angle, int aa); +sprite *spriteLoad(lua_State *L, const char *filename); void initSpriteEngine(lua_State *L); #endif /* FRAMEBUFFER_H */ diff --git a/load81.c b/load81.c index 6dd5853..4b6086b 100644 --- a/load81.c +++ b/load81.c @@ -78,6 +78,72 @@ lua_Number getNumber(char *name) { return n; } + +/* update the table_name[] table, at index, by given field, to num + e.g. table_name[1].x = 100 + stack: + table_name global + [1] index + .x field + = 100 number +*/ +void setArrayFieldNumber( char *table_name, int index, char *field, int number) +{ + lua_getglobal(l81.L, table_name); + + /* Create new if needed */ + if (lua_isnil(l81.L,-1)) { + lua_pop(l81.L,1); + lua_newtable(l81.L); + lua_setglobal(l81.L, table_name); + lua_getglobal(l81.L, table_name); + } + + /* lua: table_named[index].field = value */ + if (lua_istable(l81.L, -1)) { + lua_pushnumber(l81.L, index); + /* get table for modification */ + lua_gettable(l81.L, -2); + lua_pushstring(l81.L, field); + lua_pushnumber(l81.L, number); + lua_settable(l81.L, -3); + } + + lua_pop(l81.L, 2); +} + +/* + update the table_name[] table, .name field + e.g. table_name[1].name = "nub0" + stack: + table_name global + [1] joynum + .name field + = "nub0" value +*/ +void setArrayFieldString(char *table_name, int index, char *field, const char *value) +{ + lua_getglobal(l81.L, table_name); + if (lua_isnil(l81.L,-1)) { + lua_pop(l81.L,1); + lua_newtable(l81.L); + lua_setglobal(l81.L,table_name); + lua_getglobal(l81.L,table_name); + } + + if (lua_istable(l81.L, -1)) { + lua_pushnumber(l81.L, index); + /* get table for modification */ + lua_gettable(l81.L, -2); + lua_pushstring(l81.L, field); + lua_pushstring(l81.L, value); + lua_settable(l81.L, -3); + } + + lua_pop(l81.L, 2); +} + + /* Set a Lua global table field to the value on the top of the Lua stack. */ void setTableField(char *name, char *field) { lua_getglobal(l81.L,name); /* Stack: val table */ @@ -135,6 +201,11 @@ int fillBinding(lua_State *L) { return 0; } +int filledBinding(lua_State *L) { + l81.filled = lua_toboolean(L,-1); + return 0; +} + int rectBinding(lua_State *L) { int x,y,w,h; @@ -142,7 +213,7 @@ int rectBinding(lua_State *L) { y = lua_tonumber(L,-3); w = lua_tonumber(L,-2); h = lua_tonumber(L,-1); - drawBox(l81.fb,x,y,x+(w-1),y+(h-1),l81.r,l81.g,l81.b,l81.alpha); + drawBox(l81.fb,x,y,x+(w-1),y+(h-1),l81.r,l81.g,l81.b,l81.alpha,l81.filled); return 0; } @@ -153,7 +224,7 @@ int ellipseBinding(lua_State *L) { y = lua_tonumber(L,-3); rx = lua_tonumber(L,-2); ry = lua_tonumber(L,-1); - drawEllipse(l81.fb,x,y,rx,ry,l81.r,l81.g,l81.b,l81.alpha); + drawEllipse(l81.fb,x,y,rx,ry,l81.r,l81.g,l81.b,l81.alpha,l81.filled); return 0; } @@ -166,7 +237,7 @@ int triangleBinding(lua_State *L) { y2 = lua_tonumber(L,-3); x3 = lua_tonumber(L,-2); y3 = lua_tonumber(L,-1); - drawTriangle(l81.fb,x1,y1,x2,y2,x3,y3,l81.r,l81.g,l81.b,l81.alpha); + drawTriangle(l81.fb,x1,y1,x2,y2,x3,y3,l81.r,l81.g,l81.b,l81.alpha,l81.filled); return 0; } @@ -212,6 +283,49 @@ int backgroundBinding(lua_State *L) { return 0; } +int polygonBinding(lua_State *L) { + Sint16* polyBufferX; + Sint16* polyBufferY; + + if (!(lua_gettop(L) == 2 && lua_istable(L,-1) && lua_istable(L,-2))) { + programError("Invalid arguments for polygon"); + return 0; + } + + int size = (int)lua_objlen(L,-1), i=0; + polyBufferY = (Sint16*)malloc(size * sizeof(Sint16)); + lua_pushnil(L); + while(lua_next(L,-2) != 0) { + polyBufferY[i++] = (Sint16)lua_tonumber(L,-1); + lua_pop(L,1); + if (i > size) break; + } + + lua_pop(L,1); + + if (size != (int)lua_objlen(L,-1)) { + programError("Array size mismatch in call to polygon"); + return 0; + } + polyBufferX = (Sint16*)malloc(size * sizeof(Sint16)); + lua_pushnil(L); + i=0; + while(lua_next(L,-2) != 0) { + polyBufferX[i++] = (Sint16)lua_tonumber(L,-1); + lua_pop(L,1); + if (i > size) break; + } + + drawPolygon(l81.fb, polyBufferX, polyBufferY, size, l81.r, l81.g, l81.b, l81.alpha, l81.filled); + + free(polyBufferX); + free(polyBufferY); + return 0; +} + + + + int getpixelBinding(lua_State *L) { Uint32 pixel; Uint8 r, g, b; @@ -255,15 +369,16 @@ int getpixelBinding(lua_State *L) { int spriteBinding(lua_State *L) { const char *filename; int x, y, angle, antialiasing; - void *sprite; + sprite *sprite; filename = lua_tostring(L, 1); - x = lua_tonumber(L, 2); - y = lua_tonumber(L, 3); + x = luaL_optnumber(L, 2, -1); + y = luaL_optnumber(L, 3, -1); angle = luaL_optnumber(L,4,0); antialiasing = lua_toboolean(L,5); sprite = spriteLoad(L,filename); - spriteBlit(l81.fb, sprite, x, y, angle, antialiasing); + if (x >= 0 && y >= 0) + spriteBlit(l81.fb, sprite, x, y, -1, angle, antialiasing); return 1; } @@ -328,6 +443,24 @@ void mouseButtonEvent(int button, int pressed) { updatePressedState("mouse",buttonname,pressed); } +void joystickXMovedEvent(int joy_num, Sint16 x) { + if (joy_num < MAX_JOYSTICKS) { + setArrayFieldNumber("joystick", joy_num, "x", x); + } +} + +void joystickYMovedEvent(int joy_num, Sint16 y) { + if (joy_num < MAX_JOYSTICKS) { + setArrayFieldNumber("joystick", joy_num, "y", y); + } +} +void joystickButtonEvent(int joy_num, int down) +{ + if (joy_num < MAX_JOYSTICKS) { + setArrayFieldNumber("joystick", joy_num, "button", down); + } +} + void resetEvents(void) { setTableFieldString("keyboard","state","none"); setTableFieldString("keyboard","key",""); @@ -339,7 +472,7 @@ void showFPS(void) { if (!elapsed_ms) return; snprintf(buf,sizeof(buf),"FPS: %.2f",(float)(l81.epoch*1000)/elapsed_ms); - drawBox(l81.fb,0,0,100,20,0,0,0,255); + drawBox(l81.fb,0,0,100,20,0,0,0,255,1); bfWriteString(l81.fb,0,0,buf,strlen(buf),128,128,128,255); } @@ -348,6 +481,7 @@ int processSdlEvents(void) { resetEvents(); while (SDL_PollEvent(&event)) { + SDL_PumpEvents(); switch(event.type) { case SDL_KEYDOWN: switch(event.key.keysym.sym) { @@ -372,6 +506,21 @@ int processSdlEvents(void) { case SDL_MOUSEBUTTONUP: mouseButtonEvent(event.button.button,0); break; + case SDL_JOYAXISMOTION: /* Handle Joystick Motion */ + if( event.jaxis.axis == 0) { /* x-axis */ + joystickXMovedEvent(event.jaxis.which + 1, event.jaxis.value); /* C vs. Lua offsets */ + } + if( event.jaxis.axis == 1) { /* y-axis */ + joystickYMovedEvent(event.jaxis.which + 1, event.jaxis.value); /* C vs. Lua offsets */ + } + break; + case SDL_JOYBUTTONUP: /* Handle Joystick Button Presses */ + joystickButtonEvent(event.jbutton.which + 1, 0); + break; + case SDL_JOYBUTTONDOWN: /* Handle Joystick Button Presses */ + joystickButtonEvent(event.jbutton.which + 1, 1); + break; + case SDL_QUIT: exit(0); break; @@ -413,6 +562,7 @@ void initConfig(void) { l81.r = 255; l81.g = l81.b = 0; l81.alpha = 255; + l81.filled = 1; l81.L = NULL; l81.luaerr = 0; l81.opt_show_fps = 0; @@ -446,10 +596,58 @@ void initScreen(void) { l81.bpp,l81.opt_full_screen); } +void initJoysticks(frameBuffer *fb) { + int cur_joy; + for(cur_joy=0; cur_joy < MAX_JOYSTICKS; cur_joy++ ) { + fb->joysticks[cur_joy] = NULL; + } +} + +void resetJoysticks(frameBuffer *fb) { + int cur_joy, sdl_joys, num_joys; + char joyscript[70]; + + /* Initialize Joysticks */ + SDL_JoystickEventState(SDL_ENABLE); + + + for(sdl_joys = SDL_NumJoysticks(), cur_joy=0, num_joys=0; cur_joy < sdl_joys; cur_joy++ ) { + + if (cur_joy == 0) { + snprintf(joyscript, sizeof(joyscript), + "for jn = 1, %d, 1 do joystick[jn]={x=0;y=0;name=nil;button=0}; end ", sdl_joys); + luaL_loadbuffer(l81.L,joyscript,strlen(joyscript),"joyscript"); + lua_pcall(l81.L,0,0,0); + } + + if (fb->joysticks[cur_joy] != NULL) + SDL_JoystickClose( fb->joysticks[cur_joy]); + + if (cur_joy < MAX_JOYSTICKS) { + fb->joysticks[cur_joy] = SDL_JoystickOpen(cur_joy); + + if (fb->joysticks[cur_joy] != NULL) { + setArrayFieldString("joystick", cur_joy + 1, "name", SDL_JoystickName(cur_joy)); + setArrayFieldNumber("joystick", cur_joy + 1, "axes", SDL_JoystickNumAxes(fb->joysticks[cur_joy])); + setArrayFieldNumber("joystick", cur_joy + 1, "trackballs", SDL_JoystickNumBalls(fb->joysticks[cur_joy])); + setArrayFieldNumber("joystick", cur_joy + 1, "hats", SDL_JoystickNumHats(fb->joysticks[cur_joy])); + setArrayFieldNumber("joystick", cur_joy + 1, "buttons", SDL_JoystickNumButtons(fb->joysticks[cur_joy])); + setArrayFieldNumber("joystick", cur_joy + 1, "x", 0); + setArrayFieldNumber("joystick", cur_joy + 1, "y", 0); + num_joys ++; + } + } + } + + setTableFieldNumber("joystick", "count", num_joys); +} + + void resetProgram(void) { char *initscript = - "keyboard={}; keyboard['pressed']={};" - "mouse={}; mouse['pressed']={};" + "keyboard={}; keyboard['pressed']={};" \ + "mouse={}; mouse['pressed']={};" \ + "joystick={}; " \ "sprites={}"; l81.epoch = 0; @@ -460,6 +658,7 @@ void resetProgram(void) { luaopen_string(l81.L); luaopen_math(l81.L); luaopen_debug(l81.L); + luaopen_lfs(l81.L); setNumber("WIDTH",l81.width); setNumber("HEIGHT",l81.height); luaL_loadbuffer(l81.L,initscript,strlen(initscript),"initscript"); @@ -472,9 +671,14 @@ void resetProgram(void) { setTableFieldNumber("mouse","xrel",0); setTableFieldNumber("mouse","yrel",0); + /* Reset joysticks */ + resetJoysticks(l81.fb); + /* Register API */ lua_pushcfunction(l81.L,fillBinding); lua_setglobal(l81.L,"fill"); + lua_pushcfunction(l81.L,filledBinding); + lua_setglobal(l81.L,"filled"); lua_pushcfunction(l81.L,rectBinding); lua_setglobal(l81.L,"rect"); lua_pushcfunction(l81.L,ellipseBinding); @@ -493,6 +697,8 @@ void resetProgram(void) { lua_setglobal(l81.L,"getpixel"); lua_pushcfunction(l81.L,spriteBinding); lua_setglobal(l81.L,"sprite"); + lua_pushcfunction(l81.L,polygonBinding); + lua_setglobal(l81.L,"polygon"); initSpriteEngine(l81.L); @@ -562,6 +768,7 @@ int main(int argc, char **argv) { initConfig(); parseOptions(argc,argv); initScreen(); + initJoysticks(l81.fb); initEditor(l81.fb,30,30,30,30); editorOpen(l81.filename); while(1) { diff --git a/load81.h b/load81.h index 7f62925..249819d 100644 --- a/load81.h +++ b/load81.h @@ -15,6 +15,7 @@ struct globalConfig { /* Runtime */ int r,g,b; int alpha; + char filled; int fps; long long start_ms; long long epoch; diff --git a/lua/src/Makefile b/lua/src/Makefile index e0d4c9f..e6c4a16 100644 --- a/lua/src/Makefile +++ b/lua/src/Makefile @@ -8,7 +8,7 @@ PLAT= none CC= gcc -CFLAGS= -O2 -Wall $(MYCFLAGS) +CFLAGS= -O2 -Wall $(MYCFLAGS) -I/usr/include AR= ar rcu RANLIB= ranlib RM= rm -f @@ -25,9 +25,9 @@ PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris LUA_A= liblua.a CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \ lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ - lundump.o lvm.o lzio.o + lundump.o lvm.o lzio.o LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ - lstrlib.o loadlib.o linit.o + lstrlib.o loadlib.o linit.o lfs.o LUA_T= lua LUA_O= lua.o @@ -153,6 +153,7 @@ lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \ ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h +lfs.o: lfs.c lfs.h lua.h luaconf.h lualib.h lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ lfunc.h lstring.h lgc.h ltable.h diff --git a/lua/src/lfs.c b/lua/src/lfs.c new file mode 100644 index 0000000..a842f17 --- /dev/null +++ b/lua/src/lfs.c @@ -0,0 +1,946 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2016 (http://keplerproject.github.io/luafilesystem) +** +** File system manipulation library. +** This library offers these functions: +** lfs.attributes (filepath [, attributename | attributetable]) +** lfs.chdir (path) +** lfs.currentdir () +** lfs.dir (path) +** lfs.link (old, new[, symlink]) +** lfs.lock (fh, mode) +** lfs.lock_dir (path) +** lfs.mkdir (path) +** lfs.rmdir (path) +** lfs.setmode (filepath, mode) +** lfs.symlinkattributes (filepath [, attributename]) +** lfs.touch (filepath [, atime [, mtime]]) +** lfs.unlock (fh) +*/ + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#ifndef _WIN32 +#ifndef _AIX +#define _FILE_OFFSET_BITS 64 /* Linux, Solaris and HP-UX */ +#else +#define _LARGE_FILES 1 /* AIX */ +#endif +#endif +#endif + +#ifndef LFS_DO_NOT_USE_LARGE_FILE +#define _LARGEFILE64_SOURCE +#endif + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include +#include +#include +#ifdef __BORLANDC__ + #include +#else + #include +#endif +#include +#else +#include +#include +#include +#include +#include +#endif + +#include "lua.h" +#include "lauxlib.h" +#include "lualib.h" + +#include "lfs.h" + +#define LFS_VERSION "1.6.3" +#define LFS_LIBNAME "lfs" + +#if LUA_VERSION_NUM >= 503 /* Lua 5.3 */ + +#ifndef luaL_optlong +#define luaL_optlong luaL_optinteger +#endif + +#endif + +#if LUA_VERSION_NUM < 502 +# define luaL_newlib(L,l) (lua_newtable(L), luaL_register(L,NULL,l)) +#endif + +/* Define 'strerror' for systems that do not implement it */ +#ifdef NO_STRERROR +#define strerror(_) "System unable to describe the error" +#endif + +/* Define 'getcwd' for systems that do not implement it */ +#ifdef NO_GETCWD +#define getcwd(p,s) NULL +#define getcwd_error "Function 'getcwd' not provided by system" +#else +#define getcwd_error strerror(errno) + #ifdef _WIN32 + /* MAX_PATH seems to be 260. Seems kind of small. Is there a better one? */ + #define LFS_MAXPATHLEN MAX_PATH + #else + /* For MAXPATHLEN: */ + #include + #define LFS_MAXPATHLEN MAXPATHLEN + #endif +#endif + +#define DIR_METATABLE "directory metatable" +typedef struct dir_data { + int closed; +#ifdef _WIN32 + intptr_t hFile; + char pattern[MAX_PATH+1]; +#else + DIR *dir; +#endif +} dir_data; + +#define LOCK_METATABLE "lock metatable" + +#ifdef _WIN32 + #ifdef __BORLANDC__ + #define lfs_setmode(file, m) (setmode(_fileno(file), m)) + #define STAT_STRUCT struct stati64 + #else + #define lfs_setmode(file, m) (_setmode(_fileno(file), m)) + #define STAT_STRUCT struct _stati64 + #endif +#define STAT_FUNC _stati64 +#define LSTAT_FUNC STAT_FUNC +#else +#define _O_TEXT 0 +#define _O_BINARY 0 +#define lfs_setmode(file, m) ((void)file, (void)m, 0) +#define STAT_STRUCT struct stat +#define STAT_FUNC stat +#define LSTAT_FUNC lstat +#endif + +/* +** Utility functions +*/ +static int pusherror(lua_State *L, const char *info) +{ + lua_pushnil(L); + if (info==NULL) + lua_pushstring(L, strerror(errno)); + else + lua_pushfstring(L, "%s: %s", info, strerror(errno)); + lua_pushinteger(L, errno); + return 3; +} + +static int pushresult(lua_State *L, int i, const char *info) +{ + if (i==-1) + return pusherror(L, info); + lua_pushinteger(L, i); + return 1; +} + + +/* +** This function changes the working (current) directory +*/ +static int change_dir (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + if (chdir(path)) { + lua_pushnil (L); + lua_pushfstring (L,"Unable to change working directory to '%s'\n%s\n", + path, chdir_error); + return 2; + } else { + lua_pushboolean (L, 1); + return 1; + } +} + +/* +** This function returns the current directory +** If unable to get the current directory, it returns nil +** and a string describing the error +*/ +static int get_dir (lua_State *L) { + char *path; + /* Passing (NULL, 0) is not guaranteed to work. Use a temp buffer and size instead. */ + char buf[LFS_MAXPATHLEN]; + if ((path = getcwd(buf, LFS_MAXPATHLEN)) == NULL) { + lua_pushnil(L); + lua_pushstring(L, getcwd_error); + return 2; + } + else { + lua_pushstring(L, path); + return 1; + } +} + +/* +** Check if the given element on the stack is a file and returns it. +*/ +static FILE *check_file (lua_State *L, int idx, const char *funcname) { +#if LUA_VERSION_NUM == 501 + FILE **fh = (FILE **)luaL_checkudata (L, idx, "FILE*"); + if (*fh == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return *fh; +#elif LUA_VERSION_NUM >= 502 && LUA_VERSION_NUM <= 503 + luaL_Stream *fh = (luaL_Stream *)luaL_checkudata (L, idx, "FILE*"); + if (fh->closef == 0 || fh->f == NULL) { + luaL_error (L, "%s: closed file", funcname); + return 0; + } else + return fh->f; +#else +#error unsupported Lua version +#endif +} + + +/* +** +*/ +static int _file_lock (lua_State *L, FILE *fh, const char *mode, const long start, long len, const char *funcname) { + int code; +#ifdef _WIN32 + /* lkmode valid values are: + LK_LOCK Locks the specified bytes. If the bytes cannot be locked, the program immediately tries again after 1 second. If, after 10 attempts, the bytes cannot be locked, the constant returns an error. + LK_NBLCK Locks the specified bytes. If the bytes cannot be locked, the constant returns an error. + LK_NBRLCK Same as _LK_NBLCK. + LK_RLCK Same as _LK_LOCK. + LK_UNLCK Unlocks the specified bytes, which must have been previously locked. + + Regions should be locked only briefly and should be unlocked before closing a file or exiting the program. + + http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt__locking.asp + */ + int lkmode; + switch (*mode) { + case 'r': lkmode = LK_NBLCK; break; + case 'w': lkmode = LK_NBLCK; break; + case 'u': lkmode = LK_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + if (!len) { + fseek (fh, 0L, SEEK_END); + len = ftell (fh); + } + fseek (fh, start, SEEK_SET); +#ifdef __BORLANDC__ + code = locking (fileno(fh), lkmode, len); +#else + code = _locking (fileno(fh), lkmode, len); +#endif +#else + struct flock f; + switch (*mode) { + case 'w': f.l_type = F_WRLCK; break; + case 'r': f.l_type = F_RDLCK; break; + case 'u': f.l_type = F_UNLCK; break; + default : return luaL_error (L, "%s: invalid mode", funcname); + } + f.l_whence = SEEK_SET; + f.l_start = (off_t)start; + f.l_len = (off_t)len; + code = fcntl (fileno(fh), F_SETLK, &f); +#endif + return (code != -1); +} + +#ifdef _WIN32 +typedef struct lfs_Lock { + HANDLE fd; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + size_t pathl; HANDLE fd; + lfs_Lock *lock; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if((fd = CreateFile(ln, GENERIC_WRITE, 0, NULL, CREATE_NEW, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, NULL)) == INVALID_HANDLE_VALUE) { + int en = GetLastError(); + free(ln); lua_pushnil(L); + if(en == ERROR_FILE_EXISTS || en == ERROR_SHARING_VIOLATION) + lua_pushstring(L, "File exists"); + else + lua_pushstring(L, strerror(en)); + return 2; + } + free(ln); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + lock->fd = fd; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->fd != INVALID_HANDLE_VALUE) { + CloseHandle(lock->fd); + lock->fd=INVALID_HANDLE_VALUE; + } + return 0; +} +#else +typedef struct lfs_Lock { + char *ln; +} lfs_Lock; +static int lfs_lock_dir(lua_State *L) { + lfs_Lock *lock; + size_t pathl; + char *ln; + const char *lockfile = "/lockfile.lfs"; + const char *path = luaL_checklstring(L, 1, &pathl); + lock = (lfs_Lock*)lua_newuserdata(L, sizeof(lfs_Lock)); + ln = (char*)malloc(pathl + strlen(lockfile) + 1); + if(!ln) { + lua_pushnil(L); lua_pushstring(L, strerror(errno)); return 2; + } + strcpy(ln, path); strcat(ln, lockfile); + if(symlink("lock", ln) == -1) { + free(ln); lua_pushnil(L); + lua_pushstring(L, strerror(errno)); return 2; + } + lock->ln = ln; + luaL_getmetatable (L, LOCK_METATABLE); + lua_setmetatable (L, -2); + return 1; +} +static int lfs_unlock_dir(lua_State *L) { + lfs_Lock *lock = (lfs_Lock *)luaL_checkudata(L, 1, LOCK_METATABLE); + if(lock->ln) { + unlink(lock->ln); + free(lock->ln); + lock->ln = NULL; + } + return 0; +} +#endif + +static int lfs_g_setmode (lua_State *L, FILE *f, int arg) { + static const int mode[] = {_O_BINARY, _O_TEXT}; + static const char *const modenames[] = {"binary", "text", NULL}; + int op = luaL_checkoption(L, arg, NULL, modenames); + int res = lfs_setmode(f, mode[op]); + if (res != -1) { + int i; + lua_pushboolean(L, 1); + for (i = 0; modenames[i] != NULL; i++) { + if (mode[i] == res) { + lua_pushstring(L, modenames[i]); + return 2; + } + } + lua_pushnil(L); + return 2; + } else { + return pusherror(L, NULL); + } +} + +static int lfs_f_setmode(lua_State *L) { + return lfs_g_setmode(L, check_file(L, 1, "setmode"), 2); +} + +/* +** Locks a file. +** @param #1 File handle. +** @param #2 String with lock mode ('w'rite, 'r'ead). +** @param #3 Number with start position (optional). +** @param #4 Number with length (optional). +*/ +static int file_lock (lua_State *L) { + FILE *fh = check_file (L, 1, "lock"); + const char *mode = luaL_checkstring (L, 2); + const long start = (long) luaL_optinteger (L, 3, 0); + long len = (long) luaL_optinteger (L, 4, 0); + if (_file_lock (L, fh, mode, start, len, "lock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Unlocks a file. +** @param #1 File handle. +** @param #2 Number with start position (optional). +** @param #3 Number with length (optional). +*/ +static int file_unlock (lua_State *L) { + FILE *fh = check_file (L, 1, "unlock"); + const long start = (long) luaL_optinteger (L, 2, 0); + long len = (long) luaL_optinteger (L, 3, 0); + if (_file_lock (L, fh, "u", start, len, "unlock")) { + lua_pushboolean (L, 1); + return 1; + } else { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } +} + + +/* +** Creates a link. +** @param #1 Object to link to. +** @param #2 Name of link. +** @param #3 True if link is symbolic (optional). +*/ +static int make_link(lua_State *L) +{ +#ifndef _WIN32 + const char *oldpath = luaL_checkstring(L, 1); + const char *newpath = luaL_checkstring(L, 2); + return pushresult(L, + (lua_toboolean(L,3) ? symlink : link)(oldpath, newpath), NULL); +#else + errno = ENOSYS; /* = "Function not implemented" */ + return pushresult(L, -1, "make_link is not supported on Windows"); +#endif +} + + +/* +** Creates a directory. +** @param #1 Directory path. +*/ +static int make_dir (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + int fail; +#ifdef _WIN32 + fail = _mkdir (path); +#else + fail = mkdir (path, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | + S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH ); +#endif + if (fail) { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* +** Removes a directory. +** @param #1 Directory path. +*/ +static int remove_dir (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + int fail; + + fail = rmdir (path); + + if (fail) { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror(errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* +** Directory iterator +*/ +static int dir_iter (lua_State *L) { +#ifdef _WIN32 + struct _finddata_t c_file; +#else + struct dirent *entry; +#endif + dir_data *d = (dir_data *)luaL_checkudata (L, 1, DIR_METATABLE); + luaL_argcheck (L, d->closed == 0, 1, "closed directory"); +#ifdef _WIN32 + if (d->hFile == 0L) { /* first entry */ + if ((d->hFile = _findfirst (d->pattern, &c_file)) == -1L) { + lua_pushnil (L); + lua_pushstring (L, strerror (errno)); + d->closed = 1; + return 2; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } else { /* next entry */ + if (_findnext (d->hFile, &c_file) == -1L) { + /* no more entries => close directory */ + _findclose (d->hFile); + d->closed = 1; + return 0; + } else { + lua_pushstring (L, c_file.name); + return 1; + } + } +#else + if ((entry = readdir (d->dir)) != NULL) { + lua_pushstring (L, entry->d_name); + return 1; + } else { + /* no more entries => close directory */ + closedir (d->dir); + d->closed = 1; + return 0; + } +#endif +} + + +/* +** Closes directory iterators +*/ +static int dir_close (lua_State *L) { + dir_data *d = (dir_data *)lua_touserdata (L, 1); +#ifdef _WIN32 + if (!d->closed && d->hFile) { + _findclose (d->hFile); + } +#else + if (!d->closed && d->dir) { + closedir (d->dir); + } +#endif + d->closed = 1; + return 0; +} + + +/* +** Factory of directory iterators +*/ +static int dir_iter_factory (lua_State *L) { + const char *path = luaL_checkstring (L, 1); + dir_data *d; + lua_pushcfunction (L, dir_iter); + d = (dir_data *) lua_newuserdata (L, sizeof(dir_data)); + luaL_getmetatable (L, DIR_METATABLE); + lua_setmetatable (L, -2); + d->closed = 0; +#ifdef _WIN32 + d->hFile = 0L; + if (strlen(path) > MAX_PATH-2) + luaL_error (L, "path too long: %s", path); + else + sprintf (d->pattern, "%s/*", path); +#else + d->dir = opendir (path); + if (d->dir == NULL) + luaL_error (L, "cannot open %s: %s", path, strerror (errno)); +#endif + return 2; +} + + +/* +** Creates directory metatable. +*/ +static int dir_create_meta (lua_State *L) { + luaL_newmetatable (L, DIR_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction (L, dir_iter); + lua_setfield(L, -2, "next"); + lua_pushcfunction (L, dir_close); + lua_setfield(L, -2, "close"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction (L, dir_close); + lua_setfield (L, -2, "__gc"); + return 1; +} + + +/* +** Creates lock metatable. +*/ +static int lock_create_meta (lua_State *L) { + luaL_newmetatable (L, LOCK_METATABLE); + + /* Method table */ + lua_newtable(L); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "free"); + + /* Metamethods */ + lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lfs_unlock_dir); + lua_setfield(L, -2, "__gc"); + return 1; +} + + +#ifdef _WIN32 + #ifndef S_ISDIR + #define S_ISDIR(mode) (mode&_S_IFDIR) + #endif + #ifndef S_ISREG + #define S_ISREG(mode) (mode&_S_IFREG) + #endif + #ifndef S_ISLNK + #define S_ISLNK(mode) (0) + #endif + #ifndef S_ISSOCK + #define S_ISSOCK(mode) (0) + #endif + #ifndef S_ISFIFO + #define S_ISFIFO(mode) (0) + #endif + #ifndef S_ISCHR + #define S_ISCHR(mode) (mode&_S_IFCHR) + #endif + #ifndef S_ISBLK + #define S_ISBLK(mode) (0) + #endif +#endif +/* +** Convert the inode protection mode to a string. +*/ +#ifdef _WIN32 +static const char *mode2string (unsigned short mode) { +#else +static const char *mode2string (mode_t mode) { +#endif + if ( S_ISREG(mode) ) + return "file"; + else if ( S_ISDIR(mode) ) + return "directory"; + else if ( S_ISLNK(mode) ) + return "link"; + else if ( S_ISSOCK(mode) ) + return "socket"; + else if ( S_ISFIFO(mode) ) + return "named pipe"; + else if ( S_ISCHR(mode) ) + return "char device"; + else if ( S_ISBLK(mode) ) + return "block device"; + else + return "other"; +} + + +/* +** Set access time and modification values for file +*/ +static int file_utime (lua_State *L) { + const char *file = luaL_checkstring (L, 1); + struct utimbuf utb, *buf; + + if (lua_gettop (L) == 1) /* set to current date/time */ + buf = NULL; + else { + utb.actime = (time_t)luaL_optnumber (L, 2, 0); + utb.modtime = (time_t) luaL_optinteger (L, 3, utb.actime); + buf = &utb; + } + if (utime (file, buf)) { + lua_pushnil (L); + lua_pushfstring (L, "%s", strerror (errno)); + return 2; + } + lua_pushboolean (L, 1); + return 1; +} + + +/* inode protection mode */ +static void push_st_mode (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, mode2string (info->st_mode)); +} +/* device inode resides on */ +static void push_st_dev (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_dev); +} +/* inode's number */ +static void push_st_ino (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_ino); +} +/* number of hard links to the file */ +static void push_st_nlink (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_nlink); +} +/* user-id of owner */ +static void push_st_uid (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_uid); +} +/* group-id of owner */ +static void push_st_gid (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_gid); +} +/* device type, for special file inode */ +static void push_st_rdev (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_rdev); +} +/* time of last access */ +static void push_st_atime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_atime); +} +/* time of last data modification */ +static void push_st_mtime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_mtime); +} +/* time of last file status change */ +static void push_st_ctime (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer) info->st_ctime); +} +/* file size, in bytes */ +static void push_st_size (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_size); +} +#ifndef _WIN32 +/* blocks allocated for file */ +static void push_st_blocks (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_blocks); +} +/* optimal file system I/O blocksize */ +static void push_st_blksize (lua_State *L, STAT_STRUCT *info) { + lua_pushinteger (L, (lua_Integer)info->st_blksize); +} +#endif + + /* +** Convert the inode protection mode to a permission list. +*/ + +#ifdef _WIN32 +static const char *perm2string (unsigned short mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & _S_IREAD) + { perms[0] = 'r'; perms[3] = 'r'; perms[6] = 'r'; } + if (mode & _S_IWRITE) + { perms[1] = 'w'; perms[4] = 'w'; perms[7] = 'w'; } + if (mode & _S_IEXEC) + { perms[2] = 'x'; perms[5] = 'x'; perms[8] = 'x'; } + return perms; +} +#else +static const char *perm2string (mode_t mode) { + static char perms[10] = "---------"; + int i; + for (i=0;i<9;i++) perms[i]='-'; + if (mode & S_IRUSR) perms[0] = 'r'; + if (mode & S_IWUSR) perms[1] = 'w'; + if (mode & S_IXUSR) perms[2] = 'x'; + if (mode & S_IRGRP) perms[3] = 'r'; + if (mode & S_IWGRP) perms[4] = 'w'; + if (mode & S_IXGRP) perms[5] = 'x'; + if (mode & S_IROTH) perms[6] = 'r'; + if (mode & S_IWOTH) perms[7] = 'w'; + if (mode & S_IXOTH) perms[8] = 'x'; + return perms; +} +#endif + +/* permssions string */ +static void push_st_perm (lua_State *L, STAT_STRUCT *info) { + lua_pushstring (L, perm2string (info->st_mode)); +} + +typedef void (*_push_function) (lua_State *L, STAT_STRUCT *info); + +struct _stat_members { + const char *name; + _push_function push; +}; + +struct _stat_members members[] = { + { "mode", push_st_mode }, + { "dev", push_st_dev }, + { "ino", push_st_ino }, + { "nlink", push_st_nlink }, + { "uid", push_st_uid }, + { "gid", push_st_gid }, + { "rdev", push_st_rdev }, + { "access", push_st_atime }, + { "modification", push_st_mtime }, + { "change", push_st_ctime }, + { "size", push_st_size }, + { "permissions", push_st_perm }, +#ifndef _WIN32 + { "blocks", push_st_blocks }, + { "blksize", push_st_blksize }, +#endif + { NULL, NULL } +}; + +/* +** Get file or symbolic link information +*/ +static int _file_info_ (lua_State *L, int (*st)(const char*, STAT_STRUCT*)) { + STAT_STRUCT info; + const char *file = luaL_checkstring (L, 1); + int i; + + if (st(file, &info)) { + lua_pushnil(L); + lua_pushfstring(L, "cannot obtain information from file '%s': %s", file, strerror(errno)); + return 2; + } + if (lua_isstring (L, 2)) { + const char *member = lua_tostring (L, 2); + for (i = 0; members[i].name; i++) { + if (strcmp(members[i].name, member) == 0) { + /* push member value and return */ + members[i].push (L, &info); + return 1; + } + } + /* member not found */ + return luaL_error(L, "invalid attribute name '%s'", member); + } + /* creates a table if none is given, removes extra arguments */ + lua_settop(L, 2); + if (!lua_istable (L, 2)) { + lua_newtable (L); + } + /* stores all members in table on top of the stack */ + for (i = 0; members[i].name; i++) { + lua_pushstring (L, members[i].name); + members[i].push (L, &info); + lua_rawset (L, -3); + } + return 1; +} + + +/* +** Get file information using stat. +*/ +static int file_info (lua_State *L) { + return _file_info_ (L, STAT_FUNC); +} + + +/* +** Push the symlink target to the top of the stack. +** Assumes the file name is at position 1 of the stack. +** Returns 1 if successful (with the target on top of the stack), +** 0 on failure (with stack unchanged, and errno set). +*/ +static int push_link_target(lua_State *L) { +#ifdef _WIN32 + errno = ENOSYS; + return 0; +#else + const char *file = luaL_checkstring(L, 1); + char *target = NULL; + int tsize, size = 256; /* size = initial buffer capacity */ + while (1) { + target = realloc(target, size); + if (!target) /* failed to allocate */ + return 0; + tsize = readlink(file, target, size); + if (tsize < 0) { /* a readlink() error occurred */ + free(target); + return 0; + } + if (tsize < size) + break; + /* possibly truncated readlink() result, double size and retry */ + size *= 2; + } + target[tsize] = '\0'; + lua_pushlstring(L, target, tsize); + free(target); + return 1; +#endif +} + +/* +** Get symbolic link information using lstat. +*/ +static int link_info (lua_State *L) { + int ret; + if (lua_isstring (L, 2) && (strcmp(lua_tostring(L, 2), "target") == 0)) { + int ok = push_link_target(L); + return ok ? 1 : pusherror(L, "could not obtain link target"); + } + ret = _file_info_ (L, LSTAT_FUNC); + if (ret == 1 && lua_type(L, -1) == LUA_TTABLE) { + int ok = push_link_target(L); + if (ok) { + lua_setfield(L, -2, "target"); + } + } + return ret; +} + + +/* +** Assumes the table is on top of the stack. +*/ +static void set_info (lua_State *L) { + lua_pushliteral(L, "Copyright (C) 2003-2016 Kepler Project"); + lua_setfield(L, -2, "_COPYRIGHT"); + lua_pushliteral(L, "LuaFileSystem is a Lua library developed to complement the set of functions related to file systems offered by the standard Lua distribution"); + lua_setfield(L, -2, "_DESCRIPTION"); + lua_pushliteral(L, "LuaFileSystem " LFS_VERSION); + lua_setfield(L, -2, "_VERSION"); +} + + +static const struct luaL_Reg fslib[] = { + {"attributes", file_info}, + {"chdir", change_dir}, + {"currentdir", get_dir}, + {"dir", dir_iter_factory}, + {"link", make_link}, + {"lock", file_lock}, + {"mkdir", make_dir}, + {"rmdir", remove_dir}, + {"symlinkattributes", link_info}, + {"setmode", lfs_f_setmode}, + {"touch", file_utime}, + {"unlock", file_unlock}, + {"lock_dir", lfs_lock_dir}, + {NULL, NULL}, +}; + +LFS_EXPORT int luaopen_lfs (lua_State *L) { + dir_create_meta (L); + lock_create_meta (L); + luaL_newlib (L, fslib); + lua_pushvalue(L, -1); + lua_setglobal(L, LFS_LIBNAME); + set_info (L); + return 1; +} diff --git a/lua/src/lfs.h b/lua/src/lfs.h new file mode 100644 index 0000000..f445ef0 --- /dev/null +++ b/lua/src/lfs.h @@ -0,0 +1,34 @@ +/* +** LuaFileSystem +** Copyright Kepler Project 2003 - 2016 (http://keplerproject.github.io/luafilesystem) +*/ + +/* Define 'chdir' for systems that do not implement it */ +#ifdef NO_CHDIR + #define chdir(p) (-1) + #define chdir_error "Function 'chdir' not provided by system" +#else + #define chdir_error strerror(errno) +#endif + +#ifdef _WIN32 + #define chdir(p) (_chdir(p)) + #define getcwd(d, s) (_getcwd(d, s)) + #define rmdir(p) (_rmdir(p)) + #define LFS_EXPORT __declspec (dllexport) + #ifndef fileno + #define fileno(f) (_fileno(f)) + #endif +#else + #define LFS_EXPORT extern +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +LFS_EXPORT int luaopen_lfs (lua_State *L); + +#ifdef __cplusplus +} +#endif diff --git a/lua/src/linit.c b/lua/src/linit.c index c1f90df..b2f9ae2 100644 --- a/lua/src/linit.c +++ b/lua/src/linit.c @@ -20,6 +20,7 @@ static const luaL_Reg lualibs[] = { {LUA_TABLIBNAME, luaopen_table}, {LUA_IOLIBNAME, luaopen_io}, {LUA_OSLIBNAME, luaopen_os}, + {LUA_LFSLIBNAME, luaopen_lfs}, {LUA_STRLIBNAME, luaopen_string}, {LUA_MATHLIBNAME, luaopen_math}, {LUA_DBLIBNAME, luaopen_debug}, diff --git a/lua/src/lualib.h b/lua/src/lualib.h index 469417f..21722ba 100644 --- a/lua/src/lualib.h +++ b/lua/src/lualib.h @@ -39,6 +39,8 @@ LUALIB_API int (luaopen_debug) (lua_State *L); #define LUA_LOADLIBNAME "package" LUALIB_API int (luaopen_package) (lua_State *L); +#define LUA_LFSLIBNAME "lfs" +LUALIB_API int (luaopen_lfs) (lua_State *L); /* open all previous libraries */ LUALIB_API void (luaL_openlibs) (lua_State *L); diff --git a/makebundle.sh b/makebundle.sh new file mode 100755 index 0000000..f9ab863 --- /dev/null +++ b/makebundle.sh @@ -0,0 +1,36 @@ +export PROGRAM=load81 +export OUTPUTPATH=/tmp/load81/bundle/ +export BUNDLE=$OUTPUTPATH/$PROGRAM.app +mkdir -p $BUNDLE/Contents +mkdir -p $BUNDLE/Contents/MacOS +mkdir -p $BUNDLE/Contents/Resources +mkdir -p $BUNDLE/Contents/Frameworks + +#Copy frameworks like SDL: + +cp -RH /Library/Frameworks/SDL2.framework $BUNDLE/Contents/Frameworks/ +rm -fr $BUNDLE/Contents/Frameworks/SDL2.framework/Versions/A/Headers/ +rm -fr $BUNDLE/Contents/Frameworks/SDL2.framework/Headers + +#Copy assets, icon file, Info.plist, etc., into resources folder: + +cp -rfv examples/ $BUNDLE/Contents/Resources +#cp -f $ICONFILE $BUNDLE/Contents/Resources/$ICONFILE +cp Info.plist $BUNDLE/Contents/Resources + +#Create the executable from your object files and place in MacOS directory: +cp load81 $BUNDLE/Contents/MacOS/$PROGRAM + +#Change resource path of SDL not sure why but this is necessary: + +install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 $BUNDLE/Contents/MacOS/$PROGRAM + +#Codesign entitlements if necessary: + +codesign -f -v -s "3rd Party Mac Developer Application: load81" "$BUNDLE/Contents/Frameworks/SDL2.framework/Versions/A/SDL2" +codesign -f -v -s "3rd Party Mac Developer Application: load81" --entitlements entitlements.plist "$BUNDLE" + +#Create pkg file: +MASPKGFILENAME=$PROGRAM-$PROGVER.pkg +cd $OUTPUTPATH && productbuild --component "load81" /Applications --sign "3rd Party Mac Developer Installer: load81" "$MASPKGFILENAME" + diff --git a/qu.sh b/qu.sh new file mode 100755 index 0000000..75cc2a4 --- /dev/null +++ b/qu.sh @@ -0,0 +1,4 @@ +application="load81" +date=$(printf %x $(date +%s)) +uuid=$(/usr/bin/uuidgen) +/usr/bin/xattr -w com.apple.quarantine "0002;${date};${application};${uuid}" load81