This figure shows the architecture of an application created with Scrupp. The whole development happens on the right side using Lua.
The behaviour of Scrupp depends on the command line arguments and the operating system. If you drop a file or directory on the Scrupp executable, it's like this file or directory is the first command line argument.
On startup, Scrupp constructs a search path. This search path is a list of paths and zip archives. If Scrupp tries to open a file, it looks for it in these paths/archives. If the file exists in multiple paths, the first one is chosen.
In order to understand the way Scrupp constructs the search path and sets the working directory the following definitions are needed:
SHARE_DIR
/usr/share/scrupp
(defined in Main.h).
If you copy the support files ("main.slua" and the directories
"fonts" and "scripts") to /usr/share/scrupp
they can be found by Scrupp.The following figure shows Scrupp's behaviour on startup, i. e. how it sets the search path and working directory. Click it for a bigger version.
The following list shows the possibilitis for running Scrupp.
$ scrupp [-- arg1 arg2 ... argN]
Scrupp tries to open the file main.slua
.
The working directory of the script will be the one containing
the executable. If this file is not found, Scrupp will try to
open the zip archive main.sar
which has to contain
the file main.slua
. If both files are not found,
an error will be thrown. Extra arguments
to the script have to be separated from the executable's name by
--
.
$ scrupp <Lua file> [arg1 arg2 ... argN]
Scrupp tries to execute the Lua file. The working directory will be the one containing the Lua file. It is possible to give extra arguments to the Lua file.
$ scrupp <zip archive> [arg1 arg2 ... argN]
The archive is prepended to the search path. The directory that
contains the archive becomes the working directory. After this
Scrupp tries to execute main.slua
which should be located
inside this archive. It is possible to give extra
arguments to the Lua file.
$ scrupp <directory> [arg1 arg2 ... argN]
The directory is prepended to the search path and set as the
working directory. After this Scrupp tries to execute
main.slua
which should be located inside this directory.
It is possible to give extra arguments to the
Lua file.
Before running a Lua file, Scrupp collects all command line
arguments in the global table arg
. The name of the
chosen Lua file, directory or archive becomes the element at index
0 in that table. Additional arguments are stored in index 1, 2 and
so on. The name of the executable goes to index -1. The script can
access these arguments by using arg[1], arg[2],
etc.
or the vararg expression '...'.
-- print the name of the script
print("name of the script:", arg[0])
-- print the number of arguments to the script
print("number of arguments:", select('#', ...) )
-- print all arguments
print("arguments:", ...)
-- this prints the first argument
print("Argument 1: ", arg[1] or "none")
-- exiting Scrupp
scrupp.exit()
Just save this example code in a file called
argtest.lua
and run
$ scrupp argtest.lua arg1 arg2
This can be done with every example from the manual. To get started have a look at the General Functions, save the example in a file and run this file with Scrupp.
At the beginning, Scrupp runs a Lua file which is chosen depending on the command line arguments. Read "Getting Started" for details. This file can run other Lua files, load any media type, define functions and so on. It has to define a table with the name 'main' which contains the callbacks.
-- create a resizable window
scrupp.init("Callback Test", 600, 400, 32, false, true)
local image = scrupp.addImage("path_to_image_file")
main = {
render = function(dt)
image:render(mouse.getX(), mouse.getY())
end,
resized = function(width, height)
scrupp.init("Resized!", width, height, 32, false, true)
end,
mousepressed = function(x, y, button)
print(button .." button pressed at "..x..", "..y)
end,
mousereleased = function(x, y, button)
print(button .." button released at "..x..", "..y)
end,
keypressed = function(k)
if k == "ESCAPE" then
scrupp.exit()
end
print("key '" .. k .. "' pressed")
end,
keyreleased = function(k)
print("key '" .. k .. "' released")
end
}
scrupp.init("Draw Test - Click to test!", 600, 400, 32, false)
-- prepare one table for big white pixels
local pixels = { antialiasing = false, size = 20, pixellist = true }
-- prepare one table for red connected lines
local lines = { color = {255,0,0}, antialiasing = true, connect = true }
main = {
render = function(dt)
scrupp.draw(lines)
scrupp.draw(pixels)
end,
mousepressed = function(x, y, button)
-- add the point where the mouse was pressed to the list of pixels
pixels[#pixels+1] = x
pixels[#pixels+1] = y
-- add the point to the list of lines, too
lines[#lines+1] = x
lines[#lines+1] = y
end
}
table = { x, -- x-coordinate of the image position -- mandatory y, -- y-coordinate of the image position -- mandatory centerX = [x-coordinate of the center for positioning, scaling and rotation], centerY = [y-coordinate of the center for positioning, scaling and rotation], scaleX = [scale factor for the x-axis], scaleY = [scale factor for the y-axis], rotate = [angle in degrees to rotate the image], rect = { x, y, width, height }, -- optional color = { red, green, blue, alpha } -- optional }The first two elements of the array part of the table have to be the x- and y-coordinate of the point that the image should be rendered at. The table may have an optional rect entry. This has to be a table describing the rectangle the image should be clipped to. It contains the x- and y-coordinate of the upper left corner and the width and height of the rectangle inside the image. The entries centerX and centerY are optional and default to zero, the left-upper corner of the image. scaleX and scaleY are optional as well and default to one. rotate has a default value of zero dregrees and is optional, too. The optional color entry is a table defining some kind of color filter. If you have a grayscale image, it will be colorized with this color. As always the table looks like this: {red, green, blue, [alpha]} with each value between 0 and 255.
scrupp.init("Image Test", 600, 400, 32, false)
-- load an image file
local image = scrupp.addImage("path_to_image_file")
-- get the dimension of the image
local width = image:getWidth()
local height = image:getHeight()
-- this has the same result:
local width, height = image:getSize()
if width > scrupp.getWindowWidth() or height > scrupp.getWindowHeight() then
print("Please choose an image with smaller dimensions.")
scrupp.exit()
end
-- calculate the x- and y-coordinate of the image
local x = (scrupp.getWindowWidth()-width)/2
local y = (scrupp.getWindowHeight()-height)/2
main = {
render = function(dt)
-- get the position of the mouse pointer
mx, my = mouse:getPos()
-- set the image alpha depending on the x-coordinate of the
-- mouse pointer
image:setAlpha(mx/scrupp.getWindowWidth()*200+50)
-- if you click with the left mouse button,
-- only a part of the image is rendered
if mouse.isDown("left") then
image:render{
mx, my,
rect = { mx-x, my-y, width/2, height/2 }
}
-- otherwise the whole image is rendered
else
image:render(x,y)
end
end
}
Have a look at the font plugin. This make the usage of truetype fonts easier.
table = { text, color = {red, green, blue, [alpha]} -- optional }The first element of the array part of the table has to be the text. The table may have an optional color entry. This has to be a table containing three numbers between 0 and 255, one for each color component (red, green, blue). An optional fourth entry is the transparency (0-255).
scrupp.init("Font Test", 600, 400, 32, false)
-- load a font
local font = scrupp.addFont("fonts/Vera.ttf", 20)
-- get the recommended line height in pixel
local lineSkip = font:getLineSkip()
-- get the text size of a sample text
local w, h = font:getTextSize("Hello World")
-- define a color (opaque blue)
local cBlue = {0, 0, 255, 255}
-- the transparency defaults to 255 (opaque), so this is the same:
local cBlue = {0, 0, 255}
-- generate an image containing some text using the default color
local image_1 = font:generateImage("Hello World")
-- generate an image using the defined color cBlue
-- -- because font:generateImage() gets only one parameter and this is a table
-- -- we can omit the parenthesises and just use the curly brackets of the table
-- -- constructor
local image_2 = font:generateImage{"Hello World", color = cBlue}
-- this has the same result:
local image_2 = font:generateImage{"Hello World", color = {0, 0, 255}}
main = {
render = function(dt)
-- render the already loaded images:
image_1:render(100,50)
image_2:render(100 + w, 50 + lineSkip)
end
}
The sound support is implemented using SDL_mixer.
Note: If you load a sound and play it multiple times in parallel all sound manipulating methods will affect every instance of this sound!
scrupp.init("Sound Test", 600, 400, 32, false)
-- load a sound file
local sound = scrupp.addSound("path_to_sound_file")
main = {
-- empty render function
render = function(dt)
end,
-- play the sound every time a mouse button is pressed
mousepressed = function(x, y, button)
sound:play()
end
}
The music support is implemented using SDL_mixer.
Scrupp supports only one music file playing at a time. Because of that there is only one method for a music object: play. All the other music manipulating functions are independant from a special music object. They change whatever music file is playing at the moment.
$ id3convert -s -2 <mp3 file>
scrupp.init("Music Test", 600, 400, 32, false)
-- load some music
local music = scrupp.addMusic("path_to_music_file")
-- start music
music:play()
main = {
-- empty render function
render = function(dt)
end
}
scrupp.init("Mouse Test", 600, 400, 32, false)
scrupp.addFile("scripts/class.lua")
scrupp.addFile("scripts/font.lua")
local font = Font("fonts/Vera.ttf", 20)
local text = ""
local cursor = scrupp.addImage("path_to_cursor_image")
main = {
render = function(dt)
font:print(10,10, text)
if scrupp.mouseButtonIsDown("left") then
cursor:render(scrupp.getMouseX(), scrupp.getMouseY())
--or: cursor:render(scrupp.getMousePos())
end
end,
mousepressed = function(x, y, button)
text = button .." button pressed at "..x..", "..y
end,
mousereleased = function(x, y, button)
text = button .." button released at "..x..", "..y
end
}
The first table shows virtual key strings. These keys do not exist. You should not try to use them with the callbacks. Instead of e.g. "SHIFT", test for "LSHIFT" or "RSHIFT"
They should be used with scrupp.keyIsDown() to test for some special keys which happen to be twice on a standard keyboard. scrupp.keyIsDown("SHIFT") will return true if either of the two shift keys is pressed.
"SHIFT" | either of the shift keys |
"CTRL" | either of the ctrl keys |
"ALT" | either of the alt keys |
"META" | either of the meta keys |
"SUPER" | either of the windows keys |
The following table shows all key strings usable with Scrupp.
"UNKNOWN" | unknown key |
"BACKSPACE" | backspace |
"TAB" | tab |
"CLEAR" | clear |
"RETURN" | return |
"PAUSE" | pause |
"ESCAPE" | escape |
"SPACE" | space |
"!" | exclaim |
""" | quotedbl |
"#" | hash |
"$" | dollar |
"&" | ampersand |
"'" | quote |
"(" | left parenthesis |
")" | right parenthesis |
"*" | asterisk |
"+" | plus sign |
"," | comma |
"-" | minus sign |
"." | period |
"/" | forward slash |
"0" | 0 |
"1" | 1 |
"2" | 2 |
"3" | 3 |
"4" | 4 |
"5" | 5 |
"6" | 6 |
"7" | 7 |
"8" | 8 |
"9" | 9 |
":" | colon |
";" | semicolon |
"<" | less-than sign |
"=" | equals sign |
">" | greater-than sign |
"?" | question mark |
"@" | at |
"[" | left bracket |
"\" | backslash |
"]" | right bracket |
"^" | caret |
"_" | underscore |
"`" | grave |
"a" | a |
"b" | b |
"c" | c |
"d" | d |
"e" | e |
"f" | f |
"g" | g |
"h" | h |
"i" | i |
"j" | j |
"k" | k |
"l" | l |
"m" | m |
"n" | n |
"o" | o |
"p" | p |
"q" | q |
"r" | r |
"s" | s |
"t" | t |
"u" | u |
"v" | v |
"w" | w |
"x" | x |
"y" | y |
"z" | z |
"DELETE" | delete |
"KP0" | keypad 0 |
"KP1" | keypad 1 |
"KP2" | keypad 2 |
"KP3" | keypad 3 |
"KP4" | keypad 4 |
"KP5" | keypad 5 |
"KP6" | keypad 6 |
"KP7" | keypad 7 |
"KP8" | keypad 8 |
"KP9" | keypad 9 |
"KP_PERIOD" | keypad period |
"KP_DIVIDE" | keypad divide |
"KP_MULTIPLY" | keypad multiply |
"KP_MINUS" | keypad minus |
"KP_PLUS" | keypad plus |
"KP_ENTER" | keypad enter |
"KP_EQUALS" | keypad equals |
"UP" | up arrow |
"DOWN" | down arrow |
"RIGHT" | right arrow |
"LEFT" | left arrow |
"INSERT" | insert |
"HOME" | home |
"END" | end |
"PAGEUP" | page up |
"PAGEDOWN" | page down |
"F1" | F1 |
"F2" | F2 |
"F3" | F3 |
"F4" | F4 |
"F5" | F5 |
"F6" | F6 |
"F7" | F7 |
"F8" | F8 |
"F9" | F9 |
"F10" | F10 |
"F11" | F11 |
"F12" | F12 |
"F13" | F13 |
"F14" | F14 |
"F15" | F15 |
"NUMLOCK" | numlock |
"CAPSLOCK" | capslock |
"SCROLLOCK" | scrollock |
"RSHIFT" | right shift |
"LSHIFT" | left shift |
"RCTRL" | right ctrl |
"LCTRL" | left ctrl |
"RALT" | right alt |
"LALT" | left alt |
"RMETA" | right meta |
"LMETA" | left meta |
"LSUPER" | left windows key |
"RSUPER" | right windows key |
"MODE" | mode shift |
"COMPOSE" | compose |
"HELP" | help |
"PRINT" | print-screen |
"SYSREQ" | SysRq |
"BREAK" | break |
"MENU" | menu |
"POWER" | power |
"EURO" | euro |
"UNDO" | undo |
The source of this table is the SDL man page for SDLKey.
Additionally, there exist strings for keys on international keyboards. They are named from "WORLD_0" to "WORLD_95".
scrupp.init("Keyboard Test", 600, 400, 32, false)
scrupp.addFile("scripts/class.lua")
scrupp.addFile("scripts/font.lua")
local font = Font("fonts/Vera.ttf", 20)
local text = "Press any key."
main = {
render = function(dt)
font:print(10, 10, text)
end,
keypressed = function(key)
text = key .. " pressed."
-- the escape key exits the demo
if key == "ESCAPE" then
scrupp.exit()
end
end,
keyreleased = function(key)
text = key .. " released."
end
}
This is a simple class implementation. It was taken from the lua-users wiki. The original author is unknown.
The returned objects can be used to create new instances. Any changes to the returned object will influence all instances of this class. An example usage of this feature is the implementation of methods after the creation of the class.
If a new instance of a class is created, the constructor will be called with the new instance as first parameter followed by any additional parameters. The constructor can be used to set any values of the new instance to either default values or values which depend on the additional parameters.
In order to create a new instance of a class just call the object returned by the class function with any optional arguments to the constructor.
Every instance of a new class implements the following methods.
scrupp.init("Class Test", 600, 400, 32, false)
-- load required plugins
scrupp.addFile("scripts/class.lua")
scrupp.addFile("scripts/font.lua")
-- load a font
local font = Font("fonts/Vera.ttf", 20)
-- define a class for animals
local Animal = class(function(a, name)
a.name = name
end)
-- define a method that returns the name of the animal
function Animal:getName()
return self.name
end
-- define a class for dogs
local Dog = class(Animal, function(a, name, lord)
a.name = name
a.lord = lord
end)
-- define a method that returns the name of the lord of the dog
-- remark: the method getName() is taken from the base class
function Dog:getNameOfLord()
return self.lord
end
-- create an instance of the class Dog
local Hasso = Dog("Hasso", "Mr. Smith")
main = {
render = function(dt)
font:print(10,10, "Name: ", Hasso:getName())
font:print(10,40, "Lord: ", Hasso:getNameOfLord())
-- tostring() is nescessary in order to print a boolean value
font:print(10,70, "Hasso is an animal: ", tostring(Hasso:is_a(Animal)))
font:print(10,100, "Hasso is a dog: ", tostring(Hasso:is_a(Dog)))
end
}
This class provides an easy to use interface for animations.
scrupp.init("Animation Test", 600, 400, 32, false)
-- load required plugins
scrupp.addFile("scripts/class.lua")
scrupp.addFile("scripts/animation.lua")
local animation = Animation()
-- loads the frames from animation.png which contains 5x5 = 25
-- single frames, each with a size of 20x20 pixels, with no
-- separating pixels in between
-- each frame is shown for 150 ms
animation:addFrames("animation.png", 5, 5, 20, 20, 0, 150)
main = {
render = function(dt)
animation:render(10,10, dt)
end
}
This class provides a high level interface to TTF fonts. If you want to print some text to the screen by only using the functions provided by the core of Scrupp, you will have to create a new image for every text. This is very inefficient if the text changes in every frame. This class creates an image for every single letter and prints text to the screen by placing them in the right order.
scrupp.init("Font Test", 600, 400, 32, false)
-- load required plugins
scrupp.addFile("scripts/class.lua")
scrupp.addFile("scripts/font.lua")
local font = Font("fonts/Vera.ttf", 20)
main = {
render = function(dt)
font:print(10,10, "Hello world!")
font:print(10,40, "Hello ", "world!")
font:print(10,70, "Strings with embedded newlines\nare supported as well.")
end
}
This class provides an implementation of timers.
scrupp.init("Timer Test", 600, 400, 32, false)
-- load required plugins
scrupp.addFile("scripts/class.lua")
scrupp.addFile("scripts/font.lua")
scrupp.addFile("scripts/timer.lua")
local font = Font("fonts/Vera.ttf", 20)
-- this variable is used to distinguish between tick and tack :)
local tick = true
-- create a new timer with a duration of 1 second
-- (= 1000 milliseconds)
-- the callback negates the tick variable
local timer = Timer(1000, function(timer)
tick = not tick
end)
-- start the timer
timer:start()
main = {
render = function(dt)
-- update the timer
timer:update()
-- print Tick or Tack depending on the state of the
-- tick variable
if tick then
font:print(10,10, "Tick!")
else
font:print(10,10, "Tack!")
end
end
}