Files
love-luigi/luigi/backend/ffisdl.lua
2015-12-07 04:22:36 -05:00

332 lines
9.2 KiB
Lua

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 Text = require((...) .. '.text')
local IntOut = ffi.typeof 'int[1]'
-- create window and renderer
sdl.setHint(sdl.HINT_VIDEO_ALLOW_SCREENSAVER, '1')
local window = sdl.createWindow('', 0, 0, 800, 600,
sdl.WINDOW_SHOWN + sdl.WINDOW_RESIZABLE)
if window == nil then
error(ffi.string(sdl.getError()))
end
ffi.gc(window, sdl.destroyWindow)
local renderer = sdl.createRenderer(window, -1,
sdl.RENDERER_ACCELERATED + sdl.RENDERER_PRESENTVSYNC)
if renderer == nil then
error(ffi.string(sdl.getError()))
end
ffi.gc(renderer, sdl.destroyRenderer)
local Backend = {}
Backend.sdl = sdl
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,
wheelmoved = 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
local window = event.window
callback.resize(window.data1, window.data2)
elseif event.type == sdl.MOUSEBUTTONDOWN then
local button = event.button
callback.mousepressed(button.x, button.y, button.button)
elseif event.type == sdl.MOUSEBUTTONUP then
local button = event.button
callback.mousereleased(button.x, button.y, button.button)
elseif event.type == sdl.MOUSEMOTION then
local motion = event.motion
callback.mousemoved(motion.x, 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))
elseif event.type == sdl.MOUSEWHEEL then
local wheel = event.wheel
callback.wheelmoved(wheel.x, wheel.y)
end
end
sdl.renderSetClipRect(renderer, nil)
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.Text = function (...)
return Text(renderer, ...)
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)
if drawable.draw then
return drawable:draw(x, y, sx, sy)
end
if drawable.sdlTexture == nil
or drawable.sdlRenderer == nil
or drawable.getWidth == nil
or drawable.getHeight == nil
then return
end
local w = drawable:getWidth() * (sx or 1)
local h = drawable:getHeight() * (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(drawable.sdlRenderer, -1, -1)
-- Draw the image.
sdl.renderCopy(drawable.sdlRenderer, drawable.sdlTexture,
nil, sdl.Rect(x, y, w, h))
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()
local lastColor
-- 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(lastColor or { 0, 0, 0, 255 })
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 = function ()
return ffi.string(sdl.getClipboardText())
end
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
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]
local color = history.color or { 0, 0, 0, 255 }
sdl.setRenderDrawColor(renderer,
color[1], color[2], color[3], color[4] or 255)
sdl.renderSetClipRect(renderer, history.scissor) -- 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)
hook(layout, 'wheelmoved', function (x, y)
return input:handleWheelMove(layout, x, y)
end)
end
return Backend