mirror of
https://github.com/airstruck/luigi.git
synced 2025-11-18 12:25:06 +00:00
add dedicated backend (WIP)
This commit is contained in:
@@ -72,7 +72,7 @@ local mainForm = { id = 'mainWindow', type = 'panel',
|
||||
{ text = 'Hi, I\'m centered middle. ', style = 'listThing',
|
||||
align = 'middle center' },
|
||||
{ text = 'Hi, I\'m centered bottom. ', style = 'listThing',
|
||||
align = 'bottom center' },
|
||||
align = 'bottom center', slices = 'luigi/theme/light/button.png' },
|
||||
{ text = 'Hi, I\'m centered top. ', style = 'listThing',
|
||||
align = 'top center' },
|
||||
{ text = 'A man, a plan, a canal: Panama!', style = 'listThing' },
|
||||
@@ -161,8 +161,12 @@ erge rg eg erg er ergs erg er ge rh erh rth]]
|
||||
|
||||
layout.mainCanvas.align = 'top'
|
||||
|
||||
layout.menuQuit:onPress(function (event) love.event.quit() end)
|
||||
local Backend = require 'luigi.backend'
|
||||
|
||||
layout.themeLight:onPress(function (event) love.event.quit() end)
|
||||
layout.menuQuit:onPress(function (event) Backend.quit() end)
|
||||
|
||||
layout.themeLight:onPress(function (event) Backend.quit() end)
|
||||
|
||||
layout:show()
|
||||
|
||||
Backend.run()
|
||||
|
||||
7
luigi/backend.lua
Normal file
7
luigi/backend.lua
Normal file
@@ -0,0 +1,7 @@
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
if _G.love and _G.love._version_minor > 8 then
|
||||
return require(ROOT .. 'backend.love')
|
||||
else
|
||||
return require(ROOT .. 'backend.ffisdl')
|
||||
end
|
||||
288
luigi/backend/ffisdl.lua
Normal file
288
luigi/backend/ffisdl.lua
Normal file
@@ -0,0 +1,288 @@
|
||||
local ROOT = (...):gsub('[^.]*.[^.]*$', '')
|
||||
|
||||
local Hooker = require(ROOT .. 'hooker')
|
||||
|
||||
local ffi = require 'ffi'
|
||||
local sdl = require((...) .. '.sdl')
|
||||
local Image = require((...) .. '.image')
|
||||
local Font = require((...) .. '.font')
|
||||
local Keyboard = require((...) .. '.keyboard')
|
||||
|
||||
local IntOut = ffi.typeof 'int[1]'
|
||||
|
||||
-- create window and renderer
|
||||
|
||||
local window = sdl.createWindow('', 0, 0, 800, 600,
|
||||
sdl.WINDOW_SHOWN)
|
||||
|
||||
if window == nil then
|
||||
io.stderr:write(ffi.string(sdl.getError()))
|
||||
sdl.quit()
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
ffi.gc(window, sdl.destroyWindow)
|
||||
|
||||
local renderer = sdl.createRenderer(window, -1,
|
||||
sdl.RENDERER_ACCELERATED + sdl.RENDERER_PRESENTVSYNC)
|
||||
|
||||
if renderer == nil then
|
||||
io.stderr:write(ffi.string(sdl.getError()))
|
||||
sdl.quit()
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
ffi.gc(renderer, sdl.destroyRenderer)
|
||||
|
||||
local Backend = {}
|
||||
|
||||
local callback = {
|
||||
draw = function () end,
|
||||
resize = function () end,
|
||||
mousepressed = function () end,
|
||||
mousereleased = function () end,
|
||||
mousemoved = function () end,
|
||||
keypressed = function () end,
|
||||
keyreleased = function () end,
|
||||
textinput = function () end,
|
||||
}
|
||||
|
||||
Backend.run = function ()
|
||||
local event = sdl.Event()
|
||||
|
||||
while true do
|
||||
sdl.pumpEvents()
|
||||
|
||||
while sdl.pollEvent(event) ~= 0 do
|
||||
if event.type == sdl.QUIT then
|
||||
return
|
||||
elseif event.type == sdl.WINDOWEVENT
|
||||
and event.window.event == sdl.WINDOWEVENT_RESIZED then
|
||||
callback.resize(event.window.data1, event.window.data2)
|
||||
elseif event.type == sdl.MOUSEBUTTONDOWN then
|
||||
callback.mousepressed(event.button.x, event.button.y, event.button.button)
|
||||
elseif event.type == sdl.MOUSEBUTTONUP then
|
||||
callback.mousereleased(event.button.x, event.button.y, event.button.button)
|
||||
elseif event.type == sdl.MOUSEMOTION then
|
||||
callback.mousemoved(event.motion.x, event.motion.y)
|
||||
elseif event.type == sdl.KEYDOWN then
|
||||
local key = Keyboard.stringByKeycode[event.key.keysym.sym]
|
||||
callback.keypressed(key, event.key['repeat'])
|
||||
elseif event.type == sdl.KEYUP then
|
||||
local key = Keyboard.stringByKeycode[event.key.keysym.sym]
|
||||
callback.keyreleased(key, event.key['repeat'])
|
||||
elseif event.type == sdl.TEXTINPUT then
|
||||
callback.textinput(ffi.string(event.text.text))
|
||||
end
|
||||
end
|
||||
|
||||
sdl.setRenderDrawColor(renderer, 0, 0, 0, 255)
|
||||
sdl.renderClear(renderer)
|
||||
callback.draw()
|
||||
sdl.renderPresent(renderer)
|
||||
sdl.delay(1)
|
||||
end
|
||||
end
|
||||
|
||||
Backend.Cursor = function (image, x, y)
|
||||
return sdl.createColorCursor(image.sdlSurface, x, y)
|
||||
end
|
||||
|
||||
Backend.Font = Font
|
||||
|
||||
Backend.Image = function (path)
|
||||
return Image(renderer, path)
|
||||
end
|
||||
|
||||
Backend.Quad = function (x, y, w, h)
|
||||
return { x, y, w, h }
|
||||
end
|
||||
|
||||
Backend.SpriteBatch = require((...) .. '.spritebatch')
|
||||
|
||||
Backend.draw = function (drawable, x, y, sx, sy)
|
||||
return drawable:draw(x, y, sx, sy)
|
||||
end
|
||||
|
||||
Backend.drawRectangle = function (mode, x, y, w, h)
|
||||
if mode == 'fill' then
|
||||
sdl.renderFillRect(renderer, sdl.Rect(x, y, w, h))
|
||||
else
|
||||
sdl.renderDrawRect(renderer, sdl.Rect(x, y, w, h))
|
||||
end
|
||||
end
|
||||
|
||||
local currentFont = Font()
|
||||
|
||||
-- print( text, x, y, r, sx, sy, ox, oy, kx, ky )
|
||||
Backend.print = function (text, x, y)
|
||||
if not text or text == '' then return end
|
||||
local font = currentFont.sdlFont
|
||||
local color = sdl.Color(currentFont.color)
|
||||
local write = Font.SDL2_ttf.TTF_RenderUTF8_Blended
|
||||
|
||||
local surface = write(font, text, color)
|
||||
ffi.gc(surface, sdl.freeSurface)
|
||||
local texture = sdl.createTextureFromSurface(renderer, surface)
|
||||
ffi.gc(texture, sdl.destroyTexture)
|
||||
sdl.renderCopy(renderer, texture, nil, sdl.Rect(x, y, surface.w, surface.h))
|
||||
end
|
||||
|
||||
Backend.printf = Backend.print
|
||||
|
||||
Backend.getClipboardText = sdl.getClipboardText
|
||||
|
||||
Backend.setClipboardText = sdl.setClipboardText
|
||||
|
||||
Backend.getMousePosition = function ()
|
||||
local x, y = IntOut(), IntOut()
|
||||
sdl.getMouseState(x, y)
|
||||
return x[0], y[0]
|
||||
end
|
||||
|
||||
local function SystemCursor (id)
|
||||
local cursor = sdl.createSystemCursor(id)
|
||||
ffi.gc(cursor, sdl.freeCursor)
|
||||
return cursor
|
||||
end
|
||||
|
||||
local systemCursors = {
|
||||
arrow = SystemCursor(sdl.SYSTEM_CURSOR_ARROW),
|
||||
ibeam = SystemCursor(sdl.SYSTEM_CURSOR_IBEAM),
|
||||
wait = SystemCursor(sdl.SYSTEM_CURSOR_WAIT),
|
||||
crosshair = SystemCursor(sdl.SYSTEM_CURSOR_CROSSHAIR),
|
||||
waitarrow = SystemCursor(sdl.SYSTEM_CURSOR_WAITARROW),
|
||||
sizenwse = SystemCursor(sdl.SYSTEM_CURSOR_SIZENWSE),
|
||||
sizenesw = SystemCursor(sdl.SYSTEM_CURSOR_SIZENESW),
|
||||
sizewe = SystemCursor(sdl.SYSTEM_CURSOR_SIZEWE),
|
||||
sizens = SystemCursor(sdl.SYSTEM_CURSOR_SIZENS),
|
||||
sizeall = SystemCursor(sdl.SYSTEM_CURSOR_SIZEALL),
|
||||
no = SystemCursor(sdl.SYSTEM_CURSOR_NO),
|
||||
hand = SystemCursor(sdl.SYSTEM_CURSOR_HAND),
|
||||
}
|
||||
|
||||
Backend.getSystemCursor = function (name)
|
||||
return systemCursors[name] or systemCursors.arrow
|
||||
end
|
||||
|
||||
Backend.getWindowSize = function ()
|
||||
local x, y = IntOut(), IntOut()
|
||||
sdl.getWindowSize(window, x, y)
|
||||
return x[0], y[0]
|
||||
end
|
||||
|
||||
Backend.getTime = function ()
|
||||
return sdl.getTicks() * 0.001
|
||||
end
|
||||
|
||||
Backend.isKeyDown = function (...)
|
||||
local state = sdl.getKeyboardState(nil)
|
||||
|
||||
for i = 1, select('#', ...) do
|
||||
local name = select(i, ...)
|
||||
local scan = Keyboard.scancodeByString[name]
|
||||
if scan and state[scan] ~= 0 then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
Backend.isMouseDown = function ()
|
||||
end
|
||||
|
||||
Backend.quit = function ()
|
||||
sdl.quit()
|
||||
os.exit()
|
||||
end
|
||||
|
||||
local lastColor
|
||||
|
||||
Backend.setColor = function (color)
|
||||
lastColor = color
|
||||
sdl.setRenderDrawColor(renderer,
|
||||
color[1], color[2], color[3], color[4] or 255)
|
||||
end
|
||||
|
||||
Backend.setCursor = function (cursor)
|
||||
sdl.setCursor(cursor or Backend.getSystemCursor('arrow'))
|
||||
end
|
||||
|
||||
Backend.setFont = function (font)
|
||||
currentFont = font
|
||||
end
|
||||
|
||||
local lastScissor
|
||||
|
||||
Backend.setScissor = function (x, y, w, h)
|
||||
lastScissor = x and sdl.Rect(x, y, w, h)
|
||||
sdl.renderSetClipRect(renderer, lastScissor)
|
||||
end
|
||||
|
||||
function Backend.hide (layout)
|
||||
for _, item in ipairs(layout.hooks) do
|
||||
Hooker.unhook(item)
|
||||
end
|
||||
layout.hooks = {}
|
||||
end
|
||||
|
||||
local function hook (layout, key, method, hookLast)
|
||||
layout.hooks[#layout.hooks + 1] = Hooker.hook(
|
||||
callback, key, method, hookLast)
|
||||
end
|
||||
|
||||
local stack = {}
|
||||
|
||||
Backend.pop = function ()
|
||||
local history = stack[#stack]
|
||||
Backend.setColor(history.color or { 0, 0, 0, 255 })
|
||||
Backend.setScissor(history.scissor)
|
||||
stack[#stack] = nil
|
||||
end
|
||||
|
||||
Backend.push = function ()
|
||||
stack[#stack + 1] = {
|
||||
color = lastColor,
|
||||
scissor = lastScissor,
|
||||
}
|
||||
end
|
||||
|
||||
local isMouseDown = function ()
|
||||
return sdl.getMouseState(nil, nil) > 0
|
||||
end
|
||||
|
||||
function Backend.show (layout)
|
||||
local input = layout.input
|
||||
|
||||
hook(layout, 'draw', function ()
|
||||
input:handleDisplay(layout)
|
||||
end, true)
|
||||
hook(layout, 'resize', function (width, height)
|
||||
return input:handleReshape(layout, width, height)
|
||||
end)
|
||||
hook(layout, 'mousepressed', function (x, y, button)
|
||||
return input:handlePressStart(layout, button, x, y)
|
||||
end)
|
||||
hook(layout, 'mousereleased', function (x, y, button)
|
||||
return input:handlePressEnd(layout, button, x, y)
|
||||
end)
|
||||
hook(layout, 'mousemoved', function (x, y, dx, dy)
|
||||
if isMouseDown() then
|
||||
return input:handlePressedMove(layout, x, y)
|
||||
else
|
||||
return input:handleMove(layout, x, y)
|
||||
end
|
||||
end)
|
||||
hook(layout, 'keypressed', function (key, isRepeat)
|
||||
return input:handleKeyPress(layout, key, Backend.getMousePosition())
|
||||
end)
|
||||
hook(layout, 'keyreleased', function (key)
|
||||
return input:handleKeyRelease(layout, key, Backend.getMousePosition())
|
||||
end)
|
||||
hook(layout, 'textinput', function (text)
|
||||
return input:handleTextInput(layout, text, Backend.getMousePosition())
|
||||
end)
|
||||
end
|
||||
|
||||
return Backend
|
||||
270
luigi/backend/ffisdl/font.lua
Normal file
270
luigi/backend/ffisdl/font.lua
Normal file
@@ -0,0 +1,270 @@
|
||||
local REL = (...):gsub('[^.]*$', '')
|
||||
|
||||
local ffi = require 'ffi'
|
||||
local sdl = require(REL .. 'sdl')
|
||||
|
||||
local SDL2_ttf = ffi.load 'SDL2_ttf'
|
||||
|
||||
local IntOut = ffi.typeof 'int[1]'
|
||||
|
||||
ffi.cdef [[
|
||||
|
||||
/* The internal structure containing font information */
|
||||
typedef struct _TTF_Font TTF_Font;
|
||||
|
||||
/* Initialize the TTF engine - returns 0 if successful, -1 on error */
|
||||
int TTF_Init(void);
|
||||
|
||||
/* Open a font file and create a font of the specified point size.
|
||||
* Some .fon fonts will have several sizes embedded in the file, so the
|
||||
* point size becomes the index of choosing which size. If the value
|
||||
* is too high, the last indexed size will be the default. */
|
||||
TTF_Font *TTF_OpenFont(const char *file, int ptsize);
|
||||
TTF_Font *TTF_OpenFontIndex(const char *file, int ptsize, long index);
|
||||
TTF_Font *TTF_OpenFontRW(SDL_RWops *src, int freesrc, int ptsize);
|
||||
TTF_Font *TTF_OpenFontIndexRW(SDL_RWops *src, int freesrc, int ptsize, long index);
|
||||
|
||||
/* Set and retrieve the font style
|
||||
#define TTF_STYLE_NORMAL 0x00
|
||||
#define TTF_STYLE_BOLD 0x01
|
||||
#define TTF_STYLE_ITALIC 0x02
|
||||
#define TTF_STYLE_UNDERLINE 0x04
|
||||
#define TTF_STYLE_STRIKETHROUGH 0x08
|
||||
*/
|
||||
|
||||
int TTF_GetFontStyle(const TTF_Font *font);
|
||||
void TTF_SetFontStyle(TTF_Font *font, int style);
|
||||
int TTF_GetFontOutline(const TTF_Font *font);
|
||||
void TTF_SetFontOutline(TTF_Font *font, int outline);
|
||||
|
||||
/* Set and retrieve FreeType hinter settings
|
||||
#define TTF_HINTING_NORMAL 0
|
||||
#define TTF_HINTING_LIGHT 1
|
||||
#define TTF_HINTING_MONO 2
|
||||
#define TTF_HINTING_NONE 3
|
||||
*/
|
||||
|
||||
int TTF_GetFontHinting(const TTF_Font *font);
|
||||
void TTF_SetFontHinting(TTF_Font *font, int hinting);
|
||||
|
||||
/* Get the total height of the font - usually equal to point size */
|
||||
int TTF_FontHeight(const TTF_Font *font);
|
||||
|
||||
/* Get the offset from the baseline to the top of the font
|
||||
This is a positive value, relative to the baseline.
|
||||
*/
|
||||
int TTF_FontAscent(const TTF_Font *font);
|
||||
|
||||
/* Get the offset from the baseline to the bottom of the font
|
||||
This is a negative value, relative to the baseline.
|
||||
*/
|
||||
int TTF_FontDescent(const TTF_Font *font);
|
||||
|
||||
/* Get the recommended spacing between lines of text for this font */
|
||||
int TTF_FontLineSkip(const TTF_Font *font);
|
||||
|
||||
/* Get/Set whether or not kerning is allowed for this font */
|
||||
int TTF_GetFontKerning(const TTF_Font *font);
|
||||
void TTF_SetFontKerning(TTF_Font *font, int allowed);
|
||||
|
||||
/* Get the number of faces of the font */
|
||||
long TTF_FontFaces(const TTF_Font *font);
|
||||
|
||||
/* Get the font face attributes, if any */
|
||||
int TTF_FontFaceIsFixedWidth(const TTF_Font *font);
|
||||
char *TTF_FontFaceFamilyName(const TTF_Font *font);
|
||||
char *TTF_FontFaceStyleName(const TTF_Font *font);
|
||||
|
||||
/* Check wether a glyph is provided by the font or not */
|
||||
int TTF_GlyphIsProvided(const TTF_Font *font, Uint16 ch);
|
||||
|
||||
/* Get the metrics (dimensions) of a glyph
|
||||
To understand what these metrics mean, here is a useful link:
|
||||
http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
|
||||
*/
|
||||
int TTF_GlyphMetrics(TTF_Font *font, Uint16 ch,
|
||||
int *minx, int *maxx,
|
||||
int *miny, int *maxy, int *advance);
|
||||
|
||||
/* Get the dimensions of a rendered string of text */
|
||||
int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h);
|
||||
int TTF_SizeUTF8(TTF_Font *font, const char *text, int *w, int *h);
|
||||
int TTF_SizeUTF8_Wrapped(TTF_Font *font, const char *text, int wrapLength, int *w, int *h, int *lineCount);
|
||||
int TTF_SizeUNICODE(TTF_Font *font, const Uint16 *text, int *w, int *h);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given text at
|
||||
fast quality with the given font and color. The 0 pixel is the
|
||||
colorkey, giving a transparent background, and the 1 pixel is set
|
||||
to the text color.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderText_Solid(TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
SDL_Surface *TTF_RenderUTF8_Solid(TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
SDL_Surface *TTF_RenderUNICODE_Solid(TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given glyph at
|
||||
fast quality with the given font and color. The 0 pixel is the
|
||||
colorkey, giving a transparent background, and the 1 pixel is set
|
||||
to the text color. The glyph is rendered without any padding or
|
||||
centering in the X direction, and aligned normally in the Y direction.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderGlyph_Solid(TTF_Font *font,
|
||||
Uint16 ch, SDL_Color fg);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given text at
|
||||
high quality with the given font and colors. The 0 pixel is background,
|
||||
while other pixels have varying degrees of the foreground color.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderText_Shaded(TTF_Font *font,
|
||||
const char *text, SDL_Color fg, SDL_Color bg);
|
||||
SDL_Surface *TTF_RenderUTF8_Shaded(TTF_Font *font,
|
||||
const char *text, SDL_Color fg, SDL_Color bg);
|
||||
SDL_Surface *TTF_RenderUNICODE_Shaded(TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg, SDL_Color bg);
|
||||
|
||||
/* Create an 8-bit palettized surface and render the given glyph at
|
||||
high quality with the given font and colors. The 0 pixel is background,
|
||||
while other pixels have varying degrees of the foreground color.
|
||||
The glyph is rendered without any padding or centering in the X
|
||||
direction, and aligned normally in the Y direction.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderGlyph_Shaded(TTF_Font *font,
|
||||
Uint16 ch, SDL_Color fg, SDL_Color bg);
|
||||
|
||||
/* Create a 32-bit ARGB surface and render the given text at high quality,
|
||||
using alpha blending to dither the font with the given color.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderText_Blended(TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
SDL_Surface *TTF_RenderUTF8_Blended(TTF_Font *font,
|
||||
const char *text, SDL_Color fg);
|
||||
SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg);
|
||||
|
||||
|
||||
/* Create a 32-bit ARGB surface and render the given text at high quality,
|
||||
using alpha blending to dither the font with the given color.
|
||||
Text is wrapped to multiple lines on line endings and on word boundaries
|
||||
if it extends beyond wrapLength in pixels.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderText_Blended_Wrapped(TTF_Font *font,
|
||||
const char *text, SDL_Color fg, Uint32 wrapLength);
|
||||
SDL_Surface *TTF_RenderUTF8_Blended_Wrapped(TTF_Font *font,
|
||||
const char *text, SDL_Color fg, Uint32 wrapLength);
|
||||
SDL_Surface *TTF_RenderUNICODE_Blended_Wrapped(TTF_Font *font,
|
||||
const Uint16 *text, SDL_Color fg, Uint32 wrapLength);
|
||||
|
||||
/* Create a 32-bit ARGB surface and render the given glyph at high quality,
|
||||
using alpha blending to dither the font with the given color.
|
||||
The glyph is rendered without any padding or centering in the X
|
||||
direction, and aligned normally in the Y direction.
|
||||
This function returns the new surface, or NULL if there was an error.
|
||||
*/
|
||||
SDL_Surface *TTF_RenderGlyph_Blended(TTF_Font *font,
|
||||
Uint16 ch, SDL_Color fg);
|
||||
|
||||
/* Close an opened font file */
|
||||
void TTF_CloseFont(TTF_Font *font);
|
||||
|
||||
/* De-initialize the TTF engine */
|
||||
void TTF_Quit(void);
|
||||
|
||||
/* Check if the TTF engine is initialized */
|
||||
int TTF_WasInit(void);
|
||||
|
||||
/* Get the kerning size of two glyphs */
|
||||
int TTF_GetFontKerningSize(TTF_Font *font, int prev_index, int index);
|
||||
]]
|
||||
|
||||
if SDL2_ttf.TTF_Init() ~= 0 then
|
||||
error(ffi.string(sdl.getError()))
|
||||
end
|
||||
|
||||
local Font = setmetatable({}, { __call = function (self, ...)
|
||||
local object = setmetatable({}, { __index = self })
|
||||
return object, self.constructor(object, ...)
|
||||
end })
|
||||
|
||||
Font.SDL2_ttf = SDL2_ttf
|
||||
|
||||
local fontCache = {}
|
||||
|
||||
function Font:constructor (path, size, color)
|
||||
if not size then
|
||||
size = 12
|
||||
end
|
||||
if not color then
|
||||
color = { 0, 0, 0, 255 }
|
||||
end
|
||||
if not path then
|
||||
path = REL:gsub('%.', '/') .. 'resource/DejaVuSans.ttf'
|
||||
end
|
||||
local key = path .. '_' .. size
|
||||
|
||||
if not fontCache[key] then
|
||||
local font = SDL2_ttf.TTF_OpenFont(path, size)
|
||||
|
||||
if font == nil then
|
||||
io.stderr:write(ffi.string(sdl.getError()))
|
||||
sdl.quit()
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
fontCache[key] = font
|
||||
end
|
||||
|
||||
self.sdlFont = fontCache[key]
|
||||
self.color = color
|
||||
end
|
||||
|
||||
function Font:setAlignment (align)
|
||||
self.align = align
|
||||
end
|
||||
|
||||
function Font:setWidth (width)
|
||||
self.width = width
|
||||
end
|
||||
|
||||
function Font:getLineHeight ()
|
||||
return SDL2_ttf.TTF_FontHeight(self.sdlFont)
|
||||
end
|
||||
|
||||
function Font:getAscender ()
|
||||
return SDL2_ttf.TTF_FontAscent(self.sdlFont)
|
||||
end
|
||||
|
||||
function Font:getDescender ()
|
||||
return SDL2_ttf.TTF_FontDescent(self.sdlFont)
|
||||
end
|
||||
|
||||
function Font:getAdvance (text)
|
||||
local w, h = IntOut(), IntOut()
|
||||
SDL2_ttf.TTF_SizeUTF8(self.sdlFont, text, w, h)
|
||||
return w[0]
|
||||
end
|
||||
|
||||
function Font:getWrappedHeight (text)
|
||||
--[[
|
||||
-- TTF_Font *font, const char *text, int wrapLength, int *w, int *h, int *lineCount
|
||||
local w, h, lineCount = IntOut(), IntOut(), IntOut()
|
||||
|
||||
SDL2_ttf.TTF_SizeUTF8_Wrapped(self.sdlFont, text, self.width,
|
||||
w, h, lineCount)
|
||||
|
||||
return self:getLineHeight() * lineCount
|
||||
--]]
|
||||
|
||||
local w, h = IntOut(), IntOut()
|
||||
SDL2_ttf.TTF_SizeUTF8(self.sdlFont, text, w, h)
|
||||
return h[0]
|
||||
end
|
||||
|
||||
return Font
|
||||
46
luigi/backend/ffisdl/image.lua
Normal file
46
luigi/backend/ffisdl/image.lua
Normal file
@@ -0,0 +1,46 @@
|
||||
local REL = (...):gsub('[^.]*$', '')
|
||||
|
||||
local ffi = require 'ffi'
|
||||
local sdl = require(REL .. 'sdl')
|
||||
|
||||
local SDL2_image = ffi.load 'SDL2_image'
|
||||
|
||||
ffi.cdef [[ SDL_Surface *IMG_Load(const char *file); ]]
|
||||
|
||||
local Image = setmetatable({}, { __call = function (self, ...)
|
||||
local object = setmetatable({}, { __index = self })
|
||||
return object, self.constructor(object, ...)
|
||||
end })
|
||||
|
||||
function Image:constructor (renderer, path)
|
||||
self.sdlRenderer = renderer
|
||||
self.sdlSurface = SDL2_image.IMG_Load(path)
|
||||
ffi.gc(self.sdlSurface, sdl.freeSurface)
|
||||
self.sdlTexture = sdl.createTextureFromSurface(renderer, self.sdlSurface)
|
||||
ffi.gc(self.sdlTexture, sdl.destroyTexture)
|
||||
self.width = self.sdlSurface.w
|
||||
self.height = self.sdlSurface.h
|
||||
end
|
||||
|
||||
function Image:getWidth ()
|
||||
return self.width
|
||||
end
|
||||
|
||||
function Image:getHeight ()
|
||||
return self.height
|
||||
end
|
||||
|
||||
function Image:draw (x, y, sx, sy)
|
||||
local w = self.width * (sx or 1)
|
||||
local h = self.height * (sy or 1)
|
||||
|
||||
-- HACK. Somehow drawing something first prevents renderCopy from
|
||||
-- incorrectly scaling up in some cases (after rendering slices).
|
||||
-- For example http://stackoverflow.com/questions/28218906
|
||||
sdl.renderDrawPoint(self.sdlRenderer, -1, -1)
|
||||
|
||||
-- Draw the image.
|
||||
sdl.renderCopy(self.sdlRenderer, self.sdlTexture, nil, sdl.Rect(x, y, w, h))
|
||||
end
|
||||
|
||||
return Image
|
||||
494
luigi/backend/ffisdl/keyboard.lua
Normal file
494
luigi/backend/ffisdl/keyboard.lua
Normal file
@@ -0,0 +1,494 @@
|
||||
local REL = (...):gsub('[^.]*$', '')
|
||||
|
||||
local sdl = require(REL .. 'sdl')
|
||||
|
||||
local Keyboard = {
|
||||
scancodeByString = {},
|
||||
stringByScancode = {},
|
||||
keycodeByString = {},
|
||||
stringByKeycode = {},
|
||||
}
|
||||
|
||||
local function registerScancodes (registry)
|
||||
for _, entry in ipairs(registry) do
|
||||
Keyboard.scancodeByString[entry[1]] = entry[2]
|
||||
Keyboard.stringByScancode[entry[2]] = entry[1]
|
||||
end
|
||||
end
|
||||
|
||||
local function registerKeycodes (registry)
|
||||
for _, entry in ipairs(registry) do
|
||||
Keyboard.keycodeByString[entry[1]] = entry[2]
|
||||
Keyboard.stringByKeycode[entry[2]] = entry[1]
|
||||
end
|
||||
end
|
||||
|
||||
registerScancodes {
|
||||
{ "unknown", sdl.SCANCODE_UNKNOWN },
|
||||
{ "a", sdl.SCANCODE_A },
|
||||
{ "b", sdl.SCANCODE_B },
|
||||
{ "c", sdl.SCANCODE_C },
|
||||
{ "d", sdl.SCANCODE_D },
|
||||
{ "e", sdl.SCANCODE_E },
|
||||
{ "f", sdl.SCANCODE_F },
|
||||
{ "g", sdl.SCANCODE_G },
|
||||
{ "h", sdl.SCANCODE_H },
|
||||
{ "i", sdl.SCANCODE_I },
|
||||
{ "j", sdl.SCANCODE_J },
|
||||
{ "k", sdl.SCANCODE_K },
|
||||
{ "l", sdl.SCANCODE_L },
|
||||
{ "m", sdl.SCANCODE_M },
|
||||
{ "n", sdl.SCANCODE_N },
|
||||
{ "o", sdl.SCANCODE_O },
|
||||
{ "p", sdl.SCANCODE_P },
|
||||
{ "q", sdl.SCANCODE_Q },
|
||||
{ "r", sdl.SCANCODE_R },
|
||||
{ "s", sdl.SCANCODE_S },
|
||||
{ "t", sdl.SCANCODE_T },
|
||||
{ "u", sdl.SCANCODE_U },
|
||||
{ "v", sdl.SCANCODE_V },
|
||||
{ "w", sdl.SCANCODE_W },
|
||||
{ "x", sdl.SCANCODE_X },
|
||||
{ "y", sdl.SCANCODE_Y },
|
||||
{ "z", sdl.SCANCODE_Z },
|
||||
|
||||
{ "1", sdl.SCANCODE_1 },
|
||||
{ "2", sdl.SCANCODE_2 },
|
||||
{ "3", sdl.SCANCODE_3 },
|
||||
{ "4", sdl.SCANCODE_4 },
|
||||
{ "5", sdl.SCANCODE_5 },
|
||||
{ "6", sdl.SCANCODE_6 },
|
||||
{ "7", sdl.SCANCODE_7 },
|
||||
{ "8", sdl.SCANCODE_8 },
|
||||
{ "9", sdl.SCANCODE_9 },
|
||||
{ "0", sdl.SCANCODE_0 },
|
||||
|
||||
{ "return", sdl.SCANCODE_RETURN },
|
||||
{ "escape", sdl.SCANCODE_ESCAPE },
|
||||
{ "backspace", sdl.SCANCODE_BACKSPACE },
|
||||
{ "tab", sdl.SCANCODE_TAB },
|
||||
{ "space", sdl.SCANCODE_SPACE },
|
||||
|
||||
{ "-", sdl.SCANCODE_MINUS },
|
||||
{ "=", sdl.SCANCODE_EQUALS },
|
||||
{ "[", sdl.SCANCODE_LEFTBRACKET },
|
||||
{ "]", sdl.SCANCODE_RIGHTBRACKET },
|
||||
{ "\\", sdl.SCANCODE_BACKSLASH },
|
||||
{ "nonus#", sdl.SCANCODE_NONUSHASH },
|
||||
{ ";", sdl.SCANCODE_SEMICOLON },
|
||||
{ "'", sdl.SCANCODE_APOSTROPHE },
|
||||
{ "`", sdl.SCANCODE_GRAVE },
|
||||
{ ",", sdl.SCANCODE_COMMA },
|
||||
{ ".", sdl.SCANCODE_PERIOD },
|
||||
{ "/", sdl.SCANCODE_SLASH },
|
||||
|
||||
{ "capslock", sdl.SCANCODE_CAPSLOCK },
|
||||
|
||||
{ "f1", sdl.SCANCODE_F1 },
|
||||
{ "f2", sdl.SCANCODE_F2 },
|
||||
{ "f3", sdl.SCANCODE_F3 },
|
||||
{ "f4", sdl.SCANCODE_F4 },
|
||||
{ "f5", sdl.SCANCODE_F5 },
|
||||
{ "f6", sdl.SCANCODE_F6 },
|
||||
{ "f7", sdl.SCANCODE_F7 },
|
||||
{ "f8", sdl.SCANCODE_F8 },
|
||||
{ "f9", sdl.SCANCODE_F9 },
|
||||
{ "f10", sdl.SCANCODE_F10 },
|
||||
{ "f11", sdl.SCANCODE_F11 },
|
||||
{ "f12", sdl.SCANCODE_F12 },
|
||||
|
||||
{ "printscreen", sdl.SCANCODE_PRINTSCREEN },
|
||||
{ "scrolllock", sdl.SCANCODE_SCROLLLOCK },
|
||||
{ "pause", sdl.SCANCODE_PAUSE },
|
||||
{ "insert", sdl.SCANCODE_INSERT },
|
||||
{ "home", sdl.SCANCODE_HOME },
|
||||
{ "pageup", sdl.SCANCODE_PAGEUP },
|
||||
{ "delete", sdl.SCANCODE_DELETE },
|
||||
{ "end", sdl.SCANCODE_END },
|
||||
{ "pagedown", sdl.SCANCODE_PAGEDOWN },
|
||||
{ "right", sdl.SCANCODE_RIGHT },
|
||||
{ "left", sdl.SCANCODE_LEFT },
|
||||
{ "down", sdl.SCANCODE_DOWN },
|
||||
{ "up", sdl.SCANCODE_UP },
|
||||
|
||||
{ "numlock", sdl.SCANCODE_NUMLOCKCLEAR },
|
||||
{ "kp/", sdl.SCANCODE_KP_DIVIDE },
|
||||
{ "kp*", sdl.SCANCODE_KP_MULTIPLY },
|
||||
{ "kp-", sdl.SCANCODE_KP_MINUS },
|
||||
{ "kp+", sdl.SCANCODE_KP_PLUS },
|
||||
{ "kpenter", sdl.SCANCODE_KP_ENTER },
|
||||
{ "kp1", sdl.SCANCODE_KP_1 },
|
||||
{ "kp2", sdl.SCANCODE_KP_2 },
|
||||
{ "kp3", sdl.SCANCODE_KP_3 },
|
||||
{ "kp4", sdl.SCANCODE_KP_4 },
|
||||
{ "kp5", sdl.SCANCODE_KP_5 },
|
||||
{ "kp6", sdl.SCANCODE_KP_6 },
|
||||
{ "kp7", sdl.SCANCODE_KP_7 },
|
||||
{ "kp8", sdl.SCANCODE_KP_8 },
|
||||
{ "kp9", sdl.SCANCODE_KP_9 },
|
||||
{ "kp0", sdl.SCANCODE_KP_0 },
|
||||
{ "kp.", sdl.SCANCODE_KP_PERIOD },
|
||||
|
||||
{ "nonusbackslash", sdl.SCANCODE_NONUSBACKSLASH },
|
||||
{ "application", sdl.SCANCODE_APPLICATION },
|
||||
{ "power", sdl.SCANCODE_POWER },
|
||||
{ "=", sdl.SCANCODE_KP_EQUALS },
|
||||
{ "f13", sdl.SCANCODE_F13 },
|
||||
{ "f14", sdl.SCANCODE_F14 },
|
||||
{ "f15", sdl.SCANCODE_F15 },
|
||||
{ "f16", sdl.SCANCODE_F16 },
|
||||
{ "f17", sdl.SCANCODE_F17 },
|
||||
{ "f18", sdl.SCANCODE_F18 },
|
||||
{ "f19", sdl.SCANCODE_F19 },
|
||||
{ "f20", sdl.SCANCODE_F20 },
|
||||
{ "f21", sdl.SCANCODE_F21 },
|
||||
{ "f22", sdl.SCANCODE_F22 },
|
||||
{ "f23", sdl.SCANCODE_F23 },
|
||||
{ "f24", sdl.SCANCODE_F24 },
|
||||
{ "execute", sdl.SCANCODE_EXECUTE },
|
||||
{ "help", sdl.SCANCODE_HELP },
|
||||
{ "menu", sdl.SCANCODE_MENU },
|
||||
{ "select", sdl.SCANCODE_SELECT },
|
||||
{ "stop", sdl.SCANCODE_STOP },
|
||||
{ "again", sdl.SCANCODE_AGAIN },
|
||||
{ "undo", sdl.SCANCODE_UNDO },
|
||||
{ "cut", sdl.SCANCODE_CUT },
|
||||
{ "copy", sdl.SCANCODE_COPY },
|
||||
{ "paste", sdl.SCANCODE_PASTE },
|
||||
{ "find", sdl.SCANCODE_FIND },
|
||||
{ "mute", sdl.SCANCODE_MUTE },
|
||||
{ "volumeup", sdl.SCANCODE_VOLUMEUP },
|
||||
{ "volumedown", sdl.SCANCODE_VOLUMEDOWN },
|
||||
{ "kp,", sdl.SCANCODE_KP_COMMA },
|
||||
{ "kp=400", sdl.SCANCODE_KP_EQUALSAS400 },
|
||||
|
||||
{ "international1", sdl.SCANCODE_INTERNATIONAL1 },
|
||||
{ "international2", sdl.SCANCODE_INTERNATIONAL2 },
|
||||
{ "international3", sdl.SCANCODE_INTERNATIONAL3 },
|
||||
{ "international4", sdl.SCANCODE_INTERNATIONAL4 },
|
||||
{ "international5", sdl.SCANCODE_INTERNATIONAL5 },
|
||||
{ "international6", sdl.SCANCODE_INTERNATIONAL6 },
|
||||
{ "international7", sdl.SCANCODE_INTERNATIONAL7 },
|
||||
{ "international8", sdl.SCANCODE_INTERNATIONAL8 },
|
||||
{ "international9", sdl.SCANCODE_INTERNATIONAL9 },
|
||||
{ "lang1", sdl.SCANCODE_LANG1 },
|
||||
{ "lang2", sdl.SCANCODE_LANG2 },
|
||||
{ "lang3", sdl.SCANCODE_LANG3 },
|
||||
{ "lang4", sdl.SCANCODE_LANG4 },
|
||||
{ "lang5", sdl.SCANCODE_LANG5 },
|
||||
{ "lang6", sdl.SCANCODE_LANG6 },
|
||||
{ "lang7", sdl.SCANCODE_LANG7 },
|
||||
{ "lang8", sdl.SCANCODE_LANG8 },
|
||||
{ "lang9", sdl.SCANCODE_LANG9 },
|
||||
|
||||
{ "alterase", sdl.SCANCODE_ALTERASE },
|
||||
{ "sysreq", sdl.SCANCODE_SYSREQ },
|
||||
{ "cancel", sdl.SCANCODE_CANCEL },
|
||||
{ "clear", sdl.SCANCODE_CLEAR },
|
||||
{ "prior", sdl.SCANCODE_PRIOR },
|
||||
{ "return2", sdl.SCANCODE_RETURN2 },
|
||||
{ "separator", sdl.SCANCODE_SEPARATOR },
|
||||
{ "out", sdl.SCANCODE_OUT },
|
||||
{ "oper", sdl.SCANCODE_OPER },
|
||||
{ "clearagain", sdl.SCANCODE_CLEARAGAIN },
|
||||
{ "crsel", sdl.SCANCODE_CRSEL },
|
||||
{ "exsel", sdl.SCANCODE_EXSEL },
|
||||
|
||||
{ "kp00", sdl.SCANCODE_KP_00 },
|
||||
{ "kp000", sdl.SCANCODE_KP_000 },
|
||||
{ "thsousandsseparator", sdl.SCANCODE_THOUSANDSSEPARATOR },
|
||||
{ "decimalseparator", sdl.SCANCODE_DECIMALSEPARATOR },
|
||||
{ "currencyunit", sdl.SCANCODE_CURRENCYUNIT },
|
||||
{ "currencysubunit", sdl.SCANCODE_CURRENCYSUBUNIT },
|
||||
{ "kp(", sdl.SCANCODE_KP_LEFTPAREN },
|
||||
{ "kp)", sdl.SCANCODE_KP_RIGHTPAREN },
|
||||
{ "kp{", sdl.SCANCODE_KP_LEFTBRACE },
|
||||
{ "kp}", sdl.SCANCODE_KP_RIGHTBRACE },
|
||||
{ "kptab", sdl.SCANCODE_KP_TAB },
|
||||
{ "kpbackspace", sdl.SCANCODE_KP_BACKSPACE },
|
||||
{ "kpa", sdl.SCANCODE_KP_A },
|
||||
{ "kpb", sdl.SCANCODE_KP_B },
|
||||
{ "kpc", sdl.SCANCODE_KP_C },
|
||||
{ "kpd", sdl.SCANCODE_KP_D },
|
||||
{ "kpe", sdl.SCANCODE_KP_E },
|
||||
{ "kpf", sdl.SCANCODE_KP_F },
|
||||
{ "kpxor", sdl.SCANCODE_KP_XOR },
|
||||
{ "kpower", sdl.SCANCODE_KP_POWER },
|
||||
{ "kp%", sdl.SCANCODE_KP_PERCENT },
|
||||
{ "kp<", sdl.SCANCODE_KP_LESS },
|
||||
{ "kp>", sdl.SCANCODE_KP_GREATER },
|
||||
{ "kp&", sdl.SCANCODE_KP_AMPERSAND },
|
||||
{ "kp&&", sdl.SCANCODE_KP_DBLAMPERSAND },
|
||||
{ "kp|", sdl.SCANCODE_KP_VERTICALBAR },
|
||||
{ "kp||", sdl.SCANCODE_KP_DBLVERTICALBAR },
|
||||
{ "kp:", sdl.SCANCODE_KP_COLON },
|
||||
{ "kp#", sdl.SCANCODE_KP_HASH },
|
||||
{ "kp ", sdl.SCANCODE_KP_SPACE },
|
||||
{ "kp@", sdl.SCANCODE_KP_AT },
|
||||
{ "kp!", sdl.SCANCODE_KP_EXCLAM },
|
||||
{ "kpmemstore", sdl.SCANCODE_KP_MEMSTORE },
|
||||
{ "kpmemrecall", sdl.SCANCODE_KP_MEMRECALL },
|
||||
{ "kpmemclear", sdl.SCANCODE_KP_MEMCLEAR },
|
||||
{ "kpmem+", sdl.SCANCODE_KP_MEMADD },
|
||||
{ "kpmem-", sdl.SCANCODE_KP_MEMSUBTRACT },
|
||||
{ "kpmem*", sdl.SCANCODE_KP_MEMMULTIPLY },
|
||||
{ "kpmem/", sdl.SCANCODE_KP_MEMDIVIDE },
|
||||
{ "kp+-", sdl.SCANCODE_KP_PLUSMINUS },
|
||||
{ "kpclear", sdl.SCANCODE_KP_CLEAR },
|
||||
{ "kpclearentry", sdl.SCANCODE_KP_CLEARENTRY },
|
||||
{ "kpbinary", sdl.SCANCODE_KP_BINARY },
|
||||
{ "kpoctal", sdl.SCANCODE_KP_OCTAL },
|
||||
{ "kpdecimal", sdl.SCANCODE_KP_DECIMAL },
|
||||
{ "kphex", sdl.SCANCODE_KP_HEXADECIMAL },
|
||||
|
||||
{ "lctrl", sdl.SCANCODE_LCTRL },
|
||||
{ "lshift", sdl.SCANCODE_LSHIFT },
|
||||
{ "lalt", sdl.SCANCODE_LALT },
|
||||
{ "lgui", sdl.SCANCODE_LGUI },
|
||||
{ "rctrl", sdl.SCANCODE_RCTRL },
|
||||
{ "rshift", sdl.SCANCODE_RSHIFT },
|
||||
{ "ralt", sdl.SCANCODE_RALT },
|
||||
{ "rgui", sdl.SCANCODE_RGUI },
|
||||
|
||||
{ "mode", sdl.SCANCODE_MODE },
|
||||
|
||||
{ "audionext", sdl.SCANCODE_AUDIONEXT },
|
||||
{ "audioprev", sdl.SCANCODE_AUDIOPREV },
|
||||
{ "audiostop", sdl.SCANCODE_AUDIOSTOP },
|
||||
{ "audioplay", sdl.SCANCODE_AUDIOPLAY },
|
||||
{ "audiomute", sdl.SCANCODE_AUDIOMUTE },
|
||||
{ "mediaselect", sdl.SCANCODE_MEDIASELECT },
|
||||
{ "www", sdl.SCANCODE_WWW },
|
||||
{ "mail", sdl.SCANCODE_MAIL },
|
||||
{ "calculator", sdl.SCANCODE_CALCULATOR },
|
||||
{ "computer", sdl.SCANCODE_COMPUTER },
|
||||
{ "acsearch", sdl.SCANCODE_AC_SEARCH },
|
||||
{ "achome", sdl.SCANCODE_AC_HOME },
|
||||
{ "acback", sdl.SCANCODE_AC_BACK },
|
||||
{ "acforward", sdl.SCANCODE_AC_FORWARD },
|
||||
{ "acstop", sdl.SCANCODE_AC_STOP },
|
||||
{ "acrefresh", sdl.SCANCODE_AC_REFRESH },
|
||||
{ "acbookmarks", sdl.SCANCODE_AC_BOOKMARKS },
|
||||
|
||||
{ "brightnessdown", sdl.SCANCODE_BRIGHTNESSDOWN },
|
||||
{ "brightnessup", sdl.SCANCODE_BRIGHTNESSUP },
|
||||
{ "displayswitch", sdl.SCANCODE_DISPLAYSWITCH },
|
||||
{ "kbdillumtoggle", sdl.SCANCODE_KBDILLUMTOGGLE },
|
||||
{ "kbdillumdown", sdl.SCANCODE_KBDILLUMDOWN },
|
||||
{ "kbdillumup", sdl.SCANCODE_KBDILLUMUP },
|
||||
{ "eject", sdl.SCANCODE_EJECT },
|
||||
{ "sleep", sdl.SCANCODE_SLEEP },
|
||||
|
||||
{ "app1", sdl.SCANCODE_APP1 },
|
||||
{ "app2", sdl.SCANCODE_APP2 },
|
||||
}
|
||||
|
||||
registerKeycodes {
|
||||
{ "unknown", sdl.C.SDLK_UNKNOWN },
|
||||
|
||||
{ "return", sdl.C.SDLK_RETURN },
|
||||
{ "escape", sdl.C.SDLK_ESCAPE },
|
||||
{ "backspace", sdl.C.SDLK_BACKSPACE },
|
||||
{ "tab", sdl.C.SDLK_TAB },
|
||||
{ "space", sdl.C.SDLK_SPACE },
|
||||
{ "!", sdl.C.SDLK_EXCLAIM },
|
||||
{ "\"", sdl.C.SDLK_QUOTEDBL },
|
||||
{ "#", sdl.C.SDLK_HASH },
|
||||
{ "%", sdl.C.SDLK_PERCENT },
|
||||
{ "$", sdl.C.SDLK_DOLLAR },
|
||||
{ "&", sdl.C.SDLK_AMPERSAND },
|
||||
{ "'", sdl.C.SDLK_QUOTE },
|
||||
{ "(", sdl.C.SDLK_LEFTPAREN },
|
||||
{ ")", sdl.C.SDLK_RIGHTPAREN },
|
||||
{ "*", sdl.C.SDLK_ASTERISK },
|
||||
{ "+", sdl.C.SDLK_PLUS },
|
||||
{ ",", sdl.C.SDLK_COMMA },
|
||||
{ "-", sdl.C.SDLK_MINUS },
|
||||
{ ".", sdl.C.SDLK_PERIOD },
|
||||
{ "/", sdl.C.SDLK_SLASH },
|
||||
{ "0", sdl.C.SDLK_0 },
|
||||
{ "1", sdl.C.SDLK_1 },
|
||||
{ "2", sdl.C.SDLK_2 },
|
||||
{ "3", sdl.C.SDLK_3 },
|
||||
{ "4", sdl.C.SDLK_4 },
|
||||
{ "5", sdl.C.SDLK_5 },
|
||||
{ "6", sdl.C.SDLK_6 },
|
||||
{ "7", sdl.C.SDLK_7 },
|
||||
{ "8", sdl.C.SDLK_8 },
|
||||
{ "9", sdl.C.SDLK_9 },
|
||||
{ ":", sdl.C.SDLK_COLON },
|
||||
{ ";", sdl.C.SDLK_SEMICOLON },
|
||||
{ "<", sdl.C.SDLK_LESS },
|
||||
{ "=", sdl.C.SDLK_EQUALS },
|
||||
{ ">", sdl.C.SDLK_GREATER },
|
||||
{ "?", sdl.C.SDLK_QUESTION },
|
||||
{ "@", sdl.C.SDLK_AT },
|
||||
|
||||
{ "[", sdl.C.SDLK_LEFTBRACKET },
|
||||
{ "\\", sdl.C.SDLK_BACKSLASH },
|
||||
{ "]", sdl.C.SDLK_RIGHTBRACKET },
|
||||
{ "^", sdl.C.SDLK_CARET },
|
||||
{ "_", sdl.C.SDLK_UNDERSCORE },
|
||||
{ "`", sdl.C.SDLK_BACKQUOTE },
|
||||
{ "a", sdl.C.SDLK_a },
|
||||
{ "b", sdl.C.SDLK_b },
|
||||
{ "c", sdl.C.SDLK_c },
|
||||
{ "d", sdl.C.SDLK_d },
|
||||
{ "e", sdl.C.SDLK_e },
|
||||
{ "f", sdl.C.SDLK_f },
|
||||
{ "g", sdl.C.SDLK_g },
|
||||
{ "h", sdl.C.SDLK_h },
|
||||
{ "i", sdl.C.SDLK_i },
|
||||
{ "j", sdl.C.SDLK_j },
|
||||
{ "k", sdl.C.SDLK_k },
|
||||
{ "l", sdl.C.SDLK_l },
|
||||
{ "m", sdl.C.SDLK_m },
|
||||
{ "n", sdl.C.SDLK_n },
|
||||
{ "o", sdl.C.SDLK_o },
|
||||
{ "p", sdl.C.SDLK_p },
|
||||
{ "q", sdl.C.SDLK_q },
|
||||
{ "r", sdl.C.SDLK_r },
|
||||
{ "s", sdl.C.SDLK_s },
|
||||
{ "t", sdl.C.SDLK_t },
|
||||
{ "u", sdl.C.SDLK_u },
|
||||
{ "v", sdl.C.SDLK_v },
|
||||
{ "w", sdl.C.SDLK_w },
|
||||
{ "x", sdl.C.SDLK_x },
|
||||
{ "y", sdl.C.SDLK_y },
|
||||
{ "z", sdl.C.SDLK_z },
|
||||
|
||||
{ "capslock", sdl.C.SDLK_CAPSLOCK },
|
||||
|
||||
{ "f1", sdl.C.SDLK_F1 },
|
||||
{ "f2", sdl.C.SDLK_F2 },
|
||||
{ "f3", sdl.C.SDLK_F3 },
|
||||
{ "f4", sdl.C.SDLK_F4 },
|
||||
{ "f5", sdl.C.SDLK_F5 },
|
||||
{ "f6", sdl.C.SDLK_F6 },
|
||||
{ "f7", sdl.C.SDLK_F7 },
|
||||
{ "f8", sdl.C.SDLK_F8 },
|
||||
{ "f9", sdl.C.SDLK_F9 },
|
||||
{ "f10", sdl.C.SDLK_F10 },
|
||||
{ "f11", sdl.C.SDLK_F11 },
|
||||
{ "f12", sdl.C.SDLK_F12 },
|
||||
|
||||
{ "printscreen", sdl.C.SDLK_PRINTSCREEN },
|
||||
{ "scrolllock", sdl.C.SDLK_SCROLLLOCK },
|
||||
{ "pause", sdl.C.SDLK_PAUSE },
|
||||
{ "insert", sdl.C.SDLK_INSERT },
|
||||
{ "home", sdl.C.SDLK_HOME },
|
||||
{ "pageup", sdl.C.SDLK_PAGEUP },
|
||||
{ "delete", sdl.C.SDLK_DELETE },
|
||||
{ "end", sdl.C.SDLK_END },
|
||||
{ "pagedown", sdl.C.SDLK_PAGEDOWN },
|
||||
{ "right", sdl.C.SDLK_RIGHT },
|
||||
{ "left", sdl.C.SDLK_LEFT },
|
||||
{ "down", sdl.C.SDLK_DOWN },
|
||||
{ "up", sdl.C.SDLK_UP },
|
||||
|
||||
{ "numlock", sdl.C.SDLK_NUMLOCKCLEAR },
|
||||
{ "kp/", sdl.C.SDLK_KP_DIVIDE },
|
||||
{ "kp*", sdl.C.SDLK_KP_MULTIPLY },
|
||||
{ "kp-", sdl.C.SDLK_KP_MINUS },
|
||||
{ "kp+", sdl.C.SDLK_KP_PLUS },
|
||||
{ "kpenter", sdl.C.SDLK_KP_ENTER },
|
||||
{ "kp0", sdl.C.SDLK_KP_0 },
|
||||
{ "kp1", sdl.C.SDLK_KP_1 },
|
||||
{ "kp2", sdl.C.SDLK_KP_2 },
|
||||
{ "kp3", sdl.C.SDLK_KP_3 },
|
||||
{ "kp4", sdl.C.SDLK_KP_4 },
|
||||
{ "kp5", sdl.C.SDLK_KP_5 },
|
||||
{ "kp6", sdl.C.SDLK_KP_6 },
|
||||
{ "kp7", sdl.C.SDLK_KP_7 },
|
||||
{ "kp8", sdl.C.SDLK_KP_8 },
|
||||
{ "kp9", sdl.C.SDLK_KP_9 },
|
||||
{ "kp.", sdl.C.SDLK_KP_PERIOD },
|
||||
{ "kp,", sdl.C.SDLK_KP_COMMA },
|
||||
{ "kp=", sdl.C.SDLK_KP_EQUALS },
|
||||
|
||||
{ "application", sdl.C.SDLK_APPLICATION },
|
||||
{ "power", sdl.C.SDLK_POWER },
|
||||
{ "f13", sdl.C.SDLK_F13 },
|
||||
{ "f14", sdl.C.SDLK_F14 },
|
||||
{ "f15", sdl.C.SDLK_F15 },
|
||||
{ "f16", sdl.C.SDLK_F16 },
|
||||
{ "f17", sdl.C.SDLK_F17 },
|
||||
{ "f18", sdl.C.SDLK_F18 },
|
||||
{ "f19", sdl.C.SDLK_F19 },
|
||||
{ "f20", sdl.C.SDLK_F20 },
|
||||
{ "f21", sdl.C.SDLK_F21 },
|
||||
{ "f22", sdl.C.SDLK_F22 },
|
||||
{ "f23", sdl.C.SDLK_F23 },
|
||||
{ "f24", sdl.C.SDLK_F24 },
|
||||
{ "execute", sdl.C.SDLK_EXECUTE },
|
||||
{ "help", sdl.C.SDLK_HELP },
|
||||
{ "menu", sdl.C.SDLK_MENU },
|
||||
{ "select", sdl.C.SDLK_SELECT },
|
||||
{ "stop", sdl.C.SDLK_STOP },
|
||||
{ "again", sdl.C.SDLK_AGAIN },
|
||||
{ "undo", sdl.C.SDLK_UNDO },
|
||||
{ "cut", sdl.C.SDLK_CUT },
|
||||
{ "copy", sdl.C.SDLK_COPY },
|
||||
{ "paste", sdl.C.SDLK_PASTE },
|
||||
{ "find", sdl.C.SDLK_FIND },
|
||||
{ "mute", sdl.C.SDLK_MUTE },
|
||||
{ "volumeup", sdl.C.SDLK_VOLUMEUP },
|
||||
{ "volumedown", sdl.C.SDLK_VOLUMEDOWN },
|
||||
|
||||
{ "alterase", sdl.C.SDLK_ALTERASE },
|
||||
{ "sysreq", sdl.C.SDLK_SYSREQ },
|
||||
{ "cancel", sdl.C.SDLK_CANCEL },
|
||||
{ "clear", sdl.C.SDLK_CLEAR },
|
||||
{ "prior", sdl.C.SDLK_PRIOR },
|
||||
{ "return2", sdl.C.SDLK_RETURN2 },
|
||||
{ "separator", sdl.C.SDLK_SEPARATOR },
|
||||
{ "out", sdl.C.SDLK_OUT },
|
||||
{ "oper", sdl.C.SDLK_OPER },
|
||||
{ "clearagain", sdl.C.SDLK_CLEARAGAIN },
|
||||
|
||||
{ "thsousandsseparator", sdl.C.SDLK_THOUSANDSSEPARATOR },
|
||||
{ "decimalseparator", sdl.C.SDLK_DECIMALSEPARATOR },
|
||||
{ "currencyunit", sdl.C.SDLK_CURRENCYUNIT },
|
||||
{ "currencysubunit", sdl.C.SDLK_CURRENCYSUBUNIT },
|
||||
|
||||
{ "lctrl", sdl.C.SDLK_LCTRL },
|
||||
{ "lshift", sdl.C.SDLK_LSHIFT },
|
||||
{ "lalt", sdl.C.SDLK_LALT },
|
||||
{ "lgui", sdl.C.SDLK_LGUI },
|
||||
{ "rctrl", sdl.C.SDLK_RCTRL },
|
||||
{ "rshift", sdl.C.SDLK_RSHIFT },
|
||||
{ "ralt", sdl.C.SDLK_RALT },
|
||||
{ "rgui", sdl.C.SDLK_RGUI },
|
||||
|
||||
{ "mode", sdl.C.SDLK_MODE },
|
||||
|
||||
{ "audionext", sdl.C.SDLK_AUDIONEXT },
|
||||
{ "audioprev", sdl.C.SDLK_AUDIOPREV },
|
||||
{ "audiostop", sdl.C.SDLK_AUDIOSTOP },
|
||||
{ "audioplay", sdl.C.SDLK_AUDIOPLAY },
|
||||
{ "audiomute", sdl.C.SDLK_AUDIOMUTE },
|
||||
{ "mediaselect", sdl.C.SDLK_MEDIASELECT },
|
||||
{ "www", sdl.C.SDLK_WWW },
|
||||
{ "mail", sdl.C.SDLK_MAIL },
|
||||
{ "calculator", sdl.C.SDLK_CALCULATOR },
|
||||
{ "computer", sdl.C.SDLK_COMPUTER },
|
||||
|
||||
{ "appsearch", sdl.C.SDLK_AC_SEARCH },
|
||||
{ "apphome", sdl.C.SDLK_AC_HOME },
|
||||
{ "appback", sdl.C.SDLK_AC_BACK },
|
||||
{ "appforward", sdl.C.SDLK_AC_FORWARD },
|
||||
{ "appstop", sdl.C.SDLK_AC_STOP },
|
||||
{ "apprefresh", sdl.C.SDLK_AC_REFRESH },
|
||||
{ "appbookmarks", sdl.C.SDLK_AC_BOOKMARKS },
|
||||
|
||||
{ "brightnessdown", sdl.C.SDLK_BRIGHTNESSDOWN },
|
||||
{ "brightnessup", sdl.C.SDLK_BRIGHTNESSUP },
|
||||
{ "displayswitch", sdl.C.SDLK_DISPLAYSWITCH },
|
||||
{ "kbdillumtoggle", sdl.C.SDLK_KBDILLUMTOGGLE },
|
||||
{ "kbdillumdown", sdl.C.SDLK_KBDILLUMDOWN },
|
||||
{ "kbdillumup", sdl.C.SDLK_KBDILLUMUP },
|
||||
{ "eject", sdl.C.SDLK_EJECT },
|
||||
{ "sleep", sdl.C.SDLK_SLEEP },
|
||||
}
|
||||
|
||||
return Keyboard
|
||||
BIN
luigi/backend/ffisdl/resource/DejaVuSans.ttf
Normal file
BIN
luigi/backend/ffisdl/resource/DejaVuSans.ttf
Normal file
Binary file not shown.
67
luigi/backend/ffisdl/sdl.lua
Normal file
67
luigi/backend/ffisdl/sdl.lua
Normal file
@@ -0,0 +1,67 @@
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
local ffi = require 'ffi'
|
||||
local sdl = require(ROOT .. 'sdl2.init')
|
||||
|
||||
sdl.AudioCVT = ffi.typeof 'SDL_AudioCVT'
|
||||
-- sdl.AudioDeviceEvent = ffi.typeof 'SDL_AudioDeviceEvent'
|
||||
sdl.AudioSpec = ffi.typeof 'SDL_AudioSpec'
|
||||
sdl.Color = ffi.typeof 'SDL_Color'
|
||||
sdl.ControllerAxisEvent = ffi.typeof 'SDL_ControllerAxisEvent'
|
||||
sdl.ControllerButtonEvent = ffi.typeof 'SDL_ControllerButtonEvent'
|
||||
sdl.ControllerDeviceEvent = ffi.typeof 'SDL_ControllerDeviceEvent'
|
||||
sdl.DisplayMode = ffi.typeof 'SDL_DisplayMode'
|
||||
sdl.DollarGestureEvent = ffi.typeof 'SDL_DollarGestureEvent'
|
||||
sdl.DropEvent = ffi.typeof 'SDL_DropEvent'
|
||||
sdl.Event = ffi.typeof 'SDL_Event'
|
||||
sdl.Finger = ffi.typeof 'SDL_Finger'
|
||||
sdl.HapticCondition = ffi.typeof 'SDL_HapticCondition'
|
||||
sdl.HapticConstant = ffi.typeof 'SDL_HapticConstant'
|
||||
sdl.HapticCustom = ffi.typeof 'SDL_HapticCustom'
|
||||
sdl.HapticDirection = ffi.typeof 'SDL_HapticDirection'
|
||||
sdl.HapticEffect = ffi.typeof 'SDL_HapticEffect'
|
||||
sdl.HapticLeftRight = ffi.typeof 'SDL_HapticLeftRight'
|
||||
sdl.HapticPeriodic = ffi.typeof 'SDL_HapticPeriodic'
|
||||
sdl.HapticRamp = ffi.typeof 'SDL_HapticRamp'
|
||||
sdl.JoyAxisEvent = ffi.typeof 'SDL_JoyAxisEvent'
|
||||
sdl.JoyBallEvent = ffi.typeof 'SDL_JoyBallEvent'
|
||||
sdl.JoyButtonEvent = ffi.typeof 'SDL_JoyButtonEvent'
|
||||
sdl.JoyDeviceEvent = ffi.typeof 'SDL_JoyDeviceEvent'
|
||||
sdl.JoyHatEvent = ffi.typeof 'SDL_JoyHatEvent'
|
||||
sdl.KeyboardEvent = ffi.typeof 'SDL_KeyboardEvent'
|
||||
sdl.Keysym = ffi.typeof 'SDL_Keysym'
|
||||
sdl.MessageBoxButtonData = ffi.typeof 'SDL_MessageBoxButtonData'
|
||||
sdl.MessageBoxColor = ffi.typeof 'SDL_MessageBoxColor'
|
||||
sdl.MessageBoxColorScheme = ffi.typeof 'SDL_MessageBoxColorScheme'
|
||||
sdl.MessageBoxData = ffi.typeof 'SDL_MessageBoxData'
|
||||
sdl.MouseButtonEvent = ffi.typeof 'SDL_MouseButtonEvent'
|
||||
sdl.MouseMotionEvent = ffi.typeof 'SDL_MouseMotionEvent'
|
||||
sdl.MouseWheelEvent = ffi.typeof 'SDL_MouseWheelEvent'
|
||||
sdl.MultiGestureEvent = ffi.typeof 'SDL_MultiGestureEvent'
|
||||
sdl.Palette = ffi.typeof 'SDL_Palette'
|
||||
sdl.PixelFormat = ffi.typeof 'SDL_PixelFormat'
|
||||
sdl.Point = ffi.typeof 'SDL_Point'
|
||||
sdl.QuitEvent = ffi.typeof 'SDL_QuitEvent'
|
||||
sdl.RWops = ffi.typeof 'SDL_RWops'
|
||||
sdl.Rect = ffi.typeof 'SDL_Rect'
|
||||
sdl.RendererInfo = ffi.typeof 'SDL_RendererInfo'
|
||||
sdl.Surface = ffi.typeof 'SDL_Surface'
|
||||
sdl.SysWMEvent = ffi.typeof 'SDL_SysWMEvent'
|
||||
-- sdl.SysWMinfo = ffi.typeof 'SDL_SysWMinfo'
|
||||
sdl.SysWMmsg = ffi.typeof 'SDL_SysWMmsg'
|
||||
sdl.TextEditingEvent = ffi.typeof 'SDL_TextEditingEvent'
|
||||
sdl.TextInputEvent = ffi.typeof 'SDL_TextInputEvent'
|
||||
sdl.Texture = ffi.typeof 'SDL_Texture'
|
||||
sdl.TouchFingerEvent = ffi.typeof 'SDL_TouchFingerEvent'
|
||||
sdl.UserEvent = ffi.typeof 'SDL_UserEvent'
|
||||
sdl.WindowEvent = ffi.typeof 'SDL_WindowEvent'
|
||||
sdl.assert_data = ffi.typeof 'SDL_assert_data'
|
||||
sdl.atomic_t = ffi.typeof 'SDL_atomic_t'
|
||||
sdl.version = ffi.typeof 'SDL_version'
|
||||
|
||||
if sdl.init(sdl.INIT_VIDEO) ~= 0 then
|
||||
io.stderr:write(ffi.string(sdl.getError()))
|
||||
os.exit(1)
|
||||
end
|
||||
|
||||
return sdl
|
||||
36
luigi/backend/ffisdl/sdl2/LICENSE
Normal file
36
luigi/backend/ffisdl/sdl2/LICENSE
Normal file
@@ -0,0 +1,36 @@
|
||||
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
|
||||
Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
|
||||
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
|
||||
Copyright (c) 2011-2013 NYU (Clement Farabet)
|
||||
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston)
|
||||
Copyright (c) 2006 Idiap Research Institute (Samy Bengio)
|
||||
Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz)
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the names of Deepmind Technologies, NYU, NEC Laboratories America
|
||||
and IDIAP Research Institute nor the names of its contributors may be
|
||||
used to endorse or promote products derived from this software without
|
||||
specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
32
luigi/backend/ffisdl/sdl2/README.md
Normal file
32
luigi/backend/ffisdl/sdl2/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
sdl2-ffi
|
||||
========
|
||||
|
||||
A LuaJIT interface to SDL2
|
||||
|
||||
# Installation #
|
||||
|
||||
First, make sure SDL2 is installed on your system. This package only requires the binary shared libraries (.so, .dylib, .dll).
|
||||
Please see your package management system to install SDL2. You can also download yourself binaries on the
|
||||
[SDL2 web page](http://libsdl.org/download-2.0.php)
|
||||
|
||||
```sh
|
||||
luarocks install https://raw.github.com/torch/sdl2-ffi/master/rocks/sdl2-scm-1.rockspec
|
||||
```
|
||||
|
||||
*Note*: this SDL interface supports only SDL2, not SDL 1.2.
|
||||
|
||||
# Usage #
|
||||
|
||||
```lua
|
||||
local sdl = require 'sdl2'
|
||||
sdl.init(sdl.INIT_VIDEO)
|
||||
...
|
||||
```
|
||||
|
||||
All SDL C functions are available in the `sdl` namespace returned by require. The only difference is the naming, which is not prefixed
|
||||
by `SDL_` anymore. The same goes for all C defines (like `SDL_INIT_VIDEO`, which can now be accessed with `sdl.INIT_VIDEO`).
|
||||
|
||||
Although the interface is quite complete, there are still few defines not ported in this package. Fill free to post a message about it,
|
||||
or to request pulls.
|
||||
|
||||
|
||||
2599
luigi/backend/ffisdl/sdl2/cdefs.lua
Normal file
2599
luigi/backend/ffisdl/sdl2/cdefs.lua
Normal file
File diff suppressed because it is too large
Load Diff
58
luigi/backend/ffisdl/sdl2/defines.lua
Normal file
58
luigi/backend/ffisdl/sdl2/defines.lua
Normal file
@@ -0,0 +1,58 @@
|
||||
-- Function definitions which were not output by
|
||||
-- the C preprocessor
|
||||
|
||||
local sdl
|
||||
|
||||
local function registerdefines(sdl)
|
||||
|
||||
-- audio
|
||||
|
||||
function sdl.AUDIO_BITSIZE(x)
|
||||
return bit.band(x, sdl.AUDIO_MASK_BITSIZE)
|
||||
end
|
||||
|
||||
function sdl.AUDIO_ISFLOAT(x)
|
||||
return bit.band(x, sdl.AUDIO_MASK_DATATYPE) ~= 0
|
||||
end
|
||||
|
||||
function sdl.AUDIO_ISBIGENDIAN(x)
|
||||
return bit.band(x, sdl.AUDIO_MASK_ENDIAN) ~= 0
|
||||
end
|
||||
|
||||
function sdl.AUDIO_ISSIGNED(x)
|
||||
return bit.band(x, sdl.AUDIO_MASK_SIGNED) ~= 0
|
||||
end
|
||||
|
||||
function sdl.AUDIO_ISINT(x)
|
||||
return not sdl.AUDIO_ISFLOAT(x)
|
||||
end
|
||||
|
||||
function sdl.AUDIO_ISLITTLEENDIAN(x)
|
||||
return not sdl.AUDIO_ISBIGENDIAN(x)
|
||||
end
|
||||
|
||||
function sdl.AUDIO_ISUNSIGNED(x)
|
||||
return not sdl.AUDIO_ISSIGNED(x)
|
||||
end
|
||||
|
||||
function sdl.loadWAV(file, spec, audio_buf, audio_len)
|
||||
return sdl.loadWAV_RW(sdl.RWFromFile(file, "rb"), 1, spec, audio_buf, audio_len)
|
||||
end
|
||||
|
||||
-- surface
|
||||
sdl.blitSurface = sdl.upperBlit
|
||||
|
||||
function sdl.MUSTLOCK(S)
|
||||
return bit.band(S.flags, sdl.RLEACCEL)
|
||||
end
|
||||
|
||||
function sdl.loadBMP(file)
|
||||
return sdl.loadBMP_RW(sdl.RWFromFile(file, 'rb'), 1)
|
||||
end
|
||||
|
||||
function sdl.saveBMP(surface, file)
|
||||
return sdl.saveBMP_RW(surface, sdl.RWFromFile(file, 'wb'), 1)
|
||||
end
|
||||
end
|
||||
|
||||
return registerdefines
|
||||
1500
luigi/backend/ffisdl/sdl2/init.lua
Normal file
1500
luigi/backend/ffisdl/sdl2/init.lua
Normal file
File diff suppressed because it is too large
Load Diff
75
luigi/backend/ffisdl/spritebatch.lua
Normal file
75
luigi/backend/ffisdl/spritebatch.lua
Normal file
@@ -0,0 +1,75 @@
|
||||
local REL = (...):gsub('[^.]*$', '')
|
||||
|
||||
local sdl = require(REL .. 'sdl')
|
||||
|
||||
local SpriteBatch = setmetatable({}, { __call = function (self, ...)
|
||||
local object = setmetatable({}, { __index = self })
|
||||
return object, self.constructor(object, ...)
|
||||
end })
|
||||
|
||||
--[[
|
||||
spriteBatch = SpriteBatch( image, size )
|
||||
|
||||
Arguments
|
||||
|
||||
Image image
|
||||
The Image to use for the sprites.
|
||||
number size (1000)
|
||||
The max number of sprites.
|
||||
|
||||
Returns
|
||||
|
||||
SpriteBatch spriteBatch
|
||||
The new SpriteBatch.
|
||||
--]]
|
||||
function SpriteBatch:constructor (image)
|
||||
self.image = image
|
||||
self.sprites = {}
|
||||
end
|
||||
|
||||
--[[
|
||||
id = SpriteBatch:add( quad, x, y, r, sx, sy )
|
||||
|
||||
Arguments
|
||||
|
||||
Quad quad
|
||||
The Quad to add.
|
||||
number x
|
||||
The position to draw the object (x-axis).
|
||||
number y
|
||||
The position to draw the object (y-axis).
|
||||
number r (0)
|
||||
Orientation (radians). (not implemented)
|
||||
number sx (1)
|
||||
Scale factor (x-axis).
|
||||
number sy (sx)
|
||||
Scale factor (y-axis).
|
||||
|
||||
Returns
|
||||
|
||||
number id
|
||||
An identifier for the added sprite.
|
||||
--]]
|
||||
function SpriteBatch:add (quad, x, y, r, sx, sy)
|
||||
local sprites = self.sprites
|
||||
|
||||
sprites[#sprites + 1] = { quad = quad, x = x, y = y,
|
||||
sx = sx or 1, sy = sy or 1 }
|
||||
end
|
||||
|
||||
function SpriteBatch:draw ()
|
||||
local image = self.image
|
||||
local renderer = image.sdlRenderer
|
||||
local texture = image.sdlTexture
|
||||
|
||||
for _, sprite in ipairs(self.sprites) do
|
||||
local quad = sprite.quad
|
||||
local w = math.ceil(quad[3] * sprite.sx)
|
||||
local h = math.ceil(quad[4] * sprite.sy)
|
||||
local src = sdl.Rect(quad)
|
||||
local dst = sdl.Rect(sprite.x, sprite.y, w, h)
|
||||
sdl.renderCopy(renderer, texture, src, dst)
|
||||
end
|
||||
end
|
||||
|
||||
return SpriteBatch
|
||||
133
luigi/backend/love.lua
Normal file
133
luigi/backend/love.lua
Normal file
@@ -0,0 +1,133 @@
|
||||
local ROOT = (...):gsub('[^.]*.[^.]*$', '')
|
||||
|
||||
local Base = require(ROOT .. 'base')
|
||||
local Hooker = require(ROOT .. 'hooker')
|
||||
|
||||
local Backend = {}
|
||||
|
||||
Backend.run = function () end
|
||||
|
||||
Backend.Cursor = love.mouse.newCursor
|
||||
|
||||
Backend.Font = require(ROOT .. 'backend.love.font')
|
||||
|
||||
Backend.Image = love.graphics.newImage
|
||||
|
||||
Backend.Quad = love.graphics.newQuad
|
||||
|
||||
Backend.SpriteBatch = love.graphics.newSpriteBatch
|
||||
|
||||
Backend.draw = love.graphics.draw
|
||||
|
||||
Backend.drawRectangle = love.graphics.rectangle
|
||||
|
||||
Backend.print = love.graphics.print
|
||||
|
||||
Backend.printf = love.graphics.printf
|
||||
|
||||
Backend.getClipboardText = love.system.getClipboardText
|
||||
|
||||
Backend.setClipboardText = love.system.setClipboardText
|
||||
|
||||
Backend.getMousePosition = love.mouse.getPosition
|
||||
|
||||
Backend.getMousePosition = love.mouse.getPosition
|
||||
|
||||
Backend.getSystemCursor = love.mouse.getSystemCursor
|
||||
|
||||
Backend.getWindowSize = function ()
|
||||
return love.graphics.getWidth(), love.graphics.getHeight()
|
||||
end
|
||||
|
||||
Backend.getTime = love.timer.getTime
|
||||
|
||||
Backend.isKeyDown = love.keyboard.isDown
|
||||
|
||||
Backend.isMouseDown = love.mouse.isDown
|
||||
|
||||
Backend.pop = love.graphics.pop
|
||||
|
||||
local push = love.graphics.push
|
||||
|
||||
Backend.push = function ()
|
||||
return push 'all'
|
||||
end
|
||||
|
||||
Backend.quit = love.event.quit
|
||||
|
||||
Backend.setColor = love.graphics.setColor
|
||||
|
||||
Backend.setCursor = love.mouse.setCursor
|
||||
|
||||
Backend.setFont = function (font)
|
||||
return love.graphics.setFont(font.loveFont)
|
||||
end
|
||||
|
||||
Backend.setScissor = love.graphics.setScissor
|
||||
|
||||
function Backend.hide (layout)
|
||||
for _, item in ipairs(layout.hooks) do
|
||||
Hooker.unhook(item)
|
||||
end
|
||||
layout.hooks = {}
|
||||
end
|
||||
|
||||
local function hook (layout, key, method, hookLast)
|
||||
layout.hooks[#layout.hooks + 1] = Hooker.hook(love, key, method, hookLast)
|
||||
end
|
||||
|
||||
local getMouseButtonId, isMouseDown
|
||||
|
||||
if love._version_minor < 10 then
|
||||
getMouseButtonId = function (value)
|
||||
return value == 'l' and 1
|
||||
or value == 'r' and 2
|
||||
or value == 'm' and 3
|
||||
end
|
||||
isMouseDown = function ()
|
||||
return love.mouse.isDown('l', 'r', 'm')
|
||||
end
|
||||
else
|
||||
getMouseButtonId = function (value)
|
||||
return value
|
||||
end
|
||||
isMouseDown = function ()
|
||||
return love.mouse.isDown(1, 2, 3)
|
||||
end
|
||||
end
|
||||
|
||||
function Backend.show (layout)
|
||||
|
||||
local input = layout.input
|
||||
|
||||
hook(layout, 'draw', function ()
|
||||
input:handleDisplay(layout)
|
||||
end, true)
|
||||
hook(layout, 'resize', function (width, height)
|
||||
return input:handleReshape(layout, width, height)
|
||||
end)
|
||||
hook(layout, 'mousepressed', function (x, y, button)
|
||||
return input:handlePressStart(layout, getMouseButtonId(button), x, y)
|
||||
end)
|
||||
hook(layout, 'mousereleased', function (x, y, button)
|
||||
return input:handlePressEnd(layout, getMouseButtonId(button), x, y)
|
||||
end)
|
||||
hook(layout, 'mousemoved', function (x, y, dx, dy)
|
||||
if isMouseDown() then
|
||||
return input:handlePressedMove(layout, x, y)
|
||||
else
|
||||
return input:handleMove(layout, x, y)
|
||||
end
|
||||
end)
|
||||
hook(layout, 'keypressed', function (key, isRepeat)
|
||||
return input:handleKeyPress(layout, key, Backend.getMousePosition())
|
||||
end)
|
||||
hook(layout, 'keyreleased', function (key)
|
||||
return input:handleKeyRelease(layout, key, Backend.getMousePosition())
|
||||
end)
|
||||
hook(layout, 'textinput', function (text)
|
||||
return input:handleTextInput(layout, text, Backend.getMousePosition())
|
||||
end)
|
||||
end
|
||||
|
||||
return Backend
|
||||
65
luigi/backend/love/font.lua
Normal file
65
luigi/backend/love/font.lua
Normal file
@@ -0,0 +1,65 @@
|
||||
local Font = setmetatable({}, { __call = function (self, ...)
|
||||
local object = setmetatable({}, { __index = self })
|
||||
return object, self.constructor(object, ...)
|
||||
end })
|
||||
|
||||
local fontCache = {}
|
||||
|
||||
function Font:constructor (path, size, color)
|
||||
if not size then
|
||||
size = 12
|
||||
end
|
||||
if not color then
|
||||
color = { 0, 0, 0 }
|
||||
end
|
||||
local key = (path or '') .. '_' .. size
|
||||
|
||||
if not fontCache[key] then
|
||||
if path then
|
||||
fontCache[key] = love.graphics.newFont(path, size)
|
||||
else
|
||||
fontCache[key] = love.graphics.newFont(size)
|
||||
end
|
||||
end
|
||||
|
||||
self.loveFont = fontCache[key]
|
||||
self.color = color
|
||||
end
|
||||
|
||||
function Font:setAlignment (align)
|
||||
self.align = align
|
||||
end
|
||||
|
||||
function Font:setWidth (width)
|
||||
self.width = width
|
||||
end
|
||||
|
||||
function Font:getLineHeight ()
|
||||
return self.loveFont:getHeight()
|
||||
end
|
||||
|
||||
function Font:getAscender ()
|
||||
return self.loveFont:getAscent()
|
||||
end
|
||||
|
||||
function Font:getDescender ()
|
||||
return self.loveFont:getDescent()
|
||||
end
|
||||
|
||||
function Font:getAdvance (text)
|
||||
return (self.loveFont:getWidth(text))
|
||||
end
|
||||
|
||||
if love._version_minor < 10 then
|
||||
function Font:getWrappedHeight (text)
|
||||
local _, lines = self.loveFont:getWrap(text, self.width)
|
||||
return lines * self.loveFont:getHeight()
|
||||
end
|
||||
else
|
||||
function Font:getWrappedHeight (text)
|
||||
local _, lines = self.loveFont:getWrap(text, self.width)
|
||||
return #lines * self.loveFont:getHeight()
|
||||
end
|
||||
end
|
||||
|
||||
return Font
|
||||
@@ -1,67 +1,5 @@
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
local Base = require(ROOT .. 'base')
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
|
||||
local Font = Base:extend()
|
||||
|
||||
local cache = {}
|
||||
|
||||
function Font:constructor (path, size, color)
|
||||
if not size then
|
||||
size = 12
|
||||
end
|
||||
if not color then
|
||||
color = { 0, 0, 0 }
|
||||
end
|
||||
local key = (path or '') .. '_' .. size
|
||||
|
||||
if not cache[key] then
|
||||
if path then
|
||||
cache[key] = love.graphics.newFont(path, size)
|
||||
else
|
||||
cache[key] = love.graphics.newFont(size)
|
||||
end
|
||||
end
|
||||
|
||||
self.layout = {}
|
||||
self.font = cache[key]
|
||||
self.color = color
|
||||
end
|
||||
|
||||
function Font:setAlignment (align)
|
||||
self.layout.align = align
|
||||
end
|
||||
|
||||
function Font:setWidth (width)
|
||||
self.layout.width = width
|
||||
end
|
||||
|
||||
function Font:getLineHeight ()
|
||||
return self.font:getHeight()
|
||||
end
|
||||
|
||||
function Font:getAscender ()
|
||||
return self.font:getAscent()
|
||||
end
|
||||
|
||||
function Font:getDescender ()
|
||||
return self.font:getDescent()
|
||||
end
|
||||
|
||||
function Font:getAdvance (text)
|
||||
return (self.font:getWidth(text))
|
||||
end
|
||||
|
||||
if love._version_minor < 10 then
|
||||
function Font:getWrappedHeight (text)
|
||||
local _, lines = self.font:getWrap(text, self.layout.width)
|
||||
return lines * self.font:getHeight()
|
||||
end
|
||||
else
|
||||
function Font:getWrappedHeight (text)
|
||||
local _, lines = self.font:getWrap(text, self.layout.width)
|
||||
return #lines * self.font:getHeight()
|
||||
end
|
||||
end
|
||||
|
||||
return Font
|
||||
return Backend.Font
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
local Base = require(ROOT .. 'base')
|
||||
local Event = require(ROOT .. 'event')
|
||||
local Renderer = require(ROOT .. 'renderer')
|
||||
@@ -21,9 +22,9 @@ function Input:handleDisplay (layout)
|
||||
end
|
||||
|
||||
function Input:getModifierFlags ()
|
||||
local alt = love.keyboard.isDown('lalt', 'ralt') and 1 or 0
|
||||
local ctrl = love.keyboard.isDown('lctrl', 'rctrl') and 2 or 0
|
||||
local shift = love.keyboard.isDown('lshift', 'rshift') and 4 or 0
|
||||
local alt = Backend.isKeyDown('lalt', 'ralt') and 1 or 0
|
||||
local ctrl = Backend.isKeyDown('lctrl', 'rctrl') and 2 or 0
|
||||
local shift = Backend.isKeyDown('lshift', 'rshift') and 4 or 0
|
||||
|
||||
return alt + ctrl + shift
|
||||
end
|
||||
@@ -96,9 +97,9 @@ function Input:handleMove (layout, x, y)
|
||||
x = x, y = y
|
||||
})
|
||||
if widget.cursor then
|
||||
love.mouse.setCursor(love.mouse.getSystemCursor(widget.cursor))
|
||||
Backend.setCursor(Backend.getSystemCursor(widget.cursor))
|
||||
else
|
||||
love.mouse.setCursor()
|
||||
Backend.setCursor()
|
||||
end
|
||||
self.previousMoveWidget = widget
|
||||
end
|
||||
|
||||
113
luigi/layout.lua
113
luigi/layout.lua
@@ -16,12 +16,13 @@ and are trapped by the first layout that can handle the event
|
||||
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
local Base = require(ROOT .. 'base')
|
||||
local Event = require(ROOT .. 'event')
|
||||
local Widget = require(ROOT .. 'widget')
|
||||
local Input = require(ROOT .. 'input')
|
||||
local Style = require(ROOT .. 'style')
|
||||
local Hooker = require(ROOT .. 'hooker')
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
|
||||
local Layout = Base:extend()
|
||||
|
||||
@@ -50,8 +51,8 @@ function Layout:constructor (data, master)
|
||||
|
||||
self:addDefaultHandlers()
|
||||
|
||||
self.isShown = false
|
||||
self.hooks = {}
|
||||
self.isShown = false
|
||||
self.root = data
|
||||
|
||||
Widget(self, data)
|
||||
@@ -136,15 +137,17 @@ Hooks all appropriate Love events and callbacks.
|
||||
--]]--
|
||||
function Layout:show ()
|
||||
if self.isShown then
|
||||
self:unhook() -- return
|
||||
Backend.hide(self)
|
||||
self.isShown = nil
|
||||
end
|
||||
|
||||
self.isShown = true
|
||||
|
||||
if not self.input then
|
||||
self.input = Input.default -- Input(self)
|
||||
end
|
||||
|
||||
self:manageInput()
|
||||
Backend.show(self)
|
||||
self.root:reshape()
|
||||
end
|
||||
|
||||
@@ -158,7 +161,7 @@ function Layout:hide ()
|
||||
return
|
||||
end
|
||||
self.isShown = nil
|
||||
self:unhook()
|
||||
Backend.hide(self)
|
||||
end
|
||||
|
||||
--[[--
|
||||
@@ -251,7 +254,7 @@ function Layout:addDefaultHandlers ()
|
||||
|
||||
-- tab/shift-tab cycles focused widget
|
||||
if event.key == 'tab' then
|
||||
if love.keyboard.isDown('lshift', 'rshift') then
|
||||
if Backend.isKeyDown('lshift', 'rshift') then
|
||||
self:focusPreviousWidget()
|
||||
else
|
||||
self:focusNextWidget()
|
||||
@@ -301,104 +304,6 @@ function Layout:addDefaultHandlers ()
|
||||
end)
|
||||
end
|
||||
|
||||
-- event stuff
|
||||
|
||||
function Layout:hook (key, method, hookLast)
|
||||
self.hooks[#self.hooks + 1] = Hooker.hook(love, key, method, hookLast)
|
||||
end
|
||||
|
||||
function Layout:unhook ()
|
||||
for _, item in ipairs(self.hooks) do
|
||||
Hooker.unhook(item)
|
||||
end
|
||||
self.hooks = {}
|
||||
end
|
||||
|
||||
local getMouseButtonId, isMouseDown
|
||||
|
||||
if love._version_minor < 10 then
|
||||
getMouseButtonId = function (value)
|
||||
return value == 'l' and 1
|
||||
or value == 'r' and 2
|
||||
or value == 'm' and 3
|
||||
end
|
||||
isMouseDown = function ()
|
||||
return love.mouse.isDown('l', 'r', 'm')
|
||||
end
|
||||
else
|
||||
getMouseButtonId = function (value)
|
||||
return value
|
||||
end
|
||||
isMouseDown = function ()
|
||||
return love.mouse.isDown(1, 2, 3)
|
||||
end
|
||||
end
|
||||
|
||||
function Layout:manageInput ()
|
||||
if self.isShown then
|
||||
return
|
||||
end
|
||||
self.isShown = true
|
||||
|
||||
local input = self.input
|
||||
|
||||
self:hook('draw', function ()
|
||||
input:handleDisplay(self)
|
||||
end, true)
|
||||
self:hook('resize', function (width, height)
|
||||
return input:handleReshape(self, width, height)
|
||||
end)
|
||||
self:hook('mousepressed', function (x, y, button)
|
||||
return input:handlePressStart(self, getMouseButtonId(button), x, y)
|
||||
end)
|
||||
self:hook('mousereleased', function (x, y, button)
|
||||
return input:handlePressEnd(self, getMouseButtonId(button), x, y)
|
||||
end)
|
||||
self:hook('mousemoved', function (x, y, dx, dy)
|
||||
if isMouseDown() then
|
||||
return input:handlePressedMove(self, x, y)
|
||||
else
|
||||
return input:handleMove(self, x, y)
|
||||
end
|
||||
end)
|
||||
self:hook('keypressed', function (key, isRepeat)
|
||||
return input:handleKeyPress(
|
||||
self, key, love.mouse.getX(), love.mouse.getY())
|
||||
end)
|
||||
self:hook('keyreleased', function (key)
|
||||
return input:handleKeyRelease(
|
||||
self, key, love.mouse.getX(), love.mouse.getY())
|
||||
end)
|
||||
self:hook('textinput', function (text)
|
||||
return input:handleTextInput(
|
||||
self, text, love.mouse.getX(), love.mouse.getY())
|
||||
end)
|
||||
end
|
||||
|
||||
-- event binders
|
||||
|
||||
Event.injectBinders(Layout)
|
||||
|
||||
-- ffi
|
||||
|
||||
function Layout:showMessage () end
|
||||
|
||||
local _, ffi = pcall(require, 'ffi')
|
||||
if ffi then
|
||||
|
||||
ffi.cdef [[
|
||||
int SDL_ShowSimpleMessageBox(
|
||||
uint32_t flags,
|
||||
const char* title,
|
||||
const char* message,
|
||||
void* window
|
||||
);
|
||||
]]
|
||||
|
||||
function Layout:showMessage (title, message)
|
||||
ffi.C.SDL_ShowSimpleMessageBox(0, title, message, nil)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return Layout
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
local Base = require(ROOT .. 'base')
|
||||
local Event = require(ROOT .. 'event')
|
||||
local Font = require(ROOT .. 'font')
|
||||
@@ -11,7 +12,7 @@ local sliceCache = {}
|
||||
|
||||
function Renderer:loadImage (path)
|
||||
if not imageCache[path] then
|
||||
imageCache[path] = love.graphics.newImage(path)
|
||||
imageCache[path] = Backend.Image(path)
|
||||
end
|
||||
|
||||
return imageCache[path]
|
||||
@@ -26,7 +27,7 @@ function Renderer:loadSlices (path)
|
||||
local image = self:loadImage(path)
|
||||
local iw, ih = image:getWidth(), image:getHeight()
|
||||
local w, h = math.floor(iw / 3), math.floor(ih / 3)
|
||||
local Quad = love.graphics.newQuad
|
||||
local Quad = Backend.Quad
|
||||
|
||||
slices.image = image
|
||||
slices.width = w
|
||||
@@ -49,6 +50,7 @@ function Renderer:loadSlices (path)
|
||||
end
|
||||
|
||||
function Renderer:renderSlices (widget)
|
||||
|
||||
local path = widget.slices
|
||||
if not path then return end
|
||||
|
||||
@@ -56,7 +58,7 @@ function Renderer:renderSlices (widget)
|
||||
|
||||
local slices = self:loadSlices(path)
|
||||
|
||||
local batch = love.graphics.newSpriteBatch(slices.image)
|
||||
local batch = Backend.SpriteBatch(slices.image)
|
||||
|
||||
local xScale = ((x2 - x1) - slices.width * 2) / slices.width
|
||||
local yScale = ((y2 - y1) - slices.height * 2) / slices.height
|
||||
@@ -79,27 +81,27 @@ function Renderer:renderSlices (widget)
|
||||
batch:add(slices.bottomLeft, x1, y2 - slices.height)
|
||||
batch:add(slices.bottomRight, x2 - slices.width, y2 - slices.height)
|
||||
|
||||
love.graphics.draw(batch)
|
||||
Backend.draw(batch)
|
||||
end
|
||||
|
||||
function Renderer:renderBackground (widget)
|
||||
if not widget.background then return end
|
||||
local x1, y1, x2, y2 = widget:getRectangle(true)
|
||||
|
||||
love.graphics.push('all')
|
||||
love.graphics.setColor(widget.background)
|
||||
love.graphics.rectangle('fill', x1, y1, x2 - x1, y2 - y1)
|
||||
love.graphics.pop()
|
||||
Backend.push()
|
||||
Backend.setColor(widget.background)
|
||||
Backend.drawRectangle('fill', x1, y1, x2 - x1, y2 - y1)
|
||||
Backend.pop()
|
||||
end
|
||||
|
||||
function Renderer:renderOutline (widget)
|
||||
if not widget.outline then return end
|
||||
local x1, y1, x2, y2 = widget:getRectangle(true)
|
||||
|
||||
love.graphics.push('all')
|
||||
love.graphics.setColor(widget.outline)
|
||||
love.graphics.rectangle('line', x1 + 0.5, y1 + 0.5, x2 - x1, y2 - y1)
|
||||
love.graphics.pop()
|
||||
Backend.push()
|
||||
Backend.setColor(widget.outline)
|
||||
Backend.drawRectangle('line', x1 + 0.5, y1 + 0.5, x2 - x1, y2 - y1)
|
||||
Backend.pop()
|
||||
end
|
||||
|
||||
-- returns icon coordinates and rectangle with remaining space
|
||||
@@ -188,9 +190,9 @@ function Renderer:renderIconAndText (widget)
|
||||
return
|
||||
end
|
||||
|
||||
love.graphics.push('all')
|
||||
Backend.push()
|
||||
|
||||
love.graphics.setScissor(x1, y1, x2 - x1, y2 - y1)
|
||||
Backend.setScissor(x1, y1, x2 - x1, y2 - y1)
|
||||
|
||||
local iconX, iconY, textX, textY, font
|
||||
|
||||
@@ -228,23 +230,20 @@ function Renderer:renderIconAndText (widget)
|
||||
if icon then
|
||||
iconX, iconY = math.floor(iconX), math.floor(iconY)
|
||||
if widget.tint then
|
||||
love.graphics.setBlendMode('alpha', true)
|
||||
love.graphics.setColor(widget.tint)
|
||||
Backend.setColor(widget.tint)
|
||||
end
|
||||
love.graphics.draw(icon, iconX, iconY)
|
||||
Backend.draw(icon, iconX, iconY)
|
||||
end
|
||||
|
||||
-- draw the text
|
||||
if text and x2 > x1 then
|
||||
textX, textY = math.floor(textX), math.floor(textY)
|
||||
love.graphics.setFont(font.font)
|
||||
love.graphics.setColor(font.color)
|
||||
|
||||
local layout = font.layout
|
||||
love.graphics.printf(text, textX, textY, x2 - x1, layout.align)
|
||||
Backend.setFont(font)
|
||||
Backend.setColor(font.color)
|
||||
Backend.printf(text, textX, textY, x2 - x1, font.align)
|
||||
end
|
||||
|
||||
love.graphics.pop()
|
||||
Backend.pop()
|
||||
end
|
||||
|
||||
function Renderer:renderChildren (widget)
|
||||
|
||||
318
luigi/utf8.lua
Normal file
318
luigi/utf8.lua
Normal file
@@ -0,0 +1,318 @@
|
||||
--utf8 module (Cosmin Apreutesei, public domain).
|
||||
--byte indices are i's, char (codepoint) indices are ci's.
|
||||
--invalid characters are counted as 1-byte chars so they don't get lost. validate/sanitize beforehand as needed.
|
||||
|
||||
local utf8 = {}
|
||||
|
||||
--byte index of the next char after the char at byte index i, followed by a valid flag for the char at byte index i.
|
||||
--nil if not found. invalid characters are iterated as 1-byte chars.
|
||||
function utf8.next_raw(s, i)
|
||||
if not i then
|
||||
if #s == 0 then return nil end
|
||||
return 1, true --fake flag (doesn't matter since this flag is not to be taken as full validation)
|
||||
end
|
||||
if i > #s then return end
|
||||
local c = s:byte(i)
|
||||
if c >= 0x00 and c <= 0x7F then
|
||||
i = i + 1
|
||||
elseif c >= 0xC2 and c <= 0xDF then
|
||||
i = i + 2
|
||||
elseif c >= 0xE0 and c <= 0xEF then
|
||||
i = i + 3
|
||||
elseif c >= 0xF0 and c <= 0xF4 then
|
||||
i = i + 4
|
||||
else --invalid
|
||||
return i + 1, false
|
||||
end
|
||||
if i > #s then return end
|
||||
return i, true
|
||||
end
|
||||
|
||||
--next() is the generic iterator and can be replaced for different semantics. next_raw() must preserve its semantics.
|
||||
utf8.next = utf8.next_raw
|
||||
|
||||
--iterate chars, returning the byte index where each char starts
|
||||
function utf8.byte_indices(s, previ)
|
||||
return utf8.next, s, previ
|
||||
end
|
||||
|
||||
--number of chars in string
|
||||
function utf8.len(s)
|
||||
local len = 0
|
||||
for _ in utf8.byte_indices(s) do
|
||||
len = len + 1
|
||||
end
|
||||
return len
|
||||
end
|
||||
|
||||
--byte index given char index. nil if the index is outside the string.
|
||||
function utf8.byte_index(s, target_ci)
|
||||
if target_ci < 1 then return end
|
||||
local ci = 0
|
||||
for i in utf8.byte_indices(s) do
|
||||
ci = ci + 1
|
||||
if ci == target_ci then
|
||||
return i
|
||||
end
|
||||
end
|
||||
assert(target_ci > ci, 'invalid index')
|
||||
end
|
||||
|
||||
--char index given byte index. nil if the index is outside the string.
|
||||
function utf8.char_index(s, target_i)
|
||||
if target_i < 1 or target_i > #s then return end
|
||||
local ci = 0
|
||||
for i in utf8.byte_indices(s) do
|
||||
ci = ci + 1
|
||||
if i == target_i then
|
||||
return ci
|
||||
end
|
||||
end
|
||||
error'invalid index'
|
||||
end
|
||||
|
||||
--byte index of the prev. char before the char at byte index i, which defaults to #s + 1.
|
||||
--nil if the index is outside the 2..#s+1 range.
|
||||
--NOTE: unlike next(), this is a O(N) operation!
|
||||
function utf8.prev(s, nexti)
|
||||
nexti = nexti or #s + 1
|
||||
if nexti <= 1 or nexti > #s + 1 then return end
|
||||
local lasti, lastvalid = utf8.next(s)
|
||||
for i, valid in utf8.byte_indices(s) do
|
||||
if i == nexti then
|
||||
return lasti, lastvalid
|
||||
end
|
||||
lasti, lastvalid = i, valid
|
||||
end
|
||||
if nexti == #s + 1 then
|
||||
return lasti, lastvalid
|
||||
end
|
||||
error'invalid index'
|
||||
end
|
||||
|
||||
--iterate chars in reverse order, returning the byte index where each char starts.
|
||||
function utf8.byte_indices_reverse(s, nexti)
|
||||
if #s < 200 then
|
||||
--using prev() is a O(N^2/2) operation, ok for small strings (200 chars need 40,000 iterations)
|
||||
return utf8.prev, s, nexti
|
||||
else
|
||||
--store byte indices in a table and iterate them in reverse.
|
||||
--this is 40x slower than byte_indices() but still fast at 2mil chars/second (but eats RAM and makes garbage).
|
||||
local t = {}
|
||||
for i in utf8.byte_indices(s) do
|
||||
if nexti and i >= nexti then break end
|
||||
table.insert(t, i)
|
||||
end
|
||||
local i = #t + 1
|
||||
return function()
|
||||
i = i - 1
|
||||
return t[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--sub based on char indices, which, unlike with standard string.sub(), can't be negative.
|
||||
--start_ci can be 1..inf and end_ci can be 0..inf. end_ci can be nil meaning last char.
|
||||
--if start_ci is out of range or end_ci < start_ci, the empty string is returned.
|
||||
--if end_ci is out of range, it is considered to be the last position in the string.
|
||||
function utf8.sub(s, start_ci, end_ci)
|
||||
--assert for positive indices because we might implement negative indices in the future.
|
||||
assert(start_ci >= 1)
|
||||
assert(not end_ci or end_ci >= 0)
|
||||
local ci = 0
|
||||
local start_i, end_i
|
||||
for i in utf8.byte_indices(s) do
|
||||
ci = ci + 1
|
||||
if ci == start_ci then
|
||||
start_i = i
|
||||
end
|
||||
if ci == end_ci then
|
||||
end_i = i
|
||||
end
|
||||
end
|
||||
if not start_i then
|
||||
assert(start_ci > ci, 'invalid index')
|
||||
return ''
|
||||
end
|
||||
if end_ci and not end_i then
|
||||
if end_ci < start_ci then
|
||||
return ''
|
||||
end
|
||||
assert(end_ci > ci, 'invalid index')
|
||||
end
|
||||
return s:sub(start_i, end_i)
|
||||
end
|
||||
|
||||
--check if a string contains a substring at byte index i without making garbage.
|
||||
--nil if the index is out of range. true if searching for the empty string.
|
||||
function utf8.contains(s, i, sub)
|
||||
if i < 1 or i > #s then return nil end
|
||||
for si = 1, #sub do
|
||||
if s:byte(i + si - 1) ~= sub:byte(si) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--count the number of occurences of a substring in a string. the substring cannot be the empty string.
|
||||
function utf8.count(s, sub)
|
||||
assert(#sub > 0)
|
||||
local count = 0
|
||||
local i = 1
|
||||
while i do
|
||||
if utf8.contains(s, i, sub) then
|
||||
count = count + 1
|
||||
i = i + #sub
|
||||
if i > #s then break end
|
||||
else
|
||||
i = utf8.next(s, i)
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
--utf8 validation and sanitization
|
||||
|
||||
--check if there's a valid utf8 codepoint at byte index i. valid ranges for each utf8 byte are:
|
||||
-- byte 1 2 3 4
|
||||
--------------------------------------------
|
||||
-- 00 - 7F
|
||||
-- C2 - DF 80 - BF
|
||||
-- E0 A0 - BF 80 - BF
|
||||
-- E1 - EC 80 - BF 80 - BF
|
||||
-- ED 80 - 9F 80 - BF
|
||||
-- EE - EF 80 - BF 80 - BF
|
||||
-- F0 90 - BF 80 - BF 80 - BF
|
||||
-- F1 - F3 80 - BF 80 - BF 80 - BF
|
||||
-- F4 80 - 8F 80 - BF 80 - BF
|
||||
function utf8.isvalid(s, i)
|
||||
local c = s:byte(i)
|
||||
if not c then
|
||||
return false
|
||||
elseif c >= 0x00 and c <= 0x7F then
|
||||
return true
|
||||
elseif c >= 0xC2 and c <= 0xDF then
|
||||
local c2 = s:byte(i + 1)
|
||||
return c2 and c2 >= 0x80 and c2 <= 0xBF
|
||||
elseif c >= 0xE0 and c <= 0xEF then
|
||||
local c2 = s:byte(i + 1)
|
||||
local c3 = s:byte(i + 2)
|
||||
if c == 0xE0 then
|
||||
return c2 and c3 and
|
||||
c2 >= 0xA0 and c2 <= 0xBF and
|
||||
c3 >= 0x80 and c3 <= 0xBF
|
||||
elseif c >= 0xE1 and c <= 0xEC then
|
||||
return c2 and c3 and
|
||||
c2 >= 0x80 and c2 <= 0xBF and
|
||||
c3 >= 0x80 and c3 <= 0xBF
|
||||
elseif c == 0xED then
|
||||
return c2 and c3 and
|
||||
c2 >= 0x80 and c2 <= 0x9F and
|
||||
c3 >= 0x80 and c3 <= 0xBF
|
||||
elseif c >= 0xEE and c <= 0xEF then
|
||||
if c == 0xEF and c2 == 0xBF and (c3 == 0xBE or c3 == 0xBF) then
|
||||
return false --uFFFE and uFFFF non-characters
|
||||
end
|
||||
return c2 and c3 and
|
||||
c2 >= 0x80 and c2 <= 0xBF and
|
||||
c3 >= 0x80 and c3 <= 0xBF
|
||||
end
|
||||
elseif c >= 0xF0 and c <= 0xF4 then
|
||||
local c2 = s:byte(i + 1)
|
||||
local c3 = s:byte(i + 2)
|
||||
local c4 = s:byte(i + 3)
|
||||
if c == 0xF0 then
|
||||
return c2 and c3 and c4 and
|
||||
c2 >= 0x90 and c2 <= 0xBF and
|
||||
c3 >= 0x80 and c3 <= 0xBF and
|
||||
c4 >= 0x80 and c4 <= 0xBF
|
||||
elseif c >= 0xF1 and c <= 0xF3 then
|
||||
return c2 and c3 and c4 and
|
||||
c2 >= 0x80 and c2 <= 0xBF and
|
||||
c3 >= 0x80 and c3 <= 0xBF and
|
||||
c4 >= 0x80 and c4 <= 0xBF
|
||||
elseif c == 0xF4 then
|
||||
return c2 and c3 and c4 and
|
||||
c2 >= 0x80 and c2 <= 0x8F and
|
||||
c3 >= 0x80 and c3 <= 0xBF and
|
||||
c4 >= 0x80 and c4 <= 0xBF
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
--byte index of the next valid utf8 char after the char at byte index i.
|
||||
--nil if indices go out of range. invalid characters are skipped.
|
||||
function utf8.next_valid(s, i)
|
||||
local valid
|
||||
i, valid = utf8.next_raw(s, i)
|
||||
while i and (not valid or not utf8.isvalid(s, i)) do
|
||||
i, valid = utf8.next(s, i)
|
||||
end
|
||||
return i
|
||||
end
|
||||
|
||||
--iterate valid chars, returning the byte index where each char starts
|
||||
function utf8.valid_byte_indices(s)
|
||||
return utf8.next_valid, s
|
||||
end
|
||||
|
||||
--assert that a string only contains valid utf8 characters
|
||||
function utf8.validate(s)
|
||||
for i, valid in utf8.byte_indices(s) do
|
||||
if not valid or not utf8.isvalid(s, i) then
|
||||
error(string.format('invalid utf8 char at #%d', i))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function table_lookup(s, i, j, t)
|
||||
return t[s:sub(i, j)]
|
||||
end
|
||||
|
||||
--replace characters in string based on a function f(s, i, j, ...) -> replacement_string | nil
|
||||
function utf8.replace(s, f, ...)
|
||||
if type(f) == 'table' then
|
||||
return utf8.replace(s, table_lookup, f)
|
||||
end
|
||||
if s == '' then
|
||||
return s
|
||||
end
|
||||
local t = {}
|
||||
local lasti = 1
|
||||
for i in utf8.byte_indices(s) do
|
||||
local nexti = utf8.next(s, i) or #s + 1
|
||||
local repl = f(s, i, nexti - 1, ...)
|
||||
if repl then
|
||||
table.insert(t, s:sub(lasti, i - 1))
|
||||
table.insert(t, repl)
|
||||
lasti = nexti
|
||||
end
|
||||
end
|
||||
table.insert(t, s:sub(lasti))
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
local function replace_invalid(s, i, j, repl_char)
|
||||
if not utf8.isvalid(s, i) then
|
||||
return repl_char
|
||||
end
|
||||
end
|
||||
|
||||
--replace invalid utf8 chars with a replacement char
|
||||
function utf8.sanitize(s, repl_char)
|
||||
repl_char = repl_char or '<EFBFBD>' --\uFFFD
|
||||
return utf8.replace(s, replace_invalid, repl_char)
|
||||
end
|
||||
|
||||
-- added for partial compatibility with lua 5.3
|
||||
|
||||
function utf8.offset(s, char_offset, byte_index)
|
||||
local ci = utf8.char_index(s, byte_index)
|
||||
return utf8.byte_index(s, ci + char_offset)
|
||||
end
|
||||
|
||||
utf8.codes = utf8.byte_indices
|
||||
|
||||
return utf8
|
||||
@@ -6,6 +6,7 @@ Widget class.
|
||||
|
||||
local ROOT = (...):gsub('[^.]*$', '')
|
||||
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
local Event = require(ROOT .. 'event')
|
||||
local Font = require(ROOT .. 'font')
|
||||
|
||||
@@ -340,8 +341,9 @@ function Widget:calculateDimension (name)
|
||||
local min = (name == 'width') and (self.minwidth or 0)
|
||||
or (self.minheight or 0)
|
||||
|
||||
local max = name == 'width' and love.graphics.getWidth()
|
||||
or love.graphics.getHeight()
|
||||
local windowWidth, windowHeight = Backend.getWindowSize()
|
||||
|
||||
local max = name == 'width' and windowWidth or windowHeight
|
||||
|
||||
if self[name] then
|
||||
self.dimensions[name] = clamp(self[name], min, max)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
local utf8 = require 'utf8'
|
||||
local ROOT = (...):gsub('[^.]*.[^.]*$', '')
|
||||
|
||||
local blendMultiply = love._version_minor < 10 and 'multiplicative'
|
||||
or 'multiply'
|
||||
local utf8 = require(ROOT .. 'utf8')
|
||||
local Backend = require(ROOT .. 'backend')
|
||||
|
||||
local function scrollToCaret (self)
|
||||
local x1, y1, x2, y2 = self:getRectangle(true, true)
|
||||
@@ -23,9 +23,9 @@ local function scrollToCaret (self)
|
||||
end
|
||||
|
||||
local function findCaretFromText (self, text)
|
||||
local font = self.fontData.font
|
||||
local font = self.fontData
|
||||
local x1, y1, x2, y2 = self:getRectangle(true, true)
|
||||
return #text, font:getWidth(text) + x1 - self.scrollX
|
||||
return #text, font:getAdvance(text) + x1 - self.scrollX
|
||||
end
|
||||
|
||||
local function setCaretFromText (self, text, mode)
|
||||
@@ -45,15 +45,14 @@ end
|
||||
local function findCaretFromPoint (self, x, y)
|
||||
local x1, y1, x2, y2 = self:getRectangle(true, true)
|
||||
|
||||
local font = self.fontData.font
|
||||
local font = self.fontData
|
||||
local width, lastWidth = 0
|
||||
local characters = utf8.codes(self.value)
|
||||
local lastPosition = 0
|
||||
|
||||
local function checkPosition (position)
|
||||
local text = self.value:sub(1, position - 1)
|
||||
lastWidth = width
|
||||
width = font:getWidth(text)
|
||||
width = font:getAdvance(text)
|
||||
if width > x + self.scrollX - x1 then
|
||||
if position == 1 then
|
||||
return 0, x1 - self.scrollX
|
||||
@@ -149,13 +148,13 @@ local function copyRangeToClipboard (self)
|
||||
local text = self.value
|
||||
local first, last = getRange(self)
|
||||
if last >= first + 1 then
|
||||
love.system.setClipboardText(text:sub(first + 1, last))
|
||||
Backend.setClipboardText(text:sub(first + 1, last))
|
||||
end
|
||||
end
|
||||
|
||||
local function pasteFromClipboard (self)
|
||||
local text = self.value
|
||||
local pasted = love.system.getClipboardText() or ''
|
||||
local pasted = Backend.getClipboardText() or ''
|
||||
local first, last = getRange(self)
|
||||
local left = text:sub(1, first) .. pasted
|
||||
text = left .. text:sub(last + 1)
|
||||
@@ -203,22 +202,22 @@ return function (self)
|
||||
|
||||
elseif event.key == 'left' then
|
||||
|
||||
moveCaretLeft(self, love.keyboard.isDown('lshift', 'rshift'))
|
||||
moveCaretLeft(self, Backend.isKeyDown('lshift', 'rshift'))
|
||||
|
||||
elseif event.key == 'right' then
|
||||
|
||||
moveCaretRight(self, love.keyboard.isDown('lshift', 'rshift'))
|
||||
moveCaretRight(self, Backend.isKeyDown('lshift', 'rshift'))
|
||||
|
||||
elseif event.key == 'x' and love.keyboard.isDown('lctrl', 'rctrl') then
|
||||
elseif event.key == 'x' and Backend.isKeyDown('lctrl', 'rctrl') then
|
||||
|
||||
copyRangeToClipboard(self)
|
||||
deleteRange(self)
|
||||
|
||||
elseif event.key == 'c' and love.keyboard.isDown('lctrl', 'rctrl') then
|
||||
elseif event.key == 'c' and Backend.isKeyDown('lctrl', 'rctrl') then
|
||||
|
||||
copyRangeToClipboard(self)
|
||||
|
||||
elseif event.key == 'v' and love.keyboard.isDown('lctrl', 'rctrl') then
|
||||
elseif event.key == 'v' and Backend.isKeyDown('lctrl', 'rctrl') then
|
||||
|
||||
pasteFromClipboard(self)
|
||||
|
||||
@@ -230,28 +229,30 @@ return function (self)
|
||||
local startX, endX = self.startX or 0, self.endX or 0
|
||||
local x1, y1, x2, y2 = self:getRectangle(true, true)
|
||||
local width, height = endX - startX, y2 - y1
|
||||
local fontData = self.fontData
|
||||
local font = fontData.font
|
||||
local textColor = fontData.color
|
||||
local textTop = math.floor(y1 + ((y2 - y1) - font:getHeight()) / 2)
|
||||
local font = self.fontData
|
||||
local textColor = font.color
|
||||
local textTop = math.floor(y1 + ((y2 - y1) - font:getLineHeight()) / 2)
|
||||
|
||||
love.graphics.push('all')
|
||||
love.graphics.setScissor(x1, y1, x2 - x1, y2 - y1)
|
||||
love.graphics.setFont(font)
|
||||
love.graphics.setColor(textColor)
|
||||
love.graphics.print(self.value, x1 - self.scrollX, textTop)
|
||||
Backend.push()
|
||||
Backend.setScissor(x1, y1, x2 - x1, y2 - y1)
|
||||
Backend.setFont(font)
|
||||
|
||||
-- draw highlight
|
||||
Backend.setColor(self.highlight)
|
||||
Backend.drawRectangle('fill', startX, y1, width, height)
|
||||
if Backend.getTime() % 2 < 1.75 then
|
||||
Backend.setColor(textColor)
|
||||
Backend.drawRectangle('fill', endX, y1, 1, height)
|
||||
end
|
||||
|
||||
-- draw text
|
||||
Backend.setColor(textColor)
|
||||
Backend.print(self.value, x1 - self.scrollX, textTop)
|
||||
if not self.focused then
|
||||
love.graphics.pop()
|
||||
Backend.pop()
|
||||
return
|
||||
end
|
||||
-- TODO: expose highlight blend mode for dark themes
|
||||
love.graphics.setBlendMode(blendMultiply)
|
||||
love.graphics.setColor(self.highlight)
|
||||
love.graphics.rectangle('fill', startX, y1, width, height)
|
||||
if love.timer.getTime() % 2 < 1.75 then
|
||||
love.graphics.setColor(textColor)
|
||||
love.graphics.rectangle('fill', endX, y1, 1, height)
|
||||
end
|
||||
love.graphics.pop()
|
||||
|
||||
Backend.pop()
|
||||
end)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user