diff --git a/src/lib/luigi/attribute.lua b/src/lib/luigi/attribute.lua new file mode 100644 index 0000000..915a9f9 --- /dev/null +++ b/src/lib/luigi/attribute.lua @@ -0,0 +1,602 @@ +--[[-- +Widget attributes. + +This module defines "attributes" (special fields) that are +recognized by all widgets. Their interpretation may vary +depending on the `type` of widget. Some widget types may also +recognize additional attributes. + +Setting attributes can have side effects. For example, setting +`height` or `width` causes the parent widget and its descendants +to recalculate their size and position. +--]]-- + +local ROOT = (...):gsub('[^.]*$', '') + +local Shortcut = require(ROOT .. 'shortcut') + +local Attribute = {} + +local function cascade (widget, attribute) + local value = rawget(widget, 'attributes')[attribute] + if value ~= nil then return value end + local parent = rawget(widget, 'parent') + return parent and parent[attribute] +end + +--[[-- +Type of widget. + +Should contain a string identifying the widget's type. +After the layout is built, may be replaced by an array +of strings identifying multiple types. This is used +internally by some widgets to provide information about +the widget's state to the theme (themes describe the +appearance of widgets according to their type). + +If a type is registered with the widget's layout, the registered +type initializer function will run once when the widget is constructed. + +@see Widget.register + +@attrib type +--]]-- +Attribute.type = {} + +function Attribute.type.set (widget, value) + local oldType = widget.attributes.type + + widget.attributes.type = value + + if value and not widget.hasType then + widget.hasType = true + local Widget = require(ROOT .. 'widget') + local decorate = Widget.typeDecorators[value] + + if decorate then + decorate(widget) + end + end +end + +--[[-- +Widget identifier. + +Should contain a unique string identifying the widget, if present. + +A reference to the widget will be stored in the associated layout +in a property having the same name as the widget's id. + +Setting this attribute re-registers the widget with its layout. + +@attrib id +--]]-- +Attribute.id = {} + +function Attribute.id.set (widget, value) + local layout = widget.layout.master or widget.layout + local oldValue = widget.attributes.id + + if oldValue then + layout[oldValue] = nil + end + + if value then + layout[value] = widget + end + + widget.attributes.id = value +end + +--[[-- +Widget value. + +Some widget types expect the value to be of a specific type and +within a specific range. For example, `slider` and `progress` +widgets expect a normalized number, `text` widgets expect +a string, and `check` and `radio` widgets expect a boolean. + +Setting this attribute bubbles the `Change` event. + +@attrib value +--]]-- +Attribute.value = {} + +function Attribute.value.set (widget, value) + local oldValue = widget.value + widget.attributes.value = value + widget:bubbleEvent('Change', { value = value, oldValue = oldValue }) +end + +--[[-- +Solidity. + +Should true or false. + +@attrib icon +--]]-- +Attribute.solid = {} + +function Attribute.solid.set (widget, value) + widget.attributes.solid = value +end + +Attribute.solid.get = cascade + +--[[-- +Context menu. + +- This attribute cascades. + +@attrib context +--]]-- +Attribute.context = {} + +function Attribute.context.set (widget, value) + widget.attributes.context = value + if not value then return end + value.isContextMenu = true + widget.layout:createWidget { type = 'menu', value } +end + +Attribute.context.get = cascade + +--[[-- +Widget style. + +Should contain a string or array of strings identifying +style rules to be applied to the widget. When resolving +any attribute with a `nil` value, these style rules are +searched for a corresponding attribute. + +Setting this attribute resets the `Font` and `Text` object +associated with this widget. + +Setting this attribute recalculates the size and position +of the parent widget and its descendants. + +@attrib style +--]]-- +Attribute.style = {} + +function Attribute.style.set (widget, value) + widget.attributes.style = value + widget.fontData = nil + widget.textData = nil + widget.reshape(widget.parent or widget) +end + +--[[-- +Status message. + +Should contain a string with a short message describing the +purpose or state of the widget. + +This message will appear in the last created `status` widget +in the same layout, or in the master layout if one exists. + +- This attribute cascades. + +@attrib status +--]]-- +Attribute.status = {} + +Attribute.status.get = cascade + +--[[-- +Scroll ability. + +Should contain `true` or `false` (or `nil`). + +If set to `true`, moving the scroll wheel over the widget will adjust +its scroll position when the widget's contents overflow its boundary. + +@attrib scroll +--]]-- +Attribute.scroll = {} + +--[[-- +Keyboard Attributes. + +@section keyboard +--]]-- + +--[[-- +Focusable. + +Should contain `true` if the widget can be focused by pressing the tab key. + +@attrib focusable +--]]-- +Attribute.focusable = {} + +--[[-- +Keyboard shortcut. + +Should contain a string representing a key and optional modifiers, +separated by dashes; for example `'ctrl-c'` or `'alt-shift-escape'`. + +Pressing this key combination bubbles a `Press` event on the widget, +as if it had been pressed with a mouse or touch interface. + +Setting this attribute re-registers the widget with its layout. + +@attrib shortcut +--]]-- +Attribute.shortcut = {} + +local function setShortcut (layout, shortcut, value) + local mainKey, modifierFlags = Shortcut.parseKeyCombo(shortcut) + if mainKey then + layout.shortcuts[modifierFlags][mainKey] = value + end +end + +function Attribute.shortcut.set (widget, value) + local layout = widget.layout.master or widget.layout + local oldValue = widget.attributes.shortcut + + if oldValue then + if type(oldValue) == 'table' then + for _, v in ipairs(oldValue) do + setShortcut(layout, v, nil) + end + else + setShortcut(layout, oldValue, nil) + end + end + + if value then + if type(value) == 'table' then + for _, v in ipairs(value) do + setShortcut(layout, v, widget) + end + else + setShortcut(layout, value, widget) + end + end + + widget.attributes.shortcut = value +end + +--[[-- +Size Attributes. + +Setting these attributes recalculates the size and position +of the parent widget and its descendants. + +@section size +--]]-- + +--[[-- +Flow axis. + +Should equal either `'x'` or `'y'`. Defaults to `'y'`. + +This attribute determines the placement and default dimensions +of any child widgets. + +When flow is `'x'`, the `height` of child widgets defaults +to this widget's height, and each child is placed to the +right of the previous child. When flow is `'y'`, the `width` +of child widgets defaults to this widget's width, and each +child is placed below the previous child. + +Setting this attribute resets the `Text` object associated +with this widget. + +@attrib flow +--]]-- +Attribute.flow = {} + +function Attribute.flow.set (widget, value) + widget.attributes.flow = value + widget.textData = nil + widget.reshape(widget.parent or widget) +end + +--[[-- +Width. + +This attribute may not always hold a numeric value. +To get the calculated width, use `Widget:getWidth`. + +Setting this attribute when the `wrap` attribute is +also present resets the `Text` object associated +with this widget. + +@attrib width +--]]-- +Attribute.width = {} + +function Attribute.width.set (widget, value) + if value ~= 'auto' then + value = value and math.max(value, widget.minwidth or 0) + end + widget.attributes.width = value + if widget.wrap then + widget.textData = nil + end + widget.reshape(widget.parent or widget) +end + +--[[-- +Height. + +This attribute may not always hold a numeric value. +To get the calculated height, use `Widget:getHeight`. + +@attrib height +--]]-- +Attribute.height = {} + +function Attribute.height.set (widget, value) + if value ~= 'auto' then + value = value and math.max(value, widget.minheight or 0) + end + widget.attributes.height = value + widget.reshape(widget.parent or widget) +end + +--[[-- +Minimum width. + +@attrib minwidth +--]]-- +Attribute.minwidth = {} + +function Attribute.minwidth.set (widget, value) + local attributes = widget.attributes + attributes.minwidth = value + if type(value) == 'number' then + local current = attributes.width + if type(current) == 'number' then + attributes.width = math.max(current, value) + end + end + widget.reshape(widget.parent or widget) +end + +--[[-- +Minimum height. + +@attrib minheight +--]]-- +Attribute.minheight = {} + +function Attribute.minheight.set (widget, value) + local attributes = widget.attributes + attributes.minheight = value + if type(value) == 'number' then + local current = attributes.height + if type(current) == 'number' then + attributes.height = math.max(current, value) + end + end + widget.reshape(widget.parent or widget) +end + +--[[-- +Font Attributes. + +Setting these attributes resets the Font and Text +objects associated with the widget. + +@section font +--]]-- + +--[[-- +Font path. + +Should contain a path to a TrueType font to use for displaying +this widget's `text`. + +- This attribute cascades. + +@attrib font +--]]-- +Attribute.font = {} + +local function resetFont (widget) + rawset(widget, 'fontData', nil) + rawset(widget, 'textData', nil) + for _, child in ipairs(widget) do + resetFont(child) + end + local items = widget.items + if items then + for _, child in ipairs(items) do + resetFont(child) + end + end +end + +function Attribute.font.set (widget, value) + widget.attributes.font = value + resetFont(widget) +end + +Attribute.font.get = cascade + +--[[-- +Font size. + +Should contain a number representing the size of the font, in points. +Defaults to 12. + +- This attribute cascades. + +@attrib size +--]]-- +Attribute.size = {} + +function Attribute.size.set (widget, value) + widget.attributes.size = value + widget.fontData = nil + widget.textData = nil +end + +Attribute.size.get = cascade + +--[[-- +Text Attributes. + +Setting these attributes resets the Text object +associated with the widget. + +@section text +--]]-- + +--[[-- +Text to display. + +@attrib text +--]]-- +Attribute.text = {} + +function Attribute.text.set (widget, value) + widget.attributes.text = value + widget.textData = nil +end + +--[[-- +Text color. + +Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255. + +- This attribute cascades. + +@attrib color +--]]-- +Attribute.color = {} + +function Attribute.color.set (widget, value) + widget.attributes.color = value + widget.textData = nil +end + +Attribute.color.get = cascade + +--[[-- +Text and icon alignment. + +Should contain a string defining vertical and horizontal alignment. +Vertical alignment is defined by either 'top', 'middle', or 'bottom', +and horizontal alignment is defined by either 'left', 'center', or 'right'. + +For example, `align = 'top left'` + +- This attribute cascades. + +@attrib align +--]]-- +Attribute.align = {} + +function Attribute.align.set (widget, value) + widget.attributes.align = value + widget.textData = nil +end + +Attribute.align.get = cascade + +--[[-- +Wrap text onto multiple lines. + +Should contain `true` for multiline text, or `false` or `nil` +for a single line. Even text containing line breaks will display +as a single line when this attribute is not set to `true`. + +- This attribute cascades. + +@attrib wrap +--]]-- +Attribute.wrap = {} + +function Attribute.wrap.set (widget, value) + widget.attributes.wrap = value + widget.textData = nil +end + +Attribute.wrap.get = cascade + +--[[-- +Visual Attributes. + +@section visual +--]]-- + +--[[-- +Background color. + +Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255. + +@attrib background +--]]-- +Attribute.background = {} + +--[[-- +Outline color. + +Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255. + +@attrib outline +--]]-- +Attribute.outline = {} + +--[[-- +Slice image. + +Should contain a path to an image with "slices" to display for this widget. + +@attrib slices +--]]-- +Attribute.slices = {} + +--[[-- +Margin size. + +The margin area occupies space outside of the `outline` and `slices`. + +@attrib margin +--]]-- +Attribute.margin = {} + +function Attribute.margin.set (widget, value) + widget.attributes.margin = value + widget.textData = nil + widget:reshape() +end + +--[[-- +Padding size. + +The padding area occupies space inside the `outline` and `slices`, +and outside the space where the `icon` and `text` and any +child widgets appear. + +@attrib padding +--]]-- +Attribute.padding = {} + +function Attribute.padding.set (widget, value) + widget.attributes.padding = value + widget.textData = nil + widget:reshape() +end + +--[[-- +Icon path. + +Should contain a path to an image file. + +@attrib icon +--]]-- +Attribute.icon = {} + +function Attribute.icon.set (widget, value) + widget.attributes.icon = value + widget.textData = nil +end + + +return Attribute diff --git a/src/lib/luigi/backend.lua b/src/lib/luigi/backend.lua new file mode 100644 index 0000000..4c4552a --- /dev/null +++ b/src/lib/luigi/backend.lua @@ -0,0 +1,27 @@ +local ROOT = (...):gsub('[^.]*$', '') +local Backend + +if _G.love and (_G.love._version_major or _G.love._version_minor) then + Backend = require(ROOT .. 'backend.love') +else + Backend = require(ROOT .. 'backend.ffisdl') +end + +Backend.intersectScissor = Backend.intersectScissor or function (x, y, w, h) + local sx, sy, sw, sh = Backend.getScissor() + if not sx then + return Backend.setScissor(x, y, w, h) + end + local x1 = math.max(sx, x) + local y1 = math.max(sy, y) + local x2 = math.min(sx + sw, x + w) + local y2 = math.min(sy + sh, y + h) + if x2 > x1 and y2 > y1 then + Backend.setScissor(x1, y1, x2 - x1, y2 - y1) + else + -- HACK + Backend.setScissor(-100, -100, 1, 1) + end +end + +return Backend diff --git a/src/lib/luigi/backend/ffisdl.lua b/src/lib/luigi/backend/ffisdl.lua new file mode 100644 index 0000000..d031a77 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl.lua @@ -0,0 +1,540 @@ +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]' + +local stack = {} + +-- create window and renderer +sdl.enableScreenSaver() + +local window = sdl.createWindow('', + sdl.WINDOWPOS_CENTERED, sdl.WINDOWPOS_CENTERED, 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) + +sdl.setRenderDrawBlendMode(renderer, sdl.BLENDMODE_BLEND) + +local Backend = {} + +Backend.sdl = sdl + +Backend.isMac = function () + return sdl.getPlatform() == 'Mac OS X' +end + +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() + local tickInterval = 16 -- ~60 fps (with room) + local nextTick = 0 + local sdl = sdl + + 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] or 'unknown' + local scanCode = Keyboard.stringByScancode[event.key.keysym.scancode] or 'unknown' + callback.keypressed(key, scanCode, event.key['repeat']) + elseif event.type == sdl.KEYUP then + local key = Keyboard.stringByKeycode[event.key.keysym.sym] or 'unknown' + local scanCode = Keyboard.stringByScancode[event.key.keysym.scancode] or 'unknown' + callback.keyreleased(key, scanCode, 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() + + local now = sdl.getTicks() + if nextTick > now then + sdl.delay(nextTick - now) + end + nextTick = now + tickInterval + + sdl.renderPresent(renderer) + 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.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) + return ffi.gc(sdl.createSystemCursor(id), sdl.freeCursor) +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) + -- y = y and Backend.getWindowHeight() - (y + h) + lastScissor = x and sdl.Rect(x, y, w, h) + sdl.renderSetClipRect(renderer, lastScissor) +end + +Backend.getScissor = function () + if lastScissor ~= nil then + local x, y = lastScissor.x, lastScissor.y + local w, h = lastScissor.w, lastScissor.h + -- y = y and Backend.getWindowHeight() - (y + h) + return x, y, w, h + end +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 + +Backend.pop = function () + local history = stack[#stack] + lastColor = history.color or { 0, 0, 0, 255 } + lastScissor = history.scissor + + sdl.setRenderDrawColor(renderer, + lastColor[1], lastColor[2], lastColor[3], lastColor[4] or 255) + sdl.renderSetClipRect(renderer, lastScissor) -- 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 + +local buttonIds = { + [sdl.BUTTON_LEFT] = 'left', + [sdl.BUTTON_MIDDLE] = 'middle', + [sdl.BUTTON_RIGHT] = 'right', + -- [sdl.BUTTON_X1] = 'x1', + -- [sdl.BUTTON_X2] = 'x2', +} + +local function getMouseButtonId (value) + return value and buttonIds[value] or value +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, scanCode, isRepeat) + return input:handleKeyPress(layout, key, scanCode, Backend.getMousePosition()) + end) + hook(layout, 'keyreleased', function (key, scanCode) + return input:handleKeyRelease(layout, key, scanCode, 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 + +function Backend.getWindowMaximized () + local flags = sdl.getWindowFlags(window) + return bit.band(flags, sdl.WINDOW_MAXIMIZED) ~= 0 +end + +function Backend.setWindowMaximized (maximized) + if maximized then + sdl.maximizeWindow(window) + else + sdl.restoreWindow(window) + end +end + +function Backend.getWindowMinimized () + local flags = sdl.getWindowFlags(window) + return bit.band(flags, sdl.WINDOW_MINIMIZED) ~= 0 +end + +function Backend.setWindowMinimized (minimized) + if minimized then + sdl.minimizeWindow(window) + else + sdl.restoreWindow(window) + end +end + +function Backend.getWindowBorderless () + local flags = sdl.getWindowFlags(window) + return bit.band(flags, sdl.WINDOW_BORDERLESS) ~= 0 +end + +function Backend.setWindowBorderless (borderless) + return sdl.setWindowBordered(window, not borderless) +end + +function Backend.getWindowFullscreen () + local flags = sdl.getWindowFlags(window) + return bit.band(flags, sdl.WINDOW_FULLSCREEN) ~= 0 +end + +function Backend.setWindowFullscreen (fullscreen) + return sdl.setWindowFullscreen(window, not not fullscreen) +end + +function Backend.getWindowGrab () + return sdl.getWindowGrab(window) +end + +function Backend.setWindowGrab (grab) + return sdl.setWindowGrab(window, not not grab) +end + +local SDL2_image = ffi.load 'SDL2_image' + +function Backend.setWindowIcon (icon) + -- XXX: is it safe to free this? + local surface = ffi.gc(SDL2_image.IMG_Load(icon), sdl.freeSurface) + + if surface == nil then + error(ffi.string(sdl.getError())) + end + + sdl.setWindowIcon(window, surface) +end + +function Backend.getWindowMaxwidth () + local w, h = IntOut(), IntOut() + sdl.getWindowMaximumSize(window, w, h) + return w[0] +end + +function Backend.setWindowMaxwidth (maxwidth) + local w, h = IntOut(), IntOut() + sdl.getWindowMaximumSize(window, w, h) + sdl.setWindowMaximumSize(window, maxwidth, h[0] or 16384) +end + +function Backend.getWindowMaxheight () + local w, h = IntOut(), IntOut() + sdl.getWindowMaximumSize(window, w, h) + return h[0] +end + +function Backend.setWindowMaxheight (maxheight) + local w, h = IntOut(), IntOut() + sdl.getWindowMaximumSize(window, w, h) + sdl.setWindowMaximumSize(window, w[0] or 16384, maxheight) +end + +function Backend.getWindowMinwidth () + local w, h = IntOut(), IntOut() + sdl.getWindowMinimumSize(window, w, h) + return w[0] +end + +function Backend.setWindowMinwidth (minwidth) + local w, h = IntOut(), IntOut() + sdl.getWindowMinimumSize(window, w, h) + sdl.setWindowMinimumSize(window, minwidth, h[0] or 0) +end + +function Backend.getWindowMinheight () + local w, h = IntOut(), IntOut() + sdl.getWindowMinimumSize(window, w, h) + return h[0] +end + +function Backend.setWindowMinheight (minheight) + local w, h = IntOut(), IntOut() + sdl.getWindowMinimumSize(window, w, h) + sdl.setWindowMinimumSize(window, w[0] or 0, minheight) +end + +function Backend.getWindowTop () + local x, y = IntOut(), IntOut() + sdl.getWindowPosition(window, x, y) + return y[0] +end + +function Backend.setWindowTop (top) + local x, y = IntOut(), IntOut() + sdl.getWindowPosition(window, x, y) + sdl.setWindowPosition(window, x[0] or 0, top) +end + +function Backend.getWindowLeft () + local x, y = IntOut(), IntOut() + sdl.getWindowPosition(window, x, y) + return x[0] +end + +function Backend.setWindowLeft (left) + local x, y = IntOut(), IntOut() + sdl.getWindowPosition(window, x, y) + sdl.setWindowPosition(window, left, y[0] or 0) +end + +function Backend.getWindowWidth () + local w, h = IntOut(), IntOut() + sdl.getWindowSize(window, w, h) + return w[0] +end + +function Backend.setWindowWidth (width) + local w, h = IntOut(), IntOut() + sdl.getWindowSize(window, w, h) + sdl.setWindowSize(window, width, h[0] or 600) +end + +function Backend.getWindowHeight () + local w, h = IntOut(), IntOut() + sdl.getWindowSize(window, w, h) + return h[0] +end + +function Backend.setWindowHeight (height) + local w, h = IntOut(), IntOut() + sdl.getWindowSize(window, w, h) + sdl.setWindowSize(window, w[0] or 800, height) +end + +function Backend.getWindowTitle (title) + return sdl.getWindowTitle(window) +end + +function Backend.setWindowTitle (title) + sdl.setWindowTitle(window, title) +end + + + +return Backend diff --git a/src/lib/luigi/backend/ffisdl/font.lua b/src/lib/luigi/backend/ffisdl/font.lua new file mode 100644 index 0000000..aabd869 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/font.lua @@ -0,0 +1,249 @@ +local REL = (...):gsub('[^.]*$', '') +local APP_ROOT = rawget(_G, 'LUIGI_APP_ROOT') or '' + +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) + if not size then + size = 12 + 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(APP_ROOT .. path, size) + + if font == nil then + error(ffi.string(sdl.getError())) + end + + fontCache[key] = font + end + + self.sdlFont = fontCache[key] +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 + +return Font diff --git a/src/lib/luigi/backend/ffisdl/image.lua b/src/lib/luigi/backend/ffisdl/image.lua new file mode 100644 index 0000000..805da32 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/image.lua @@ -0,0 +1,46 @@ +local REL = (...):gsub('[^.]*$', '') +local APP_ROOT = rawget(_G, 'LUIGI_APP_ROOT') or '' + +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 = ffi.gc( + SDL2_image.IMG_Load(APP_ROOT .. path), + sdl.freeSurface) + + if self.sdlSurface == nil then + error(ffi.string(sdl.getError())) + end + + self.sdlTexture = ffi.gc( + sdl.createTextureFromSurface(renderer, self.sdlSurface), + sdl.destroyTexture) + + if self.sdlTexture == nil then + error(ffi.string(sdl.getError())) + end + + 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 + +return Image diff --git a/src/lib/luigi/backend/ffisdl/keyboard.lua b/src/lib/luigi/backend/ffisdl/keyboard.lua new file mode 100644 index 0000000..b9b9cbd --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/keyboard.lua @@ -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 diff --git a/src/lib/luigi/backend/ffisdl/resource/DejaVuSans.ttf b/src/lib/luigi/backend/ffisdl/resource/DejaVuSans.ttf new file mode 100644 index 0000000..5267218 Binary files /dev/null and b/src/lib/luigi/backend/ffisdl/resource/DejaVuSans.ttf differ diff --git a/src/lib/luigi/backend/ffisdl/sdl.lua b/src/lib/luigi/backend/ffisdl/sdl.lua new file mode 100644 index 0000000..427f454 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/sdl.lua @@ -0,0 +1,66 @@ +local REL = (...):gsub('[^.]*$', '') + +local ffi = require 'ffi' +local sdl = require(REL .. '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 + error(ffi.string(sdl.getError())) +end + +return sdl diff --git a/src/lib/luigi/backend/ffisdl/sdl2/LICENSE b/src/lib/luigi/backend/ffisdl/sdl2/LICENSE new file mode 100644 index 0000000..c9cc784 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/sdl2/LICENSE @@ -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. diff --git a/src/lib/luigi/backend/ffisdl/sdl2/README.md b/src/lib/luigi/backend/ffisdl/sdl2/README.md new file mode 100644 index 0000000..379ce5d --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/sdl2/README.md @@ -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. + + diff --git a/src/lib/luigi/backend/ffisdl/sdl2/cdefs.lua b/src/lib/luigi/backend/ffisdl/sdl2/cdefs.lua new file mode 100644 index 0000000..f02cd1b --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/sdl2/cdefs.lua @@ -0,0 +1,2600 @@ +-- Cut and paste from the C preprocessor output +-- Removed inline/defined functions which are not supported by luajit +-- Instead, those are defined into defines.lua +-- Note there are some tests here and there to stay cross-platform + +local ffi = require 'ffi' + +ffi.cdef[[ +typedef struct _FILE FILE; +]] + +ffi.cdef[[ + +const char * SDL_GetPlatform (void); +typedef enum +{ + SDL_FALSE = 0, + SDL_TRUE = 1 +} SDL_bool; +typedef int8_t Sint8; +typedef uint8_t Uint8; +typedef int16_t Sint16; +typedef uint16_t Uint16; +typedef int32_t Sint32; +typedef uint32_t Uint32; +typedef int64_t Sint64; +typedef uint64_t Uint64; +typedef int SDL_dummy_uint8[(sizeof(Uint8) == 1) * 2 - 1]; +typedef int SDL_dummy_sint8[(sizeof(Sint8) == 1) * 2 - 1]; +typedef int SDL_dummy_uint16[(sizeof(Uint16) == 2) * 2 - 1]; +typedef int SDL_dummy_sint16[(sizeof(Sint16) == 2) * 2 - 1]; +typedef int SDL_dummy_uint32[(sizeof(Uint32) == 4) * 2 - 1]; +typedef int SDL_dummy_sint32[(sizeof(Sint32) == 4) * 2 - 1]; +typedef int SDL_dummy_uint64[(sizeof(Uint64) == 8) * 2 - 1]; +typedef int SDL_dummy_sint64[(sizeof(Sint64) == 8) * 2 - 1]; +typedef enum +{ + DUMMY_ENUM_VALUE +} SDL_DUMMY_ENUM; +typedef int SDL_dummy_enum[(sizeof(SDL_DUMMY_ENUM) == sizeof(int)) * 2 - 1]; +void * SDL_malloc(size_t size); +void * SDL_calloc(size_t nmemb, size_t size); +void * SDL_realloc(void *mem, size_t size); +void SDL_free(void *mem); +char * SDL_getenv(const char *name); +int SDL_setenv(const char *name, const char *value, int overwrite); +void SDL_qsort(void *base, size_t nmemb, size_t size, int (*compare) (const void *, const void *)); +int SDL_abs(int x); +int SDL_isdigit(int x); +int SDL_isspace(int x); +int SDL_toupper(int x); +int SDL_tolower(int x); +void * SDL_memset(void *dst, int c, size_t len); +void * SDL_memcpy(void *dst, const void *src, size_t len); +void * SDL_memmove(void *dst, const void *src, size_t len); +int SDL_memcmp(const void *s1, const void *s2, size_t len); +size_t SDL_wcslen(const wchar_t *wstr); +size_t SDL_wcslcpy(wchar_t *dst, const wchar_t *src, size_t maxlen); +size_t SDL_wcslcat(wchar_t *dst, const wchar_t *src, size_t maxlen); +size_t SDL_strlen(const char *str); +size_t SDL_strlcpy(char *dst, const char *src, size_t maxlen); +size_t SDL_utf8strlcpy(char *dst, const char *src, size_t dst_bytes); +size_t SDL_strlcat(char *dst, const char *src, size_t maxlen); +char * SDL_strdup(const char *str); +char * SDL_strrev(char *str); +char * SDL_strupr(char *str); +char * SDL_strlwr(char *str); +char * SDL_strchr(const char *str, int c); +char * SDL_strrchr(const char *str, int c); +char * SDL_strstr(const char *haystack, const char *needle); +char * SDL_itoa(int value, char *str, int radix); +char * SDL_uitoa(unsigned int value, char *str, int radix); +char * SDL_ltoa(long value, char *str, int radix); +char * SDL_ultoa(unsigned long value, char *str, int radix); +char * SDL_lltoa(Sint64 value, char *str, int radix); +char * SDL_ulltoa(Uint64 value, char *str, int radix); +int SDL_atoi(const char *str); +double SDL_atof(const char *str); +long SDL_strtol(const char *str, char **endp, int base); +unsigned long SDL_strtoul(const char *str, char **endp, int base); +Sint64 SDL_strtoll(const char *str, char **endp, int base); +Uint64 SDL_strtoull(const char *str, char **endp, int base); +double SDL_strtod(const char *str, char **endp); +int SDL_strcmp(const char *str1, const char *str2); +int SDL_strncmp(const char *str1, const char *str2, size_t maxlen); +int SDL_strcasecmp(const char *str1, const char *str2); +int SDL_strncasecmp(const char *str1, const char *str2, size_t len); +int SDL_sscanf(const char *text, const char *fmt, ...); +int SDL_snprintf(char *text, size_t maxlen, const char *fmt, ...); +int SDL_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap); +double SDL_atan(double x); +double SDL_atan2(double x, double y); +double SDL_ceil(double x); +double SDL_copysign(double x, double y); +double SDL_cos(double x); +float SDL_cosf(float x); +double SDL_fabs(double x); +double SDL_floor(double x); +double SDL_log(double x); +double SDL_pow(double x, double y); +double SDL_scalbn(double x, int n); +double SDL_sin(double x); +float SDL_sinf(float x); +double SDL_sqrt(double x); +typedef struct _SDL_iconv_t *SDL_iconv_t; +SDL_iconv_t SDL_iconv_open(const char *tocode, + const char *fromcode); +int SDL_iconv_close(SDL_iconv_t cd); +size_t SDL_iconv(SDL_iconv_t cd, const char **inbuf, + size_t * inbytesleft, char **outbuf, + size_t * outbytesleft); +char * SDL_iconv_string(const char *tocode, + const char *fromcode, + const char *inbuf, + size_t inbytesleft); +int SDL_main(int argc, char *argv[]); +void SDL_SetMainReady(void); +typedef enum +{ + SDL_ASSERTION_RETRY, + SDL_ASSERTION_BREAK, + SDL_ASSERTION_ABORT, + SDL_ASSERTION_IGNORE, + SDL_ASSERTION_ALWAYS_IGNORE +} SDL_assert_state; +typedef struct SDL_assert_data +{ + int always_ignore; + unsigned int trigger_count; + const char *condition; + const char *filename; + int linenum; + const char *function; + const struct SDL_assert_data *next; +} SDL_assert_data; +SDL_assert_state SDL_ReportAssertion(SDL_assert_data *, + const char *, + const char *, int); +typedef SDL_assert_state ( *SDL_AssertionHandler)( + const SDL_assert_data* data, void* userdata); +void SDL_SetAssertionHandler( + SDL_AssertionHandler handler, + void *userdata); +const SDL_assert_data * SDL_GetAssertionReport(void); +void SDL_ResetAssertionReport(void); +typedef int SDL_SpinLock; +SDL_bool SDL_AtomicTryLock(SDL_SpinLock *lock); +void SDL_AtomicLock(SDL_SpinLock *lock); +void SDL_AtomicUnlock(SDL_SpinLock *lock); +typedef struct { int value; } SDL_atomic_t; +int SDL_SetError(const char *fmt, ...); +const char * SDL_GetError(void); +void SDL_ClearError(void); +typedef enum +{ + SDL_ENOMEM, + SDL_EFREAD, + SDL_EFWRITE, + SDL_EFSEEK, + SDL_UNSUPPORTED, + SDL_LASTERROR +} SDL_errorcode; +int SDL_Error(SDL_errorcode code); +struct SDL_mutex; +typedef struct SDL_mutex SDL_mutex; +SDL_mutex * SDL_CreateMutex(void); +int SDL_LockMutex(SDL_mutex * mutex); +int SDL_TryLockMutex(SDL_mutex * mutex); +int SDL_UnlockMutex(SDL_mutex * mutex); +void SDL_DestroyMutex(SDL_mutex * mutex); +struct SDL_semaphore; +typedef struct SDL_semaphore SDL_sem; +SDL_sem * SDL_CreateSemaphore(Uint32 initial_value); +void SDL_DestroySemaphore(SDL_sem * sem); +int SDL_SemWait(SDL_sem * sem); +int SDL_SemTryWait(SDL_sem * sem); +int SDL_SemWaitTimeout(SDL_sem * sem, Uint32 ms); +int SDL_SemPost(SDL_sem * sem); +Uint32 SDL_SemValue(SDL_sem * sem); +struct SDL_cond; +typedef struct SDL_cond SDL_cond; +SDL_cond * SDL_CreateCond(void); +void SDL_DestroyCond(SDL_cond * cond); +int SDL_CondSignal(SDL_cond * cond); +int SDL_CondBroadcast(SDL_cond * cond); +int SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex); +int SDL_CondWaitTimeout(SDL_cond * cond, + SDL_mutex * mutex, Uint32 ms); +struct SDL_Thread; +typedef struct SDL_Thread SDL_Thread; +typedef unsigned long SDL_threadID; +typedef unsigned int SDL_TLSID; +typedef enum { + SDL_THREAD_PRIORITY_LOW, + SDL_THREAD_PRIORITY_NORMAL, + SDL_THREAD_PRIORITY_HIGH +} SDL_ThreadPriority; +typedef int ( * SDL_ThreadFunction) (void *data); +]] + +if jit.os == 'Windows' then + ffi.cdef[[ + +typedef uintptr_t (*pfnSDL_CurrentBeginThread) (void *, unsigned, + unsigned (*func)(void*), + void *arg, unsigned, + unsigned *threadID); + +typedef void (*pfnSDL_CurrentEndThread) (unsigned code); + +uintptr_t _beginthreadex(void *, unsigned, + unsigned (*func)(void*), + void *arg, unsigned, + unsigned *threadID); + +void _endthreadex(unsigned retval); + +/* note: this fails. why? + pfnSDL_CurrentBeginThread _beginthreadex; + pfnSDL_CurrentEndThread _endthreadex; +*/ + +SDL_Thread * +SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data, + pfnSDL_CurrentBeginThread pfnBeginThread, + pfnSDL_CurrentEndThread pfnEndThread); +]] +else + ffi.cdef[[ +SDL_Thread * +SDL_CreateThread(SDL_ThreadFunction fn, const char *name, void *data); +]] +end + +ffi.cdef[[ +const char * SDL_GetThreadName(SDL_Thread *thread); +SDL_threadID SDL_ThreadID(void); +SDL_threadID SDL_GetThreadID(SDL_Thread * thread); +int SDL_SetThreadPriority(SDL_ThreadPriority priority); +void SDL_WaitThread(SDL_Thread * thread, int *status); +SDL_TLSID SDL_TLSCreate(void); +void * SDL_TLSGet(SDL_TLSID id); +int SDL_TLSSet(SDL_TLSID id, const void *value, void (*destructor)(void*)); +typedef struct SDL_RWops +{ + Sint64 ( * size) (struct SDL_RWops * context); + Sint64 ( * seek) (struct SDL_RWops * context, Sint64 offset, + int whence); + size_t ( * read) (struct SDL_RWops * context, void *ptr, + size_t size, size_t maxnum); + size_t ( * write) (struct SDL_RWops * context, const void *ptr, + size_t size, size_t num); + int ( * close) (struct SDL_RWops * context); + Uint32 type; + union + { + struct + { + SDL_bool autoclose; + FILE *fp; + } stdio; + struct + { + Uint8 *base; + Uint8 *here; + Uint8 *stop; + } mem; + struct + { + void *data1; + void *data2; + } unknown; + } hidden; +} SDL_RWops; +SDL_RWops * SDL_RWFromFile(const char *file, + const char *mode); +SDL_RWops * SDL_RWFromFP(FILE * fp, + SDL_bool autoclose); +SDL_RWops * SDL_RWFromMem(void *mem, int size); +SDL_RWops * SDL_RWFromConstMem(const void *mem, + int size); +SDL_RWops * SDL_AllocRW(void); +void SDL_FreeRW(SDL_RWops * area); +Uint8 SDL_ReadU8(SDL_RWops * src); +Uint16 SDL_ReadLE16(SDL_RWops * src); +Uint16 SDL_ReadBE16(SDL_RWops * src); +Uint32 SDL_ReadLE32(SDL_RWops * src); +Uint32 SDL_ReadBE32(SDL_RWops * src); +Uint64 SDL_ReadLE64(SDL_RWops * src); +Uint64 SDL_ReadBE64(SDL_RWops * src); +size_t SDL_WriteU8(SDL_RWops * dst, Uint8 value); +size_t SDL_WriteLE16(SDL_RWops * dst, Uint16 value); +size_t SDL_WriteBE16(SDL_RWops * dst, Uint16 value); +size_t SDL_WriteLE32(SDL_RWops * dst, Uint32 value); +size_t SDL_WriteBE32(SDL_RWops * dst, Uint32 value); +size_t SDL_WriteLE64(SDL_RWops * dst, Uint64 value); +size_t SDL_WriteBE64(SDL_RWops * dst, Uint64 value); +typedef Uint16 SDL_AudioFormat; +typedef void ( * SDL_AudioCallback) (void *userdata, Uint8 * stream, + int len); +typedef struct SDL_AudioSpec +{ + int freq; + SDL_AudioFormat format; + Uint8 channels; + Uint8 silence; + Uint16 samples; + Uint16 padding; + Uint32 size; + SDL_AudioCallback callback; + void *userdata; +} SDL_AudioSpec; +struct SDL_AudioCVT; +typedef void ( * SDL_AudioFilter) (struct SDL_AudioCVT * cvt, + SDL_AudioFormat format); +typedef struct SDL_AudioCVT +{ + int needed; + SDL_AudioFormat src_format; + SDL_AudioFormat dst_format; + double rate_incr; + Uint8 *buf; + int len; + int len_cvt; + int len_mult; + double len_ratio; + SDL_AudioFilter filters[10]; + int filter_index; +} __attribute__((packed)) SDL_AudioCVT; +int SDL_GetNumAudioDrivers(void); +const char * SDL_GetAudioDriver(int index); +int SDL_AudioInit(const char *driver_name); +void SDL_AudioQuit(void); +const char * SDL_GetCurrentAudioDriver(void); +int SDL_OpenAudio(SDL_AudioSpec * desired, + SDL_AudioSpec * obtained); +typedef Uint32 SDL_AudioDeviceID; +int SDL_GetNumAudioDevices(int iscapture); +const char * SDL_GetAudioDeviceName(int index, + int iscapture); +SDL_AudioDeviceID SDL_OpenAudioDevice(const char + *device, + int iscapture, + const + SDL_AudioSpec * + desired, + SDL_AudioSpec * + obtained, + int + allowed_changes); +typedef enum +{ + SDL_AUDIO_STOPPED = 0, + SDL_AUDIO_PLAYING, + SDL_AUDIO_PAUSED +} SDL_AudioStatus; +SDL_AudioStatus SDL_GetAudioStatus(void); +SDL_AudioStatus +SDL_GetAudioDeviceStatus(SDL_AudioDeviceID dev); +void SDL_PauseAudio(int pause_on); +void SDL_PauseAudioDevice(SDL_AudioDeviceID dev, + int pause_on); +SDL_AudioSpec * SDL_LoadWAV_RW(SDL_RWops * src, + int freesrc, + SDL_AudioSpec * spec, + Uint8 ** audio_buf, + Uint32 * audio_len); +void SDL_FreeWAV(Uint8 * audio_buf); +int SDL_BuildAudioCVT(SDL_AudioCVT * cvt, + SDL_AudioFormat src_format, + Uint8 src_channels, + int src_rate, + SDL_AudioFormat dst_format, + Uint8 dst_channels, + int dst_rate); +int SDL_ConvertAudio(SDL_AudioCVT * cvt); +void SDL_MixAudio(Uint8 * dst, const Uint8 * src, + Uint32 len, int volume); +void SDL_MixAudioFormat(Uint8 * dst, + const Uint8 * src, + SDL_AudioFormat format, + Uint32 len, int volume); +void SDL_LockAudio(void); +void SDL_LockAudioDevice(SDL_AudioDeviceID dev); +void SDL_UnlockAudio(void); +void SDL_UnlockAudioDevice(SDL_AudioDeviceID dev); +void SDL_CloseAudio(void); +void SDL_CloseAudioDevice(SDL_AudioDeviceID dev); +int SDL_SetClipboardText(const char *text); +char * SDL_GetClipboardText(void); +SDL_bool SDL_HasClipboardText(void); +int SDL_GetCPUCount(void); +int SDL_GetCPUCacheLineSize(void); +SDL_bool SDL_HasRDTSC(void); +SDL_bool SDL_HasAltiVec(void); +SDL_bool SDL_HasMMX(void); +SDL_bool SDL_Has3DNow(void); +SDL_bool SDL_HasSSE(void); +SDL_bool SDL_HasSSE2(void); +SDL_bool SDL_HasSSE3(void); +SDL_bool SDL_HasSSE41(void); +SDL_bool SDL_HasSSE42(void); +enum +{ + SDL_PIXELTYPE_UNKNOWN, + SDL_PIXELTYPE_INDEX1, + SDL_PIXELTYPE_INDEX4, + SDL_PIXELTYPE_INDEX8, + SDL_PIXELTYPE_PACKED8, + SDL_PIXELTYPE_PACKED16, + SDL_PIXELTYPE_PACKED32, + SDL_PIXELTYPE_ARRAYU8, + SDL_PIXELTYPE_ARRAYU16, + SDL_PIXELTYPE_ARRAYU32, + SDL_PIXELTYPE_ARRAYF16, + SDL_PIXELTYPE_ARRAYF32 +}; +enum +{ + SDL_BITMAPORDER_NONE, + SDL_BITMAPORDER_4321, + SDL_BITMAPORDER_1234 +}; +enum +{ + SDL_PACKEDORDER_NONE, + SDL_PACKEDORDER_XRGB, + SDL_PACKEDORDER_RGBX, + SDL_PACKEDORDER_ARGB, + SDL_PACKEDORDER_RGBA, + SDL_PACKEDORDER_XBGR, + SDL_PACKEDORDER_BGRX, + SDL_PACKEDORDER_ABGR, + SDL_PACKEDORDER_BGRA +}; +enum +{ + SDL_ARRAYORDER_NONE, + SDL_ARRAYORDER_RGB, + SDL_ARRAYORDER_RGBA, + SDL_ARRAYORDER_ARGB, + SDL_ARRAYORDER_BGR, + SDL_ARRAYORDER_BGRA, + SDL_ARRAYORDER_ABGR +}; +enum +{ + SDL_PACKEDLAYOUT_NONE, + SDL_PACKEDLAYOUT_332, + SDL_PACKEDLAYOUT_4444, + SDL_PACKEDLAYOUT_1555, + SDL_PACKEDLAYOUT_5551, + SDL_PACKEDLAYOUT_565, + SDL_PACKEDLAYOUT_8888, + SDL_PACKEDLAYOUT_2101010, + SDL_PACKEDLAYOUT_1010102 +}; +enum +{ + SDL_PIXELFORMAT_UNKNOWN, + SDL_PIXELFORMAT_INDEX1LSB = + ((1 << 28) | ((SDL_PIXELTYPE_INDEX1) << 24) | ((SDL_BITMAPORDER_4321) << 20) | ((0) << 16) | ((1) << 8) | ((0) << 0)), + SDL_PIXELFORMAT_INDEX1MSB = + ((1 << 28) | ((SDL_PIXELTYPE_INDEX1) << 24) | ((SDL_BITMAPORDER_1234) << 20) | ((0) << 16) | ((1) << 8) | ((0) << 0)), + SDL_PIXELFORMAT_INDEX4LSB = + ((1 << 28) | ((SDL_PIXELTYPE_INDEX4) << 24) | ((SDL_BITMAPORDER_4321) << 20) | ((0) << 16) | ((4) << 8) | ((0) << 0)), + SDL_PIXELFORMAT_INDEX4MSB = + ((1 << 28) | ((SDL_PIXELTYPE_INDEX4) << 24) | ((SDL_BITMAPORDER_1234) << 20) | ((0) << 16) | ((4) << 8) | ((0) << 0)), + SDL_PIXELFORMAT_INDEX8 = + ((1 << 28) | ((SDL_PIXELTYPE_INDEX8) << 24) | ((0) << 20) | ((0) << 16) | ((8) << 8) | ((1) << 0)), + SDL_PIXELFORMAT_RGB332 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED8) << 24) | ((SDL_PACKEDORDER_XRGB) << 20) | ((SDL_PACKEDLAYOUT_332) << 16) | ((8) << 8) | ((1) << 0)), + SDL_PIXELFORMAT_RGB444 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_XRGB) << 20) | ((SDL_PACKEDLAYOUT_4444) << 16) | ((12) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_RGB555 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_XRGB) << 20) | ((SDL_PACKEDLAYOUT_1555) << 16) | ((15) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_BGR555 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_XBGR) << 20) | ((SDL_PACKEDLAYOUT_1555) << 16) | ((15) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_ARGB4444 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_ARGB) << 20) | ((SDL_PACKEDLAYOUT_4444) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_RGBA4444 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_RGBA) << 20) | ((SDL_PACKEDLAYOUT_4444) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_ABGR4444 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_ABGR) << 20) | ((SDL_PACKEDLAYOUT_4444) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_BGRA4444 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_BGRA) << 20) | ((SDL_PACKEDLAYOUT_4444) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_ARGB1555 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_ARGB) << 20) | ((SDL_PACKEDLAYOUT_1555) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_RGBA5551 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_RGBA) << 20) | ((SDL_PACKEDLAYOUT_5551) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_ABGR1555 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_ABGR) << 20) | ((SDL_PACKEDLAYOUT_1555) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_BGRA5551 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_BGRA) << 20) | ((SDL_PACKEDLAYOUT_5551) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_RGB565 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_XRGB) << 20) | ((SDL_PACKEDLAYOUT_565) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_BGR565 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED16) << 24) | ((SDL_PACKEDORDER_XBGR) << 20) | ((SDL_PACKEDLAYOUT_565) << 16) | ((16) << 8) | ((2) << 0)), + SDL_PIXELFORMAT_RGB24 = + ((1 << 28) | ((SDL_PIXELTYPE_ARRAYU8) << 24) | ((SDL_ARRAYORDER_RGB) << 20) | ((0) << 16) | ((24) << 8) | ((3) << 0)), + SDL_PIXELFORMAT_BGR24 = + ((1 << 28) | ((SDL_PIXELTYPE_ARRAYU8) << 24) | ((SDL_ARRAYORDER_BGR) << 20) | ((0) << 16) | ((24) << 8) | ((3) << 0)), + SDL_PIXELFORMAT_RGB888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_XRGB) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((24) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_RGBX8888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_RGBX) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((24) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_BGR888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_XBGR) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((24) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_BGRX8888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_BGRX) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((24) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_ARGB8888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_ARGB) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((32) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_RGBA8888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_RGBA) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((32) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_ABGR8888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_ABGR) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((32) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_BGRA8888 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_BGRA) << 20) | ((SDL_PACKEDLAYOUT_8888) << 16) | ((32) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_ARGB2101010 = + ((1 << 28) | ((SDL_PIXELTYPE_PACKED32) << 24) | ((SDL_PACKEDORDER_ARGB) << 20) | ((SDL_PACKEDLAYOUT_2101010) << 16) | ((32) << 8) | ((4) << 0)), + SDL_PIXELFORMAT_YV12 = + ((((Uint32)(((Uint8)(('Y'))))) << 0) | (((Uint32)(((Uint8)(('V'))))) << 8) | (((Uint32)(((Uint8)(('1'))))) << 16) | (((Uint32)(((Uint8)(('2'))))) << 24)), + SDL_PIXELFORMAT_IYUV = + ((((Uint32)(((Uint8)(('I'))))) << 0) | (((Uint32)(((Uint8)(('Y'))))) << 8) | (((Uint32)(((Uint8)(('U'))))) << 16) | (((Uint32)(((Uint8)(('V'))))) << 24)), + SDL_PIXELFORMAT_YUY2 = + ((((Uint32)(((Uint8)(('Y'))))) << 0) | (((Uint32)(((Uint8)(('U'))))) << 8) | (((Uint32)(((Uint8)(('Y'))))) << 16) | (((Uint32)(((Uint8)(('2'))))) << 24)), + SDL_PIXELFORMAT_UYVY = + ((((Uint32)(((Uint8)(('U'))))) << 0) | (((Uint32)(((Uint8)(('Y'))))) << 8) | (((Uint32)(((Uint8)(('V'))))) << 16) | (((Uint32)(((Uint8)(('Y'))))) << 24)), + SDL_PIXELFORMAT_YVYU = + ((((Uint32)(((Uint8)(('Y'))))) << 0) | (((Uint32)(((Uint8)(('V'))))) << 8) | (((Uint32)(((Uint8)(('Y'))))) << 16) | (((Uint32)(((Uint8)(('U'))))) << 24)) +}; +typedef struct SDL_Color +{ + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 a; +} SDL_Color; +typedef struct SDL_Palette +{ + int ncolors; + SDL_Color *colors; + Uint32 version; + int refcount; +} SDL_Palette; +typedef struct SDL_PixelFormat +{ + Uint32 format; + SDL_Palette *palette; + Uint8 BitsPerPixel; + Uint8 BytesPerPixel; + Uint8 padding[2]; + Uint32 Rmask; + Uint32 Gmask; + Uint32 Bmask; + Uint32 Amask; + Uint8 Rloss; + Uint8 Gloss; + Uint8 Bloss; + Uint8 Aloss; + Uint8 Rshift; + Uint8 Gshift; + Uint8 Bshift; + Uint8 Ashift; + int refcount; + struct SDL_PixelFormat *next; +} SDL_PixelFormat; +const char* SDL_GetPixelFormatName(Uint32 format); +SDL_bool SDL_PixelFormatEnumToMasks(Uint32 format, + int *bpp, + Uint32 * Rmask, + Uint32 * Gmask, + Uint32 * Bmask, + Uint32 * Amask); +Uint32 SDL_MasksToPixelFormatEnum(int bpp, + Uint32 Rmask, + Uint32 Gmask, + Uint32 Bmask, + Uint32 Amask); +SDL_PixelFormat * SDL_AllocFormat(Uint32 pixel_format); +void SDL_FreeFormat(SDL_PixelFormat *format); +SDL_Palette * SDL_AllocPalette(int ncolors); +int SDL_SetPixelFormatPalette(SDL_PixelFormat * format, + SDL_Palette *palette); +int SDL_SetPaletteColors(SDL_Palette * palette, + const SDL_Color * colors, + int firstcolor, int ncolors); +void SDL_FreePalette(SDL_Palette * palette); +Uint32 SDL_MapRGB(const SDL_PixelFormat * format, + Uint8 r, Uint8 g, Uint8 b); +Uint32 SDL_MapRGBA(const SDL_PixelFormat * format, + Uint8 r, Uint8 g, Uint8 b, + Uint8 a); +void SDL_GetRGB(Uint32 pixel, + const SDL_PixelFormat * format, + Uint8 * r, Uint8 * g, Uint8 * b); +void SDL_GetRGBA(Uint32 pixel, + const SDL_PixelFormat * format, + Uint8 * r, Uint8 * g, Uint8 * b, + Uint8 * a); +void SDL_CalculateGammaRamp(float gamma, Uint16 * ramp); +typedef struct +{ + int x; + int y; +} SDL_Point; +typedef struct SDL_Rect +{ + int x, y; + int w, h; +} SDL_Rect; +SDL_bool SDL_HasIntersection(const SDL_Rect * A, + const SDL_Rect * B); +SDL_bool SDL_IntersectRect(const SDL_Rect * A, + const SDL_Rect * B, + SDL_Rect * result); +void SDL_UnionRect(const SDL_Rect * A, + const SDL_Rect * B, + SDL_Rect * result); +SDL_bool SDL_EnclosePoints(const SDL_Point * points, + int count, + const SDL_Rect * clip, + SDL_Rect * result); +SDL_bool SDL_IntersectRectAndLine(const SDL_Rect * + rect, int *X1, + int *Y1, int *X2, + int *Y2); +typedef enum +{ + SDL_BLENDMODE_NONE = 0x00000000, + SDL_BLENDMODE_BLEND = 0x00000001, + SDL_BLENDMODE_ADD = 0x00000002, + SDL_BLENDMODE_MOD = 0x00000004 +} SDL_BlendMode; +typedef struct SDL_Surface +{ + Uint32 flags; + SDL_PixelFormat *format; + int w, h; + int pitch; + void *pixels; + void *userdata; + int locked; + void *lock_data; + SDL_Rect clip_rect; + struct SDL_BlitMap *map; + int refcount; +} SDL_Surface; +typedef int (*SDL_blit) (struct SDL_Surface * src, SDL_Rect * srcrect, + struct SDL_Surface * dst, SDL_Rect * dstrect); +SDL_Surface * SDL_CreateRGBSurface + (Uint32 flags, int width, int height, int depth, + Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask); +SDL_Surface * SDL_CreateRGBSurfaceFrom(void *pixels, + int width, + int height, + int depth, + int pitch, + Uint32 Rmask, + Uint32 Gmask, + Uint32 Bmask, + Uint32 Amask); +void SDL_FreeSurface(SDL_Surface * surface); +int SDL_SetSurfacePalette(SDL_Surface * surface, + SDL_Palette * palette); +int SDL_LockSurface(SDL_Surface * surface); +void SDL_UnlockSurface(SDL_Surface * surface); +SDL_Surface * SDL_LoadBMP_RW(SDL_RWops * src, + int freesrc); +int SDL_SaveBMP_RW + (SDL_Surface * surface, SDL_RWops * dst, int freedst); +int SDL_SetSurfaceRLE(SDL_Surface * surface, + int flag); +int SDL_SetColorKey(SDL_Surface * surface, + int flag, Uint32 key); +int SDL_GetColorKey(SDL_Surface * surface, + Uint32 * key); +int SDL_SetSurfaceColorMod(SDL_Surface * surface, + Uint8 r, Uint8 g, Uint8 b); +int SDL_GetSurfaceColorMod(SDL_Surface * surface, + Uint8 * r, Uint8 * g, + Uint8 * b); +int SDL_SetSurfaceAlphaMod(SDL_Surface * surface, + Uint8 alpha); +int SDL_GetSurfaceAlphaMod(SDL_Surface * surface, + Uint8 * alpha); +int SDL_SetSurfaceBlendMode(SDL_Surface * surface, + SDL_BlendMode blendMode); +int SDL_GetSurfaceBlendMode(SDL_Surface * surface, + SDL_BlendMode *blendMode); +SDL_bool SDL_SetClipRect(SDL_Surface * surface, + const SDL_Rect * rect); +void SDL_GetClipRect(SDL_Surface * surface, + SDL_Rect * rect); +SDL_Surface * SDL_ConvertSurface + (SDL_Surface * src, SDL_PixelFormat * fmt, Uint32 flags); +SDL_Surface * SDL_ConvertSurfaceFormat + (SDL_Surface * src, Uint32 pixel_format, Uint32 flags); +int SDL_ConvertPixels(int width, int height, + Uint32 src_format, + const void * src, int src_pitch, + Uint32 dst_format, + void * dst, int dst_pitch); +int SDL_FillRect + (SDL_Surface * dst, const SDL_Rect * rect, Uint32 color); +int SDL_FillRects + (SDL_Surface * dst, const SDL_Rect * rects, int count, Uint32 color); +int SDL_UpperBlit + (SDL_Surface * src, const SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); +int SDL_LowerBlit + (SDL_Surface * src, SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); +int SDL_SoftStretch(SDL_Surface * src, + const SDL_Rect * srcrect, + SDL_Surface * dst, + const SDL_Rect * dstrect); +int SDL_UpperBlitScaled + (SDL_Surface * src, const SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); +int SDL_LowerBlitScaled + (SDL_Surface * src, SDL_Rect * srcrect, + SDL_Surface * dst, SDL_Rect * dstrect); +typedef struct +{ + Uint32 format; + int w; + int h; + int refresh_rate; + void *driverdata; +} SDL_DisplayMode; +typedef struct SDL_Window SDL_Window; +typedef enum +{ + SDL_WINDOW_FULLSCREEN = 0x00000001, + SDL_WINDOW_OPENGL = 0x00000002, + SDL_WINDOW_SHOWN = 0x00000004, + SDL_WINDOW_HIDDEN = 0x00000008, + SDL_WINDOW_BORDERLESS = 0x00000010, + SDL_WINDOW_RESIZABLE = 0x00000020, + SDL_WINDOW_MINIMIZED = 0x00000040, + SDL_WINDOW_MAXIMIZED = 0x00000080, + SDL_WINDOW_INPUT_GRABBED = 0x00000100, + SDL_WINDOW_INPUT_FOCUS = 0x00000200, + SDL_WINDOW_MOUSE_FOCUS = 0x00000400, + SDL_WINDOW_FULLSCREEN_DESKTOP = ( SDL_WINDOW_FULLSCREEN | 0x00001000 ), + SDL_WINDOW_FOREIGN = 0x00000800 +} SDL_WindowFlags; +typedef enum +{ + SDL_WINDOWEVENT_NONE, + SDL_WINDOWEVENT_SHOWN, + SDL_WINDOWEVENT_HIDDEN, + SDL_WINDOWEVENT_EXPOSED, + SDL_WINDOWEVENT_MOVED, + SDL_WINDOWEVENT_RESIZED, + SDL_WINDOWEVENT_SIZE_CHANGED, + SDL_WINDOWEVENT_MINIMIZED, + SDL_WINDOWEVENT_MAXIMIZED, + SDL_WINDOWEVENT_RESTORED, + SDL_WINDOWEVENT_ENTER, + SDL_WINDOWEVENT_LEAVE, + SDL_WINDOWEVENT_FOCUS_GAINED, + SDL_WINDOWEVENT_FOCUS_LOST, + SDL_WINDOWEVENT_CLOSE +} SDL_WindowEventID; +typedef void *SDL_GLContext; +typedef enum +{ + SDL_GL_RED_SIZE, + SDL_GL_GREEN_SIZE, + SDL_GL_BLUE_SIZE, + SDL_GL_ALPHA_SIZE, + SDL_GL_BUFFER_SIZE, + SDL_GL_DOUBLEBUFFER, + SDL_GL_DEPTH_SIZE, + SDL_GL_STENCIL_SIZE, + SDL_GL_ACCUM_RED_SIZE, + SDL_GL_ACCUM_GREEN_SIZE, + SDL_GL_ACCUM_BLUE_SIZE, + SDL_GL_ACCUM_ALPHA_SIZE, + SDL_GL_STEREO, + SDL_GL_MULTISAMPLEBUFFERS, + SDL_GL_MULTISAMPLESAMPLES, + SDL_GL_ACCELERATED_VISUAL, + SDL_GL_RETAINED_BACKING, + SDL_GL_CONTEXT_MAJOR_VERSION, + SDL_GL_CONTEXT_MINOR_VERSION, + SDL_GL_CONTEXT_EGL, + SDL_GL_CONTEXT_FLAGS, + SDL_GL_CONTEXT_PROFILE_MASK, + SDL_GL_SHARE_WITH_CURRENT_CONTEXT +} SDL_GLattr; +typedef enum +{ + SDL_GL_CONTEXT_PROFILE_CORE = 0x0001, + SDL_GL_CONTEXT_PROFILE_COMPATIBILITY = 0x0002, + SDL_GL_CONTEXT_PROFILE_ES = 0x0004 +} SDL_GLprofile; +typedef enum +{ + SDL_GL_CONTEXT_DEBUG_FLAG = 0x0001, + SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG = 0x0002, + SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG = 0x0004, + SDL_GL_CONTEXT_RESET_ISOLATION_FLAG = 0x0008 +} SDL_GLcontextFlag; +int SDL_GetNumVideoDrivers(void); +const char * SDL_GetVideoDriver(int index); +int SDL_VideoInit(const char *driver_name); +void SDL_VideoQuit(void); +const char * SDL_GetCurrentVideoDriver(void); +int SDL_GetNumVideoDisplays(void); +const char * SDL_GetDisplayName(int displayIndex); +int SDL_GetDisplayBounds(int displayIndex, SDL_Rect * rect); +int SDL_GetNumDisplayModes(int displayIndex); +int SDL_GetDisplayMode(int displayIndex, int modeIndex, + SDL_DisplayMode * mode); +int SDL_GetDesktopDisplayMode(int displayIndex, SDL_DisplayMode * mode); +int SDL_GetCurrentDisplayMode(int displayIndex, SDL_DisplayMode * mode); +SDL_DisplayMode * SDL_GetClosestDisplayMode(int displayIndex, const SDL_DisplayMode * mode, SDL_DisplayMode * closest); +int SDL_GetWindowDisplayIndex(SDL_Window * window); +int SDL_SetWindowDisplayMode(SDL_Window * window, + const SDL_DisplayMode + * mode); +int SDL_GetWindowDisplayMode(SDL_Window * window, + SDL_DisplayMode * mode); +Uint32 SDL_GetWindowPixelFormat(SDL_Window * window); +SDL_Window * SDL_CreateWindow(const char *title, + int x, int y, int w, + int h, Uint32 flags); +SDL_Window * SDL_CreateWindowFrom(const void *data); +Uint32 SDL_GetWindowID(SDL_Window * window); +SDL_Window * SDL_GetWindowFromID(Uint32 id); +Uint32 SDL_GetWindowFlags(SDL_Window * window); +void SDL_SetWindowTitle(SDL_Window * window, + const char *title); +const char * SDL_GetWindowTitle(SDL_Window * window); +void SDL_SetWindowIcon(SDL_Window * window, + SDL_Surface * icon); +void* SDL_SetWindowData(SDL_Window * window, + const char *name, + void *userdata); +void * SDL_GetWindowData(SDL_Window * window, + const char *name); +void SDL_SetWindowPosition(SDL_Window * window, + int x, int y); +void SDL_GetWindowPosition(SDL_Window * window, + int *x, int *y); +void SDL_SetWindowSize(SDL_Window * window, int w, + int h); +void SDL_GetWindowSize(SDL_Window * window, int *w, + int *h); +void SDL_SetWindowMinimumSize(SDL_Window * window, + int min_w, int min_h); +void SDL_GetWindowMinimumSize(SDL_Window * window, + int *w, int *h); +void SDL_SetWindowMaximumSize(SDL_Window * window, + int max_w, int max_h); +void SDL_GetWindowMaximumSize(SDL_Window * window, + int *w, int *h); +void SDL_SetWindowBordered(SDL_Window * window, + SDL_bool bordered); +void SDL_ShowWindow(SDL_Window * window); +void SDL_HideWindow(SDL_Window * window); +void SDL_RaiseWindow(SDL_Window * window); +void SDL_MaximizeWindow(SDL_Window * window); +void SDL_MinimizeWindow(SDL_Window * window); +void SDL_RestoreWindow(SDL_Window * window); +int SDL_SetWindowFullscreen(SDL_Window * window, + Uint32 flags); +SDL_Surface * SDL_GetWindowSurface(SDL_Window * window); +int SDL_UpdateWindowSurface(SDL_Window * window); +int SDL_UpdateWindowSurfaceRects(SDL_Window * window, + const SDL_Rect * rects, + int numrects); +void SDL_SetWindowGrab(SDL_Window * window, + SDL_bool grabbed); +SDL_bool SDL_GetWindowGrab(SDL_Window * window); +int SDL_SetWindowBrightness(SDL_Window * window, float brightness); +float SDL_GetWindowBrightness(SDL_Window * window); +int SDL_SetWindowGammaRamp(SDL_Window * window, + const Uint16 * red, + const Uint16 * green, + const Uint16 * blue); +int SDL_GetWindowGammaRamp(SDL_Window * window, + Uint16 * red, + Uint16 * green, + Uint16 * blue); +void SDL_DestroyWindow(SDL_Window * window); +SDL_bool SDL_IsScreenSaverEnabled(void); +void SDL_EnableScreenSaver(void); +void SDL_DisableScreenSaver(void); +int SDL_GL_LoadLibrary(const char *path); +void * SDL_GL_GetProcAddress(const char *proc); +void SDL_GL_UnloadLibrary(void); +SDL_bool SDL_GL_ExtensionSupported(const char + *extension); +int SDL_GL_SetAttribute(SDL_GLattr attr, int value); +int SDL_GL_GetAttribute(SDL_GLattr attr, int *value); +SDL_GLContext SDL_GL_CreateContext(SDL_Window * + window); +int SDL_GL_MakeCurrent(SDL_Window * window, + SDL_GLContext context); +SDL_Window* SDL_GL_GetCurrentWindow(void); +SDL_GLContext SDL_GL_GetCurrentContext(void); +int SDL_GL_SetSwapInterval(int interval); +int SDL_GL_GetSwapInterval(void); +void SDL_GL_SwapWindow(SDL_Window * window); +void SDL_GL_DeleteContext(SDL_GLContext context); +typedef Sint32 SDL_Scancode; +enum +{ + SDL_SCANCODE_UNKNOWN = 0, + SDL_SCANCODE_A = 4, + SDL_SCANCODE_B = 5, + SDL_SCANCODE_C = 6, + SDL_SCANCODE_D = 7, + SDL_SCANCODE_E = 8, + SDL_SCANCODE_F = 9, + SDL_SCANCODE_G = 10, + SDL_SCANCODE_H = 11, + SDL_SCANCODE_I = 12, + SDL_SCANCODE_J = 13, + SDL_SCANCODE_K = 14, + SDL_SCANCODE_L = 15, + SDL_SCANCODE_M = 16, + SDL_SCANCODE_N = 17, + SDL_SCANCODE_O = 18, + SDL_SCANCODE_P = 19, + SDL_SCANCODE_Q = 20, + SDL_SCANCODE_R = 21, + SDL_SCANCODE_S = 22, + SDL_SCANCODE_T = 23, + SDL_SCANCODE_U = 24, + SDL_SCANCODE_V = 25, + SDL_SCANCODE_W = 26, + SDL_SCANCODE_X = 27, + SDL_SCANCODE_Y = 28, + SDL_SCANCODE_Z = 29, + SDL_SCANCODE_1 = 30, + SDL_SCANCODE_2 = 31, + SDL_SCANCODE_3 = 32, + SDL_SCANCODE_4 = 33, + SDL_SCANCODE_5 = 34, + SDL_SCANCODE_6 = 35, + SDL_SCANCODE_7 = 36, + SDL_SCANCODE_8 = 37, + SDL_SCANCODE_9 = 38, + SDL_SCANCODE_0 = 39, + SDL_SCANCODE_RETURN = 40, + SDL_SCANCODE_ESCAPE = 41, + SDL_SCANCODE_BACKSPACE = 42, + SDL_SCANCODE_TAB = 43, + SDL_SCANCODE_SPACE = 44, + SDL_SCANCODE_MINUS = 45, + SDL_SCANCODE_EQUALS = 46, + SDL_SCANCODE_LEFTBRACKET = 47, + SDL_SCANCODE_RIGHTBRACKET = 48, + SDL_SCANCODE_BACKSLASH = 49, + SDL_SCANCODE_NONUSHASH = 50, + SDL_SCANCODE_SEMICOLON = 51, + SDL_SCANCODE_APOSTROPHE = 52, + SDL_SCANCODE_GRAVE = 53, + SDL_SCANCODE_COMMA = 54, + SDL_SCANCODE_PERIOD = 55, + SDL_SCANCODE_SLASH = 56, + SDL_SCANCODE_CAPSLOCK = 57, + SDL_SCANCODE_F1 = 58, + SDL_SCANCODE_F2 = 59, + SDL_SCANCODE_F3 = 60, + SDL_SCANCODE_F4 = 61, + SDL_SCANCODE_F5 = 62, + SDL_SCANCODE_F6 = 63, + SDL_SCANCODE_F7 = 64, + SDL_SCANCODE_F8 = 65, + SDL_SCANCODE_F9 = 66, + SDL_SCANCODE_F10 = 67, + SDL_SCANCODE_F11 = 68, + SDL_SCANCODE_F12 = 69, + SDL_SCANCODE_PRINTSCREEN = 70, + SDL_SCANCODE_SCROLLLOCK = 71, + SDL_SCANCODE_PAUSE = 72, + SDL_SCANCODE_INSERT = 73, + SDL_SCANCODE_HOME = 74, + SDL_SCANCODE_PAGEUP = 75, + SDL_SCANCODE_DELETE = 76, + SDL_SCANCODE_END = 77, + SDL_SCANCODE_PAGEDOWN = 78, + SDL_SCANCODE_RIGHT = 79, + SDL_SCANCODE_LEFT = 80, + SDL_SCANCODE_DOWN = 81, + SDL_SCANCODE_UP = 82, + SDL_SCANCODE_NUMLOCKCLEAR = 83, + SDL_SCANCODE_KP_DIVIDE = 84, + SDL_SCANCODE_KP_MULTIPLY = 85, + SDL_SCANCODE_KP_MINUS = 86, + SDL_SCANCODE_KP_PLUS = 87, + SDL_SCANCODE_KP_ENTER = 88, + SDL_SCANCODE_KP_1 = 89, + SDL_SCANCODE_KP_2 = 90, + SDL_SCANCODE_KP_3 = 91, + SDL_SCANCODE_KP_4 = 92, + SDL_SCANCODE_KP_5 = 93, + SDL_SCANCODE_KP_6 = 94, + SDL_SCANCODE_KP_7 = 95, + SDL_SCANCODE_KP_8 = 96, + SDL_SCANCODE_KP_9 = 97, + SDL_SCANCODE_KP_0 = 98, + SDL_SCANCODE_KP_PERIOD = 99, + SDL_SCANCODE_NONUSBACKSLASH = 100, + SDL_SCANCODE_APPLICATION = 101, + SDL_SCANCODE_POWER = 102, + SDL_SCANCODE_KP_EQUALS = 103, + SDL_SCANCODE_F13 = 104, + SDL_SCANCODE_F14 = 105, + SDL_SCANCODE_F15 = 106, + SDL_SCANCODE_F16 = 107, + SDL_SCANCODE_F17 = 108, + SDL_SCANCODE_F18 = 109, + SDL_SCANCODE_F19 = 110, + SDL_SCANCODE_F20 = 111, + SDL_SCANCODE_F21 = 112, + SDL_SCANCODE_F22 = 113, + SDL_SCANCODE_F23 = 114, + SDL_SCANCODE_F24 = 115, + SDL_SCANCODE_EXECUTE = 116, + SDL_SCANCODE_HELP = 117, + SDL_SCANCODE_MENU = 118, + SDL_SCANCODE_SELECT = 119, + SDL_SCANCODE_STOP = 120, + SDL_SCANCODE_AGAIN = 121, + SDL_SCANCODE_UNDO = 122, + SDL_SCANCODE_CUT = 123, + SDL_SCANCODE_COPY = 124, + SDL_SCANCODE_PASTE = 125, + SDL_SCANCODE_FIND = 126, + SDL_SCANCODE_MUTE = 127, + SDL_SCANCODE_VOLUMEUP = 128, + SDL_SCANCODE_VOLUMEDOWN = 129, + SDL_SCANCODE_KP_COMMA = 133, + SDL_SCANCODE_KP_EQUALSAS400 = 134, + SDL_SCANCODE_INTERNATIONAL1 = 135, + SDL_SCANCODE_INTERNATIONAL2 = 136, + SDL_SCANCODE_INTERNATIONAL3 = 137, + SDL_SCANCODE_INTERNATIONAL4 = 138, + SDL_SCANCODE_INTERNATIONAL5 = 139, + SDL_SCANCODE_INTERNATIONAL6 = 140, + SDL_SCANCODE_INTERNATIONAL7 = 141, + SDL_SCANCODE_INTERNATIONAL8 = 142, + SDL_SCANCODE_INTERNATIONAL9 = 143, + SDL_SCANCODE_LANG1 = 144, + SDL_SCANCODE_LANG2 = 145, + SDL_SCANCODE_LANG3 = 146, + SDL_SCANCODE_LANG4 = 147, + SDL_SCANCODE_LANG5 = 148, + SDL_SCANCODE_LANG6 = 149, + SDL_SCANCODE_LANG7 = 150, + SDL_SCANCODE_LANG8 = 151, + SDL_SCANCODE_LANG9 = 152, + SDL_SCANCODE_ALTERASE = 153, + SDL_SCANCODE_SYSREQ = 154, + SDL_SCANCODE_CANCEL = 155, + SDL_SCANCODE_CLEAR = 156, + SDL_SCANCODE_PRIOR = 157, + SDL_SCANCODE_RETURN2 = 158, + SDL_SCANCODE_SEPARATOR = 159, + SDL_SCANCODE_OUT = 160, + SDL_SCANCODE_OPER = 161, + SDL_SCANCODE_CLEARAGAIN = 162, + SDL_SCANCODE_CRSEL = 163, + SDL_SCANCODE_EXSEL = 164, + SDL_SCANCODE_KP_00 = 176, + SDL_SCANCODE_KP_000 = 177, + SDL_SCANCODE_THOUSANDSSEPARATOR = 178, + SDL_SCANCODE_DECIMALSEPARATOR = 179, + SDL_SCANCODE_CURRENCYUNIT = 180, + SDL_SCANCODE_CURRENCYSUBUNIT = 181, + SDL_SCANCODE_KP_LEFTPAREN = 182, + SDL_SCANCODE_KP_RIGHTPAREN = 183, + SDL_SCANCODE_KP_LEFTBRACE = 184, + SDL_SCANCODE_KP_RIGHTBRACE = 185, + SDL_SCANCODE_KP_TAB = 186, + SDL_SCANCODE_KP_BACKSPACE = 187, + SDL_SCANCODE_KP_A = 188, + SDL_SCANCODE_KP_B = 189, + SDL_SCANCODE_KP_C = 190, + SDL_SCANCODE_KP_D = 191, + SDL_SCANCODE_KP_E = 192, + SDL_SCANCODE_KP_F = 193, + SDL_SCANCODE_KP_XOR = 194, + SDL_SCANCODE_KP_POWER = 195, + SDL_SCANCODE_KP_PERCENT = 196, + SDL_SCANCODE_KP_LESS = 197, + SDL_SCANCODE_KP_GREATER = 198, + SDL_SCANCODE_KP_AMPERSAND = 199, + SDL_SCANCODE_KP_DBLAMPERSAND = 200, + SDL_SCANCODE_KP_VERTICALBAR = 201, + SDL_SCANCODE_KP_DBLVERTICALBAR = 202, + SDL_SCANCODE_KP_COLON = 203, + SDL_SCANCODE_KP_HASH = 204, + SDL_SCANCODE_KP_SPACE = 205, + SDL_SCANCODE_KP_AT = 206, + SDL_SCANCODE_KP_EXCLAM = 207, + SDL_SCANCODE_KP_MEMSTORE = 208, + SDL_SCANCODE_KP_MEMRECALL = 209, + SDL_SCANCODE_KP_MEMCLEAR = 210, + SDL_SCANCODE_KP_MEMADD = 211, + SDL_SCANCODE_KP_MEMSUBTRACT = 212, + SDL_SCANCODE_KP_MEMMULTIPLY = 213, + SDL_SCANCODE_KP_MEMDIVIDE = 214, + SDL_SCANCODE_KP_PLUSMINUS = 215, + SDL_SCANCODE_KP_CLEAR = 216, + SDL_SCANCODE_KP_CLEARENTRY = 217, + SDL_SCANCODE_KP_BINARY = 218, + SDL_SCANCODE_KP_OCTAL = 219, + SDL_SCANCODE_KP_DECIMAL = 220, + SDL_SCANCODE_KP_HEXADECIMAL = 221, + SDL_SCANCODE_LCTRL = 224, + SDL_SCANCODE_LSHIFT = 225, + SDL_SCANCODE_LALT = 226, + SDL_SCANCODE_LGUI = 227, + SDL_SCANCODE_RCTRL = 228, + SDL_SCANCODE_RSHIFT = 229, + SDL_SCANCODE_RALT = 230, + SDL_SCANCODE_RGUI = 231, + SDL_SCANCODE_MODE = 257, + SDL_SCANCODE_AUDIONEXT = 258, + SDL_SCANCODE_AUDIOPREV = 259, + SDL_SCANCODE_AUDIOSTOP = 260, + SDL_SCANCODE_AUDIOPLAY = 261, + SDL_SCANCODE_AUDIOMUTE = 262, + SDL_SCANCODE_MEDIASELECT = 263, + SDL_SCANCODE_WWW = 264, + SDL_SCANCODE_MAIL = 265, + SDL_SCANCODE_CALCULATOR = 266, + SDL_SCANCODE_COMPUTER = 267, + SDL_SCANCODE_AC_SEARCH = 268, + SDL_SCANCODE_AC_HOME = 269, + SDL_SCANCODE_AC_BACK = 270, + SDL_SCANCODE_AC_FORWARD = 271, + SDL_SCANCODE_AC_STOP = 272, + SDL_SCANCODE_AC_REFRESH = 273, + SDL_SCANCODE_AC_BOOKMARKS = 274, + SDL_SCANCODE_BRIGHTNESSDOWN = 275, + SDL_SCANCODE_BRIGHTNESSUP = 276, + SDL_SCANCODE_DISPLAYSWITCH = 277, + SDL_SCANCODE_KBDILLUMTOGGLE = 278, + SDL_SCANCODE_KBDILLUMDOWN = 279, + SDL_SCANCODE_KBDILLUMUP = 280, + SDL_SCANCODE_EJECT = 281, + SDL_SCANCODE_SLEEP = 282, + SDL_SCANCODE_APP1 = 283, + SDL_SCANCODE_APP2 = 284, + SDL_NUM_SCANCODES = 512 +}; +typedef Sint32 SDL_Keycode; +enum +{ + SDLK_UNKNOWN = 0, + SDLK_RETURN = '\r', + SDLK_ESCAPE = '\033', + SDLK_BACKSPACE = '\b', + SDLK_TAB = '\t', + SDLK_SPACE = ' ', + SDLK_EXCLAIM = '!', + SDLK_QUOTEDBL = '"', + SDLK_HASH = '#', + SDLK_PERCENT = '%', + SDLK_DOLLAR = '$', + SDLK_AMPERSAND = '&', + SDLK_QUOTE = '\'', + SDLK_LEFTPAREN = '(', + SDLK_RIGHTPAREN = ')', + SDLK_ASTERISK = '*', + SDLK_PLUS = '+', + SDLK_COMMA = ',', + SDLK_MINUS = '-', + SDLK_PERIOD = '.', + SDLK_SLASH = '/', + SDLK_0 = '0', + SDLK_1 = '1', + SDLK_2 = '2', + SDLK_3 = '3', + SDLK_4 = '4', + SDLK_5 = '5', + SDLK_6 = '6', + SDLK_7 = '7', + SDLK_8 = '8', + SDLK_9 = '9', + SDLK_COLON = ':', + SDLK_SEMICOLON = ';', + SDLK_LESS = '<', + SDLK_EQUALS = '=', + SDLK_GREATER = '>', + SDLK_QUESTION = '?', + SDLK_AT = '@', + SDLK_LEFTBRACKET = '[', + SDLK_BACKSLASH = '\\', + SDLK_RIGHTBRACKET = ']', + SDLK_CARET = '^', + SDLK_UNDERSCORE = '_', + SDLK_BACKQUOTE = '`', + SDLK_a = 'a', + SDLK_b = 'b', + SDLK_c = 'c', + SDLK_d = 'd', + SDLK_e = 'e', + SDLK_f = 'f', + SDLK_g = 'g', + SDLK_h = 'h', + SDLK_i = 'i', + SDLK_j = 'j', + SDLK_k = 'k', + SDLK_l = 'l', + SDLK_m = 'm', + SDLK_n = 'n', + SDLK_o = 'o', + SDLK_p = 'p', + SDLK_q = 'q', + SDLK_r = 'r', + SDLK_s = 's', + SDLK_t = 't', + SDLK_u = 'u', + SDLK_v = 'v', + SDLK_w = 'w', + SDLK_x = 'x', + SDLK_y = 'y', + SDLK_z = 'z', + SDLK_CAPSLOCK = (SDL_SCANCODE_CAPSLOCK | (1<<30)), + SDLK_F1 = (SDL_SCANCODE_F1 | (1<<30)), + SDLK_F2 = (SDL_SCANCODE_F2 | (1<<30)), + SDLK_F3 = (SDL_SCANCODE_F3 | (1<<30)), + SDLK_F4 = (SDL_SCANCODE_F4 | (1<<30)), + SDLK_F5 = (SDL_SCANCODE_F5 | (1<<30)), + SDLK_F6 = (SDL_SCANCODE_F6 | (1<<30)), + SDLK_F7 = (SDL_SCANCODE_F7 | (1<<30)), + SDLK_F8 = (SDL_SCANCODE_F8 | (1<<30)), + SDLK_F9 = (SDL_SCANCODE_F9 | (1<<30)), + SDLK_F10 = (SDL_SCANCODE_F10 | (1<<30)), + SDLK_F11 = (SDL_SCANCODE_F11 | (1<<30)), + SDLK_F12 = (SDL_SCANCODE_F12 | (1<<30)), + SDLK_PRINTSCREEN = (SDL_SCANCODE_PRINTSCREEN | (1<<30)), + SDLK_SCROLLLOCK = (SDL_SCANCODE_SCROLLLOCK | (1<<30)), + SDLK_PAUSE = (SDL_SCANCODE_PAUSE | (1<<30)), + SDLK_INSERT = (SDL_SCANCODE_INSERT | (1<<30)), + SDLK_HOME = (SDL_SCANCODE_HOME | (1<<30)), + SDLK_PAGEUP = (SDL_SCANCODE_PAGEUP | (1<<30)), + SDLK_DELETE = '\177', + SDLK_END = (SDL_SCANCODE_END | (1<<30)), + SDLK_PAGEDOWN = (SDL_SCANCODE_PAGEDOWN | (1<<30)), + SDLK_RIGHT = (SDL_SCANCODE_RIGHT | (1<<30)), + SDLK_LEFT = (SDL_SCANCODE_LEFT | (1<<30)), + SDLK_DOWN = (SDL_SCANCODE_DOWN | (1<<30)), + SDLK_UP = (SDL_SCANCODE_UP | (1<<30)), + SDLK_NUMLOCKCLEAR = (SDL_SCANCODE_NUMLOCKCLEAR | (1<<30)), + SDLK_KP_DIVIDE = (SDL_SCANCODE_KP_DIVIDE | (1<<30)), + SDLK_KP_MULTIPLY = (SDL_SCANCODE_KP_MULTIPLY | (1<<30)), + SDLK_KP_MINUS = (SDL_SCANCODE_KP_MINUS | (1<<30)), + SDLK_KP_PLUS = (SDL_SCANCODE_KP_PLUS | (1<<30)), + SDLK_KP_ENTER = (SDL_SCANCODE_KP_ENTER | (1<<30)), + SDLK_KP_1 = (SDL_SCANCODE_KP_1 | (1<<30)), + SDLK_KP_2 = (SDL_SCANCODE_KP_2 | (1<<30)), + SDLK_KP_3 = (SDL_SCANCODE_KP_3 | (1<<30)), + SDLK_KP_4 = (SDL_SCANCODE_KP_4 | (1<<30)), + SDLK_KP_5 = (SDL_SCANCODE_KP_5 | (1<<30)), + SDLK_KP_6 = (SDL_SCANCODE_KP_6 | (1<<30)), + SDLK_KP_7 = (SDL_SCANCODE_KP_7 | (1<<30)), + SDLK_KP_8 = (SDL_SCANCODE_KP_8 | (1<<30)), + SDLK_KP_9 = (SDL_SCANCODE_KP_9 | (1<<30)), + SDLK_KP_0 = (SDL_SCANCODE_KP_0 | (1<<30)), + SDLK_KP_PERIOD = (SDL_SCANCODE_KP_PERIOD | (1<<30)), + SDLK_APPLICATION = (SDL_SCANCODE_APPLICATION | (1<<30)), + SDLK_POWER = (SDL_SCANCODE_POWER | (1<<30)), + SDLK_KP_EQUALS = (SDL_SCANCODE_KP_EQUALS | (1<<30)), + SDLK_F13 = (SDL_SCANCODE_F13 | (1<<30)), + SDLK_F14 = (SDL_SCANCODE_F14 | (1<<30)), + SDLK_F15 = (SDL_SCANCODE_F15 | (1<<30)), + SDLK_F16 = (SDL_SCANCODE_F16 | (1<<30)), + SDLK_F17 = (SDL_SCANCODE_F17 | (1<<30)), + SDLK_F18 = (SDL_SCANCODE_F18 | (1<<30)), + SDLK_F19 = (SDL_SCANCODE_F19 | (1<<30)), + SDLK_F20 = (SDL_SCANCODE_F20 | (1<<30)), + SDLK_F21 = (SDL_SCANCODE_F21 | (1<<30)), + SDLK_F22 = (SDL_SCANCODE_F22 | (1<<30)), + SDLK_F23 = (SDL_SCANCODE_F23 | (1<<30)), + SDLK_F24 = (SDL_SCANCODE_F24 | (1<<30)), + SDLK_EXECUTE = (SDL_SCANCODE_EXECUTE | (1<<30)), + SDLK_HELP = (SDL_SCANCODE_HELP | (1<<30)), + SDLK_MENU = (SDL_SCANCODE_MENU | (1<<30)), + SDLK_SELECT = (SDL_SCANCODE_SELECT | (1<<30)), + SDLK_STOP = (SDL_SCANCODE_STOP | (1<<30)), + SDLK_AGAIN = (SDL_SCANCODE_AGAIN | (1<<30)), + SDLK_UNDO = (SDL_SCANCODE_UNDO | (1<<30)), + SDLK_CUT = (SDL_SCANCODE_CUT | (1<<30)), + SDLK_COPY = (SDL_SCANCODE_COPY | (1<<30)), + SDLK_PASTE = (SDL_SCANCODE_PASTE | (1<<30)), + SDLK_FIND = (SDL_SCANCODE_FIND | (1<<30)), + SDLK_MUTE = (SDL_SCANCODE_MUTE | (1<<30)), + SDLK_VOLUMEUP = (SDL_SCANCODE_VOLUMEUP | (1<<30)), + SDLK_VOLUMEDOWN = (SDL_SCANCODE_VOLUMEDOWN | (1<<30)), + SDLK_KP_COMMA = (SDL_SCANCODE_KP_COMMA | (1<<30)), + SDLK_KP_EQUALSAS400 = + (SDL_SCANCODE_KP_EQUALSAS400 | (1<<30)), + SDLK_ALTERASE = (SDL_SCANCODE_ALTERASE | (1<<30)), + SDLK_SYSREQ = (SDL_SCANCODE_SYSREQ | (1<<30)), + SDLK_CANCEL = (SDL_SCANCODE_CANCEL | (1<<30)), + SDLK_CLEAR = (SDL_SCANCODE_CLEAR | (1<<30)), + SDLK_PRIOR = (SDL_SCANCODE_PRIOR | (1<<30)), + SDLK_RETURN2 = (SDL_SCANCODE_RETURN2 | (1<<30)), + SDLK_SEPARATOR = (SDL_SCANCODE_SEPARATOR | (1<<30)), + SDLK_OUT = (SDL_SCANCODE_OUT | (1<<30)), + SDLK_OPER = (SDL_SCANCODE_OPER | (1<<30)), + SDLK_CLEARAGAIN = (SDL_SCANCODE_CLEARAGAIN | (1<<30)), + SDLK_CRSEL = (SDL_SCANCODE_CRSEL | (1<<30)), + SDLK_EXSEL = (SDL_SCANCODE_EXSEL | (1<<30)), + SDLK_KP_00 = (SDL_SCANCODE_KP_00 | (1<<30)), + SDLK_KP_000 = (SDL_SCANCODE_KP_000 | (1<<30)), + SDLK_THOUSANDSSEPARATOR = + (SDL_SCANCODE_THOUSANDSSEPARATOR | (1<<30)), + SDLK_DECIMALSEPARATOR = + (SDL_SCANCODE_DECIMALSEPARATOR | (1<<30)), + SDLK_CURRENCYUNIT = (SDL_SCANCODE_CURRENCYUNIT | (1<<30)), + SDLK_CURRENCYSUBUNIT = + (SDL_SCANCODE_CURRENCYSUBUNIT | (1<<30)), + SDLK_KP_LEFTPAREN = (SDL_SCANCODE_KP_LEFTPAREN | (1<<30)), + SDLK_KP_RIGHTPAREN = (SDL_SCANCODE_KP_RIGHTPAREN | (1<<30)), + SDLK_KP_LEFTBRACE = (SDL_SCANCODE_KP_LEFTBRACE | (1<<30)), + SDLK_KP_RIGHTBRACE = (SDL_SCANCODE_KP_RIGHTBRACE | (1<<30)), + SDLK_KP_TAB = (SDL_SCANCODE_KP_TAB | (1<<30)), + SDLK_KP_BACKSPACE = (SDL_SCANCODE_KP_BACKSPACE | (1<<30)), + SDLK_KP_A = (SDL_SCANCODE_KP_A | (1<<30)), + SDLK_KP_B = (SDL_SCANCODE_KP_B | (1<<30)), + SDLK_KP_C = (SDL_SCANCODE_KP_C | (1<<30)), + SDLK_KP_D = (SDL_SCANCODE_KP_D | (1<<30)), + SDLK_KP_E = (SDL_SCANCODE_KP_E | (1<<30)), + SDLK_KP_F = (SDL_SCANCODE_KP_F | (1<<30)), + SDLK_KP_XOR = (SDL_SCANCODE_KP_XOR | (1<<30)), + SDLK_KP_POWER = (SDL_SCANCODE_KP_POWER | (1<<30)), + SDLK_KP_PERCENT = (SDL_SCANCODE_KP_PERCENT | (1<<30)), + SDLK_KP_LESS = (SDL_SCANCODE_KP_LESS | (1<<30)), + SDLK_KP_GREATER = (SDL_SCANCODE_KP_GREATER | (1<<30)), + SDLK_KP_AMPERSAND = (SDL_SCANCODE_KP_AMPERSAND | (1<<30)), + SDLK_KP_DBLAMPERSAND = + (SDL_SCANCODE_KP_DBLAMPERSAND | (1<<30)), + SDLK_KP_VERTICALBAR = + (SDL_SCANCODE_KP_VERTICALBAR | (1<<30)), + SDLK_KP_DBLVERTICALBAR = + (SDL_SCANCODE_KP_DBLVERTICALBAR | (1<<30)), + SDLK_KP_COLON = (SDL_SCANCODE_KP_COLON | (1<<30)), + SDLK_KP_HASH = (SDL_SCANCODE_KP_HASH | (1<<30)), + SDLK_KP_SPACE = (SDL_SCANCODE_KP_SPACE | (1<<30)), + SDLK_KP_AT = (SDL_SCANCODE_KP_AT | (1<<30)), + SDLK_KP_EXCLAM = (SDL_SCANCODE_KP_EXCLAM | (1<<30)), + SDLK_KP_MEMSTORE = (SDL_SCANCODE_KP_MEMSTORE | (1<<30)), + SDLK_KP_MEMRECALL = (SDL_SCANCODE_KP_MEMRECALL | (1<<30)), + SDLK_KP_MEMCLEAR = (SDL_SCANCODE_KP_MEMCLEAR | (1<<30)), + SDLK_KP_MEMADD = (SDL_SCANCODE_KP_MEMADD | (1<<30)), + SDLK_KP_MEMSUBTRACT = + (SDL_SCANCODE_KP_MEMSUBTRACT | (1<<30)), + SDLK_KP_MEMMULTIPLY = + (SDL_SCANCODE_KP_MEMMULTIPLY | (1<<30)), + SDLK_KP_MEMDIVIDE = (SDL_SCANCODE_KP_MEMDIVIDE | (1<<30)), + SDLK_KP_PLUSMINUS = (SDL_SCANCODE_KP_PLUSMINUS | (1<<30)), + SDLK_KP_CLEAR = (SDL_SCANCODE_KP_CLEAR | (1<<30)), + SDLK_KP_CLEARENTRY = (SDL_SCANCODE_KP_CLEARENTRY | (1<<30)), + SDLK_KP_BINARY = (SDL_SCANCODE_KP_BINARY | (1<<30)), + SDLK_KP_OCTAL = (SDL_SCANCODE_KP_OCTAL | (1<<30)), + SDLK_KP_DECIMAL = (SDL_SCANCODE_KP_DECIMAL | (1<<30)), + SDLK_KP_HEXADECIMAL = + (SDL_SCANCODE_KP_HEXADECIMAL | (1<<30)), + SDLK_LCTRL = (SDL_SCANCODE_LCTRL | (1<<30)), + SDLK_LSHIFT = (SDL_SCANCODE_LSHIFT | (1<<30)), + SDLK_LALT = (SDL_SCANCODE_LALT | (1<<30)), + SDLK_LGUI = (SDL_SCANCODE_LGUI | (1<<30)), + SDLK_RCTRL = (SDL_SCANCODE_RCTRL | (1<<30)), + SDLK_RSHIFT = (SDL_SCANCODE_RSHIFT | (1<<30)), + SDLK_RALT = (SDL_SCANCODE_RALT | (1<<30)), + SDLK_RGUI = (SDL_SCANCODE_RGUI | (1<<30)), + SDLK_MODE = (SDL_SCANCODE_MODE | (1<<30)), + SDLK_AUDIONEXT = (SDL_SCANCODE_AUDIONEXT | (1<<30)), + SDLK_AUDIOPREV = (SDL_SCANCODE_AUDIOPREV | (1<<30)), + SDLK_AUDIOSTOP = (SDL_SCANCODE_AUDIOSTOP | (1<<30)), + SDLK_AUDIOPLAY = (SDL_SCANCODE_AUDIOPLAY | (1<<30)), + SDLK_AUDIOMUTE = (SDL_SCANCODE_AUDIOMUTE | (1<<30)), + SDLK_MEDIASELECT = (SDL_SCANCODE_MEDIASELECT | (1<<30)), + SDLK_WWW = (SDL_SCANCODE_WWW | (1<<30)), + SDLK_MAIL = (SDL_SCANCODE_MAIL | (1<<30)), + SDLK_CALCULATOR = (SDL_SCANCODE_CALCULATOR | (1<<30)), + SDLK_COMPUTER = (SDL_SCANCODE_COMPUTER | (1<<30)), + SDLK_AC_SEARCH = (SDL_SCANCODE_AC_SEARCH | (1<<30)), + SDLK_AC_HOME = (SDL_SCANCODE_AC_HOME | (1<<30)), + SDLK_AC_BACK = (SDL_SCANCODE_AC_BACK | (1<<30)), + SDLK_AC_FORWARD = (SDL_SCANCODE_AC_FORWARD | (1<<30)), + SDLK_AC_STOP = (SDL_SCANCODE_AC_STOP | (1<<30)), + SDLK_AC_REFRESH = (SDL_SCANCODE_AC_REFRESH | (1<<30)), + SDLK_AC_BOOKMARKS = (SDL_SCANCODE_AC_BOOKMARKS | (1<<30)), + SDLK_BRIGHTNESSDOWN = + (SDL_SCANCODE_BRIGHTNESSDOWN | (1<<30)), + SDLK_BRIGHTNESSUP = (SDL_SCANCODE_BRIGHTNESSUP | (1<<30)), + SDLK_DISPLAYSWITCH = (SDL_SCANCODE_DISPLAYSWITCH | (1<<30)), + SDLK_KBDILLUMTOGGLE = + (SDL_SCANCODE_KBDILLUMTOGGLE | (1<<30)), + SDLK_KBDILLUMDOWN = (SDL_SCANCODE_KBDILLUMDOWN | (1<<30)), + SDLK_KBDILLUMUP = (SDL_SCANCODE_KBDILLUMUP | (1<<30)), + SDLK_EJECT = (SDL_SCANCODE_EJECT | (1<<30)), + SDLK_SLEEP = (SDL_SCANCODE_SLEEP | (1<<30)) +}; +typedef enum +{ + SDL_KMOD_NONE = 0x0000, + SDL_KMOD_LSHIFT = 0x0001, + SDL_KMOD_RSHIFT = 0x0002, + SDL_KMOD_LCTRL = 0x0040, + SDL_KMOD_RCTRL = 0x0080, + SDL_KMOD_LALT = 0x0100, + SDL_KMOD_RALT = 0x0200, + SDL_KMOD_LGUI = 0x0400, + SDL_KMOD_RGUI = 0x0800, + SDL_KMOD_NUM = 0x1000, + SDL_KMOD_CAPS = 0x2000, + SDL_KMOD_MODE = 0x4000, + SDL_KMOD_RESERVED = 0x8000 +} SDL_Keymod; +typedef struct SDL_Keysym +{ + SDL_Scancode scancode; + SDL_Keycode sym; + Uint16 mod; + Uint32 unused; +} SDL_Keysym; +SDL_Window * SDL_GetKeyboardFocus(void); +const Uint8 * SDL_GetKeyboardState(int *numkeys); +SDL_Keymod SDL_GetModState(void); +void SDL_SetModState(SDL_Keymod modstate); +SDL_Keycode SDL_GetKeyFromScancode(SDL_Scancode scancode); +SDL_Scancode SDL_GetScancodeFromKey(SDL_Keycode key); +const char * SDL_GetScancodeName(SDL_Scancode scancode); +SDL_Scancode SDL_GetScancodeFromName(const char *name); +const char * SDL_GetKeyName(SDL_Keycode key); +SDL_Keycode SDL_GetKeyFromName(const char *name); +void SDL_StartTextInput(void); +SDL_bool SDL_IsTextInputActive(void); +void SDL_StopTextInput(void); +void SDL_SetTextInputRect(SDL_Rect *rect); +SDL_bool SDL_HasScreenKeyboardSupport(void); +SDL_bool SDL_IsScreenKeyboardShown(SDL_Window *window); +typedef struct SDL_Cursor SDL_Cursor; +typedef enum +{ + SDL_SYSTEM_CURSOR_ARROW, + SDL_SYSTEM_CURSOR_IBEAM, + SDL_SYSTEM_CURSOR_WAIT, + SDL_SYSTEM_CURSOR_CROSSHAIR, + SDL_SYSTEM_CURSOR_WAITARROW, + SDL_SYSTEM_CURSOR_SIZENWSE, + SDL_SYSTEM_CURSOR_SIZENESW, + SDL_SYSTEM_CURSOR_SIZEWE, + SDL_SYSTEM_CURSOR_SIZENS, + SDL_SYSTEM_CURSOR_SIZEALL, + SDL_SYSTEM_CURSOR_NO, + SDL_SYSTEM_CURSOR_HAND, + SDL_NUM_SYSTEM_CURSORS +} SDL_SystemCursor; +SDL_Window * SDL_GetMouseFocus(void); +Uint32 SDL_GetMouseState(int *x, int *y); +Uint32 SDL_GetRelativeMouseState(int *x, int *y); +void SDL_WarpMouseInWindow(SDL_Window * window, + int x, int y); +int SDL_SetRelativeMouseMode(SDL_bool enabled); +SDL_bool SDL_GetRelativeMouseMode(void); +SDL_Cursor * SDL_CreateCursor(const Uint8 * data, + const Uint8 * mask, + int w, int h, int hot_x, + int hot_y); +SDL_Cursor * SDL_CreateColorCursor(SDL_Surface *surface, + int hot_x, + int hot_y); +SDL_Cursor * SDL_CreateSystemCursor(SDL_SystemCursor id); +void SDL_SetCursor(SDL_Cursor * cursor); +SDL_Cursor * SDL_GetCursor(void); +SDL_Cursor * SDL_GetDefaultCursor(void); +void SDL_FreeCursor(SDL_Cursor * cursor); +int SDL_ShowCursor(int toggle); +struct _SDL_Joystick; +typedef struct _SDL_Joystick SDL_Joystick; +typedef struct { + Uint8 data[16]; +} SDL_JoystickGUID; +typedef Sint32 SDL_JoystickID; +int SDL_NumJoysticks(void); +const char * SDL_JoystickNameForIndex(int device_index); +SDL_Joystick * SDL_JoystickOpen(int device_index); +const char * SDL_JoystickName(SDL_Joystick * joystick); +SDL_JoystickGUID SDL_JoystickGetDeviceGUID(int device_index); +SDL_JoystickGUID SDL_JoystickGetGUID(SDL_Joystick * joystick); +void SDL_JoystickGetGUIDString(SDL_JoystickGUID guid, char *pszGUID, int cbGUID); +SDL_JoystickGUID SDL_JoystickGetGUIDFromString(const char *pchGUID); +SDL_bool SDL_JoystickGetAttached(SDL_Joystick * joystick); +SDL_JoystickID SDL_JoystickInstanceID(SDL_Joystick * joystick); +int SDL_JoystickNumAxes(SDL_Joystick * joystick); +int SDL_JoystickNumBalls(SDL_Joystick * joystick); +int SDL_JoystickNumHats(SDL_Joystick * joystick); +int SDL_JoystickNumButtons(SDL_Joystick * joystick); +void SDL_JoystickUpdate(void); +int SDL_JoystickEventState(int state); +Sint16 SDL_JoystickGetAxis(SDL_Joystick * joystick, + int axis); +Uint8 SDL_JoystickGetHat(SDL_Joystick * joystick, + int hat); +int SDL_JoystickGetBall(SDL_Joystick * joystick, + int ball, int *dx, int *dy); +Uint8 SDL_JoystickGetButton(SDL_Joystick * joystick, + int button); +void SDL_JoystickClose(SDL_Joystick * joystick); +struct _SDL_GameController; +typedef struct _SDL_GameController SDL_GameController; +typedef enum +{ + SDL_CONTROLLER_BINDTYPE_NONE = 0, + SDL_CONTROLLER_BINDTYPE_BUTTON, + SDL_CONTROLLER_BINDTYPE_AXIS, + SDL_CONTROLLER_BINDTYPE_HAT +} SDL_GameControllerBindType; +typedef struct SDL_GameControllerButtonBind +{ + SDL_GameControllerBindType bindType; + union + { + int button; + int axis; + struct { + int hat; + int hat_mask; + } hat; + } value; +} SDL_GameControllerButtonBind; +int SDL_GameControllerAddMapping( const char* mappingString ); +char * SDL_GameControllerMappingForGUID( SDL_JoystickGUID guid ); +char * SDL_GameControllerMapping( SDL_GameController * gamecontroller ); +SDL_bool SDL_IsGameController(int joystick_index); +const char * SDL_GameControllerNameForIndex(int joystick_index); +SDL_GameController * SDL_GameControllerOpen(int joystick_index); +const char * SDL_GameControllerName(SDL_GameController *gamecontroller); +SDL_bool SDL_GameControllerGetAttached(SDL_GameController *gamecontroller); +SDL_Joystick * SDL_GameControllerGetJoystick(SDL_GameController *gamecontroller); +int SDL_GameControllerEventState(int state); +void SDL_GameControllerUpdate(void); +typedef enum +{ + SDL_CONTROLLER_AXIS_INVALID = -1, + SDL_CONTROLLER_AXIS_LEFTX, + SDL_CONTROLLER_AXIS_LEFTY, + SDL_CONTROLLER_AXIS_RIGHTX, + SDL_CONTROLLER_AXIS_RIGHTY, + SDL_CONTROLLER_AXIS_TRIGGERLEFT, + SDL_CONTROLLER_AXIS_TRIGGERRIGHT, + SDL_CONTROLLER_AXIS_MAX +} SDL_GameControllerAxis; +SDL_GameControllerAxis SDL_GameControllerGetAxisFromString(const char *pchString); +const char* SDL_GameControllerGetStringForAxis(SDL_GameControllerAxis axis); +SDL_GameControllerButtonBind +SDL_GameControllerGetBindForAxis(SDL_GameController *gamecontroller, + SDL_GameControllerAxis axis); +Sint16 +SDL_GameControllerGetAxis(SDL_GameController *gamecontroller, + SDL_GameControllerAxis axis); +typedef enum +{ + SDL_CONTROLLER_BUTTON_INVALID = -1, + SDL_CONTROLLER_BUTTON_A, + SDL_CONTROLLER_BUTTON_B, + SDL_CONTROLLER_BUTTON_X, + SDL_CONTROLLER_BUTTON_Y, + SDL_CONTROLLER_BUTTON_BACK, + SDL_CONTROLLER_BUTTON_GUIDE, + SDL_CONTROLLER_BUTTON_START, + SDL_CONTROLLER_BUTTON_LEFTSTICK, + SDL_CONTROLLER_BUTTON_RIGHTSTICK, + SDL_CONTROLLER_BUTTON_LEFTSHOULDER, + SDL_CONTROLLER_BUTTON_RIGHTSHOULDER, + SDL_CONTROLLER_BUTTON_DPAD_UP, + SDL_CONTROLLER_BUTTON_DPAD_DOWN, + SDL_CONTROLLER_BUTTON_DPAD_LEFT, + SDL_CONTROLLER_BUTTON_DPAD_RIGHT, + SDL_CONTROLLER_BUTTON_MAX +} SDL_GameControllerButton; +SDL_GameControllerButton SDL_GameControllerGetButtonFromString(const char *pchString); +const char* SDL_GameControllerGetStringForButton(SDL_GameControllerButton button); +SDL_GameControllerButtonBind +SDL_GameControllerGetBindForButton(SDL_GameController *gamecontroller, + SDL_GameControllerButton button); +Uint8 SDL_GameControllerGetButton(SDL_GameController *gamecontroller, + SDL_GameControllerButton button); +void SDL_GameControllerClose(SDL_GameController *gamecontroller); +typedef Sint64 SDL_TouchID; +typedef Sint64 SDL_FingerID; +typedef struct SDL_Finger +{ + SDL_FingerID id; + float x; + float y; + float pressure; +} SDL_Finger; +int SDL_GetNumTouchDevices(void); +SDL_TouchID SDL_GetTouchDevice(int index); +int SDL_GetNumTouchFingers(SDL_TouchID touchID); +SDL_Finger * SDL_GetTouchFinger(SDL_TouchID touchID, int index); +typedef Sint64 SDL_GestureID; +int SDL_RecordGesture(SDL_TouchID touchId); +int SDL_SaveAllDollarTemplates(SDL_RWops *src); +int SDL_SaveDollarTemplate(SDL_GestureID gestureId,SDL_RWops *src); +int SDL_LoadDollarTemplates(SDL_TouchID touchId, SDL_RWops *src); +typedef enum +{ + SDL_FIRSTEVENT = 0, + SDL_QUIT = 0x100, + SDL_APP_TERMINATING, + SDL_APP_LOWMEMORY, + SDL_APP_WILLENTERBACKGROUND, + SDL_APP_DIDENTERBACKGROUND, + SDL_APP_WILLENTERFOREGROUND, + SDL_APP_DIDENTERFOREGROUND, + SDL_WINDOWEVENT = 0x200, + SDL_SYSWMEVENT, + SDL_KEYDOWN = 0x300, + SDL_KEYUP, + SDL_TEXTEDITING, + SDL_TEXTINPUT, + SDL_MOUSEMOTION = 0x400, + SDL_MOUSEBUTTONDOWN, + SDL_MOUSEBUTTONUP, + SDL_MOUSEWHEEL, + SDL_JOYAXISMOTION = 0x600, + SDL_JOYBALLMOTION, + SDL_JOYHATMOTION, + SDL_JOYBUTTONDOWN, + SDL_JOYBUTTONUP, + SDL_JOYDEVICEADDED, + SDL_JOYDEVICEREMOVED, + SDL_CONTROLLERAXISMOTION = 0x650, + SDL_CONTROLLERBUTTONDOWN, + SDL_CONTROLLERBUTTONUP, + SDL_CONTROLLERDEVICEADDED, + SDL_CONTROLLERDEVICEREMOVED, + SDL_CONTROLLERDEVICEREMAPPED, + SDL_FINGERDOWN = 0x700, + SDL_FINGERUP, + SDL_FINGERMOTION, + SDL_DOLLARGESTURE = 0x800, + SDL_DOLLARRECORD, + SDL_MULTIGESTURE, + SDL_CLIPBOARDUPDATE = 0x900, + SDL_DROPFILE = 0x1000, + SDL_USEREVENT = 0x8000, + SDL_LASTEVENT = 0xFFFF +} SDL_EventType; +typedef struct SDL_CommonEvent +{ + Uint32 type; + Uint32 timestamp; +} SDL_CommonEvent; +typedef struct SDL_WindowEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + Uint8 event; + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + Sint32 data1; + Sint32 data2; +} SDL_WindowEvent; +typedef struct SDL_KeyboardEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + Uint8 state; + Uint8 repeat; + Uint8 padding2; + Uint8 padding3; + SDL_Keysym keysym; +} SDL_KeyboardEvent; +typedef struct SDL_TextEditingEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + char text[(32)]; + Sint32 start; + Sint32 length; +} SDL_TextEditingEvent; +typedef struct SDL_TextInputEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + char text[(32)]; +} SDL_TextInputEvent; +typedef struct SDL_MouseMotionEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + Uint32 which; + Uint32 state; + Sint32 x; + Sint32 y; + Sint32 xrel; + Sint32 yrel; +} SDL_MouseMotionEvent; +typedef struct SDL_MouseButtonEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + Uint32 which; + Uint8 button; + Uint8 state; + Uint8 padding1; + Uint8 padding2; + Sint32 x; + Sint32 y; +} SDL_MouseButtonEvent; +typedef struct SDL_MouseWheelEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + Uint32 which; + Sint32 x; + Sint32 y; +} SDL_MouseWheelEvent; +typedef struct SDL_JoyAxisEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_JoystickID which; + Uint8 axis; + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + Sint16 value; + Uint16 padding4; +} SDL_JoyAxisEvent; +typedef struct SDL_JoyBallEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_JoystickID which; + Uint8 ball; + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + Sint16 xrel; + Sint16 yrel; +} SDL_JoyBallEvent; +typedef struct SDL_JoyHatEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_JoystickID which; + Uint8 hat; + Uint8 value; + Uint8 padding1; + Uint8 padding2; +} SDL_JoyHatEvent; +typedef struct SDL_JoyButtonEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_JoystickID which; + Uint8 button; + Uint8 state; + Uint8 padding1; + Uint8 padding2; +} SDL_JoyButtonEvent; +typedef struct SDL_JoyDeviceEvent +{ + Uint32 type; + Uint32 timestamp; + Sint32 which; +} SDL_JoyDeviceEvent; +typedef struct SDL_ControllerAxisEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_JoystickID which; + Uint8 axis; + Uint8 padding1; + Uint8 padding2; + Uint8 padding3; + Sint16 value; + Uint16 padding4; +} SDL_ControllerAxisEvent; +typedef struct SDL_ControllerButtonEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_JoystickID which; + Uint8 button; + Uint8 state; + Uint8 padding1; + Uint8 padding2; +} SDL_ControllerButtonEvent; +typedef struct SDL_ControllerDeviceEvent +{ + Uint32 type; + Uint32 timestamp; + Sint32 which; +} SDL_ControllerDeviceEvent; +typedef struct SDL_TouchFingerEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_TouchID touchId; + SDL_FingerID fingerId; + float x; + float y; + float dx; + float dy; + float pressure; +} SDL_TouchFingerEvent; +typedef struct SDL_MultiGestureEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_TouchID touchId; + float dTheta; + float dDist; + float x; + float y; + Uint16 numFingers; + Uint16 padding; +} SDL_MultiGestureEvent; +typedef struct SDL_DollarGestureEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_TouchID touchId; + SDL_GestureID gestureId; + Uint32 numFingers; + float error; + float x; + float y; +} SDL_DollarGestureEvent; +typedef struct SDL_DropEvent +{ + Uint32 type; + Uint32 timestamp; + char *file; +} SDL_DropEvent; +typedef struct SDL_QuitEvent +{ + Uint32 type; + Uint32 timestamp; +} SDL_QuitEvent; +typedef struct SDL_OSEvent +{ + Uint32 type; + Uint32 timestamp; +} SDL_OSEvent; +typedef struct SDL_UserEvent +{ + Uint32 type; + Uint32 timestamp; + Uint32 windowID; + Sint32 code; + void *data1; + void *data2; +} SDL_UserEvent; +struct SDL_SysWMmsg; +typedef struct SDL_SysWMmsg SDL_SysWMmsg; +typedef struct SDL_SysWMEvent +{ + Uint32 type; + Uint32 timestamp; + SDL_SysWMmsg *msg; +} SDL_SysWMEvent; +typedef union SDL_Event +{ + Uint32 type; + SDL_CommonEvent common; + SDL_WindowEvent window; + SDL_KeyboardEvent key; + SDL_TextEditingEvent edit; + SDL_TextInputEvent text; + SDL_MouseMotionEvent motion; + SDL_MouseButtonEvent button; + SDL_MouseWheelEvent wheel; + SDL_JoyAxisEvent jaxis; + SDL_JoyBallEvent jball; + SDL_JoyHatEvent jhat; + SDL_JoyButtonEvent jbutton; + SDL_JoyDeviceEvent jdevice; + SDL_ControllerAxisEvent caxis; + SDL_ControllerButtonEvent cbutton; + SDL_ControllerDeviceEvent cdevice; + SDL_QuitEvent quit; + SDL_UserEvent user; + SDL_SysWMEvent syswm; + SDL_TouchFingerEvent tfinger; + SDL_MultiGestureEvent mgesture; + SDL_DollarGestureEvent dgesture; + SDL_DropEvent drop; + Uint8 padding[56]; +} SDL_Event; +void SDL_PumpEvents(void); +typedef enum +{ + SDL_ADDEVENT, + SDL_PEEKEVENT, + SDL_GETEVENT +} SDL_eventaction; +int SDL_PeepEvents(SDL_Event * events, int numevents, + SDL_eventaction action, + Uint32 minType, Uint32 maxType); +SDL_bool SDL_HasEvent(Uint32 type); +SDL_bool SDL_HasEvents(Uint32 minType, Uint32 maxType); +void SDL_FlushEvent(Uint32 type); +void SDL_FlushEvents(Uint32 minType, Uint32 maxType); +int SDL_PollEvent(SDL_Event * event); +int SDL_WaitEvent(SDL_Event * event); +int SDL_WaitEventTimeout(SDL_Event * event, + int timeout); +int SDL_PushEvent(SDL_Event * event); +typedef int ( * SDL_EventFilter) (void *userdata, SDL_Event * event); +void SDL_SetEventFilter(SDL_EventFilter filter, + void *userdata); +SDL_bool SDL_GetEventFilter(SDL_EventFilter * filter, + void **userdata); +void SDL_AddEventWatch(SDL_EventFilter filter, + void *userdata); +void SDL_DelEventWatch(SDL_EventFilter filter, + void *userdata); +void SDL_FilterEvents(SDL_EventFilter filter, + void *userdata); +Uint8 SDL_EventState(Uint32 type, int state); +Uint32 SDL_RegisterEvents(int numevents); +struct _SDL_Haptic; +typedef struct _SDL_Haptic SDL_Haptic; +typedef struct SDL_HapticDirection +{ + Uint8 type; + Sint32 dir[3]; +} SDL_HapticDirection; +typedef struct SDL_HapticConstant +{ + Uint16 type; + SDL_HapticDirection direction; + Uint32 length; + Uint16 delay; + Uint16 button; + Uint16 interval; + Sint16 level; + Uint16 attack_length; + Uint16 attack_level; + Uint16 fade_length; + Uint16 fade_level; +} SDL_HapticConstant; +typedef struct SDL_HapticPeriodic +{ + Uint16 type; + SDL_HapticDirection direction; + Uint32 length; + Uint16 delay; + Uint16 button; + Uint16 interval; + Uint16 period; + Sint16 magnitude; + Sint16 offset; + Uint16 phase; + Uint16 attack_length; + Uint16 attack_level; + Uint16 fade_length; + Uint16 fade_level; +} SDL_HapticPeriodic; +typedef struct SDL_HapticCondition +{ + Uint16 type; + SDL_HapticDirection direction; + Uint32 length; + Uint16 delay; + Uint16 button; + Uint16 interval; + Uint16 right_sat[3]; + Uint16 left_sat[3]; + Sint16 right_coeff[3]; + Sint16 left_coeff[3]; + Uint16 deadband[3]; + Sint16 center[3]; +} SDL_HapticCondition; +typedef struct SDL_HapticRamp +{ + Uint16 type; + SDL_HapticDirection direction; + Uint32 length; + Uint16 delay; + Uint16 button; + Uint16 interval; + Sint16 start; + Sint16 end; + Uint16 attack_length; + Uint16 attack_level; + Uint16 fade_length; + Uint16 fade_level; +} SDL_HapticRamp; +typedef struct SDL_HapticLeftRight +{ + Uint16 type; + Uint32 length; + Uint16 large_magnitude; + Uint16 small_magnitude; +} SDL_HapticLeftRight; +typedef struct SDL_HapticCustom +{ + Uint16 type; + SDL_HapticDirection direction; + Uint32 length; + Uint16 delay; + Uint16 button; + Uint16 interval; + Uint8 channels; + Uint16 period; + Uint16 samples; + Uint16 *data; + Uint16 attack_length; + Uint16 attack_level; + Uint16 fade_length; + Uint16 fade_level; +} SDL_HapticCustom; +typedef union SDL_HapticEffect +{ + Uint16 type; + SDL_HapticConstant constant; + SDL_HapticPeriodic periodic; + SDL_HapticCondition condition; + SDL_HapticRamp ramp; + SDL_HapticLeftRight leftright; + SDL_HapticCustom custom; +} SDL_HapticEffect; +int SDL_NumHaptics(void); +const char * SDL_HapticName(int device_index); +SDL_Haptic * SDL_HapticOpen(int device_index); +int SDL_HapticOpened(int device_index); +int SDL_HapticIndex(SDL_Haptic * haptic); +int SDL_MouseIsHaptic(void); +SDL_Haptic * SDL_HapticOpenFromMouse(void); +int SDL_JoystickIsHaptic(SDL_Joystick * joystick); +SDL_Haptic * SDL_HapticOpenFromJoystick(SDL_Joystick * + joystick); +void SDL_HapticClose(SDL_Haptic * haptic); +int SDL_HapticNumEffects(SDL_Haptic * haptic); +int SDL_HapticNumEffectsPlaying(SDL_Haptic * haptic); +unsigned int SDL_HapticQuery(SDL_Haptic * haptic); +int SDL_HapticNumAxes(SDL_Haptic * haptic); +int SDL_HapticEffectSupported(SDL_Haptic * haptic, + SDL_HapticEffect * + effect); +int SDL_HapticNewEffect(SDL_Haptic * haptic, + SDL_HapticEffect * effect); +int SDL_HapticUpdateEffect(SDL_Haptic * haptic, + int effect, + SDL_HapticEffect * data); +int SDL_HapticRunEffect(SDL_Haptic * haptic, + int effect, + Uint32 iterations); +int SDL_HapticStopEffect(SDL_Haptic * haptic, + int effect); +void SDL_HapticDestroyEffect(SDL_Haptic * haptic, + int effect); +int SDL_HapticGetEffectStatus(SDL_Haptic * haptic, + int effect); +int SDL_HapticSetGain(SDL_Haptic * haptic, int gain); +int SDL_HapticSetAutocenter(SDL_Haptic * haptic, + int autocenter); +int SDL_HapticPause(SDL_Haptic * haptic); +int SDL_HapticUnpause(SDL_Haptic * haptic); +int SDL_HapticStopAll(SDL_Haptic * haptic); +int SDL_HapticRumbleSupported(SDL_Haptic * haptic); +int SDL_HapticRumbleInit(SDL_Haptic * haptic); +int SDL_HapticRumblePlay(SDL_Haptic * haptic, float strength, Uint32 length ); +int SDL_HapticRumbleStop(SDL_Haptic * haptic); +typedef enum +{ + SDL_HINT_DEFAULT, + SDL_HINT_NORMAL, + SDL_HINT_OVERRIDE +} SDL_HintPriority; +SDL_bool SDL_SetHintWithPriority(const char *name, + const char *value, + SDL_HintPriority priority); +SDL_bool SDL_SetHint(const char *name, + const char *value); +const char * SDL_GetHint(const char *name); +typedef void (*SDL_HintCallback)(void *userdata, const char *name, const char *oldValue, const char *newValue); +void SDL_AddHintCallback(const char *name, + SDL_HintCallback callback, + void *userdata); +void SDL_DelHintCallback(const char *name, + SDL_HintCallback callback, + void *userdata); +void SDL_ClearHints(void); +void * SDL_LoadObject(const char *sofile); +void * SDL_LoadFunction(void *handle, + const char *name); +void SDL_UnloadObject(void *handle); +enum +{ + SDL_LOG_CATEGORY_APPLICATION, + SDL_LOG_CATEGORY_ERROR, + SDL_LOG_CATEGORY_ASSERT, + SDL_LOG_CATEGORY_SYSTEM, + SDL_LOG_CATEGORY_AUDIO, + SDL_LOG_CATEGORY_VIDEO, + SDL_LOG_CATEGORY_RENDER, + SDL_LOG_CATEGORY_INPUT, + SDL_LOG_CATEGORY_TEST, + SDL_LOG_CATEGORY_RESERVED1, + SDL_LOG_CATEGORY_RESERVED2, + SDL_LOG_CATEGORY_RESERVED3, + SDL_LOG_CATEGORY_RESERVED4, + SDL_LOG_CATEGORY_RESERVED5, + SDL_LOG_CATEGORY_RESERVED6, + SDL_LOG_CATEGORY_RESERVED7, + SDL_LOG_CATEGORY_RESERVED8, + SDL_LOG_CATEGORY_RESERVED9, + SDL_LOG_CATEGORY_RESERVED10, + SDL_LOG_CATEGORY_CUSTOM +}; +typedef enum +{ + SDL_LOG_PRIORITY_VERBOSE = 1, + SDL_LOG_PRIORITY_DEBUG, + SDL_LOG_PRIORITY_INFO, + SDL_LOG_PRIORITY_WARN, + SDL_LOG_PRIORITY_ERROR, + SDL_LOG_PRIORITY_CRITICAL, + SDL_NUM_LOG_PRIORITIES +} SDL_LogPriority; +void SDL_LogSetAllPriority(SDL_LogPriority priority); +void SDL_LogSetPriority(int category, + SDL_LogPriority priority); +SDL_LogPriority SDL_LogGetPriority(int category); +void SDL_LogResetPriorities(void); +void SDL_Log(const char *fmt, ...); +void SDL_LogVerbose(int category, const char *fmt, ...); +void SDL_LogDebug(int category, const char *fmt, ...); +void SDL_LogInfo(int category, const char *fmt, ...); +void SDL_LogWarn(int category, const char *fmt, ...); +void SDL_LogError(int category, const char *fmt, ...); +void SDL_LogCritical(int category, const char *fmt, ...); +void SDL_LogMessage(int category, + SDL_LogPriority priority, + const char *fmt, ...); +void SDL_LogMessageV(int category, + SDL_LogPriority priority, + const char *fmt, va_list ap); +typedef void (*SDL_LogOutputFunction)(void *userdata, int category, SDL_LogPriority priority, const char *message); +void SDL_LogGetOutputFunction(SDL_LogOutputFunction *callback, void **userdata); +void SDL_LogSetOutputFunction(SDL_LogOutputFunction callback, void *userdata); +typedef enum +{ + SDL_MESSAGEBOX_ERROR = 0x00000010, + SDL_MESSAGEBOX_WARNING = 0x00000020, + SDL_MESSAGEBOX_INFORMATION = 0x00000040 +} SDL_MessageBoxFlags; +typedef enum +{ + SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT = 0x00000001, + SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT = 0x00000002 +} SDL_MessageBoxButtonFlags; +typedef struct +{ + Uint32 flags; + int buttonid; + const char * text; +} SDL_MessageBoxButtonData; +typedef struct +{ + Uint8 r, g, b; +} SDL_MessageBoxColor; +typedef enum +{ + SDL_MESSAGEBOX_COLOR_BACKGROUND, + SDL_MESSAGEBOX_COLOR_TEXT, + SDL_MESSAGEBOX_COLOR_BUTTON_BORDER, + SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND, + SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED, + SDL_MESSAGEBOX_COLOR_MAX +} SDL_MessageBoxColorType; +typedef struct +{ + SDL_MessageBoxColor colors[SDL_MESSAGEBOX_COLOR_MAX]; +} SDL_MessageBoxColorScheme; +typedef struct +{ + Uint32 flags; + SDL_Window *window; + const char *title; + const char *message; + int numbuttons; + const SDL_MessageBoxButtonData *buttons; + const SDL_MessageBoxColorScheme *colorScheme; +} SDL_MessageBoxData; +int SDL_ShowMessageBox(const SDL_MessageBoxData *messageboxdata, int *buttonid); +int SDL_ShowSimpleMessageBox(Uint32 flags, const char *title, const char *message, SDL_Window *window); +typedef enum +{ + SDL_POWERSTATE_UNKNOWN, + SDL_POWERSTATE_ON_BATTERY, + SDL_POWERSTATE_NO_BATTERY, + SDL_POWERSTATE_CHARGING, + SDL_POWERSTATE_CHARGED +} SDL_PowerState; +SDL_PowerState SDL_GetPowerInfo(int *secs, int *pct); +typedef enum +{ + SDL_RENDERER_SOFTWARE = 0x00000001, + SDL_RENDERER_ACCELERATED = 0x00000002, + SDL_RENDERER_PRESENTVSYNC = 0x00000004, + SDL_RENDERER_TARGETTEXTURE = 0x00000008 +} SDL_RendererFlags; +typedef struct SDL_RendererInfo +{ + const char *name; + Uint32 flags; + Uint32 num_texture_formats; + Uint32 texture_formats[16]; + int max_texture_width; + int max_texture_height; +} SDL_RendererInfo; +typedef enum +{ + SDL_TEXTUREACCESS_STATIC, + SDL_TEXTUREACCESS_STREAMING, + SDL_TEXTUREACCESS_TARGET +} SDL_TextureAccess; +typedef enum +{ + SDL_TEXTUREMODULATE_NONE = 0x00000000, + SDL_TEXTUREMODULATE_COLOR = 0x00000001, + SDL_TEXTUREMODULATE_ALPHA = 0x00000002 +} SDL_TextureModulate; +typedef enum +{ + SDL_FLIP_NONE = 0x00000000, + SDL_FLIP_HORIZONTAL = 0x00000001, + SDL_FLIP_VERTICAL = 0x00000002 +} SDL_RendererFlip; +struct SDL_Renderer; +typedef struct SDL_Renderer SDL_Renderer; +struct SDL_Texture; +typedef struct SDL_Texture SDL_Texture; +int SDL_GetNumRenderDrivers(void); +int SDL_GetRenderDriverInfo(int index, + SDL_RendererInfo * info); +int SDL_CreateWindowAndRenderer( + int width, int height, Uint32 window_flags, + SDL_Window **window, SDL_Renderer **renderer); +SDL_Renderer * SDL_CreateRenderer(SDL_Window * window, + int index, Uint32 flags); +SDL_Renderer * SDL_CreateSoftwareRenderer(SDL_Surface * surface); +SDL_Renderer * SDL_GetRenderer(SDL_Window * window); +int SDL_GetRendererInfo(SDL_Renderer * renderer, + SDL_RendererInfo * info); +int SDL_GetRendererOutputSize(SDL_Renderer * renderer, + int *w, int *h); +SDL_Texture * SDL_CreateTexture(SDL_Renderer * renderer, + Uint32 format, + int access, int w, + int h); +SDL_Texture * SDL_CreateTextureFromSurface(SDL_Renderer * renderer, SDL_Surface * surface); +int SDL_QueryTexture(SDL_Texture * texture, + Uint32 * format, int *access, + int *w, int *h); +int SDL_SetTextureColorMod(SDL_Texture * texture, + Uint8 r, Uint8 g, Uint8 b); +int SDL_GetTextureColorMod(SDL_Texture * texture, + Uint8 * r, Uint8 * g, + Uint8 * b); +int SDL_SetTextureAlphaMod(SDL_Texture * texture, + Uint8 alpha); +int SDL_GetTextureAlphaMod(SDL_Texture * texture, + Uint8 * alpha); +int SDL_SetTextureBlendMode(SDL_Texture * texture, + SDL_BlendMode blendMode); +int SDL_GetTextureBlendMode(SDL_Texture * texture, + SDL_BlendMode *blendMode); +int SDL_UpdateTexture(SDL_Texture * texture, + const SDL_Rect * rect, + const void *pixels, int pitch); +int SDL_LockTexture(SDL_Texture * texture, + const SDL_Rect * rect, + void **pixels, int *pitch); +void SDL_UnlockTexture(SDL_Texture * texture); +SDL_bool SDL_RenderTargetSupported(SDL_Renderer *renderer); +int SDL_SetRenderTarget(SDL_Renderer *renderer, + SDL_Texture *texture); +SDL_Texture * SDL_GetRenderTarget(SDL_Renderer *renderer); +int SDL_RenderSetLogicalSize(SDL_Renderer * renderer, int w, int h); +void SDL_RenderGetLogicalSize(SDL_Renderer * renderer, int *w, int *h); +int SDL_RenderSetViewport(SDL_Renderer * renderer, + const SDL_Rect * rect); +void SDL_RenderGetViewport(SDL_Renderer * renderer, + SDL_Rect * rect); +int SDL_RenderSetClipRect(SDL_Renderer * renderer, + const SDL_Rect * rect); +void SDL_RenderGetClipRect(SDL_Renderer * renderer, + SDL_Rect * rect); +int SDL_RenderSetScale(SDL_Renderer * renderer, + float scaleX, float scaleY); +void SDL_RenderGetScale(SDL_Renderer * renderer, + float *scaleX, float *scaleY); +int SDL_SetRenderDrawColor(SDL_Renderer * renderer, + Uint8 r, Uint8 g, Uint8 b, + Uint8 a); +int SDL_GetRenderDrawColor(SDL_Renderer * renderer, + Uint8 * r, Uint8 * g, Uint8 * b, + Uint8 * a); +int SDL_SetRenderDrawBlendMode(SDL_Renderer * renderer, + SDL_BlendMode blendMode); +int SDL_GetRenderDrawBlendMode(SDL_Renderer * renderer, + SDL_BlendMode *blendMode); +int SDL_RenderClear(SDL_Renderer * renderer); +int SDL_RenderDrawPoint(SDL_Renderer * renderer, + int x, int y); +int SDL_RenderDrawPoints(SDL_Renderer * renderer, + const SDL_Point * points, + int count); +int SDL_RenderDrawLine(SDL_Renderer * renderer, + int x1, int y1, int x2, int y2); +int SDL_RenderDrawLines(SDL_Renderer * renderer, + const SDL_Point * points, + int count); +int SDL_RenderDrawRect(SDL_Renderer * renderer, + const SDL_Rect * rect); +int SDL_RenderDrawRects(SDL_Renderer * renderer, + const SDL_Rect * rects, + int count); +int SDL_RenderFillRect(SDL_Renderer * renderer, + const SDL_Rect * rect); +int SDL_RenderFillRects(SDL_Renderer * renderer, + const SDL_Rect * rects, + int count); +int SDL_RenderCopy(SDL_Renderer * renderer, + SDL_Texture * texture, + const SDL_Rect * srcrect, + const SDL_Rect * dstrect); +int SDL_RenderCopyEx(SDL_Renderer * renderer, + SDL_Texture * texture, + const SDL_Rect * srcrect, + const SDL_Rect * dstrect, + const double angle, + const SDL_Point *center, + const SDL_RendererFlip flip); +int SDL_RenderReadPixels(SDL_Renderer * renderer, + const SDL_Rect * rect, + Uint32 format, + void *pixels, int pitch); +void SDL_RenderPresent(SDL_Renderer * renderer); +void SDL_DestroyTexture(SDL_Texture * texture); +void SDL_DestroyRenderer(SDL_Renderer * renderer); +int SDL_GL_BindTexture(SDL_Texture *texture, float *texw, float *texh); +int SDL_GL_UnbindTexture(SDL_Texture *texture); +Uint32 SDL_GetTicks(void); +Uint64 SDL_GetPerformanceCounter(void); +Uint64 SDL_GetPerformanceFrequency(void); +void SDL_Delay(Uint32 ms); +typedef Uint32 ( * SDL_TimerCallback) (Uint32 interval, void *param); +typedef int SDL_TimerID; +SDL_TimerID SDL_AddTimer(Uint32 interval, + SDL_TimerCallback callback, + void *param); +SDL_bool SDL_RemoveTimer(SDL_TimerID id); +typedef struct SDL_version +{ + Uint8 major; + Uint8 minor; + Uint8 patch; +} SDL_version; +void SDL_GetVersion(SDL_version * ver); +const char * SDL_GetRevision(void); +int SDL_GetRevisionNumber(void); +int SDL_Init(Uint32 flags); +int SDL_InitSubSystem(Uint32 flags); +void SDL_QuitSubSystem(Uint32 flags); +Uint32 SDL_WasInit(Uint32 flags); +void SDL_Quit(void); + +]] + +-- sdl + +ffi.cdef[[ +enum { +SDL_INIT_TIMER = 0x00000001, +SDL_INIT_AUDIO = 0x00000010, +SDL_INIT_VIDEO = 0x00000020, +SDL_INIT_JOYSTICK = 0x00000200, +SDL_INIT_HAPTIC = 0x00001000, +SDL_INIT_GAMECONTROLLER = 0x00002000, +SDL_INIT_EVENTS = 0x00004000, +SDL_INIT_NOPARACHUTE = 0x00100000, +SDL_INIT_EVERYTHING = ( \ + SDL_INIT_TIMER | SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_EVENTS | \ + SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC | SDL_INIT_GAMECONTROLLER \ + ) +}; +]] +-- audio + +ffi.cdef[[ +enum { +SDL_AUDIO_MASK_BITSIZE = (0xFF), +SDL_AUDIO_MASK_DATATYPE = (1<<8), +SDL_AUDIO_MASK_ENDIAN = (1<<12), +SDL_AUDIO_MASK_SIGNED = (1<<15) +}; + +enum { +SDL_AUDIO_U8 = 0x0008, +SDL_AUDIO_S8 = 0x8008, +SDL_AUDIO_U16LSB = 0x0010, +SDL_AUDIO_S16LSB = 0x8010, +SDL_AUDIO_U16MSB = 0x1010, +SDL_AUDIO_S16MSB = 0x9010, +SDL_AUDIO_U16 = SDL_AUDIO_U16LSB, +SDL_AUDIO_S16 = SDL_AUDIO_S16LSB, + +SDL_AUDIO_S32LSB = 0x8020, +SDL_AUDIO_S32MSB = 0x9020, +SDL_AUDIO_S32 = SDL_AUDIO_S32LSB, + +SDL_AUDIO_F32LSB = 0x8120, +SDL_AUDIO_F32MSB = 0x9120, +SDL_AUDIO_F32 = SDL_AUDIO_F32LSB +}; + +enum { +SDL_AUDIO_ALLOW_FREQUENCY_CHANGE = 0x00000001, +SDL_AUDIO_ALLOW_FORMAT_CHANGE = 0x00000002, +SDL_AUDIO_ALLOW_CHANNELS_CHANGE = 0x00000004, +SDL_AUDIO_ALLOW_ANY_CHANGE = (SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_FORMAT_CHANGE|SDL_AUDIO_ALLOW_CHANNELS_CHANGE), +SDL_MIX_MAXVOLUME = 128 +}; + +]] + +-- events + +ffi.cdef[[ +enum { +SDL_RELEASED = 0, +SDL_PRESSED = 1, +SDL_QUERY = -1, +SDL_IGNORE = 0, +SDL_DISABLE = 0, +SDL_ENABLE = 1 +}; +]] + +-- haptic + +ffi.cdef[[ +enum { +SDL_HAPTIC_CONSTANT = (1<<0), +SDL_HAPTIC_SINE = (1<<1), +SDL_HAPTIC_LEFTRIGHT = (1<<2), +SDL_HAPTIC_TRIANGLE = (1<<3), +SDL_HAPTIC_SAWTOOTHUP = (1<<4), +SDL_HAPTIC_SAWTOOTHDOWN = (1<<5), +SDL_HAPTIC_RAMP = (1<<6), +SDL_HAPTIC_SPRING = (1<<7), +SDL_HAPTIC_DAMPER = (1<<8), +SDL_HAPTIC_INERTIA = (1<<9), +SDL_HAPTIC_FRICTION = (1<<10), +SDL_HAPTIC_CUSTOM = (1<<11), +SDL_HAPTIC_GAIN = (1<<12), +SDL_HAPTIC_AUTOCENTER = (1<<13), +SDL_HAPTIC_STATUS = (1<<14), +SDL_HAPTIC_PAUSE = (1<<15), +SDL_HAPTIC_POLAR = 0, +SDL_HAPTIC_CARTESIAN = 1, +SDL_HAPTIC_SPHERICAL = 2, +SDL_HAPTIC_INFINITY = 4294967295U +}; +]] + +-- joystick + +ffi.cdef[[ +enum { +SDL_HAT_CENTERED = 0x00, +SDL_HAT_UP = 0x01, +SDL_HAT_RIGHT = 0x02, +SDL_HAT_DOWN = 0x04, +SDL_HAT_LEFT = 0x08, +SDL_HAT_RIGHTUP = (SDL_HAT_RIGHT|SDL_HAT_UP), +SDL_HAT_RIGHTDOWN = (SDL_HAT_RIGHT|SDL_HAT_DOWN), +SDL_HAT_LEFTUP = (SDL_HAT_LEFT|SDL_HAT_UP), +SDL_HAT_LEFTDOWN = (SDL_HAT_LEFT|SDL_HAT_DOWN) +}; +]] + +-- keycode + +ffi.cdef[[ +enum { +SDL_SCANCODE_MASK = (1<<30), +SDL_KMOD_CTRL = (SDL_KMOD_LCTRL|SDL_KMOD_RCTRL), +SDL_KMOD_SHIFT = (SDL_KMOD_LSHIFT|SDL_KMOD_RSHIFT), +SDL_KMOD_ALT = (SDL_KMOD_LALT|SDL_KMOD_RALT), +SDL_KMOD_GUI = (SDL_KMOD_LGUI|SDL_KMOD_RGUI) +}; +]] + +-- main +if jit.os == 'Windows' then + ffi.cdef[[ +int SDL_RegisterApp(char *name, Uint32 style, + void *hInst); +void SDL_UnregisterApp(void); + ]] +end + +-- mouse + +ffi.cdef[[ +enum { +SDL_BUTTON_LEFT = 1, +SDL_BUTTON_MIDDLE = 2, +SDL_BUTTON_RIGHT = 3, +SDL_BUTTON_X1 = 4, +SDL_BUTTON_X2 = 5, +SDL_BUTTON_LMASK = 1 << (SDL_BUTTON_LEFT-1), +SDL_BUTTON_MMASK = 1 << (SDL_BUTTON_MIDDLE-1), +SDL_BUTTON_RMASK = 1 << (SDL_BUTTON_RIGHT-1), +SDL_BUTTON_X1MASK = 1 << (SDL_BUTTON_X1-1), +SDL_BUTTON_X2MASK = 1 << (SDL_BUTTON_X2-1), +}; +]] + +-- mutex + +ffi.cdef[[ +enum { +SDL_MUTEX_TIMEDOUT = 1, +SDL_MUTEX_MAXWAIT = (~(Uint32)0) +}; +]] + +-- pixels + +ffi.cdef[[ +enum { +SDL_ALPHA_OPAQUE = 255, +SDL_ALPHA_TRANSPARENT = 0 +}; +]] + +-- rwops +ffi.cdef[[ +enum { +SDL_RWOPS_UNKNOWN = 0, +SDL_RWOPS_WINFILE = 1, +SDL_RWOPS_STDFILE = 2, +SDL_RWOPS_JNIFILE = 3, +SDL_RWOPS_MEMORY = 4, +SDL_RWOPS_MEMORY_RO = 5 +}; +]] + +-- shape +ffi.cdef[[ +enum { +SDL_NONSHAPEABLE_WINDOW = -1, +SDL_INVALID_SHAPE_ARGUMENT = -2, +SDL_WINDOW_LACKS_SHAPE = -3 +}; +]] + +-- surface +ffi.cdef[[ +enum { +SDL_SWSURFACE = 0, +SDL_PREALLOC = 0x00000001, +SDL_RLEACCEL = 0x00000002, +SDL_DONTFREE = 0x00000004 +}; +]] + +-- video +ffi.cdef[[ +enum { +SDL_WINDOWPOS_CENTERED_MASK = 0x2FFF0000, +SDL_WINDOWPOS_CENTERED = 0x2FFF0000 +}; +]] diff --git a/src/lib/luigi/backend/ffisdl/sdl2/defines.lua b/src/lib/luigi/backend/ffisdl/sdl2/defines.lua new file mode 100644 index 0000000..72d0195 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/sdl2/defines.lua @@ -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 diff --git a/src/lib/luigi/backend/ffisdl/sdl2/init.lua b/src/lib/luigi/backend/ffisdl/sdl2/init.lua new file mode 100644 index 0000000..eceab50 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/sdl2/init.lua @@ -0,0 +1,1500 @@ +-- Do not change this file manually +-- Generated with dev/create-init.lua + +local ROOT = (...):gsub('[^.]*$', '') + +local ffi = require 'ffi' +local C = ffi.load('SDL2') +local sdl = {C=C} +local registerdefines = require(ROOT .. 'defines') + +require(ROOT .. 'cdefs') + +local function register(luafuncname, funcname) + local symexists, msg = pcall(function() + local sym = C[funcname] + end) + if symexists then + sdl[luafuncname] = C[funcname] + end +end + +register('getPlatform', 'SDL_GetPlatform') +register('dummy_uint8[', 'SDL_dummy_uint8[') +register('dummy_sint8[', 'SDL_dummy_sint8[') +register('dummy_uint16[', 'SDL_dummy_uint16[') +register('dummy_sint16[', 'SDL_dummy_sint16[') +register('dummy_uint32[', 'SDL_dummy_uint32[') +register('dummy_sint32[', 'SDL_dummy_sint32[') +register('dummy_uint64[', 'SDL_dummy_uint64[') +register('dummy_sint64[', 'SDL_dummy_sint64[') +register('dummy_enum[', 'SDL_dummy_enum[') +register('malloc', 'SDL_malloc') +register('calloc', 'SDL_calloc') +register('realloc', 'SDL_realloc') +register('free', 'SDL_free') +register('getenv', 'SDL_getenv') +register('setenv', 'SDL_setenv') +register('qsort', 'SDL_qsort') +register('abs', 'SDL_abs') +register('isdigit', 'SDL_isdigit') +register('isspace', 'SDL_isspace') +register('toupper', 'SDL_toupper') +register('tolower', 'SDL_tolower') +register('memset', 'SDL_memset') +register('memcpy', 'SDL_memcpy') +register('memmove', 'SDL_memmove') +register('memcmp', 'SDL_memcmp') +register('wcslen', 'SDL_wcslen') +register('wcslcpy', 'SDL_wcslcpy') +register('wcslcat', 'SDL_wcslcat') +register('strlen', 'SDL_strlen') +register('strlcpy', 'SDL_strlcpy') +register('utf8strlcpy', 'SDL_utf8strlcpy') +register('strlcat', 'SDL_strlcat') +register('strdup', 'SDL_strdup') +register('strrev', 'SDL_strrev') +register('strupr', 'SDL_strupr') +register('strlwr', 'SDL_strlwr') +register('strchr', 'SDL_strchr') +register('strrchr', 'SDL_strrchr') +register('strstr', 'SDL_strstr') +register('itoa', 'SDL_itoa') +register('uitoa', 'SDL_uitoa') +register('ltoa', 'SDL_ltoa') +register('ultoa', 'SDL_ultoa') +register('lltoa', 'SDL_lltoa') +register('ulltoa', 'SDL_ulltoa') +register('atoi', 'SDL_atoi') +register('atof', 'SDL_atof') +register('strtol', 'SDL_strtol') +register('strtoul', 'SDL_strtoul') +register('strtoll', 'SDL_strtoll') +register('strtoull', 'SDL_strtoull') +register('strtod', 'SDL_strtod') +register('strcmp', 'SDL_strcmp') +register('strncmp', 'SDL_strncmp') +register('strcasecmp', 'SDL_strcasecmp') +register('strncasecmp', 'SDL_strncasecmp') +register('sscanf', 'SDL_sscanf') +register('snprintf', 'SDL_snprintf') +register('vsnprintf', 'SDL_vsnprintf') +register('atan', 'SDL_atan') +register('atan2', 'SDL_atan2') +register('ceil', 'SDL_ceil') +register('copysign', 'SDL_copysign') +register('cos', 'SDL_cos') +register('cosf', 'SDL_cosf') +register('fabs', 'SDL_fabs') +register('floor', 'SDL_floor') +register('log', 'SDL_log') +register('pow', 'SDL_pow') +register('scalbn', 'SDL_scalbn') +register('sin', 'SDL_sin') +register('sinf', 'SDL_sinf') +register('sqrt', 'SDL_sqrt') +register('iconv_open', 'SDL_iconv_open') +register('iconv_close', 'SDL_iconv_close') +register('iconv', 'SDL_iconv') +register('iconv_string', 'SDL_iconv_string') +register('main', 'SDL_main') +register('setMainReady', 'SDL_SetMainReady') +register('reportAssertion', 'SDL_ReportAssertion') +register('assert_state', 'SDL_assert_state') +register('setAssertionHandler', 'SDL_SetAssertionHandler') +register('getAssertionReport', 'SDL_GetAssertionReport') +register('resetAssertionReport', 'SDL_ResetAssertionReport') +register('atomicTryLock', 'SDL_AtomicTryLock') +register('atomicLock', 'SDL_AtomicLock') +register('atomicUnlock', 'SDL_AtomicUnlock') +register('setError', 'SDL_SetError') +register('getError', 'SDL_GetError') +register('clearError', 'SDL_ClearError') +register('error', 'SDL_Error') +register('createMutex', 'SDL_CreateMutex') +register('lockMutex', 'SDL_LockMutex') +register('tryLockMutex', 'SDL_TryLockMutex') +register('unlockMutex', 'SDL_UnlockMutex') +register('destroyMutex', 'SDL_DestroyMutex') +register('createSemaphore', 'SDL_CreateSemaphore') +register('destroySemaphore', 'SDL_DestroySemaphore') +register('semWait', 'SDL_SemWait') +register('semTryWait', 'SDL_SemTryWait') +register('semWaitTimeout', 'SDL_SemWaitTimeout') +register('semPost', 'SDL_SemPost') +register('semValue', 'SDL_SemValue') +register('createCond', 'SDL_CreateCond') +register('destroyCond', 'SDL_DestroyCond') +register('condSignal', 'SDL_CondSignal') +register('condBroadcast', 'SDL_CondBroadcast') +register('condWait', 'SDL_CondWait') +register('condWaitTimeout', 'SDL_CondWaitTimeout') + +if jit.os == 'Windows' then + sdl.createThread = + function(fn, name, data) + return C.SDL_CreateThread(fn, name, data, ffi.C._beginthreadex, ffi.C._endthreadex) + end +else + register('createThread', 'SDL_CreateThread') +end + +register('getThreadName', 'SDL_GetThreadName') +register('threadID', 'SDL_ThreadID') +register('getThreadID', 'SDL_GetThreadID') +register('setThreadPriority', 'SDL_SetThreadPriority') +register('waitThread', 'SDL_WaitThread') +register('tLSCreate', 'SDL_TLSCreate') +register('tLSGet', 'SDL_TLSGet') +register('tLSSet', 'SDL_TLSSet') +register('RWFromFile', 'SDL_RWFromFile') +register('RWFromFP', 'SDL_RWFromFP') +register('RWFromMem', 'SDL_RWFromMem') +register('RWFromConstMem', 'SDL_RWFromConstMem') +register('allocRW', 'SDL_AllocRW') +register('freeRW', 'SDL_FreeRW') +register('readU8', 'SDL_ReadU8') +register('readLE16', 'SDL_ReadLE16') +register('readBE16', 'SDL_ReadBE16') +register('readLE32', 'SDL_ReadLE32') +register('readBE32', 'SDL_ReadBE32') +register('readLE64', 'SDL_ReadLE64') +register('readBE64', 'SDL_ReadBE64') +register('writeU8', 'SDL_WriteU8') +register('writeLE16', 'SDL_WriteLE16') +register('writeBE16', 'SDL_WriteBE16') +register('writeLE32', 'SDL_WriteLE32') +register('writeBE32', 'SDL_WriteBE32') +register('writeLE64', 'SDL_WriteLE64') +register('writeBE64', 'SDL_WriteBE64') +register('getNumAudioDrivers', 'SDL_GetNumAudioDrivers') +register('getAudioDriver', 'SDL_GetAudioDriver') +register('audioInit', 'SDL_AudioInit') +register('audioQuit', 'SDL_AudioQuit') +register('getCurrentAudioDriver', 'SDL_GetCurrentAudioDriver') +register('openAudio', 'SDL_OpenAudio') +register('getNumAudioDevices', 'SDL_GetNumAudioDevices') +register('getAudioDeviceName', 'SDL_GetAudioDeviceName') +register('openAudioDevice', 'SDL_OpenAudioDevice') +register('getAudioStatus', 'SDL_GetAudioStatus') +register('getAudioDeviceStatus', 'SDL_GetAudioDeviceStatus') +register('pauseAudio', 'SDL_PauseAudio') +register('pauseAudioDevice', 'SDL_PauseAudioDevice') +register('loadWAV_RW', 'SDL_LoadWAV_RW') +register('freeWAV', 'SDL_FreeWAV') +register('buildAudioCVT', 'SDL_BuildAudioCVT') +register('convertAudio', 'SDL_ConvertAudio') +register('mixAudio', 'SDL_MixAudio') +register('mixAudioFormat', 'SDL_MixAudioFormat') +register('lockAudio', 'SDL_LockAudio') +register('lockAudioDevice', 'SDL_LockAudioDevice') +register('unlockAudio', 'SDL_UnlockAudio') +register('unlockAudioDevice', 'SDL_UnlockAudioDevice') +register('closeAudio', 'SDL_CloseAudio') +register('closeAudioDevice', 'SDL_CloseAudioDevice') +register('setClipboardText', 'SDL_SetClipboardText') +register('getClipboardText', 'SDL_GetClipboardText') +register('hasClipboardText', 'SDL_HasClipboardText') +register('getCPUCount', 'SDL_GetCPUCount') +register('getCPUCacheLineSize', 'SDL_GetCPUCacheLineSize') +register('hasRDTSC', 'SDL_HasRDTSC') +register('hasAltiVec', 'SDL_HasAltiVec') +register('hasMMX', 'SDL_HasMMX') +register('has3DNow', 'SDL_Has3DNow') +register('hasSSE', 'SDL_HasSSE') +register('hasSSE2', 'SDL_HasSSE2') +register('hasSSE3', 'SDL_HasSSE3') +register('hasSSE41', 'SDL_HasSSE41') +register('hasSSE42', 'SDL_HasSSE42') +register('getPixelFormatName', 'SDL_GetPixelFormatName') +register('pixelFormatEnumToMasks', 'SDL_PixelFormatEnumToMasks') +register('masksToPixelFormatEnum', 'SDL_MasksToPixelFormatEnum') +register('allocFormat', 'SDL_AllocFormat') +register('freeFormat', 'SDL_FreeFormat') +register('allocPalette', 'SDL_AllocPalette') +register('setPixelFormatPalette', 'SDL_SetPixelFormatPalette') +register('setPaletteColors', 'SDL_SetPaletteColors') +register('freePalette', 'SDL_FreePalette') +register('mapRGB', 'SDL_MapRGB') +register('mapRGBA', 'SDL_MapRGBA') +register('getRGB', 'SDL_GetRGB') +register('getRGBA', 'SDL_GetRGBA') +register('calculateGammaRamp', 'SDL_CalculateGammaRamp') +register('hasIntersection', 'SDL_HasIntersection') +register('intersectRect', 'SDL_IntersectRect') +register('unionRect', 'SDL_UnionRect') +register('enclosePoints', 'SDL_EnclosePoints') +register('intersectRectAndLine', 'SDL_IntersectRectAndLine') +register('createRGBSurface', 'SDL_CreateRGBSurface') +register('createRGBSurfaceFrom', 'SDL_CreateRGBSurfaceFrom') +register('freeSurface', 'SDL_FreeSurface') +register('setSurfacePalette', 'SDL_SetSurfacePalette') +register('lockSurface', 'SDL_LockSurface') +register('unlockSurface', 'SDL_UnlockSurface') +register('loadBMP_RW', 'SDL_LoadBMP_RW') +register('saveBMP_RW', 'SDL_SaveBMP_RW') +register('setSurfaceRLE', 'SDL_SetSurfaceRLE') +register('setColorKey', 'SDL_SetColorKey') +register('getColorKey', 'SDL_GetColorKey') +register('setSurfaceColorMod', 'SDL_SetSurfaceColorMod') +register('getSurfaceColorMod', 'SDL_GetSurfaceColorMod') +register('setSurfaceAlphaMod', 'SDL_SetSurfaceAlphaMod') +register('getSurfaceAlphaMod', 'SDL_GetSurfaceAlphaMod') +register('setSurfaceBlendMode', 'SDL_SetSurfaceBlendMode') +register('getSurfaceBlendMode', 'SDL_GetSurfaceBlendMode') +register('setClipRect', 'SDL_SetClipRect') +register('getClipRect', 'SDL_GetClipRect') +register('convertSurface', 'SDL_ConvertSurface') +register('convertSurfaceFormat', 'SDL_ConvertSurfaceFormat') +register('convertPixels', 'SDL_ConvertPixels') +register('fillRect', 'SDL_FillRect') +register('fillRects', 'SDL_FillRects') +register('upperBlit', 'SDL_UpperBlit') +register('lowerBlit', 'SDL_LowerBlit') +register('softStretch', 'SDL_SoftStretch') +register('upperBlitScaled', 'SDL_UpperBlitScaled') +register('lowerBlitScaled', 'SDL_LowerBlitScaled') +register('getNumVideoDrivers', 'SDL_GetNumVideoDrivers') +register('getVideoDriver', 'SDL_GetVideoDriver') +register('videoInit', 'SDL_VideoInit') +register('videoQuit', 'SDL_VideoQuit') +register('getCurrentVideoDriver', 'SDL_GetCurrentVideoDriver') +register('getNumVideoDisplays', 'SDL_GetNumVideoDisplays') +register('getDisplayName', 'SDL_GetDisplayName') +register('getDisplayBounds', 'SDL_GetDisplayBounds') +register('getNumDisplayModes', 'SDL_GetNumDisplayModes') +register('getDisplayMode', 'SDL_GetDisplayMode') +register('getDesktopDisplayMode', 'SDL_GetDesktopDisplayMode') +register('getCurrentDisplayMode', 'SDL_GetCurrentDisplayMode') +register('getClosestDisplayMode', 'SDL_GetClosestDisplayMode') +register('getWindowDisplayIndex', 'SDL_GetWindowDisplayIndex') +register('setWindowDisplayMode', 'SDL_SetWindowDisplayMode') +register('getWindowDisplayMode', 'SDL_GetWindowDisplayMode') +register('getWindowPixelFormat', 'SDL_GetWindowPixelFormat') +register('createWindow', 'SDL_CreateWindow') +register('createWindowFrom', 'SDL_CreateWindowFrom') +register('getWindowID', 'SDL_GetWindowID') +register('getWindowFromID', 'SDL_GetWindowFromID') +register('getWindowFlags', 'SDL_GetWindowFlags') +register('setWindowTitle', 'SDL_SetWindowTitle') +register('getWindowTitle', 'SDL_GetWindowTitle') +register('setWindowIcon', 'SDL_SetWindowIcon') +register('setWindowData', 'SDL_SetWindowData') +register('getWindowData', 'SDL_GetWindowData') +register('setWindowPosition', 'SDL_SetWindowPosition') +register('getWindowPosition', 'SDL_GetWindowPosition') +register('setWindowSize', 'SDL_SetWindowSize') +register('getWindowSize', 'SDL_GetWindowSize') +register('setWindowMinimumSize', 'SDL_SetWindowMinimumSize') +register('getWindowMinimumSize', 'SDL_GetWindowMinimumSize') +register('setWindowMaximumSize', 'SDL_SetWindowMaximumSize') +register('getWindowMaximumSize', 'SDL_GetWindowMaximumSize') +register('setWindowBordered', 'SDL_SetWindowBordered') +register('showWindow', 'SDL_ShowWindow') +register('hideWindow', 'SDL_HideWindow') +register('raiseWindow', 'SDL_RaiseWindow') +register('maximizeWindow', 'SDL_MaximizeWindow') +register('minimizeWindow', 'SDL_MinimizeWindow') +register('restoreWindow', 'SDL_RestoreWindow') +register('setWindowFullscreen', 'SDL_SetWindowFullscreen') +register('getWindowSurface', 'SDL_GetWindowSurface') +register('updateWindowSurface', 'SDL_UpdateWindowSurface') +register('updateWindowSurfaceRects', 'SDL_UpdateWindowSurfaceRects') +register('setWindowGrab', 'SDL_SetWindowGrab') +register('getWindowGrab', 'SDL_GetWindowGrab') +register('setWindowBrightness', 'SDL_SetWindowBrightness') +register('getWindowBrightness', 'SDL_GetWindowBrightness') +register('setWindowGammaRamp', 'SDL_SetWindowGammaRamp') +register('getWindowGammaRamp', 'SDL_GetWindowGammaRamp') +register('destroyWindow', 'SDL_DestroyWindow') +register('isScreenSaverEnabled', 'SDL_IsScreenSaverEnabled') +register('enableScreenSaver', 'SDL_EnableScreenSaver') +register('disableScreenSaver', 'SDL_DisableScreenSaver') +register('gL_LoadLibrary', 'SDL_GL_LoadLibrary') +register('gL_GetProcAddress', 'SDL_GL_GetProcAddress') +register('gL_UnloadLibrary', 'SDL_GL_UnloadLibrary') +register('gL_ExtensionSupported', 'SDL_GL_ExtensionSupported') +register('gL_SetAttribute', 'SDL_GL_SetAttribute') +register('gL_GetAttribute', 'SDL_GL_GetAttribute') +register('gL_CreateContext', 'SDL_GL_CreateContext') +register('gL_MakeCurrent', 'SDL_GL_MakeCurrent') +register('gL_GetCurrentWindow', 'SDL_GL_GetCurrentWindow') +register('gL_GetCurrentContext', 'SDL_GL_GetCurrentContext') +register('gL_SetSwapInterval', 'SDL_GL_SetSwapInterval') +register('gL_GetSwapInterval', 'SDL_GL_GetSwapInterval') +register('gL_SwapWindow', 'SDL_GL_SwapWindow') +register('gL_DeleteContext', 'SDL_GL_DeleteContext') +register('getKeyboardFocus', 'SDL_GetKeyboardFocus') +register('getKeyboardState', 'SDL_GetKeyboardState') +register('getModState', 'SDL_GetModState') +register('setModState', 'SDL_SetModState') +register('getKeyFromScancode', 'SDL_GetKeyFromScancode') +register('getScancodeFromKey', 'SDL_GetScancodeFromKey') +register('getScancodeName', 'SDL_GetScancodeName') +register('getScancodeFromName', 'SDL_GetScancodeFromName') +register('getKeyName', 'SDL_GetKeyName') +register('getKeyFromName', 'SDL_GetKeyFromName') +register('startTextInput', 'SDL_StartTextInput') +register('isTextInputActive', 'SDL_IsTextInputActive') +register('stopTextInput', 'SDL_StopTextInput') +register('setTextInputRect', 'SDL_SetTextInputRect') +register('hasScreenKeyboardSupport', 'SDL_HasScreenKeyboardSupport') +register('isScreenKeyboardShown', 'SDL_IsScreenKeyboardShown') +register('getMouseFocus', 'SDL_GetMouseFocus') +register('getMouseState', 'SDL_GetMouseState') +register('getRelativeMouseState', 'SDL_GetRelativeMouseState') +register('warpMouseInWindow', 'SDL_WarpMouseInWindow') +register('setRelativeMouseMode', 'SDL_SetRelativeMouseMode') +register('getRelativeMouseMode', 'SDL_GetRelativeMouseMode') +register('createCursor', 'SDL_CreateCursor') +register('createColorCursor', 'SDL_CreateColorCursor') +register('createSystemCursor', 'SDL_CreateSystemCursor') +register('setCursor', 'SDL_SetCursor') +register('getCursor', 'SDL_GetCursor') +register('getDefaultCursor', 'SDL_GetDefaultCursor') +register('freeCursor', 'SDL_FreeCursor') +register('showCursor', 'SDL_ShowCursor') +register('numJoysticks', 'SDL_NumJoysticks') +register('joystickNameForIndex', 'SDL_JoystickNameForIndex') +register('joystickOpen', 'SDL_JoystickOpen') +register('joystickName', 'SDL_JoystickName') +register('joystickGetDeviceGUID', 'SDL_JoystickGetDeviceGUID') +register('joystickGetGUID', 'SDL_JoystickGetGUID') +register('joystickGetGUIDString', 'SDL_JoystickGetGUIDString') +register('joystickGetGUIDFromString', 'SDL_JoystickGetGUIDFromString') +register('joystickGetAttached', 'SDL_JoystickGetAttached') +register('joystickInstanceID', 'SDL_JoystickInstanceID') +register('joystickNumAxes', 'SDL_JoystickNumAxes') +register('joystickNumBalls', 'SDL_JoystickNumBalls') +register('joystickNumHats', 'SDL_JoystickNumHats') +register('joystickNumButtons', 'SDL_JoystickNumButtons') +register('joystickUpdate', 'SDL_JoystickUpdate') +register('joystickEventState', 'SDL_JoystickEventState') +register('joystickGetAxis', 'SDL_JoystickGetAxis') +register('joystickGetHat', 'SDL_JoystickGetHat') +register('joystickGetBall', 'SDL_JoystickGetBall') +register('joystickGetButton', 'SDL_JoystickGetButton') +register('joystickClose', 'SDL_JoystickClose') +register('gameControllerAddMapping', 'SDL_GameControllerAddMapping') +register('gameControllerMappingForGUID', 'SDL_GameControllerMappingForGUID') +register('gameControllerMapping', 'SDL_GameControllerMapping') +register('isGameController', 'SDL_IsGameController') +register('gameControllerNameForIndex', 'SDL_GameControllerNameForIndex') +register('gameControllerOpen', 'SDL_GameControllerOpen') +register('gameControllerName', 'SDL_GameControllerName') +register('gameControllerGetAttached', 'SDL_GameControllerGetAttached') +register('gameControllerGetJoystick', 'SDL_GameControllerGetJoystick') +register('gameControllerEventState', 'SDL_GameControllerEventState') +register('gameControllerUpdate', 'SDL_GameControllerUpdate') +register('gameControllerGetAxisFromString', 'SDL_GameControllerGetAxisFromString') +register('gameControllerGetStringForAxis', 'SDL_GameControllerGetStringForAxis') +register('gameControllerGetBindForAxis', 'SDL_GameControllerGetBindForAxis') +register('gameControllerGetAxis', 'SDL_GameControllerGetAxis') +register('gameControllerGetButtonFromString', 'SDL_GameControllerGetButtonFromString') +register('gameControllerGetStringForButton', 'SDL_GameControllerGetStringForButton') +register('gameControllerGetBindForButton', 'SDL_GameControllerGetBindForButton') +register('gameControllerGetButton', 'SDL_GameControllerGetButton') +register('gameControllerClose', 'SDL_GameControllerClose') +register('getNumTouchDevices', 'SDL_GetNumTouchDevices') +register('getTouchDevice', 'SDL_GetTouchDevice') +register('getNumTouchFingers', 'SDL_GetNumTouchFingers') +register('getTouchFinger', 'SDL_GetTouchFinger') +register('recordGesture', 'SDL_RecordGesture') +register('saveAllDollarTemplates', 'SDL_SaveAllDollarTemplates') +register('saveDollarTemplate', 'SDL_SaveDollarTemplate') +register('loadDollarTemplates', 'SDL_LoadDollarTemplates') +register('pumpEvents', 'SDL_PumpEvents') +register('peepEvents', 'SDL_PeepEvents') +register('hasEvent', 'SDL_HasEvent') +register('hasEvents', 'SDL_HasEvents') +register('flushEvent', 'SDL_FlushEvent') +register('flushEvents', 'SDL_FlushEvents') +register('pollEvent', 'SDL_PollEvent') +register('waitEvent', 'SDL_WaitEvent') +register('waitEventTimeout', 'SDL_WaitEventTimeout') +register('pushEvent', 'SDL_PushEvent') +register('setEventFilter', 'SDL_SetEventFilter') +register('getEventFilter', 'SDL_GetEventFilter') +register('addEventWatch', 'SDL_AddEventWatch') +register('delEventWatch', 'SDL_DelEventWatch') +register('filterEvents', 'SDL_FilterEvents') +register('eventState', 'SDL_EventState') +register('registerEvents', 'SDL_RegisterEvents') +register('numHaptics', 'SDL_NumHaptics') +register('hapticName', 'SDL_HapticName') +register('hapticOpen', 'SDL_HapticOpen') +register('hapticOpened', 'SDL_HapticOpened') +register('hapticIndex', 'SDL_HapticIndex') +register('mouseIsHaptic', 'SDL_MouseIsHaptic') +register('hapticOpenFromMouse', 'SDL_HapticOpenFromMouse') +register('joystickIsHaptic', 'SDL_JoystickIsHaptic') +register('hapticOpenFromJoystick', 'SDL_HapticOpenFromJoystick') +register('hapticClose', 'SDL_HapticClose') +register('hapticNumEffects', 'SDL_HapticNumEffects') +register('hapticNumEffectsPlaying', 'SDL_HapticNumEffectsPlaying') +register('hapticQuery', 'SDL_HapticQuery') +register('hapticNumAxes', 'SDL_HapticNumAxes') +register('hapticEffectSupported', 'SDL_HapticEffectSupported') +register('hapticNewEffect', 'SDL_HapticNewEffect') +register('hapticUpdateEffect', 'SDL_HapticUpdateEffect') +register('hapticRunEffect', 'SDL_HapticRunEffect') +register('hapticStopEffect', 'SDL_HapticStopEffect') +register('hapticDestroyEffect', 'SDL_HapticDestroyEffect') +register('hapticGetEffectStatus', 'SDL_HapticGetEffectStatus') +register('hapticSetGain', 'SDL_HapticSetGain') +register('hapticSetAutocenter', 'SDL_HapticSetAutocenter') +register('hapticPause', 'SDL_HapticPause') +register('hapticUnpause', 'SDL_HapticUnpause') +register('hapticStopAll', 'SDL_HapticStopAll') +register('hapticRumbleSupported', 'SDL_HapticRumbleSupported') +register('hapticRumbleInit', 'SDL_HapticRumbleInit') +register('hapticRumblePlay', 'SDL_HapticRumblePlay') +register('hapticRumbleStop', 'SDL_HapticRumbleStop') +register('setHintWithPriority', 'SDL_SetHintWithPriority') +register('setHint', 'SDL_SetHint') +register('getHint', 'SDL_GetHint') +register('addHintCallback', 'SDL_AddHintCallback') +register('delHintCallback', 'SDL_DelHintCallback') +register('clearHints', 'SDL_ClearHints') +register('loadObject', 'SDL_LoadObject') +register('loadFunction', 'SDL_LoadFunction') +register('unloadObject', 'SDL_UnloadObject') +register('logSetAllPriority', 'SDL_LogSetAllPriority') +register('logSetPriority', 'SDL_LogSetPriority') +register('logGetPriority', 'SDL_LogGetPriority') +register('logResetPriorities', 'SDL_LogResetPriorities') +register('log', 'SDL_Log') +register('logVerbose', 'SDL_LogVerbose') +register('logDebug', 'SDL_LogDebug') +register('logInfo', 'SDL_LogInfo') +register('logWarn', 'SDL_LogWarn') +register('logError', 'SDL_LogError') +register('logCritical', 'SDL_LogCritical') +register('logMessage', 'SDL_LogMessage') +register('logMessageV', 'SDL_LogMessageV') +register('logGetOutputFunction', 'SDL_LogGetOutputFunction') +register('logSetOutputFunction', 'SDL_LogSetOutputFunction') +register('showMessageBox', 'SDL_ShowMessageBox') +register('showSimpleMessageBox', 'SDL_ShowSimpleMessageBox') +register('getPowerInfo', 'SDL_GetPowerInfo') +register('getNumRenderDrivers', 'SDL_GetNumRenderDrivers') +register('getRenderDriverInfo', 'SDL_GetRenderDriverInfo') +register('createWindowAndRenderer', 'SDL_CreateWindowAndRenderer') +register('createRenderer', 'SDL_CreateRenderer') +register('createSoftwareRenderer', 'SDL_CreateSoftwareRenderer') +register('getRenderer', 'SDL_GetRenderer') +register('getRendererInfo', 'SDL_GetRendererInfo') +register('getRendererOutputSize', 'SDL_GetRendererOutputSize') +register('createTexture', 'SDL_CreateTexture') +register('createTextureFromSurface', 'SDL_CreateTextureFromSurface') +register('queryTexture', 'SDL_QueryTexture') +register('setTextureColorMod', 'SDL_SetTextureColorMod') +register('getTextureColorMod', 'SDL_GetTextureColorMod') +register('setTextureAlphaMod', 'SDL_SetTextureAlphaMod') +register('getTextureAlphaMod', 'SDL_GetTextureAlphaMod') +register('setTextureBlendMode', 'SDL_SetTextureBlendMode') +register('getTextureBlendMode', 'SDL_GetTextureBlendMode') +register('updateTexture', 'SDL_UpdateTexture') +register('lockTexture', 'SDL_LockTexture') +register('unlockTexture', 'SDL_UnlockTexture') +register('renderTargetSupported', 'SDL_RenderTargetSupported') +register('setRenderTarget', 'SDL_SetRenderTarget') +register('getRenderTarget', 'SDL_GetRenderTarget') +register('renderSetLogicalSize', 'SDL_RenderSetLogicalSize') +register('renderGetLogicalSize', 'SDL_RenderGetLogicalSize') +register('renderSetViewport', 'SDL_RenderSetViewport') +register('renderGetViewport', 'SDL_RenderGetViewport') +register('renderSetClipRect', 'SDL_RenderSetClipRect') +register('renderGetClipRect', 'SDL_RenderGetClipRect') +register('renderSetScale', 'SDL_RenderSetScale') +register('renderGetScale', 'SDL_RenderGetScale') +register('setRenderDrawColor', 'SDL_SetRenderDrawColor') +register('getRenderDrawColor', 'SDL_GetRenderDrawColor') +register('setRenderDrawBlendMode', 'SDL_SetRenderDrawBlendMode') +register('getRenderDrawBlendMode', 'SDL_GetRenderDrawBlendMode') +register('renderClear', 'SDL_RenderClear') +register('renderDrawPoint', 'SDL_RenderDrawPoint') +register('renderDrawPoints', 'SDL_RenderDrawPoints') +register('renderDrawLine', 'SDL_RenderDrawLine') +register('renderDrawLines', 'SDL_RenderDrawLines') +register('renderDrawRect', 'SDL_RenderDrawRect') +register('renderDrawRects', 'SDL_RenderDrawRects') +register('renderFillRect', 'SDL_RenderFillRect') +register('renderFillRects', 'SDL_RenderFillRects') +register('renderCopy', 'SDL_RenderCopy') +register('renderCopyEx', 'SDL_RenderCopyEx') +register('renderReadPixels', 'SDL_RenderReadPixels') +register('renderPresent', 'SDL_RenderPresent') +register('destroyTexture', 'SDL_DestroyTexture') +register('destroyRenderer', 'SDL_DestroyRenderer') +register('gL_BindTexture', 'SDL_GL_BindTexture') +register('gL_UnbindTexture', 'SDL_GL_UnbindTexture') +register('getTicks', 'SDL_GetTicks') +register('getPerformanceCounter', 'SDL_GetPerformanceCounter') +register('getPerformanceFrequency', 'SDL_GetPerformanceFrequency') +register('delay', 'SDL_Delay') +register('addTimer', 'SDL_AddTimer') +register('removeTimer', 'SDL_RemoveTimer') +register('getVersion', 'SDL_GetVersion') +register('getRevision', 'SDL_GetRevision') +register('getRevisionNumber', 'SDL_GetRevisionNumber') +register('init', 'SDL_Init') +register('initSubSystem', 'SDL_InitSubSystem') +register('quitSubSystem', 'SDL_QuitSubSystem') +register('wasInit', 'SDL_WasInit') +register('quit', 'SDL_Quit') +register('registerApp', 'SDL_RegisterApp') +register('unregisterApp', 'SDL_UnregisterApp') + +register('FALSE', 'SDL_FALSE') +register('TRUE', 'SDL_TRUE') +register('DUMMY_ENUM', 'SDL_DUMMY_ENUM') +register('DUMMY_ENUM', 'SDL_DUMMY_ENUM') +register('ASSERTION_RETRY', 'SDL_ASSERTION_RETRY') +register('ASSERTION_BREAK', 'SDL_ASSERTION_BREAK') +register('ASSERTION_ABORT', 'SDL_ASSERTION_ABORT') +register('ASSERTION_IGNORE', 'SDL_ASSERTION_IGNORE') +register('ASSERTION_ALWAYS_IGNORE', 'SDL_ASSERTION_ALWAYS_IGNORE') +register('ENOMEM', 'SDL_ENOMEM') +register('EFREAD', 'SDL_EFREAD') +register('EFWRITE', 'SDL_EFWRITE') +register('EFSEEK', 'SDL_EFSEEK') +register('UNSUPPORTED', 'SDL_UNSUPPORTED') +register('LASTERROR', 'SDL_LASTERROR') +register('TLSID', 'SDL_TLSID') +register('THREAD_PRIORITY_LOW', 'SDL_THREAD_PRIORITY_LOW') +register('THREAD_PRIORITY_NORMAL', 'SDL_THREAD_PRIORITY_NORMAL') +register('THREAD_PRIORITY_HIGH', 'SDL_THREAD_PRIORITY_HIGH') +register('TLSID', 'SDL_TLSID') +register('TLSID', 'SDL_TLSID') +register('TLSID', 'SDL_TLSID') +register('AUDIO_STOPPED', 'SDL_AUDIO_STOPPED') +register('AUDIO_PLAYING', 'SDL_AUDIO_PLAYING') +register('AUDIO_PAUSED', 'SDL_AUDIO_PAUSED') +register('PIXELTYPE_UNKNOWN', 'SDL_PIXELTYPE_UNKNOWN') +register('PIXELTYPE_INDEX1', 'SDL_PIXELTYPE_INDEX1') +register('PIXELTYPE_INDEX4', 'SDL_PIXELTYPE_INDEX4') +register('PIXELTYPE_INDEX8', 'SDL_PIXELTYPE_INDEX8') +register('PIXELTYPE_PACKED8', 'SDL_PIXELTYPE_PACKED8') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PIXELTYPE_ARRAYU8', 'SDL_PIXELTYPE_ARRAYU8') +register('PIXELTYPE_ARRAYU16', 'SDL_PIXELTYPE_ARRAYU16') +register('PIXELTYPE_ARRAYU32', 'SDL_PIXELTYPE_ARRAYU32') +register('PIXELTYPE_ARRAYF16', 'SDL_PIXELTYPE_ARRAYF16') +register('PIXELTYPE_ARRAYF32', 'SDL_PIXELTYPE_ARRAYF32') +register('BITMAPORDER_NONE', 'SDL_BITMAPORDER_NONE') +register('BITMAPORDER_4321', 'SDL_BITMAPORDER_4321') +register('BITMAPORDER_1234', 'SDL_BITMAPORDER_1234') +register('PACKEDORDER_NONE', 'SDL_PACKEDORDER_NONE') +register('PACKEDORDER_XRGB', 'SDL_PACKEDORDER_XRGB') +register('PACKEDORDER_RGBX', 'SDL_PACKEDORDER_RGBX') +register('PACKEDORDER_ARGB', 'SDL_PACKEDORDER_ARGB') +register('PACKEDORDER_RGBA', 'SDL_PACKEDORDER_RGBA') +register('PACKEDORDER_XBGR', 'SDL_PACKEDORDER_XBGR') +register('PACKEDORDER_BGRX', 'SDL_PACKEDORDER_BGRX') +register('PACKEDORDER_ABGR', 'SDL_PACKEDORDER_ABGR') +register('PACKEDORDER_BGRA', 'SDL_PACKEDORDER_BGRA') +register('ARRAYORDER_NONE', 'SDL_ARRAYORDER_NONE') +register('ARRAYORDER_RGB', 'SDL_ARRAYORDER_RGB') +register('ARRAYORDER_RGBA', 'SDL_ARRAYORDER_RGBA') +register('ARRAYORDER_ARGB', 'SDL_ARRAYORDER_ARGB') +register('ARRAYORDER_BGR', 'SDL_ARRAYORDER_BGR') +register('ARRAYORDER_BGRA', 'SDL_ARRAYORDER_BGRA') +register('ARRAYORDER_ABGR', 'SDL_ARRAYORDER_ABGR') +register('PACKEDLAYOUT_NONE', 'SDL_PACKEDLAYOUT_NONE') +register('PACKEDLAYOUT_332', 'SDL_PACKEDLAYOUT_332') +register('PACKEDLAYOUT_4444', 'SDL_PACKEDLAYOUT_4444') +register('PACKEDLAYOUT_1555', 'SDL_PACKEDLAYOUT_1555') +register('PACKEDLAYOUT_5551', 'SDL_PACKEDLAYOUT_5551') +register('PACKEDLAYOUT_565', 'SDL_PACKEDLAYOUT_565') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PACKEDLAYOUT_2101010', 'SDL_PACKEDLAYOUT_2101010') +register('PACKEDLAYOUT_1010102', 'SDL_PACKEDLAYOUT_1010102') +register('PIXELFORMAT_UNKNOWN', 'SDL_PIXELFORMAT_UNKNOWN') +register('PIXELFORMAT_INDEX1LSB', 'SDL_PIXELFORMAT_INDEX1LSB') +register('PIXELTYPE_INDEX1', 'SDL_PIXELTYPE_INDEX1') +register('BITMAPORDER_4321', 'SDL_BITMAPORDER_4321') +register('PIXELFORMAT_INDEX1MSB', 'SDL_PIXELFORMAT_INDEX1MSB') +register('PIXELTYPE_INDEX1', 'SDL_PIXELTYPE_INDEX1') +register('BITMAPORDER_1234', 'SDL_BITMAPORDER_1234') +register('PIXELFORMAT_INDEX4LSB', 'SDL_PIXELFORMAT_INDEX4LSB') +register('PIXELTYPE_INDEX4', 'SDL_PIXELTYPE_INDEX4') +register('BITMAPORDER_4321', 'SDL_BITMAPORDER_4321') +register('PIXELFORMAT_INDEX4MSB', 'SDL_PIXELFORMAT_INDEX4MSB') +register('PIXELTYPE_INDEX4', 'SDL_PIXELTYPE_INDEX4') +register('BITMAPORDER_1234', 'SDL_BITMAPORDER_1234') +register('PIXELFORMAT_INDEX8', 'SDL_PIXELFORMAT_INDEX8') +register('PIXELTYPE_INDEX8', 'SDL_PIXELTYPE_INDEX8') +register('PIXELFORMAT_RGB332', 'SDL_PIXELFORMAT_RGB332') +register('PIXELTYPE_PACKED8', 'SDL_PIXELTYPE_PACKED8') +register('PACKEDORDER_XRGB', 'SDL_PACKEDORDER_XRGB') +register('PACKEDLAYOUT_332', 'SDL_PACKEDLAYOUT_332') +register('PIXELFORMAT_RGB444', 'SDL_PIXELFORMAT_RGB444') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_XRGB', 'SDL_PACKEDORDER_XRGB') +register('PACKEDLAYOUT_4444', 'SDL_PACKEDLAYOUT_4444') +register('PIXELFORMAT_RGB555', 'SDL_PIXELFORMAT_RGB555') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_XRGB', 'SDL_PACKEDORDER_XRGB') +register('PACKEDLAYOUT_1555', 'SDL_PACKEDLAYOUT_1555') +register('PIXELFORMAT_BGR555', 'SDL_PIXELFORMAT_BGR555') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_XBGR', 'SDL_PACKEDORDER_XBGR') +register('PACKEDLAYOUT_1555', 'SDL_PACKEDLAYOUT_1555') +register('PIXELFORMAT_ARGB4444', 'SDL_PIXELFORMAT_ARGB4444') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_ARGB', 'SDL_PACKEDORDER_ARGB') +register('PACKEDLAYOUT_4444', 'SDL_PACKEDLAYOUT_4444') +register('PIXELFORMAT_RGBA4444', 'SDL_PIXELFORMAT_RGBA4444') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_RGBA', 'SDL_PACKEDORDER_RGBA') +register('PACKEDLAYOUT_4444', 'SDL_PACKEDLAYOUT_4444') +register('PIXELFORMAT_ABGR4444', 'SDL_PIXELFORMAT_ABGR4444') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_ABGR', 'SDL_PACKEDORDER_ABGR') +register('PACKEDLAYOUT_4444', 'SDL_PACKEDLAYOUT_4444') +register('PIXELFORMAT_BGRA4444', 'SDL_PIXELFORMAT_BGRA4444') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_BGRA', 'SDL_PACKEDORDER_BGRA') +register('PACKEDLAYOUT_4444', 'SDL_PACKEDLAYOUT_4444') +register('PIXELFORMAT_ARGB1555', 'SDL_PIXELFORMAT_ARGB1555') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_ARGB', 'SDL_PACKEDORDER_ARGB') +register('PACKEDLAYOUT_1555', 'SDL_PACKEDLAYOUT_1555') +register('PIXELFORMAT_RGBA5551', 'SDL_PIXELFORMAT_RGBA5551') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_RGBA', 'SDL_PACKEDORDER_RGBA') +register('PACKEDLAYOUT_5551', 'SDL_PACKEDLAYOUT_5551') +register('PIXELFORMAT_ABGR1555', 'SDL_PIXELFORMAT_ABGR1555') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_ABGR', 'SDL_PACKEDORDER_ABGR') +register('PACKEDLAYOUT_1555', 'SDL_PACKEDLAYOUT_1555') +register('PIXELFORMAT_BGRA5551', 'SDL_PIXELFORMAT_BGRA5551') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_BGRA', 'SDL_PACKEDORDER_BGRA') +register('PACKEDLAYOUT_5551', 'SDL_PACKEDLAYOUT_5551') +register('PIXELFORMAT_RGB565', 'SDL_PIXELFORMAT_RGB565') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_XRGB', 'SDL_PACKEDORDER_XRGB') +register('PACKEDLAYOUT_565', 'SDL_PACKEDLAYOUT_565') +register('PIXELFORMAT_BGR565', 'SDL_PIXELFORMAT_BGR565') +register('PIXELTYPE_PACKED16', 'SDL_PIXELTYPE_PACKED16') +register('PACKEDORDER_XBGR', 'SDL_PACKEDORDER_XBGR') +register('PACKEDLAYOUT_565', 'SDL_PACKEDLAYOUT_565') +register('PIXELFORMAT_RGB24', 'SDL_PIXELFORMAT_RGB24') +register('PIXELTYPE_ARRAYU8', 'SDL_PIXELTYPE_ARRAYU8') +register('ARRAYORDER_RGB', 'SDL_ARRAYORDER_RGB') +register('PIXELFORMAT_BGR24', 'SDL_PIXELFORMAT_BGR24') +register('PIXELTYPE_ARRAYU8', 'SDL_PIXELTYPE_ARRAYU8') +register('ARRAYORDER_BGR', 'SDL_ARRAYORDER_BGR') +register('PIXELFORMAT_RGB888', 'SDL_PIXELFORMAT_RGB888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_XRGB', 'SDL_PACKEDORDER_XRGB') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_RGBX8888', 'SDL_PIXELFORMAT_RGBX8888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_RGBX', 'SDL_PACKEDORDER_RGBX') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_BGR888', 'SDL_PIXELFORMAT_BGR888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_XBGR', 'SDL_PACKEDORDER_XBGR') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_BGRX8888', 'SDL_PIXELFORMAT_BGRX8888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_BGRX', 'SDL_PACKEDORDER_BGRX') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_ARGB8888', 'SDL_PIXELFORMAT_ARGB8888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_ARGB', 'SDL_PACKEDORDER_ARGB') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_RGBA8888', 'SDL_PIXELFORMAT_RGBA8888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_RGBA', 'SDL_PACKEDORDER_RGBA') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_ABGR8888', 'SDL_PIXELFORMAT_ABGR8888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_ABGR', 'SDL_PACKEDORDER_ABGR') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_BGRA8888', 'SDL_PIXELFORMAT_BGRA8888') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_BGRA', 'SDL_PACKEDORDER_BGRA') +register('PACKEDLAYOUT_8888', 'SDL_PACKEDLAYOUT_8888') +register('PIXELFORMAT_ARGB2101010', 'SDL_PIXELFORMAT_ARGB2101010') +register('PIXELTYPE_PACKED32', 'SDL_PIXELTYPE_PACKED32') +register('PACKEDORDER_ARGB', 'SDL_PACKEDORDER_ARGB') +register('PACKEDLAYOUT_2101010', 'SDL_PACKEDLAYOUT_2101010') +register('PIXELFORMAT_YV12', 'SDL_PIXELFORMAT_YV12') +register('PIXELFORMAT_IYUV', 'SDL_PIXELFORMAT_IYUV') +register('PIXELFORMAT_YUY2', 'SDL_PIXELFORMAT_YUY2') +register('PIXELFORMAT_UYVY', 'SDL_PIXELFORMAT_UYVY') +register('PIXELFORMAT_YVYU', 'SDL_PIXELFORMAT_YVYU') +register('BLENDMODE_NONE', 'SDL_BLENDMODE_NONE') +register('BLENDMODE_BLEND', 'SDL_BLENDMODE_BLEND') +register('BLENDMODE_ADD', 'SDL_BLENDMODE_ADD') +register('BLENDMODE_MOD', 'SDL_BLENDMODE_MOD') +register('WINDOW_FULLSCREEN', 'SDL_WINDOW_FULLSCREEN') +register('WINDOW_OPENGL', 'SDL_WINDOW_OPENGL') +register('WINDOW_SHOWN', 'SDL_WINDOW_SHOWN') +register('WINDOW_HIDDEN', 'SDL_WINDOW_HIDDEN') +register('WINDOW_BORDERLESS', 'SDL_WINDOW_BORDERLESS') +register('WINDOW_RESIZABLE', 'SDL_WINDOW_RESIZABLE') +register('WINDOW_MINIMIZED', 'SDL_WINDOW_MINIMIZED') +register('WINDOW_MAXIMIZED', 'SDL_WINDOW_MAXIMIZED') +register('WINDOW_INPUT_GRABBED', 'SDL_WINDOW_INPUT_GRABBED') +register('WINDOW_INPUT_FOCUS', 'SDL_WINDOW_INPUT_FOCUS') +register('WINDOW_MOUSE_FOCUS', 'SDL_WINDOW_MOUSE_FOCUS') +register('WINDOW_FULLSCREEN_DESKTOP', 'SDL_WINDOW_FULLSCREEN_DESKTOP') +register('WINDOW_FULLSCREEN', 'SDL_WINDOW_FULLSCREEN') +register('WINDOW_FOREIGN', 'SDL_WINDOW_FOREIGN') +register('WINDOWEVENT_NONE', 'SDL_WINDOWEVENT_NONE') +register('WINDOWEVENT_SHOWN', 'SDL_WINDOWEVENT_SHOWN') +register('WINDOWEVENT_HIDDEN', 'SDL_WINDOWEVENT_HIDDEN') +register('WINDOWEVENT_EXPOSED', 'SDL_WINDOWEVENT_EXPOSED') +register('WINDOWEVENT_MOVED', 'SDL_WINDOWEVENT_MOVED') +register('WINDOWEVENT_RESIZED', 'SDL_WINDOWEVENT_RESIZED') +register('WINDOWEVENT_SIZE_CHANGED', 'SDL_WINDOWEVENT_SIZE_CHANGED') +register('WINDOWEVENT_MINIMIZED', 'SDL_WINDOWEVENT_MINIMIZED') +register('WINDOWEVENT_MAXIMIZED', 'SDL_WINDOWEVENT_MAXIMIZED') +register('WINDOWEVENT_RESTORED', 'SDL_WINDOWEVENT_RESTORED') +register('WINDOWEVENT_ENTER', 'SDL_WINDOWEVENT_ENTER') +register('WINDOWEVENT_LEAVE', 'SDL_WINDOWEVENT_LEAVE') +register('WINDOWEVENT_FOCUS_GAINED', 'SDL_WINDOWEVENT_FOCUS_GAINED') +register('WINDOWEVENT_FOCUS_LOST', 'SDL_WINDOWEVENT_FOCUS_LOST') +register('WINDOWEVENT_CLOSE', 'SDL_WINDOWEVENT_CLOSE') +register('GL_RED_SIZE', 'SDL_GL_RED_SIZE') +register('GL_GREEN_SIZE', 'SDL_GL_GREEN_SIZE') +register('GL_BLUE_SIZE', 'SDL_GL_BLUE_SIZE') +register('GL_ALPHA_SIZE', 'SDL_GL_ALPHA_SIZE') +register('GL_BUFFER_SIZE', 'SDL_GL_BUFFER_SIZE') +register('GL_DOUBLEBUFFER', 'SDL_GL_DOUBLEBUFFER') +register('GL_DEPTH_SIZE', 'SDL_GL_DEPTH_SIZE') +register('GL_STENCIL_SIZE', 'SDL_GL_STENCIL_SIZE') +register('GL_ACCUM_RED_SIZE', 'SDL_GL_ACCUM_RED_SIZE') +register('GL_ACCUM_GREEN_SIZE', 'SDL_GL_ACCUM_GREEN_SIZE') +register('GL_ACCUM_BLUE_SIZE', 'SDL_GL_ACCUM_BLUE_SIZE') +register('GL_ACCUM_ALPHA_SIZE', 'SDL_GL_ACCUM_ALPHA_SIZE') +register('GL_STEREO', 'SDL_GL_STEREO') +register('GL_MULTISAMPLEBUFFERS', 'SDL_GL_MULTISAMPLEBUFFERS') +register('GL_MULTISAMPLESAMPLES', 'SDL_GL_MULTISAMPLESAMPLES') +register('GL_ACCELERATED_VISUAL', 'SDL_GL_ACCELERATED_VISUAL') +register('GL_RETAINED_BACKING', 'SDL_GL_RETAINED_BACKING') +register('GL_CONTEXT_MAJOR_VERSION', 'SDL_GL_CONTEXT_MAJOR_VERSION') +register('GL_CONTEXT_MINOR_VERSION', 'SDL_GL_CONTEXT_MINOR_VERSION') +register('GL_CONTEXT_EGL', 'SDL_GL_CONTEXT_EGL') +register('GL_CONTEXT_FLAGS', 'SDL_GL_CONTEXT_FLAGS') +register('GL_CONTEXT_PROFILE_MASK', 'SDL_GL_CONTEXT_PROFILE_MASK') +register('GL_SHARE_WITH_CURRENT_CONTEXT', 'SDL_GL_SHARE_WITH_CURRENT_CONTEXT') +register('GL_CONTEXT_PROFILE_CORE', 'SDL_GL_CONTEXT_PROFILE_CORE') +register('GL_CONTEXT_PROFILE_COMPATIBILITY', 'SDL_GL_CONTEXT_PROFILE_COMPATIBILITY') +register('GL_CONTEXT_PROFILE_ES', 'SDL_GL_CONTEXT_PROFILE_ES') +register('GL_CONTEXT_DEBUG_FLAG', 'SDL_GL_CONTEXT_DEBUG_FLAG') +register('GL_CONTEXT_FORWARD_COMPATIBLE_FLAG', 'SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG') +register('GL_CONTEXT_ROBUST_ACCESS_FLAG', 'SDL_GL_CONTEXT_ROBUST_ACCESS_FLAG') +register('GL_CONTEXT_RESET_ISOLATION_FLAG', 'SDL_GL_CONTEXT_RESET_ISOLATION_FLAG') +register('SCANCODE_UNKNOWN', 'SDL_SCANCODE_UNKNOWN') +register('SCANCODE_A', 'SDL_SCANCODE_A') +register('SCANCODE_B', 'SDL_SCANCODE_B') +register('SCANCODE_C', 'SDL_SCANCODE_C') +register('SCANCODE_D', 'SDL_SCANCODE_D') +register('SCANCODE_E', 'SDL_SCANCODE_E') +register('SCANCODE_F', 'SDL_SCANCODE_F') +register('SCANCODE_G', 'SDL_SCANCODE_G') +register('SCANCODE_H', 'SDL_SCANCODE_H') +register('SCANCODE_I', 'SDL_SCANCODE_I') +register('SCANCODE_J', 'SDL_SCANCODE_J') +register('SCANCODE_K', 'SDL_SCANCODE_K') +register('SCANCODE_L', 'SDL_SCANCODE_L') +register('SCANCODE_M', 'SDL_SCANCODE_M') +register('SCANCODE_N', 'SDL_SCANCODE_N') +register('SCANCODE_O', 'SDL_SCANCODE_O') +register('SCANCODE_P', 'SDL_SCANCODE_P') +register('SCANCODE_Q', 'SDL_SCANCODE_Q') +register('SCANCODE_R', 'SDL_SCANCODE_R') +register('SCANCODE_S', 'SDL_SCANCODE_S') +register('SCANCODE_T', 'SDL_SCANCODE_T') +register('SCANCODE_U', 'SDL_SCANCODE_U') +register('SCANCODE_V', 'SDL_SCANCODE_V') +register('SCANCODE_W', 'SDL_SCANCODE_W') +register('SCANCODE_X', 'SDL_SCANCODE_X') +register('SCANCODE_Y', 'SDL_SCANCODE_Y') +register('SCANCODE_Z', 'SDL_SCANCODE_Z') +register('SCANCODE_1', 'SDL_SCANCODE_1') +register('SCANCODE_2', 'SDL_SCANCODE_2') +register('SCANCODE_3', 'SDL_SCANCODE_3') +register('SCANCODE_4', 'SDL_SCANCODE_4') +register('SCANCODE_5', 'SDL_SCANCODE_5') +register('SCANCODE_6', 'SDL_SCANCODE_6') +register('SCANCODE_7', 'SDL_SCANCODE_7') +register('SCANCODE_8', 'SDL_SCANCODE_8') +register('SCANCODE_9', 'SDL_SCANCODE_9') +register('SCANCODE_0', 'SDL_SCANCODE_0') +register('SCANCODE_RETURN', 'SDL_SCANCODE_RETURN') +register('SCANCODE_ESCAPE', 'SDL_SCANCODE_ESCAPE') +register('SCANCODE_BACKSPACE', 'SDL_SCANCODE_BACKSPACE') +register('SCANCODE_TAB', 'SDL_SCANCODE_TAB') +register('SCANCODE_SPACE', 'SDL_SCANCODE_SPACE') +register('SCANCODE_MINUS', 'SDL_SCANCODE_MINUS') +register('SCANCODE_EQUALS', 'SDL_SCANCODE_EQUALS') +register('SCANCODE_LEFTBRACKET', 'SDL_SCANCODE_LEFTBRACKET') +register('SCANCODE_RIGHTBRACKET', 'SDL_SCANCODE_RIGHTBRACKET') +register('SCANCODE_BACKSLASH', 'SDL_SCANCODE_BACKSLASH') +register('SCANCODE_NONUSHASH', 'SDL_SCANCODE_NONUSHASH') +register('SCANCODE_SEMICOLON', 'SDL_SCANCODE_SEMICOLON') +register('SCANCODE_APOSTROPHE', 'SDL_SCANCODE_APOSTROPHE') +register('SCANCODE_GRAVE', 'SDL_SCANCODE_GRAVE') +register('SCANCODE_COMMA', 'SDL_SCANCODE_COMMA') +register('SCANCODE_PERIOD', 'SDL_SCANCODE_PERIOD') +register('SCANCODE_SLASH', 'SDL_SCANCODE_SLASH') +register('SCANCODE_CAPSLOCK', 'SDL_SCANCODE_CAPSLOCK') +register('SCANCODE_F1', 'SDL_SCANCODE_F1') +register('SCANCODE_F2', 'SDL_SCANCODE_F2') +register('SCANCODE_F3', 'SDL_SCANCODE_F3') +register('SCANCODE_F4', 'SDL_SCANCODE_F4') +register('SCANCODE_F5', 'SDL_SCANCODE_F5') +register('SCANCODE_F6', 'SDL_SCANCODE_F6') +register('SCANCODE_F7', 'SDL_SCANCODE_F7') +register('SCANCODE_F8', 'SDL_SCANCODE_F8') +register('SCANCODE_F9', 'SDL_SCANCODE_F9') +register('SCANCODE_F10', 'SDL_SCANCODE_F10') +register('SCANCODE_F11', 'SDL_SCANCODE_F11') +register('SCANCODE_F12', 'SDL_SCANCODE_F12') +register('SCANCODE_PRINTSCREEN', 'SDL_SCANCODE_PRINTSCREEN') +register('SCANCODE_SCROLLLOCK', 'SDL_SCANCODE_SCROLLLOCK') +register('SCANCODE_PAUSE', 'SDL_SCANCODE_PAUSE') +register('SCANCODE_INSERT', 'SDL_SCANCODE_INSERT') +register('SCANCODE_HOME', 'SDL_SCANCODE_HOME') +register('SCANCODE_PAGEUP', 'SDL_SCANCODE_PAGEUP') +register('SCANCODE_DELETE', 'SDL_SCANCODE_DELETE') +register('SCANCODE_END', 'SDL_SCANCODE_END') +register('SCANCODE_PAGEDOWN', 'SDL_SCANCODE_PAGEDOWN') +register('SCANCODE_RIGHT', 'SDL_SCANCODE_RIGHT') +register('SCANCODE_LEFT', 'SDL_SCANCODE_LEFT') +register('SCANCODE_DOWN', 'SDL_SCANCODE_DOWN') +register('SCANCODE_UP', 'SDL_SCANCODE_UP') +register('SCANCODE_NUMLOCKCLEAR', 'SDL_SCANCODE_NUMLOCKCLEAR') +register('SCANCODE_KP_DIVIDE', 'SDL_SCANCODE_KP_DIVIDE') +register('SCANCODE_KP_MULTIPLY', 'SDL_SCANCODE_KP_MULTIPLY') +register('SCANCODE_KP_MINUS', 'SDL_SCANCODE_KP_MINUS') +register('SCANCODE_KP_PLUS', 'SDL_SCANCODE_KP_PLUS') +register('SCANCODE_KP_ENTER', 'SDL_SCANCODE_KP_ENTER') +register('SCANCODE_KP_1', 'SDL_SCANCODE_KP_1') +register('SCANCODE_KP_2', 'SDL_SCANCODE_KP_2') +register('SCANCODE_KP_3', 'SDL_SCANCODE_KP_3') +register('SCANCODE_KP_4', 'SDL_SCANCODE_KP_4') +register('SCANCODE_KP_5', 'SDL_SCANCODE_KP_5') +register('SCANCODE_KP_6', 'SDL_SCANCODE_KP_6') +register('SCANCODE_KP_7', 'SDL_SCANCODE_KP_7') +register('SCANCODE_KP_8', 'SDL_SCANCODE_KP_8') +register('SCANCODE_KP_9', 'SDL_SCANCODE_KP_9') +register('SCANCODE_KP_0', 'SDL_SCANCODE_KP_0') +register('SCANCODE_KP_PERIOD', 'SDL_SCANCODE_KP_PERIOD') +register('SCANCODE_NONUSBACKSLASH', 'SDL_SCANCODE_NONUSBACKSLASH') +register('SCANCODE_APPLICATION', 'SDL_SCANCODE_APPLICATION') +register('SCANCODE_POWER', 'SDL_SCANCODE_POWER') +register('SCANCODE_KP_EQUALS', 'SDL_SCANCODE_KP_EQUALS') +register('SCANCODE_F13', 'SDL_SCANCODE_F13') +register('SCANCODE_F14', 'SDL_SCANCODE_F14') +register('SCANCODE_F15', 'SDL_SCANCODE_F15') +register('SCANCODE_F16', 'SDL_SCANCODE_F16') +register('SCANCODE_F17', 'SDL_SCANCODE_F17') +register('SCANCODE_F18', 'SDL_SCANCODE_F18') +register('SCANCODE_F19', 'SDL_SCANCODE_F19') +register('SCANCODE_F20', 'SDL_SCANCODE_F20') +register('SCANCODE_F21', 'SDL_SCANCODE_F21') +register('SCANCODE_F22', 'SDL_SCANCODE_F22') +register('SCANCODE_F23', 'SDL_SCANCODE_F23') +register('SCANCODE_F24', 'SDL_SCANCODE_F24') +register('SCANCODE_EXECUTE', 'SDL_SCANCODE_EXECUTE') +register('SCANCODE_HELP', 'SDL_SCANCODE_HELP') +register('SCANCODE_MENU', 'SDL_SCANCODE_MENU') +register('SCANCODE_SELECT', 'SDL_SCANCODE_SELECT') +register('SCANCODE_STOP', 'SDL_SCANCODE_STOP') +register('SCANCODE_AGAIN', 'SDL_SCANCODE_AGAIN') +register('SCANCODE_UNDO', 'SDL_SCANCODE_UNDO') +register('SCANCODE_CUT', 'SDL_SCANCODE_CUT') +register('SCANCODE_COPY', 'SDL_SCANCODE_COPY') +register('SCANCODE_PASTE', 'SDL_SCANCODE_PASTE') +register('SCANCODE_FIND', 'SDL_SCANCODE_FIND') +register('SCANCODE_MUTE', 'SDL_SCANCODE_MUTE') +register('SCANCODE_VOLUMEUP', 'SDL_SCANCODE_VOLUMEUP') +register('SCANCODE_VOLUMEDOWN', 'SDL_SCANCODE_VOLUMEDOWN') +register('SCANCODE_KP_COMMA', 'SDL_SCANCODE_KP_COMMA') +register('SCANCODE_KP_EQUALSAS400', 'SDL_SCANCODE_KP_EQUALSAS400') +register('SCANCODE_INTERNATIONAL1', 'SDL_SCANCODE_INTERNATIONAL1') +register('SCANCODE_INTERNATIONAL2', 'SDL_SCANCODE_INTERNATIONAL2') +register('SCANCODE_INTERNATIONAL3', 'SDL_SCANCODE_INTERNATIONAL3') +register('SCANCODE_INTERNATIONAL4', 'SDL_SCANCODE_INTERNATIONAL4') +register('SCANCODE_INTERNATIONAL5', 'SDL_SCANCODE_INTERNATIONAL5') +register('SCANCODE_INTERNATIONAL6', 'SDL_SCANCODE_INTERNATIONAL6') +register('SCANCODE_INTERNATIONAL7', 'SDL_SCANCODE_INTERNATIONAL7') +register('SCANCODE_INTERNATIONAL8', 'SDL_SCANCODE_INTERNATIONAL8') +register('SCANCODE_INTERNATIONAL9', 'SDL_SCANCODE_INTERNATIONAL9') +register('SCANCODE_LANG1', 'SDL_SCANCODE_LANG1') +register('SCANCODE_LANG2', 'SDL_SCANCODE_LANG2') +register('SCANCODE_LANG3', 'SDL_SCANCODE_LANG3') +register('SCANCODE_LANG4', 'SDL_SCANCODE_LANG4') +register('SCANCODE_LANG5', 'SDL_SCANCODE_LANG5') +register('SCANCODE_LANG6', 'SDL_SCANCODE_LANG6') +register('SCANCODE_LANG7', 'SDL_SCANCODE_LANG7') +register('SCANCODE_LANG8', 'SDL_SCANCODE_LANG8') +register('SCANCODE_LANG9', 'SDL_SCANCODE_LANG9') +register('SCANCODE_ALTERASE', 'SDL_SCANCODE_ALTERASE') +register('SCANCODE_SYSREQ', 'SDL_SCANCODE_SYSREQ') +register('SCANCODE_CANCEL', 'SDL_SCANCODE_CANCEL') +register('SCANCODE_CLEAR', 'SDL_SCANCODE_CLEAR') +register('SCANCODE_PRIOR', 'SDL_SCANCODE_PRIOR') +register('SCANCODE_RETURN2', 'SDL_SCANCODE_RETURN2') +register('SCANCODE_SEPARATOR', 'SDL_SCANCODE_SEPARATOR') +register('SCANCODE_OUT', 'SDL_SCANCODE_OUT') +register('SCANCODE_OPER', 'SDL_SCANCODE_OPER') +register('SCANCODE_CLEARAGAIN', 'SDL_SCANCODE_CLEARAGAIN') +register('SCANCODE_CRSEL', 'SDL_SCANCODE_CRSEL') +register('SCANCODE_EXSEL', 'SDL_SCANCODE_EXSEL') +register('SCANCODE_KP_00', 'SDL_SCANCODE_KP_00') +register('SCANCODE_KP_000', 'SDL_SCANCODE_KP_000') +register('SCANCODE_THOUSANDSSEPARATOR', 'SDL_SCANCODE_THOUSANDSSEPARATOR') +register('SCANCODE_DECIMALSEPARATOR', 'SDL_SCANCODE_DECIMALSEPARATOR') +register('SCANCODE_CURRENCYUNIT', 'SDL_SCANCODE_CURRENCYUNIT') +register('SCANCODE_CURRENCYSUBUNIT', 'SDL_SCANCODE_CURRENCYSUBUNIT') +register('SCANCODE_KP_LEFTPAREN', 'SDL_SCANCODE_KP_LEFTPAREN') +register('SCANCODE_KP_RIGHTPAREN', 'SDL_SCANCODE_KP_RIGHTPAREN') +register('SCANCODE_KP_LEFTBRACE', 'SDL_SCANCODE_KP_LEFTBRACE') +register('SCANCODE_KP_RIGHTBRACE', 'SDL_SCANCODE_KP_RIGHTBRACE') +register('SCANCODE_KP_TAB', 'SDL_SCANCODE_KP_TAB') +register('SCANCODE_KP_BACKSPACE', 'SDL_SCANCODE_KP_BACKSPACE') +register('SCANCODE_KP_A', 'SDL_SCANCODE_KP_A') +register('SCANCODE_KP_B', 'SDL_SCANCODE_KP_B') +register('SCANCODE_KP_C', 'SDL_SCANCODE_KP_C') +register('SCANCODE_KP_D', 'SDL_SCANCODE_KP_D') +register('SCANCODE_KP_E', 'SDL_SCANCODE_KP_E') +register('SCANCODE_KP_F', 'SDL_SCANCODE_KP_F') +register('SCANCODE_KP_XOR', 'SDL_SCANCODE_KP_XOR') +register('SCANCODE_KP_POWER', 'SDL_SCANCODE_KP_POWER') +register('SCANCODE_KP_PERCENT', 'SDL_SCANCODE_KP_PERCENT') +register('SCANCODE_KP_LESS', 'SDL_SCANCODE_KP_LESS') +register('SCANCODE_KP_GREATER', 'SDL_SCANCODE_KP_GREATER') +register('SCANCODE_KP_AMPERSAND', 'SDL_SCANCODE_KP_AMPERSAND') +register('SCANCODE_KP_DBLAMPERSAND', 'SDL_SCANCODE_KP_DBLAMPERSAND') +register('SCANCODE_KP_VERTICALBAR', 'SDL_SCANCODE_KP_VERTICALBAR') +register('SCANCODE_KP_DBLVERTICALBAR', 'SDL_SCANCODE_KP_DBLVERTICALBAR') +register('SCANCODE_KP_COLON', 'SDL_SCANCODE_KP_COLON') +register('SCANCODE_KP_HASH', 'SDL_SCANCODE_KP_HASH') +register('SCANCODE_KP_SPACE', 'SDL_SCANCODE_KP_SPACE') +register('SCANCODE_KP_AT', 'SDL_SCANCODE_KP_AT') +register('SCANCODE_KP_EXCLAM', 'SDL_SCANCODE_KP_EXCLAM') +register('SCANCODE_KP_MEMSTORE', 'SDL_SCANCODE_KP_MEMSTORE') +register('SCANCODE_KP_MEMRECALL', 'SDL_SCANCODE_KP_MEMRECALL') +register('SCANCODE_KP_MEMCLEAR', 'SDL_SCANCODE_KP_MEMCLEAR') +register('SCANCODE_KP_MEMADD', 'SDL_SCANCODE_KP_MEMADD') +register('SCANCODE_KP_MEMSUBTRACT', 'SDL_SCANCODE_KP_MEMSUBTRACT') +register('SCANCODE_KP_MEMMULTIPLY', 'SDL_SCANCODE_KP_MEMMULTIPLY') +register('SCANCODE_KP_MEMDIVIDE', 'SDL_SCANCODE_KP_MEMDIVIDE') +register('SCANCODE_KP_PLUSMINUS', 'SDL_SCANCODE_KP_PLUSMINUS') +register('SCANCODE_KP_CLEAR', 'SDL_SCANCODE_KP_CLEAR') +register('SCANCODE_KP_CLEARENTRY', 'SDL_SCANCODE_KP_CLEARENTRY') +register('SCANCODE_KP_BINARY', 'SDL_SCANCODE_KP_BINARY') +register('SCANCODE_KP_OCTAL', 'SDL_SCANCODE_KP_OCTAL') +register('SCANCODE_KP_DECIMAL', 'SDL_SCANCODE_KP_DECIMAL') +register('SCANCODE_KP_HEXADECIMAL', 'SDL_SCANCODE_KP_HEXADECIMAL') +register('SCANCODE_LCTRL', 'SDL_SCANCODE_LCTRL') +register('SCANCODE_LSHIFT', 'SDL_SCANCODE_LSHIFT') +register('SCANCODE_LALT', 'SDL_SCANCODE_LALT') +register('SCANCODE_LGUI', 'SDL_SCANCODE_LGUI') +register('SCANCODE_RCTRL', 'SDL_SCANCODE_RCTRL') +register('SCANCODE_RSHIFT', 'SDL_SCANCODE_RSHIFT') +register('SCANCODE_RALT', 'SDL_SCANCODE_RALT') +register('SCANCODE_RGUI', 'SDL_SCANCODE_RGUI') +register('SCANCODE_MODE', 'SDL_SCANCODE_MODE') +register('SCANCODE_AUDIONEXT', 'SDL_SCANCODE_AUDIONEXT') +register('SCANCODE_AUDIOPREV', 'SDL_SCANCODE_AUDIOPREV') +register('SCANCODE_AUDIOSTOP', 'SDL_SCANCODE_AUDIOSTOP') +register('SCANCODE_AUDIOPLAY', 'SDL_SCANCODE_AUDIOPLAY') +register('SCANCODE_AUDIOMUTE', 'SDL_SCANCODE_AUDIOMUTE') +register('SCANCODE_MEDIASELECT', 'SDL_SCANCODE_MEDIASELECT') +register('SCANCODE_WWW', 'SDL_SCANCODE_WWW') +register('SCANCODE_MAIL', 'SDL_SCANCODE_MAIL') +register('SCANCODE_CALCULATOR', 'SDL_SCANCODE_CALCULATOR') +register('SCANCODE_COMPUTER', 'SDL_SCANCODE_COMPUTER') +register('SCANCODE_AC_SEARCH', 'SDL_SCANCODE_AC_SEARCH') +register('SCANCODE_AC_HOME', 'SDL_SCANCODE_AC_HOME') +register('SCANCODE_AC_BACK', 'SDL_SCANCODE_AC_BACK') +register('SCANCODE_AC_FORWARD', 'SDL_SCANCODE_AC_FORWARD') +register('SCANCODE_AC_STOP', 'SDL_SCANCODE_AC_STOP') +register('SCANCODE_AC_REFRESH', 'SDL_SCANCODE_AC_REFRESH') +register('SCANCODE_AC_BOOKMARKS', 'SDL_SCANCODE_AC_BOOKMARKS') +register('SCANCODE_BRIGHTNESSDOWN', 'SDL_SCANCODE_BRIGHTNESSDOWN') +register('SCANCODE_BRIGHTNESSUP', 'SDL_SCANCODE_BRIGHTNESSUP') +register('SCANCODE_DISPLAYSWITCH', 'SDL_SCANCODE_DISPLAYSWITCH') +register('SCANCODE_KBDILLUMTOGGLE', 'SDL_SCANCODE_KBDILLUMTOGGLE') +register('SCANCODE_KBDILLUMDOWN', 'SDL_SCANCODE_KBDILLUMDOWN') +register('SCANCODE_KBDILLUMUP', 'SDL_SCANCODE_KBDILLUMUP') +register('SCANCODE_EJECT', 'SDL_SCANCODE_EJECT') +register('SCANCODE_SLEEP', 'SDL_SCANCODE_SLEEP') +register('SCANCODE_APP1', 'SDL_SCANCODE_APP1') +register('SCANCODE_APP2', 'SDL_SCANCODE_APP2') +register('NUM_SCANCODES', 'SDL_NUM_SCANCODES') +register('SCANCODE_CAPSLOCK', 'SDL_SCANCODE_CAPSLOCK') +register('SCANCODE_F1', 'SDL_SCANCODE_F1') +register('SCANCODE_F2', 'SDL_SCANCODE_F2') +register('SCANCODE_F3', 'SDL_SCANCODE_F3') +register('SCANCODE_F4', 'SDL_SCANCODE_F4') +register('SCANCODE_F5', 'SDL_SCANCODE_F5') +register('SCANCODE_F6', 'SDL_SCANCODE_F6') +register('SCANCODE_F7', 'SDL_SCANCODE_F7') +register('SCANCODE_F8', 'SDL_SCANCODE_F8') +register('SCANCODE_F9', 'SDL_SCANCODE_F9') +register('SCANCODE_F10', 'SDL_SCANCODE_F10') +register('SCANCODE_F11', 'SDL_SCANCODE_F11') +register('SCANCODE_F12', 'SDL_SCANCODE_F12') +register('SCANCODE_PRINTSCREEN', 'SDL_SCANCODE_PRINTSCREEN') +register('SCANCODE_SCROLLLOCK', 'SDL_SCANCODE_SCROLLLOCK') +register('SCANCODE_PAUSE', 'SDL_SCANCODE_PAUSE') +register('SCANCODE_INSERT', 'SDL_SCANCODE_INSERT') +register('SCANCODE_HOME', 'SDL_SCANCODE_HOME') +register('SCANCODE_PAGEUP', 'SDL_SCANCODE_PAGEUP') +register('SCANCODE_END', 'SDL_SCANCODE_END') +register('SCANCODE_PAGEDOWN', 'SDL_SCANCODE_PAGEDOWN') +register('SCANCODE_RIGHT', 'SDL_SCANCODE_RIGHT') +register('SCANCODE_LEFT', 'SDL_SCANCODE_LEFT') +register('SCANCODE_DOWN', 'SDL_SCANCODE_DOWN') +register('SCANCODE_UP', 'SDL_SCANCODE_UP') +register('SCANCODE_NUMLOCKCLEAR', 'SDL_SCANCODE_NUMLOCKCLEAR') +register('SCANCODE_KP_DIVIDE', 'SDL_SCANCODE_KP_DIVIDE') +register('SCANCODE_KP_MULTIPLY', 'SDL_SCANCODE_KP_MULTIPLY') +register('SCANCODE_KP_MINUS', 'SDL_SCANCODE_KP_MINUS') +register('SCANCODE_KP_PLUS', 'SDL_SCANCODE_KP_PLUS') +register('SCANCODE_KP_ENTER', 'SDL_SCANCODE_KP_ENTER') +register('SCANCODE_KP_1', 'SDL_SCANCODE_KP_1') +register('SCANCODE_KP_2', 'SDL_SCANCODE_KP_2') +register('SCANCODE_KP_3', 'SDL_SCANCODE_KP_3') +register('SCANCODE_KP_4', 'SDL_SCANCODE_KP_4') +register('SCANCODE_KP_5', 'SDL_SCANCODE_KP_5') +register('SCANCODE_KP_6', 'SDL_SCANCODE_KP_6') +register('SCANCODE_KP_7', 'SDL_SCANCODE_KP_7') +register('SCANCODE_KP_8', 'SDL_SCANCODE_KP_8') +register('SCANCODE_KP_9', 'SDL_SCANCODE_KP_9') +register('SCANCODE_KP_0', 'SDL_SCANCODE_KP_0') +register('SCANCODE_KP_PERIOD', 'SDL_SCANCODE_KP_PERIOD') +register('SCANCODE_APPLICATION', 'SDL_SCANCODE_APPLICATION') +register('SCANCODE_POWER', 'SDL_SCANCODE_POWER') +register('SCANCODE_KP_EQUALS', 'SDL_SCANCODE_KP_EQUALS') +register('SCANCODE_F13', 'SDL_SCANCODE_F13') +register('SCANCODE_F14', 'SDL_SCANCODE_F14') +register('SCANCODE_F15', 'SDL_SCANCODE_F15') +register('SCANCODE_F16', 'SDL_SCANCODE_F16') +register('SCANCODE_F17', 'SDL_SCANCODE_F17') +register('SCANCODE_F18', 'SDL_SCANCODE_F18') +register('SCANCODE_F19', 'SDL_SCANCODE_F19') +register('SCANCODE_F20', 'SDL_SCANCODE_F20') +register('SCANCODE_F21', 'SDL_SCANCODE_F21') +register('SCANCODE_F22', 'SDL_SCANCODE_F22') +register('SCANCODE_F23', 'SDL_SCANCODE_F23') +register('SCANCODE_F24', 'SDL_SCANCODE_F24') +register('SCANCODE_EXECUTE', 'SDL_SCANCODE_EXECUTE') +register('SCANCODE_HELP', 'SDL_SCANCODE_HELP') +register('SCANCODE_MENU', 'SDL_SCANCODE_MENU') +register('SCANCODE_SELECT', 'SDL_SCANCODE_SELECT') +register('SCANCODE_STOP', 'SDL_SCANCODE_STOP') +register('SCANCODE_AGAIN', 'SDL_SCANCODE_AGAIN') +register('SCANCODE_UNDO', 'SDL_SCANCODE_UNDO') +register('SCANCODE_CUT', 'SDL_SCANCODE_CUT') +register('SCANCODE_COPY', 'SDL_SCANCODE_COPY') +register('SCANCODE_PASTE', 'SDL_SCANCODE_PASTE') +register('SCANCODE_FIND', 'SDL_SCANCODE_FIND') +register('SCANCODE_MUTE', 'SDL_SCANCODE_MUTE') +register('SCANCODE_VOLUMEUP', 'SDL_SCANCODE_VOLUMEUP') +register('SCANCODE_VOLUMEDOWN', 'SDL_SCANCODE_VOLUMEDOWN') +register('SCANCODE_KP_COMMA', 'SDL_SCANCODE_KP_COMMA') +register('SCANCODE_KP_EQUALSAS400', 'SDL_SCANCODE_KP_EQUALSAS400') +register('SCANCODE_ALTERASE', 'SDL_SCANCODE_ALTERASE') +register('SCANCODE_SYSREQ', 'SDL_SCANCODE_SYSREQ') +register('SCANCODE_CANCEL', 'SDL_SCANCODE_CANCEL') +register('SCANCODE_CLEAR', 'SDL_SCANCODE_CLEAR') +register('SCANCODE_PRIOR', 'SDL_SCANCODE_PRIOR') +register('SCANCODE_RETURN2', 'SDL_SCANCODE_RETURN2') +register('SCANCODE_SEPARATOR', 'SDL_SCANCODE_SEPARATOR') +register('SCANCODE_OUT', 'SDL_SCANCODE_OUT') +register('SCANCODE_OPER', 'SDL_SCANCODE_OPER') +register('SCANCODE_CLEARAGAIN', 'SDL_SCANCODE_CLEARAGAIN') +register('SCANCODE_CRSEL', 'SDL_SCANCODE_CRSEL') +register('SCANCODE_EXSEL', 'SDL_SCANCODE_EXSEL') +register('SCANCODE_KP_00', 'SDL_SCANCODE_KP_00') +register('SCANCODE_KP_000', 'SDL_SCANCODE_KP_000') +register('SCANCODE_THOUSANDSSEPARATOR', 'SDL_SCANCODE_THOUSANDSSEPARATOR') +register('SCANCODE_DECIMALSEPARATOR', 'SDL_SCANCODE_DECIMALSEPARATOR') +register('SCANCODE_CURRENCYUNIT', 'SDL_SCANCODE_CURRENCYUNIT') +register('SCANCODE_CURRENCYSUBUNIT', 'SDL_SCANCODE_CURRENCYSUBUNIT') +register('SCANCODE_KP_LEFTPAREN', 'SDL_SCANCODE_KP_LEFTPAREN') +register('SCANCODE_KP_RIGHTPAREN', 'SDL_SCANCODE_KP_RIGHTPAREN') +register('SCANCODE_KP_LEFTBRACE', 'SDL_SCANCODE_KP_LEFTBRACE') +register('SCANCODE_KP_RIGHTBRACE', 'SDL_SCANCODE_KP_RIGHTBRACE') +register('SCANCODE_KP_TAB', 'SDL_SCANCODE_KP_TAB') +register('SCANCODE_KP_BACKSPACE', 'SDL_SCANCODE_KP_BACKSPACE') +register('SCANCODE_KP_A', 'SDL_SCANCODE_KP_A') +register('SCANCODE_KP_B', 'SDL_SCANCODE_KP_B') +register('SCANCODE_KP_C', 'SDL_SCANCODE_KP_C') +register('SCANCODE_KP_D', 'SDL_SCANCODE_KP_D') +register('SCANCODE_KP_E', 'SDL_SCANCODE_KP_E') +register('SCANCODE_KP_F', 'SDL_SCANCODE_KP_F') +register('SCANCODE_KP_XOR', 'SDL_SCANCODE_KP_XOR') +register('SCANCODE_KP_POWER', 'SDL_SCANCODE_KP_POWER') +register('SCANCODE_KP_PERCENT', 'SDL_SCANCODE_KP_PERCENT') +register('SCANCODE_KP_LESS', 'SDL_SCANCODE_KP_LESS') +register('SCANCODE_KP_GREATER', 'SDL_SCANCODE_KP_GREATER') +register('SCANCODE_KP_AMPERSAND', 'SDL_SCANCODE_KP_AMPERSAND') +register('SCANCODE_KP_DBLAMPERSAND', 'SDL_SCANCODE_KP_DBLAMPERSAND') +register('SCANCODE_KP_VERTICALBAR', 'SDL_SCANCODE_KP_VERTICALBAR') +register('SCANCODE_KP_DBLVERTICALBAR', 'SDL_SCANCODE_KP_DBLVERTICALBAR') +register('SCANCODE_KP_COLON', 'SDL_SCANCODE_KP_COLON') +register('SCANCODE_KP_HASH', 'SDL_SCANCODE_KP_HASH') +register('SCANCODE_KP_SPACE', 'SDL_SCANCODE_KP_SPACE') +register('SCANCODE_KP_AT', 'SDL_SCANCODE_KP_AT') +register('SCANCODE_KP_EXCLAM', 'SDL_SCANCODE_KP_EXCLAM') +register('SCANCODE_KP_MEMSTORE', 'SDL_SCANCODE_KP_MEMSTORE') +register('SCANCODE_KP_MEMRECALL', 'SDL_SCANCODE_KP_MEMRECALL') +register('SCANCODE_KP_MEMCLEAR', 'SDL_SCANCODE_KP_MEMCLEAR') +register('SCANCODE_KP_MEMADD', 'SDL_SCANCODE_KP_MEMADD') +register('SCANCODE_KP_MEMSUBTRACT', 'SDL_SCANCODE_KP_MEMSUBTRACT') +register('SCANCODE_KP_MEMMULTIPLY', 'SDL_SCANCODE_KP_MEMMULTIPLY') +register('SCANCODE_KP_MEMDIVIDE', 'SDL_SCANCODE_KP_MEMDIVIDE') +register('SCANCODE_KP_PLUSMINUS', 'SDL_SCANCODE_KP_PLUSMINUS') +register('SCANCODE_KP_CLEAR', 'SDL_SCANCODE_KP_CLEAR') +register('SCANCODE_KP_CLEARENTRY', 'SDL_SCANCODE_KP_CLEARENTRY') +register('SCANCODE_KP_BINARY', 'SDL_SCANCODE_KP_BINARY') +register('SCANCODE_KP_OCTAL', 'SDL_SCANCODE_KP_OCTAL') +register('SCANCODE_KP_DECIMAL', 'SDL_SCANCODE_KP_DECIMAL') +register('SCANCODE_KP_HEXADECIMAL', 'SDL_SCANCODE_KP_HEXADECIMAL') +register('SCANCODE_LCTRL', 'SDL_SCANCODE_LCTRL') +register('SCANCODE_LSHIFT', 'SDL_SCANCODE_LSHIFT') +register('SCANCODE_LALT', 'SDL_SCANCODE_LALT') +register('SCANCODE_LGUI', 'SDL_SCANCODE_LGUI') +register('SCANCODE_RCTRL', 'SDL_SCANCODE_RCTRL') +register('SCANCODE_RSHIFT', 'SDL_SCANCODE_RSHIFT') +register('SCANCODE_RALT', 'SDL_SCANCODE_RALT') +register('SCANCODE_RGUI', 'SDL_SCANCODE_RGUI') +register('SCANCODE_MODE', 'SDL_SCANCODE_MODE') +register('SCANCODE_AUDIONEXT', 'SDL_SCANCODE_AUDIONEXT') +register('SCANCODE_AUDIOPREV', 'SDL_SCANCODE_AUDIOPREV') +register('SCANCODE_AUDIOSTOP', 'SDL_SCANCODE_AUDIOSTOP') +register('SCANCODE_AUDIOPLAY', 'SDL_SCANCODE_AUDIOPLAY') +register('SCANCODE_AUDIOMUTE', 'SDL_SCANCODE_AUDIOMUTE') +register('SCANCODE_MEDIASELECT', 'SDL_SCANCODE_MEDIASELECT') +register('SCANCODE_WWW', 'SDL_SCANCODE_WWW') +register('SCANCODE_MAIL', 'SDL_SCANCODE_MAIL') +register('SCANCODE_CALCULATOR', 'SDL_SCANCODE_CALCULATOR') +register('SCANCODE_COMPUTER', 'SDL_SCANCODE_COMPUTER') +register('SCANCODE_AC_SEARCH', 'SDL_SCANCODE_AC_SEARCH') +register('SCANCODE_AC_HOME', 'SDL_SCANCODE_AC_HOME') +register('SCANCODE_AC_BACK', 'SDL_SCANCODE_AC_BACK') +register('SCANCODE_AC_FORWARD', 'SDL_SCANCODE_AC_FORWARD') +register('SCANCODE_AC_STOP', 'SDL_SCANCODE_AC_STOP') +register('SCANCODE_AC_REFRESH', 'SDL_SCANCODE_AC_REFRESH') +register('SCANCODE_AC_BOOKMARKS', 'SDL_SCANCODE_AC_BOOKMARKS') +register('SCANCODE_BRIGHTNESSDOWN', 'SDL_SCANCODE_BRIGHTNESSDOWN') +register('SCANCODE_BRIGHTNESSUP', 'SDL_SCANCODE_BRIGHTNESSUP') +register('SCANCODE_DISPLAYSWITCH', 'SDL_SCANCODE_DISPLAYSWITCH') +register('SCANCODE_KBDILLUMTOGGLE', 'SDL_SCANCODE_KBDILLUMTOGGLE') +register('SCANCODE_KBDILLUMDOWN', 'SDL_SCANCODE_KBDILLUMDOWN') +register('SCANCODE_KBDILLUMUP', 'SDL_SCANCODE_KBDILLUMUP') +register('SCANCODE_EJECT', 'SDL_SCANCODE_EJECT') +register('SCANCODE_SLEEP', 'SDL_SCANCODE_SLEEP') +register('KMOD_NONE', 'SDL_KMOD_NONE') +register('KMOD_LSHIFT', 'SDL_KMOD_LSHIFT') +register('KMOD_RSHIFT', 'SDL_KMOD_RSHIFT') +register('KMOD_LCTRL', 'SDL_KMOD_LCTRL') +register('KMOD_RCTRL', 'SDL_KMOD_RCTRL') +register('KMOD_LALT', 'SDL_KMOD_LALT') +register('KMOD_RALT', 'SDL_KMOD_RALT') +register('KMOD_LGUI', 'SDL_KMOD_LGUI') +register('KMOD_RGUI', 'SDL_KMOD_RGUI') +register('KMOD_NUM', 'SDL_KMOD_NUM') +register('KMOD_CAPS', 'SDL_KMOD_CAPS') +register('KMOD_MODE', 'SDL_KMOD_MODE') +register('KMOD_RESERVED', 'SDL_KMOD_RESERVED') +register('SYSTEM_CURSOR_ARROW', 'SDL_SYSTEM_CURSOR_ARROW') +register('SYSTEM_CURSOR_IBEAM', 'SDL_SYSTEM_CURSOR_IBEAM') +register('SYSTEM_CURSOR_WAIT', 'SDL_SYSTEM_CURSOR_WAIT') +register('SYSTEM_CURSOR_CROSSHAIR', 'SDL_SYSTEM_CURSOR_CROSSHAIR') +register('SYSTEM_CURSOR_WAITARROW', 'SDL_SYSTEM_CURSOR_WAITARROW') +register('SYSTEM_CURSOR_SIZENWSE', 'SDL_SYSTEM_CURSOR_SIZENWSE') +register('SYSTEM_CURSOR_SIZENESW', 'SDL_SYSTEM_CURSOR_SIZENESW') +register('SYSTEM_CURSOR_SIZEWE', 'SDL_SYSTEM_CURSOR_SIZEWE') +register('SYSTEM_CURSOR_SIZENS', 'SDL_SYSTEM_CURSOR_SIZENS') +register('SYSTEM_CURSOR_SIZEALL', 'SDL_SYSTEM_CURSOR_SIZEALL') +register('SYSTEM_CURSOR_NO', 'SDL_SYSTEM_CURSOR_NO') +register('SYSTEM_CURSOR_HAND', 'SDL_SYSTEM_CURSOR_HAND') +register('NUM_SYSTEM_CURSORS', 'SDL_NUM_SYSTEM_CURSORS') +register('CONTROLLER_BINDTYPE_NONE', 'SDL_CONTROLLER_BINDTYPE_NONE') +register('CONTROLLER_BINDTYPE_BUTTON', 'SDL_CONTROLLER_BINDTYPE_BUTTON') +register('CONTROLLER_BINDTYPE_AXIS', 'SDL_CONTROLLER_BINDTYPE_AXIS') +register('CONTROLLER_BINDTYPE_HAT', 'SDL_CONTROLLER_BINDTYPE_HAT') +register('CONTROLLER_AXIS_INVALID', 'SDL_CONTROLLER_AXIS_INVALID') +register('CONTROLLER_AXIS_LEFTX', 'SDL_CONTROLLER_AXIS_LEFTX') +register('CONTROLLER_AXIS_LEFTY', 'SDL_CONTROLLER_AXIS_LEFTY') +register('CONTROLLER_AXIS_RIGHTX', 'SDL_CONTROLLER_AXIS_RIGHTX') +register('CONTROLLER_AXIS_RIGHTY', 'SDL_CONTROLLER_AXIS_RIGHTY') +register('CONTROLLER_AXIS_TRIGGERLEFT', 'SDL_CONTROLLER_AXIS_TRIGGERLEFT') +register('CONTROLLER_AXIS_TRIGGERRIGHT', 'SDL_CONTROLLER_AXIS_TRIGGERRIGHT') +register('CONTROLLER_AXIS_MAX', 'SDL_CONTROLLER_AXIS_MAX') +register('CONTROLLER_BUTTON_INVALID', 'SDL_CONTROLLER_BUTTON_INVALID') +register('CONTROLLER_BUTTON_A', 'SDL_CONTROLLER_BUTTON_A') +register('CONTROLLER_BUTTON_B', 'SDL_CONTROLLER_BUTTON_B') +register('CONTROLLER_BUTTON_X', 'SDL_CONTROLLER_BUTTON_X') +register('CONTROLLER_BUTTON_Y', 'SDL_CONTROLLER_BUTTON_Y') +register('CONTROLLER_BUTTON_BACK', 'SDL_CONTROLLER_BUTTON_BACK') +register('CONTROLLER_BUTTON_GUIDE', 'SDL_CONTROLLER_BUTTON_GUIDE') +register('CONTROLLER_BUTTON_START', 'SDL_CONTROLLER_BUTTON_START') +register('CONTROLLER_BUTTON_LEFTSTICK', 'SDL_CONTROLLER_BUTTON_LEFTSTICK') +register('CONTROLLER_BUTTON_RIGHTSTICK', 'SDL_CONTROLLER_BUTTON_RIGHTSTICK') +register('CONTROLLER_BUTTON_LEFTSHOULDER', 'SDL_CONTROLLER_BUTTON_LEFTSHOULDER') +register('CONTROLLER_BUTTON_RIGHTSHOULDER', 'SDL_CONTROLLER_BUTTON_RIGHTSHOULDER') +register('CONTROLLER_BUTTON_DPAD_UP', 'SDL_CONTROLLER_BUTTON_DPAD_UP') +register('CONTROLLER_BUTTON_DPAD_DOWN', 'SDL_CONTROLLER_BUTTON_DPAD_DOWN') +register('CONTROLLER_BUTTON_DPAD_LEFT', 'SDL_CONTROLLER_BUTTON_DPAD_LEFT') +register('CONTROLLER_BUTTON_DPAD_RIGHT', 'SDL_CONTROLLER_BUTTON_DPAD_RIGHT') +register('CONTROLLER_BUTTON_MAX', 'SDL_CONTROLLER_BUTTON_MAX') +register('FIRSTEVENT', 'SDL_FIRSTEVENT') +register('QUIT', 'SDL_QUIT') +register('APP_TERMINATING', 'SDL_APP_TERMINATING') +register('APP_LOWMEMORY', 'SDL_APP_LOWMEMORY') +register('APP_WILLENTERBACKGROUND', 'SDL_APP_WILLENTERBACKGROUND') +register('APP_DIDENTERBACKGROUND', 'SDL_APP_DIDENTERBACKGROUND') +register('APP_WILLENTERFOREGROUND', 'SDL_APP_WILLENTERFOREGROUND') +register('APP_DIDENTERFOREGROUND', 'SDL_APP_DIDENTERFOREGROUND') +register('WINDOWEVENT', 'SDL_WINDOWEVENT') +register('SYSWMEVENT', 'SDL_SYSWMEVENT') +register('KEYDOWN', 'SDL_KEYDOWN') +register('KEYUP', 'SDL_KEYUP') +register('TEXTEDITING', 'SDL_TEXTEDITING') +register('TEXTINPUT', 'SDL_TEXTINPUT') +register('MOUSEMOTION', 'SDL_MOUSEMOTION') +register('MOUSEBUTTONDOWN', 'SDL_MOUSEBUTTONDOWN') +register('MOUSEBUTTONUP', 'SDL_MOUSEBUTTONUP') +register('MOUSEWHEEL', 'SDL_MOUSEWHEEL') +register('JOYAXISMOTION', 'SDL_JOYAXISMOTION') +register('JOYBALLMOTION', 'SDL_JOYBALLMOTION') +register('JOYHATMOTION', 'SDL_JOYHATMOTION') +register('JOYBUTTONDOWN', 'SDL_JOYBUTTONDOWN') +register('JOYBUTTONUP', 'SDL_JOYBUTTONUP') +register('JOYDEVICEADDED', 'SDL_JOYDEVICEADDED') +register('JOYDEVICEREMOVED', 'SDL_JOYDEVICEREMOVED') +register('CONTROLLERAXISMOTION', 'SDL_CONTROLLERAXISMOTION') +register('CONTROLLERBUTTONDOWN', 'SDL_CONTROLLERBUTTONDOWN') +register('CONTROLLERBUTTONUP', 'SDL_CONTROLLERBUTTONUP') +register('CONTROLLERDEVICEADDED', 'SDL_CONTROLLERDEVICEADDED') +register('CONTROLLERDEVICEREMOVED', 'SDL_CONTROLLERDEVICEREMOVED') +register('CONTROLLERDEVICEREMAPPED', 'SDL_CONTROLLERDEVICEREMAPPED') +register('FINGERDOWN', 'SDL_FINGERDOWN') +register('FINGERUP', 'SDL_FINGERUP') +register('FINGERMOTION', 'SDL_FINGERMOTION') +register('DOLLARGESTURE', 'SDL_DOLLARGESTURE') +register('DOLLARRECORD', 'SDL_DOLLARRECORD') +register('MULTIGESTURE', 'SDL_MULTIGESTURE') +register('CLIPBOARDUPDATE', 'SDL_CLIPBOARDUPDATE') +register('DROPFILE', 'SDL_DROPFILE') +register('USEREVENT', 'SDL_USEREVENT') +register('LASTEVENT', 'SDL_LASTEVENT') +register('ADDEVENT', 'SDL_ADDEVENT') +register('PEEKEVENT', 'SDL_PEEKEVENT') +register('GETEVENT', 'SDL_GETEVENT') +register('HINT_DEFAULT', 'SDL_HINT_DEFAULT') +register('HINT_NORMAL', 'SDL_HINT_NORMAL') +register('HINT_OVERRIDE', 'SDL_HINT_OVERRIDE') +register('LOG_CATEGORY_APPLICATION', 'SDL_LOG_CATEGORY_APPLICATION') +register('LOG_CATEGORY_ERROR', 'SDL_LOG_CATEGORY_ERROR') +register('LOG_CATEGORY_ASSERT', 'SDL_LOG_CATEGORY_ASSERT') +register('LOG_CATEGORY_SYSTEM', 'SDL_LOG_CATEGORY_SYSTEM') +register('LOG_CATEGORY_AUDIO', 'SDL_LOG_CATEGORY_AUDIO') +register('LOG_CATEGORY_VIDEO', 'SDL_LOG_CATEGORY_VIDEO') +register('LOG_CATEGORY_RENDER', 'SDL_LOG_CATEGORY_RENDER') +register('LOG_CATEGORY_INPUT', 'SDL_LOG_CATEGORY_INPUT') +register('LOG_CATEGORY_TEST', 'SDL_LOG_CATEGORY_TEST') +register('LOG_CATEGORY_RESERVED1', 'SDL_LOG_CATEGORY_RESERVED1') +register('LOG_CATEGORY_RESERVED2', 'SDL_LOG_CATEGORY_RESERVED2') +register('LOG_CATEGORY_RESERVED3', 'SDL_LOG_CATEGORY_RESERVED3') +register('LOG_CATEGORY_RESERVED4', 'SDL_LOG_CATEGORY_RESERVED4') +register('LOG_CATEGORY_RESERVED5', 'SDL_LOG_CATEGORY_RESERVED5') +register('LOG_CATEGORY_RESERVED6', 'SDL_LOG_CATEGORY_RESERVED6') +register('LOG_CATEGORY_RESERVED7', 'SDL_LOG_CATEGORY_RESERVED7') +register('LOG_CATEGORY_RESERVED8', 'SDL_LOG_CATEGORY_RESERVED8') +register('LOG_CATEGORY_RESERVED9', 'SDL_LOG_CATEGORY_RESERVED9') +register('LOG_CATEGORY_RESERVED10', 'SDL_LOG_CATEGORY_RESERVED10') +register('LOG_CATEGORY_CUSTOM', 'SDL_LOG_CATEGORY_CUSTOM') +register('LOG_PRIORITY_VERBOSE', 'SDL_LOG_PRIORITY_VERBOSE') +register('LOG_PRIORITY_DEBUG', 'SDL_LOG_PRIORITY_DEBUG') +register('LOG_PRIORITY_INFO', 'SDL_LOG_PRIORITY_INFO') +register('LOG_PRIORITY_WARN', 'SDL_LOG_PRIORITY_WARN') +register('LOG_PRIORITY_ERROR', 'SDL_LOG_PRIORITY_ERROR') +register('LOG_PRIORITY_CRITICAL', 'SDL_LOG_PRIORITY_CRITICAL') +register('NUM_LOG_PRIORITIES', 'SDL_NUM_LOG_PRIORITIES') +register('MESSAGEBOX_ERROR', 'SDL_MESSAGEBOX_ERROR') +register('MESSAGEBOX_WARNING', 'SDL_MESSAGEBOX_WARNING') +register('MESSAGEBOX_INFORMATION', 'SDL_MESSAGEBOX_INFORMATION') +register('MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT', 'SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT') +register('MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT', 'SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT') +register('MESSAGEBOX_COLOR_BACKGROUND', 'SDL_MESSAGEBOX_COLOR_BACKGROUND') +register('MESSAGEBOX_COLOR_TEXT', 'SDL_MESSAGEBOX_COLOR_TEXT') +register('MESSAGEBOX_COLOR_BUTTON_BORDER', 'SDL_MESSAGEBOX_COLOR_BUTTON_BORDER') +register('MESSAGEBOX_COLOR_BUTTON_BACKGROUND', 'SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND') +register('MESSAGEBOX_COLOR_BUTTON_SELECTED', 'SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED') +register('MESSAGEBOX_COLOR_MAX', 'SDL_MESSAGEBOX_COLOR_MAX') +register('MESSAGEBOX_COLOR_MAX', 'SDL_MESSAGEBOX_COLOR_MAX') +register('POWERSTATE_UNKNOWN', 'SDL_POWERSTATE_UNKNOWN') +register('POWERSTATE_ON_BATTERY', 'SDL_POWERSTATE_ON_BATTERY') +register('POWERSTATE_NO_BATTERY', 'SDL_POWERSTATE_NO_BATTERY') +register('POWERSTATE_CHARGING', 'SDL_POWERSTATE_CHARGING') +register('POWERSTATE_CHARGED', 'SDL_POWERSTATE_CHARGED') +register('RENDERER_SOFTWARE', 'SDL_RENDERER_SOFTWARE') +register('RENDERER_ACCELERATED', 'SDL_RENDERER_ACCELERATED') +register('RENDERER_PRESENTVSYNC', 'SDL_RENDERER_PRESENTVSYNC') +register('RENDERER_TARGETTEXTURE', 'SDL_RENDERER_TARGETTEXTURE') +register('TEXTUREACCESS_STATIC', 'SDL_TEXTUREACCESS_STATIC') +register('TEXTUREACCESS_STREAMING', 'SDL_TEXTUREACCESS_STREAMING') +register('TEXTUREACCESS_TARGET', 'SDL_TEXTUREACCESS_TARGET') +register('TEXTUREMODULATE_NONE', 'SDL_TEXTUREMODULATE_NONE') +register('TEXTUREMODULATE_COLOR', 'SDL_TEXTUREMODULATE_COLOR') +register('TEXTUREMODULATE_ALPHA', 'SDL_TEXTUREMODULATE_ALPHA') +register('FLIP_NONE', 'SDL_FLIP_NONE') +register('FLIP_HORIZONTAL', 'SDL_FLIP_HORIZONTAL') +register('FLIP_VERTICAL', 'SDL_FLIP_VERTICAL') +register('INIT_TIMER', 'SDL_INIT_TIMER') +register('INIT_AUDIO', 'SDL_INIT_AUDIO') +register('INIT_VIDEO', 'SDL_INIT_VIDEO') +register('INIT_JOYSTICK', 'SDL_INIT_JOYSTICK') +register('INIT_HAPTIC', 'SDL_INIT_HAPTIC') +register('INIT_GAMECONTROLLER', 'SDL_INIT_GAMECONTROLLER') +register('INIT_EVENTS', 'SDL_INIT_EVENTS') +register('INIT_NOPARACHUTE', 'SDL_INIT_NOPARACHUTE') +register('INIT_EVERYTHING', 'SDL_INIT_EVERYTHING') +register('INIT_TIMER', 'SDL_INIT_TIMER') +register('INIT_AUDIO', 'SDL_INIT_AUDIO') +register('INIT_VIDEO', 'SDL_INIT_VIDEO') +register('INIT_EVENTS', 'SDL_INIT_EVENTS') +register('INIT_JOYSTICK', 'SDL_INIT_JOYSTICK') +register('INIT_HAPTIC', 'SDL_INIT_HAPTIC') +register('INIT_GAMECONTROLLER', 'SDL_INIT_GAMECONTROLLER') +register('AUDIO_MASK_BITSIZE', 'SDL_AUDIO_MASK_BITSIZE') +register('AUDIO_MASK_DATATYPE', 'SDL_AUDIO_MASK_DATATYPE') +register('AUDIO_MASK_ENDIAN', 'SDL_AUDIO_MASK_ENDIAN') +register('AUDIO_MASK_SIGNED', 'SDL_AUDIO_MASK_SIGNED') +register('AUDIO_U8', 'SDL_AUDIO_U8') +register('AUDIO_S8', 'SDL_AUDIO_S8') +register('AUDIO_U16LSB', 'SDL_AUDIO_U16LSB') +register('AUDIO_S16LSB', 'SDL_AUDIO_S16LSB') +register('AUDIO_U16MSB', 'SDL_AUDIO_U16MSB') +register('AUDIO_S16MSB', 'SDL_AUDIO_S16MSB') +register('AUDIO_U16', 'SDL_AUDIO_U16') +register('AUDIO_U16LSB', 'SDL_AUDIO_U16LSB') +register('AUDIO_S16', 'SDL_AUDIO_S16') +register('AUDIO_S16LSB', 'SDL_AUDIO_S16LSB') +register('AUDIO_S32LSB', 'SDL_AUDIO_S32LSB') +register('AUDIO_S32MSB', 'SDL_AUDIO_S32MSB') +register('AUDIO_S32', 'SDL_AUDIO_S32') +register('AUDIO_S32LSB', 'SDL_AUDIO_S32LSB') +register('AUDIO_F32LSB', 'SDL_AUDIO_F32LSB') +register('AUDIO_F32MSB', 'SDL_AUDIO_F32MSB') +register('AUDIO_F32', 'SDL_AUDIO_F32') +register('AUDIO_F32LSB', 'SDL_AUDIO_F32LSB') +register('AUDIO_ALLOW_FREQUENCY_CHANGE', 'SDL_AUDIO_ALLOW_FREQUENCY_CHANGE') +register('AUDIO_ALLOW_FORMAT_CHANGE', 'SDL_AUDIO_ALLOW_FORMAT_CHANGE') +register('AUDIO_ALLOW_CHANNELS_CHANGE', 'SDL_AUDIO_ALLOW_CHANNELS_CHANGE') +register('AUDIO_ALLOW_ANY_CHANGE', 'SDL_AUDIO_ALLOW_ANY_CHANGE') +register('AUDIO_ALLOW_FREQUENCY_CHANGE', 'SDL_AUDIO_ALLOW_FREQUENCY_CHANGE') +register('AUDIO_ALLOW_FORMAT_CHANGE', 'SDL_AUDIO_ALLOW_FORMAT_CHANGE') +register('AUDIO_ALLOW_CHANNELS_CHANGE', 'SDL_AUDIO_ALLOW_CHANNELS_CHANGE') +register('MIX_MAXVOLUME', 'SDL_MIX_MAXVOLUME') +register('RELEASED', 'SDL_RELEASED') +register('PRESSED', 'SDL_PRESSED') +register('QUERY', 'SDL_QUERY') +register('IGNORE', 'SDL_IGNORE') +register('DISABLE', 'SDL_DISABLE') +register('ENABLE', 'SDL_ENABLE') +register('HAPTIC_CONSTANT', 'SDL_HAPTIC_CONSTANT') +register('HAPTIC_SINE', 'SDL_HAPTIC_SINE') +register('HAPTIC_LEFTRIGHT', 'SDL_HAPTIC_LEFTRIGHT') +register('HAPTIC_TRIANGLE', 'SDL_HAPTIC_TRIANGLE') +register('HAPTIC_SAWTOOTHUP', 'SDL_HAPTIC_SAWTOOTHUP') +register('HAPTIC_SAWTOOTHDOWN', 'SDL_HAPTIC_SAWTOOTHDOWN') +register('HAPTIC_RAMP', 'SDL_HAPTIC_RAMP') +register('HAPTIC_SPRING', 'SDL_HAPTIC_SPRING') +register('HAPTIC_DAMPER', 'SDL_HAPTIC_DAMPER') +register('HAPTIC_INERTIA', 'SDL_HAPTIC_INERTIA') +register('HAPTIC_FRICTION', 'SDL_HAPTIC_FRICTION') +register('HAPTIC_CUSTOM', 'SDL_HAPTIC_CUSTOM') +register('HAPTIC_GAIN', 'SDL_HAPTIC_GAIN') +register('HAPTIC_AUTOCENTER', 'SDL_HAPTIC_AUTOCENTER') +register('HAPTIC_STATUS', 'SDL_HAPTIC_STATUS') +register('HAPTIC_PAUSE', 'SDL_HAPTIC_PAUSE') +register('HAPTIC_POLAR', 'SDL_HAPTIC_POLAR') +register('HAPTIC_CARTESIAN', 'SDL_HAPTIC_CARTESIAN') +register('HAPTIC_SPHERICAL', 'SDL_HAPTIC_SPHERICAL') +register('HAPTIC_INFINITY', 'SDL_HAPTIC_INFINITY') +register('HAT_CENTERED', 'SDL_HAT_CENTERED') +register('HAT_UP', 'SDL_HAT_UP') +register('HAT_RIGHT', 'SDL_HAT_RIGHT') +register('HAT_DOWN', 'SDL_HAT_DOWN') +register('HAT_LEFT', 'SDL_HAT_LEFT') +register('HAT_RIGHTUP', 'SDL_HAT_RIGHTUP') +register('HAT_RIGHT', 'SDL_HAT_RIGHT') +register('HAT_UP', 'SDL_HAT_UP') +register('HAT_RIGHTDOWN', 'SDL_HAT_RIGHTDOWN') +register('HAT_RIGHT', 'SDL_HAT_RIGHT') +register('HAT_DOWN', 'SDL_HAT_DOWN') +register('HAT_LEFTUP', 'SDL_HAT_LEFTUP') +register('HAT_LEFT', 'SDL_HAT_LEFT') +register('HAT_UP', 'SDL_HAT_UP') +register('HAT_LEFTDOWN', 'SDL_HAT_LEFTDOWN') +register('HAT_LEFT', 'SDL_HAT_LEFT') +register('HAT_DOWN', 'SDL_HAT_DOWN') +register('SCANCODE_MASK', 'SDL_SCANCODE_MASK') +register('KMOD_CTRL', 'SDL_KMOD_CTRL') +register('KMOD_LCTRL', 'SDL_KMOD_LCTRL') +register('KMOD_RCTRL', 'SDL_KMOD_RCTRL') +register('KMOD_SHIFT', 'SDL_KMOD_SHIFT') +register('KMOD_LSHIFT', 'SDL_KMOD_LSHIFT') +register('KMOD_RSHIFT', 'SDL_KMOD_RSHIFT') +register('KMOD_ALT', 'SDL_KMOD_ALT') +register('KMOD_LALT', 'SDL_KMOD_LALT') +register('KMOD_RALT', 'SDL_KMOD_RALT') +register('KMOD_GUI', 'SDL_KMOD_GUI') +register('KMOD_LGUI', 'SDL_KMOD_LGUI') +register('KMOD_RGUI', 'SDL_KMOD_RGUI') +register('BUTTON_LEFT', 'SDL_BUTTON_LEFT') +register('BUTTON_MIDDLE', 'SDL_BUTTON_MIDDLE') +register('BUTTON_RIGHT', 'SDL_BUTTON_RIGHT') +register('BUTTON_X1', 'SDL_BUTTON_X1') +register('BUTTON_X2', 'SDL_BUTTON_X2') +register('BUTTON_LMASK', 'SDL_BUTTON_LMASK') +register('BUTTON_LEFT-1', 'SDL_BUTTON_LEFT-1') +register('BUTTON_MMASK', 'SDL_BUTTON_MMASK') +register('BUTTON_MIDDLE-1', 'SDL_BUTTON_MIDDLE-1') +register('BUTTON_RMASK', 'SDL_BUTTON_RMASK') +register('BUTTON_RIGHT-1', 'SDL_BUTTON_RIGHT-1') +register('BUTTON_X1MASK', 'SDL_BUTTON_X1MASK') +register('BUTTON_X1-1', 'SDL_BUTTON_X1-1') +register('BUTTON_X2MASK', 'SDL_BUTTON_X2MASK') +register('BUTTON_X2-1', 'SDL_BUTTON_X2-1') +register('MUTEX_TIMEDOUT', 'SDL_MUTEX_TIMEDOUT') +register('MUTEX_MAXWAIT', 'SDL_MUTEX_MAXWAIT') +register('ALPHA_OPAQUE', 'SDL_ALPHA_OPAQUE') +register('ALPHA_TRANSPARENT', 'SDL_ALPHA_TRANSPARENT') +register('RWOPS_UNKNOWN', 'SDL_RWOPS_UNKNOWN') +register('RWOPS_WINFILE', 'SDL_RWOPS_WINFILE') +register('RWOPS_STDFILE', 'SDL_RWOPS_STDFILE') +register('RWOPS_JNIFILE', 'SDL_RWOPS_JNIFILE') +register('RWOPS_MEMORY', 'SDL_RWOPS_MEMORY') +register('RWOPS_MEMORY_RO', 'SDL_RWOPS_MEMORY_RO') +register('NONSHAPEABLE_WINDOW', 'SDL_NONSHAPEABLE_WINDOW') +register('INVALID_SHAPE_ARGUMENT', 'SDL_INVALID_SHAPE_ARGUMENT') +register('WINDOW_LACKS_SHAPE', 'SDL_WINDOW_LACKS_SHAPE') +register('SWSURFACE', 'SDL_SWSURFACE') +register('PREALLOC', 'SDL_PREALLOC') +register('RLEACCEL', 'SDL_RLEACCEL') +register('DONTFREE', 'SDL_DONTFREE') +register('WINDOWPOS_CENTERED_MASK', 'SDL_WINDOWPOS_CENTERED_MASK') +register('WINDOWPOS_CENTERED', 'SDL_WINDOWPOS_CENTERED') + +registerdefines(sdl) + +return sdl diff --git a/src/lib/luigi/backend/ffisdl/spritebatch.lua b/src/lib/luigi/backend/ffisdl/spritebatch.lua new file mode 100644 index 0000000..3adb571 --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/spritebatch.lua @@ -0,0 +1,79 @@ +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 + +function SpriteBatch:clear () + 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 diff --git a/src/lib/luigi/backend/ffisdl/text.lua b/src/lib/luigi/backend/ffisdl/text.lua new file mode 100644 index 0000000..ba5b3ac --- /dev/null +++ b/src/lib/luigi/backend/ffisdl/text.lua @@ -0,0 +1,104 @@ +local ROOT = (...):gsub('[^.]*.[^.]*.[^.]*$', '') +local REL = (...):gsub('[^.]*$', '') + +local ffi = require 'ffi' +local sdl = require(REL .. 'sdl') +local Font = require(REL .. 'font') +local ttf = Font.SDL2_ttf + +local Multiline = require(ROOT .. 'multiline') + +local Text = setmetatable({}, { __call = function (self, ...) + local object = setmetatable({}, { __index = self }) + return object, self.constructor(object, ...) +end }) + +local function renderSingle (self, font, text, color) + local alphaMod = color and color[4] + color = sdl.Color(color or 0) + local surface = ffi.gc( + ttf.TTF_RenderUTF8_Blended(font.sdlFont, text, color), + sdl.freeSurface) + self.sdlSurface = surface + self.sdlTexture = ffi.gc( + sdl.createTextureFromSurface(self.sdlRenderer, surface), + sdl.destroyTexture) + if alphaMod then + sdl.setTextureAlphaMod(self.sdlTexture, alphaMod) + end + self.width, self.height = surface.w, surface.h +end + +local function renderMulti (self, font, text, color, align, limit) + local alphaMod = color and color[4] + local lines = Multiline.wrap(font, text, limit) + local lineHeight = font:getLineHeight() + local height = #lines * lineHeight + color = sdl.Color(color or 0) + + -- mask values from SDL_ttf.c + -- TODO: something with sdl.BYTEORDER == sdl.BIG_ENDIAN ? + local r, g, b, a = 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 + + local surface = ffi.gc( + sdl.createRGBSurface(sdl.SWSURFACE, limit, height, 32, r, g, b, a), + sdl.freeSurface) + self.sdlSurface = surface + + for index, line in ipairs(lines) do + local text = table.concat(line) + local lineSurface = ffi.gc( + ttf.TTF_RenderUTF8_Blended(font.sdlFont, text, color), + sdl.freeSurface) + if lineSurface ~= nil then + sdl.setSurfaceBlendMode(lineSurface, sdl.BLENDMODE_NONE) + + local w, h = lineSurface.w, lineSurface.h + local top = (index - 1) * lineHeight + + if align == 'left' then + sdl.blitSurface(lineSurface, nil, surface, + sdl.Rect(0, top, w, h)) + elseif align == 'right' then + sdl.blitSurface(lineSurface, nil, surface, + sdl.Rect(limit - line.width, top, w, h)) + elseif align == 'center' then + sdl.blitSurface(lineSurface, nil, surface, + sdl.Rect((limit - line.width) / 2, top, w, h)) + end + end + end + + self.sdlTexture = ffi.gc( + sdl.createTextureFromSurface(self.sdlRenderer, surface), + sdl.destroyTexture) + + if alphaMod then + sdl.setTextureAlphaMod(self.sdlTexture, alphaMod) + end + + self.width, self.height = limit, height +end + +function Text:constructor (renderer, font, text, color, align, limit) + self.width, self.height = 0, 0 + if not text or text == '' then return end + + self.sdlRenderer = renderer + + if limit then + renderMulti(self, font, text, color, align, limit) + else + renderSingle(self, font, text, color) + end +end + +function Text:getWidth () + return self.width +end + +function Text:getHeight () + return self.height +end + +return Text diff --git a/src/lib/luigi/backend/love.lua b/src/lib/luigi/backend/love.lua new file mode 100644 index 0000000..8c44a9e --- /dev/null +++ b/src/lib/luigi/backend/love.lua @@ -0,0 +1,177 @@ +local ROOT = (...):gsub('[^.]*.[^.]*$', '') + +local Base = require(ROOT .. 'base') +local Hooker = require(ROOT .. 'hooker') + +local Backend = {} + +Backend.isMac = function () + return love.system.getOS() == 'OS X' +end + +Backend.run = function () end + +Backend.Cursor = love.mouse.newCursor + +Backend.Font = require(ROOT .. 'backend.love.font') + +Backend.Text = require(ROOT .. 'backend.love.text') + +Backend.Image = love.graphics.newImage + +Backend.Quad = love.graphics.newQuad + +Backend.SpriteBatch = love.graphics.newSpriteBatch + +-- love.graphics.draw( drawable, x, y, r, sx, sy, ox, oy, kx, ky ) +Backend.draw = function (drawable, ...) + if drawable.typeOf and drawable:typeOf 'Drawable' then + return love.graphics.draw(drawable, ...) + end + return drawable:draw(...) +end + +Backend.drawRectangle = love.graphics.rectangle + +Backend.print = love.graphics.print + +Backend.getClipboardText = love.system.getClipboardText + +Backend.setClipboardText = love.system.setClipboardText + +Backend.getMousePosition = love.mouse.getPosition + +Backend.setMousePosition = love.mouse.setPosition + +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 = function () + love.event.quit() +end + +if _G.love._version_major >= 11 then + Backend.setColor = function(r, g, b, a) + if type(r) == "table" then + r, g, b, a = r[1], r[2], r[3], r[4] + end + if a == nil then + a = 255 + end + love.graphics.setColor(r / 255, g / 255, b / 255, a / 255) + end +else + Backend.setColor = love.graphics.setColor +end + +Backend.setCursor = love.mouse.setCursor + +Backend.setFont = function (font) + return love.graphics.setFont(font.loveFont) +end + +Backend.setScissor = love.graphics.setScissor + +Backend.getScissor = love.graphics.getScissor + +Backend.intersectScissor = love.graphics.intersectScissor + +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_major == 0 and love._version_minor < 10 then + getMouseButtonId = function (value) + return value == 'l' and 'left' + or value == 'r' and 'right' + or value == 'm' and 'middle' + or value == 'x1' and 4 + or value == 'x2' and 5 + or value + end + isMouseDown = function () + return love.mouse.isDown('l', 'r', 'm') + end +else + getMouseButtonId = function (value) + return value == 1 and 'left' + or value == 2 and 'right' + or value == 3 and 'middle' + or 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) + if button == 'wu' or button == 'wd' then + return input:handleWheelMove(layout, 0, button == 'wu' and 1 or -1) + end + 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, sc, isRepeat) + if key == ' ' then key = 'space' end + return input:handleKeyPress(layout, key, sc, Backend.getMousePosition()) + end) + hook(layout, 'keyreleased', function (key, sc) + if key == ' ' then key = 'space' end + return input:handleKeyRelease(layout, key, sc, Backend.getMousePosition()) + end) + hook(layout, 'textinput', function (text) + return input:handleTextInput(layout, text, Backend.getMousePosition()) + end) + if (love._version_major == 0 and love._version_minor > 9) or love._version_major >= 11 then + hook(layout, 'wheelmoved', function (x, y) + return input:handleWheelMove(layout, x, y) + end) + end +end + +return Backend diff --git a/src/lib/luigi/backend/love/font.lua b/src/lib/luigi/backend/love/font.lua new file mode 100644 index 0000000..37f8562 --- /dev/null +++ b/src/lib/luigi/backend/love/font.lua @@ -0,0 +1,53 @@ +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 + +return Font diff --git a/src/lib/luigi/backend/love/text.lua b/src/lib/luigi/backend/love/text.lua new file mode 100644 index 0000000..86b9a6c --- /dev/null +++ b/src/lib/luigi/backend/love/text.lua @@ -0,0 +1,75 @@ +local ROOT = (...):gsub('[^.]*.[^.]*.[^.]*$', '') +local REL = (...):gsub('[^.]*$', '') + +local Multiline = require(ROOT .. 'multiline') + +local Text = setmetatable({}, { __call = function (self, ...) + local object = setmetatable({}, { __index = self }) + return object, self.constructor(object, ...) +end }) + +local function renderSingle (self, x, y, font, text, color) + love.graphics.push('all') + love.graphics.setColor(color or { 0, 0, 0 }) + love.graphics.setFont(font.loveFont) + love.graphics.print(text, math.floor(x), math.floor(y)) + love.graphics.pop() + + self.height = font:getLineHeight() + self.width = font:getAdvance(text) +end + +local function renderMulti (self, x, y, font, text, color, align, limit) + local lines = Multiline.wrap(font, text, limit) + local lineHeight = font:getLineHeight() + local height = #lines * lineHeight + + love.graphics.push('all') + love.graphics.setColor(color or { 0, 0, 0 }) + love.graphics.setFont(font.loveFont) + + for index, line in ipairs(lines) do + local text = table.concat(line) + local top = (index - 1) * lineHeight + local w = line.width + + if align == 'left' then + love.graphics.print(text, + math.floor(x), math.floor(top + y)) + elseif align == 'right' then + love.graphics.print(text, + math.floor(limit - w + x), math.floor(top + y)) + elseif align == 'center' then + love.graphics.print(text, + math.floor((limit - w) / 2 + x), math.floor(top + y)) + end + end + + love.graphics.pop() + + self.height = height + self.width = limit +end + +function Text:constructor (font, text, color, align, limit) + if limit then + function self:draw (x, y) + return renderMulti(self, x, y, font, text, color, align, limit) + end + else + function self:draw (x, y) + return renderSingle(self, x, y, font, text, color) + end + end + self:draw(-1000000, -1000000) +end + +function Text:getWidth () + return self.width +end + +function Text:getHeight () + return self.height +end + +return Text diff --git a/src/lib/luigi/base.lua b/src/lib/luigi/base.lua new file mode 100644 index 0000000..8663cb1 --- /dev/null +++ b/src/lib/luigi/base.lua @@ -0,0 +1,13 @@ +return { + extend = function (self, subtype) + return setmetatable(subtype or {}, { + __index = self, + __call = function (self, ...) + local instance = setmetatable({}, { __index = self }) + return instance, instance:constructor(...) + end + }) + end, + constructor = function () end, +} + diff --git a/src/lib/luigi/engine/alpha.lua b/src/lib/luigi/engine/alpha.lua new file mode 100644 index 0000000..df565a5 --- /dev/null +++ b/src/lib/luigi/engine/alpha.lua @@ -0,0 +1,251 @@ +local RESOURCE = (...):gsub('%.', '/') .. '/' + +return function (config) + config = config or {} + local resources = assert(config.resources, 'missing config.resources') + local backColor = config.backColor or { 240, 240, 240 } + local lineColor = config.lineColor or { 220, 220, 220 } + local textColor = config.textColor or { 0, 0, 0 } + local highlight = config.highlight or { 0x19, 0xAE, 0xFF } + + local button_pressed = resources .. 'button_pressed.png' + local button_focused = resources .. 'button_focused.png' + local button_hovered = resources .. 'button_hovered.png' + local button = resources .. 'button.png' + + local check_checked_pressed = resources .. 'check_checked_pressed.png' + local check_unchecked_pressed = resources .. 'check_unchecked_pressed.png' + local check_checked_focused = resources .. 'check_checked_focused.png' + local check_unchecked_focused = resources .. 'check_unchecked_focused.png' + local check_checked = resources .. 'check_checked.png' + local check_unchecked = resources .. 'check_unchecked.png' + + local radio_checked_pressed = resources .. 'radio_checked_pressed.png' + local radio_unchecked_pressed = resources .. 'radio_unchecked_pressed.png' + local radio_checked_focused = resources .. 'radio_checked_focused.png' + local radio_unchecked_focused = resources .. 'radio_unchecked_focused.png' + local radio_checked = resources .. 'radio_checked.png' + local radio_unchecked = resources .. 'radio_unchecked.png' + + local triangle_left = resources .. 'triangle_left.png' + local triangle_up = resources .. 'triangle_up.png' + local triangle_right = resources .. 'triangle_right.png' + local triangle_down = resources .. 'triangle_down.png' + + local text_focused = resources .. 'text_focused.png' + local text = resources .. 'text.png' + + local function getButtonSlices (self) + return self.pressed.left and button_pressed + or self.focused and button_focused + or self.hovered and button_hovered + or button + end + + local function getCheckIcon (self) + if self.pressed.left then + return self.value and check_checked_pressed + or check_unchecked_pressed + end + if self.focused then + return self.value and check_checked_focused + or check_unchecked_focused + end + return self.value and check_checked or check_unchecked + end + + local function getControlHeight (self) + return self.flow == 'x' and self._defaultDimension + end + + local function getControlWidth (self) + return self.flow ~= 'x' and self._defaultDimension + end + + local function getMenuItemBackground (self) + return self.active and highlight + end + + local function getRadioIcon (self) + if self.pressed.left then + return self.value and radio_checked_pressed + or radio_unchecked_pressed + end + if self.focused then + return self.value and radio_checked_focused + or radio_unchecked_focused + end + return self.value and radio_checked or radio_unchecked + end + + local function getSashBackground (self) + return self.hovered and highlight or lineColor + end + + local function getSashHeight (self) + return self.parent and self.parent.flow ~= 'x' and 4 + end + + local function getSashWidth (self) + return self.parent and self.parent.flow == 'x' and 4 + end + + local function getSliderThumbWidth (self) + return self.parent.flow == 'x' and 32 or false + end + + local function getSliderThumbHeight (self) + return self.parent.flow ~= 'x' and 32 or false + end + + local function getStepperBeforeIcon (self) + return self.parent.flow == 'x' and triangle_left or triangle_up + end + + local function getStepperAfterIcon (self) + return self.parent.flow == 'x' and triangle_right or triangle_down + end + + local function getTextSlices (self) + return self.focused and text_focused or text + end + + return { + + -- generic types for widgets to inherit + + Control = { + flow = 'x', + height = getControlHeight, + width = getControlWidth, + color = textColor, + align = 'center middle', + margin = 2, + color = textColor, + solid = true, + _defaultDimension = 36, + }, + + Line = { + margin = 0, + padding = 4, + align = 'left middle', + _defaultDimension = 24, + }, + + -- widget types + + button = { + type = { 'Control' }, + padding = 6, + slices = getButtonSlices, + focusable = true, + }, + check = { + type = { 'Line', 'Control' }, + focusable = true, + icon = getCheckIcon, + }, + label = { + type = { 'Line', 'Control' }, + }, + menu = { + flow = 'x', + height = 24, + background = backColor, + color = textColor, + }, + ['menu.expander'] = { + icon = resources .. 'triangle_right.png', + }, + ['menu.item'] = { + padding = 4, + height = 24, + align = 'left middle', + background = getMenuItemBackground, + }, + panel = { + padding = 2, + background = backColor, + color = textColor, + solid = true, + }, + progress = { + type = { 'Control' }, + slices = resources .. 'button_pressed.png', + }, + ['progress.bar'] = { + slices = resources .. 'progress.png', + minwidth = 12, + minheight = 22, + }, + radio = { + type = { 'Line', 'Control' }, + focusable = true, + icon = getRadioIcon, + }, + sash = { + background = getSashBackground, + height = getSashHeight, + width = getSashWidth, + }, + slider = { + type = { 'Control' }, + slices = resources .. 'button_pressed.png', + }, + ['slider.thumb'] = { + type = { 'button' }, + align = 'middle center', + margin = 0, + width = getSliderThumbWidth, + height = getSliderThumbHeight, + }, + status = { + type = { 'Line', 'Control' }, + background = backColor, + color = textColor, + }, + stepper = { + type = { 'Control' }, + slices = resources .. 'button_pressed.png', + }, + ['stepper.after'] = { + type = { 'button' }, + icon = getStepperAfterIcon, + margin = 0, + minwidth = 32, + minheight = 32, + }, + ['stepper.before'] = { + type = { 'button' }, + icon = getStepperBeforeIcon, + margin = 0, + minwidth = 32, + minheight = 32, + }, + ['stepper.item'] = { + align = 'center middle', + color = textColor, + }, + ['stepper.view'] = { + margin = 4, + }, + submenu = { + padding = 10, + margin = -10, + slices = resources .. 'submenu.png', + color = textColor, + solid = true, + }, + text = { + type = { 'Control' }, + align = 'left middle', + slices = getTextSlices, + padding = 6, + focusable = true, + cursor = 'ibeam', + highlight = highlight, + }, + } + +end diff --git a/src/lib/luigi/event.lua b/src/lib/luigi/event.lua new file mode 100644 index 0000000..95af72e --- /dev/null +++ b/src/lib/luigi/event.lua @@ -0,0 +1,69 @@ +--[[-- +Event class. + +@classmod Event +--]]-- + +local ROOT = (...):gsub('[^.]*$', '') + +local Base = require(ROOT .. 'base') +local Hooker = require(ROOT .. 'hooker') + +local Event = Base:extend({ name = 'Event' }) + +function Event:emit (target, data, defaultAction) + local callbacks = self.registry[target] + local result = callbacks and callbacks(data or {}) + if result ~= nil then return result end + if defaultAction then defaultAction() end +end + +function Event:bind (target, callback) + local registry = self.registry + return Hooker.hook(registry, target, callback) +end + +--[[-- +Event names. +--]]-- +Event.names = { + 'Reshape', -- A widget is being reshaped. + 'PreDisplay', -- A widget is about to be drawn. + 'Display', -- A widget was drawn. + 'KeyPress', -- A keyboard key was pressed. + 'KeyRelease', -- A keyboard key was released. + 'TextInput', -- Text was entered. + 'Move', -- The cursor moved, and no button was pressed. + 'Focus', -- A widget received focus. + 'Blur', -- A widget lost focus. + 'Enter', -- The cursor entered a widget, and no button was pressed. + 'Leave', -- The cursor left a widget, and no button was pressed. + 'PressEnter', -- The cursor entered a widget, and a button was pressed. + 'PressLeave', -- The cursor left a widget, and a button was pressed. + 'PressStart', -- A pointer button or keyboard shortcut was pressed. + 'PressEnd', -- A pointer button or keyboard shortcut was released. + 'PressDrag', -- A pressed cursor moved; targets originating widget. + 'PressMove', -- A pressed cursor moved; targets widget at cursor position. + 'Press', -- A pointer button was pressed and released on the same widget. + 'Change', -- A widget's value changed. + 'WheelMove', -- The scroll wheel on the mouse moved. + 'Show', -- A layout is shown. + 'Hide', -- A layout is hidden. +} + +local weakKeyMeta = { __mode = 'k' } + +for i, name in ipairs(Event.names) do + Event[name] = Event:extend({ + name = name, + registry = setmetatable({}, weakKeyMeta), + }) +end + +function Event.injectBinders (t) + for i, name in ipairs(Event.names) do + t['on' .. name] = function (...) return Event[name]:bind(...) end + end +end + +return Event diff --git a/src/lib/luigi/hooker.lua b/src/lib/luigi/hooker.lua new file mode 100644 index 0000000..44d3312 --- /dev/null +++ b/src/lib/luigi/hooker.lua @@ -0,0 +1,91 @@ +local Hooker = {} + +local wrapped = setmetatable({}, { __mode = 'k' }) + +local hooks = setmetatable({}, { __mode = 'k' }) + +local function unhook (item) + if item.prev then + item.prev.next = item.next + end + + if item.next then + item.next.prev = item.prev + end + + if hooks[item.host][item.key] == item then + hooks[item.host][item.key] = item.next + end +end + +local function hook (host, key, func, atEnd) + if not func then + return + end + + if not hooks[host] then + hooks[host] = {} + end + + local current = hooks[host][key] + local item = { + next = not atEnd and current or nil, + unhook = unhook, + host = host, + key = key, + func = func, + } + + if atEnd then + if current then + while current.next do + current = current.next + end + current.next = item + item.prev = current + else + hooks[host][key] = item + end + return item + end + + if current then + current.prev = item + end + + hooks[host][key] = item + + return item +end + +function Hooker.unhook (item) + return unhook(item) +end + +function Hooker.hook (host, key, func, atEnd) + if not wrapped[host] then + wrapped[host] = {} + end + + if not wrapped[host][key] then + wrapped[host][key] = true + + hook(host, key, host[key]) + + host[key] = function (...) + local item = hooks[host][key] + + while item do + local result = item.func(...) + if result ~= nil then + return result + end + item = item.next + end -- while + end -- function + end -- if + + return hook(host, key, func, atEnd) +end + +return Hooker diff --git a/src/lib/luigi/input.lua b/src/lib/luigi/input.lua new file mode 100644 index 0000000..981c44e --- /dev/null +++ b/src/lib/luigi/input.lua @@ -0,0 +1,232 @@ +local ROOT = (...):gsub('[^.]*$', '') + +local Backend = require(ROOT .. 'backend') +local Base = require(ROOT .. 'base') +local Event = require(ROOT .. 'event') +local Shortcut = require(ROOT .. 'shortcut') + +local Input = Base:extend() + +local weakValueMeta = { __mode = 'v' } + +function Input:constructor () + self.pressedWidgets = setmetatable({}, weakValueMeta) + self.passedWidgets = setmetatable({}, weakValueMeta) +end + +function Input:handleDisplay (layout) + local root = layout.root + if root then root:paint() end + Event.Display:emit(layout) +end + +function Input:handleKeyPress (layout, key, sc, x, y) + local widget = layout.focusedWidget or layout.root + local result = widget:bubbleEvent('KeyPress', { + key = key, + scanCode = sc, + modifierFlags = Shortcut.getModifierFlags(), + x = x, y = y + }) + if result ~= nil then return result end + if layout.root.modal then return false end +end + +function Input:handleKeyRelease (layout, key, sc, x, y) + local widget = layout.focusedWidget or layout.root + local result = widget:bubbleEvent('KeyRelease', { + key = key, + scanCode = sc, + modifierFlags = Shortcut.getModifierFlags(), + x = x, y = y + }) + if result ~= nil then return result end + if layout.root.modal then return false end +end + +function Input:handleTextInput (layout, text, x, y) + local widget = layout.focusedWidget or layout.root + local result = widget:bubbleEvent('TextInput', { + text = text, + x = x, y = y + }) + if result ~= nil then return result end + if layout.root.modal then return false end +end + +local function checkHit (widget, layout) + local root = layout.root + return widget and widget.solid or root.modal, widget or root +end + +function Input:handleMove (layout, x, y) + local hit, widget = checkHit(layout:getWidgetAt(x, y), layout) + local previousWidget = self.previousMoveWidget + if widget ~= previousWidget then + if previousWidget then + for ancestor in previousWidget:eachAncestor(true) do + ancestor.hovered = nil + end + end + for ancestor in widget:eachAncestor(true) do + ancestor.hovered = true + end + end + widget:bubbleEvent('Move', { + hit = hit, + oldTarget = previousWidget, + x = x, y = y + }) + if widget ~= previousWidget then + if previousWidget then + previousWidget:bubbleEvent('Leave', { + hit = hit, + newTarget = widget, + x = x, y = y + }) + end + widget:bubbleEvent('Enter', { + hit = hit, + oldTarget = previousWidget, + x = x, y = y + }) + if widget.cursor then + Backend.setCursor(Backend.getSystemCursor(widget.cursor)) + else + Backend.setCursor() + end + self.previousMoveWidget = widget + end + return hit +end + +function Input:handlePressedMove (layout, x, y) + local hit, widget = checkHit(layout:getWidgetAt(x, y), layout) + for _, button in ipairs { 'left', 'middle', 'right' } do + local originWidget = self.pressedWidgets[button] + if originWidget then + local passedWidget = self.passedWidgets[button] + originWidget:bubbleEvent('PressDrag', { + hit = hit, + newTarget = widget, + button = button, + x = x, y = y + }) + if (widget == passedWidget) then + widget:bubbleEvent('PressMove', { + hit = hit, + origin = originWidget, + button = button, + x = x, y = y + }) + else + originWidget.pressed[button] = (widget == originWidget) or nil + if passedWidget then + passedWidget:bubbleEvent('PressLeave', { + hit = hit, + newTarget = widget, + origin = originWidget, + button = button, + x = x, y = y + }) + end + widget:bubbleEvent('PressEnter', { + hit = hit, + oldTarget = passedWidget, + origin = originWidget, + button = button, + x = x, y = y + }) + self.passedWidgets[button] = widget + end + end -- if originWidget + end -- mouse buttons + return hit +end + +function Input:handlePressStart (layout, button, x, y, widget, shortcut) + local hit, widget = checkHit(widget or layout:getWidgetAt(x, y), layout) + -- if hit then + self.pressedWidgets[button] = widget + self.passedWidgets[button] = widget + widget.pressed[button] = true + if button == 'left' then + widget:focus() + end + -- end + widget:bubbleEvent('PressStart', { + hit = hit, + button = button, + shortcut = shortcut, + x = x, y = y + }) + return hit +end + +function Input:handlePressEnd (layout, button, x, y, widget, shortcut) + local originWidget = widget or self.pressedWidgets[button] + if not originWidget then return end + local hit, widget = checkHit(widget or layout:getWidgetAt(x, y), layout) + local wasPressed = originWidget.pressed[button] + if hit then + originWidget.pressed[button] = nil + end + widget:bubbleEvent('PressEnd', { + hit = hit, + origin = originWidget, + shortcut = shortcut, + button = button, + x = x, y = y + }) + if (widget == originWidget and wasPressed) then + widget:bubbleEvent('Press', { + hit = hit, + button = button, + shortcut = shortcut, + x = x, y = y + }) + end + if hit then + self.pressedWidgets[button] = nil + self.passedWidgets[button] = nil + end + return hit +end + +function Input:handleReshape (layout, width, height) + local root = layout.root + + root:reshape() + + if root.type ~= 'window' then -- FIXME: move stuff below to a Widget method + if not root.width then + root.dimensions.width = width + end + if not root.height then + root.dimensions.height = height + end + end + + Event.Reshape:emit(layout, { + target = layout, + width = width, + height = height + }) +end + +function Input:handleWheelMove (layout, scrollX, scrollY) + local x, y = Backend.getMousePosition() + local hit, widget = checkHit(layout:getWidgetAt(x, y), layout) + + widget:bubbleEvent('WheelMove', { + hit = hit, + x = x, y = y, + scrollX = scrollX, scrollY = scrollY + }) + + return hit +end + +Input.default = Input() + +return Input diff --git a/src/lib/luigi/launch.lua b/src/lib/luigi/launch.lua new file mode 100644 index 0000000..29259fa --- /dev/null +++ b/src/lib/luigi/launch.lua @@ -0,0 +1,54 @@ +--[[-- +Launcher for the LuaJIT backend. + +Looks for main.lua. Launch it like this: + + luajit myapp/lib/luigi/launch.lua + +If luigi isn't inside the project directory, pass +the path containing main.lua as the second argument. +The path must end with a directory separator. + + luajit /opt/luigi/launch.lua ./myapp/ + +If the app prefixes luigi modules with something +other then 'luigi', pass that prefix as the third +argument. + + luajit /opt/luigi/launch.lua ./myapp/ lib.luigi + +--]]-- +local packagePath = package.path +local libRoot = arg[0]:gsub('[^/\\]*%.lua$', '') +local appRoot, modPath = ... + +local function run (appRoot, modPath) + package.path = packagePath .. ';' .. appRoot .. '?.lua' + rawset(_G, 'LUIGI_APP_ROOT', appRoot) + require 'main' + require (modPath .. '.backend').run() +end + +-- expect to find main.lua in appRoot if specified +if appRoot then + return run(appRoot, modPath or 'luigi') +end + +-- try to find main.lua in a parent of this library, recursively. +local lastLibRoot = libRoot +repeat + if io.open(libRoot .. 'main.lua') then + return run(libRoot, modPath) + end + lastLibRoot = libRoot + libRoot = libRoot:gsub('([^/\\]*).$', function (m) + modPath = modPath and (m .. '.' .. modPath) or m + return '' + end) +until libRoot == lastLibRoot + +error 'main.lua not found' + + + + diff --git a/src/lib/luigi/layout.lua b/src/lib/luigi/layout.lua new file mode 100644 index 0000000..322218c --- /dev/null +++ b/src/lib/luigi/layout.lua @@ -0,0 +1,444 @@ +--[[-- +A Layout contains a tree of widgets with a single `root` widget. + +Layouts will resize to fit the window unless a `top` or `left` +property is found in the root widget. + +Layouts are drawn in the order that they were shown, so the +most recently shown layout shown will always appear on top. + +Other events are sent to layouts in the opposite direction, +and are trapped by the first layout that can handle the event +(for example, the topmost layer that is focused or hovered). + +@classmod Layout +--]]-- + +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 Layout = Base:extend() + +Layout.isLayout = true + +--[[-- +Layout constructor. + +@function Luigi.Layout + +@tparam table data +A tree of widget data. + +@treturn Layout +A Layout instance. +--]]-- +function Layout:constructor (data, master) + data = data or {} + + if master then + self:setMaster(master) + else + self:setTheme(require(ROOT .. 'theme.light')) + self:setStyle() + end + + self:addDefaultHandlers() + + self.hooks = {} + self.isShown = false + self.root = data + + Widget(self, data) + + self.isReady = true +end + +--[[-- +Create a detached widget. + +Internal function used to create widgets that are associated with +a layout, but "detached" from it. + +Used by context menus, which use their "owner" widget's layout +for theme and style information but appear in a separate layout. + +@tparam table data +A tree of widget data. + +@treturn Widget +A widget instance. +--]]-- +function Layout:createWidget (data) + return Widget(self, data) +end + +local function clearWidget (widget) + widget.textData = nil + widget.fontData = nil + widget.position = {} + widget.dimensions = {} + widget.type = widget.type + for _, child in ipairs(widget) do + clearWidget(child) + end + local items = widget.items + if items then + for _, item in ipairs(items) do + clearWidget(item) + end + end +end + +local function reset (self) + if not self.root then return end + clearWidget(self.root) +end + +--[[-- +Set the master layout for this layout. + +This layout's theme and style will be set the same as the master layout, and +widgets added to this layout will be indexed and keyboard-accelerated by the +master layout instead of this layout. + +@tparam Layout layout +Master layout + +@treturn Layout Self +--]]-- +function Layout:setMaster (layout) + self.master = layout + + reset(self) + return self +end + +--[[-- +Set the style from a definition table or function. + +@tparam table|function rules +Style definition. + +@treturn Layout Self +--]]-- +function Layout:setStyle (rules) + if type(rules) == 'function' then + rules = rules() + end + self.style = Style(rules or {}, { 'style' }) + + reset(self) + return self +end + +--[[-- +Set the theme from a definition table or function. + +@tparam table|function rules +Theme definition. +--]]-- +function Layout:setTheme (rules) + if type(rules) == 'function' then + rules = rules() + end + self.theme = Style(rules or {}, { 'type' }) + reset(self) + return self +end + +--[[-- +Get the style from master layout or this layout. + +@treturn table +Style table. +--]]-- +function Layout:getStyle () + return self.master and self.master:getStyle() or self.style +end + +--[[-- +Get the theme from master layout or this layout. + +@treturn table +Theme table. +--]]-- +function Layout:getTheme () + return self.master and self.master:getTheme() or self.theme +end + +--[[-- +Show the layout. + +Hooks all appropriate Love events and callbacks. + +@treturn Layout +Return this layout for chaining. +--]]-- +function Layout:show () + if self.isShown then + Backend.hide(self) + self.isShown = nil + end + + self.isShown = true + + if not self.input then + self.input = Input.default -- Input(self) + end + + Backend.show(self) + self.root:reshape() + + Event.Show:emit(self, self) + return self +end + +--[[-- +Hide the layout. + +Unhooks Love events and callbacks. + +@treturn Layout +Return this layout for chaining. +--]]-- +function Layout:hide () + if not self.isShown then + return + end + self.isShown = nil + Backend.hide(self) + + Event.Hide:emit(self, self) + return self +end + +--[[-- +Focus next focusable widget. + +Traverses widgets using Widget:getNextNeighbor until a focusable widget is +found, and focuses that widget. + +@treturn Widget +The widget that was focused, or nil +--]]-- +function Layout:focusNextWidget () + local widget = self.focusedWidget or self.root + local nextWidget = widget:getNextNeighbor() + + while nextWidget ~= widget do + if nextWidget:focus() then return nextWidget end + nextWidget = nextWidget:getNextNeighbor() + end +end + +--[[-- +Focus previous focusable widget. + +Traverses widgets using Widget:getPreviousNeighbor until a focusable widget is +found, and focuses that widget. + +@treturn Widget +The widget that was focused, or nil +--]]-- +function Layout:focusPreviousWidget () + local widget = self.focusedWidget or self.root + local previousWidget = widget:getPreviousNeighbor() + + while previousWidget ~= widget do + if previousWidget:focus() then return previousWidget end + previousWidget = previousWidget:getPreviousNeighbor() + end +end + +--[[-- +Get the innermost widget at given coordinates. + +@tparam number x +Number of pixels from window's left edge. + +@tparam number y +Number of pixels from window's top edge. + +@tparam[opt] Widget root +Widget to search within, defaults to layout root. +--]]-- +function Layout:getWidgetAt (x, y, root) + if not root then + root = self.root + end + -- Loop through in reverse, because siblings defined later in the tree + -- will overdraw earlier siblings. + for i = #root, 1, -1 do + local child = root[i] + if child:isAt(x, y) then + local inner = self:getWidgetAt(x, y, child) + if inner then return inner end + end + end + + if root:isAt(x, y) then return root end +end + +--[[-- +Place a layout near a point or rectangle. + +@tparam number left +Number of pixels from window's left edge. + +@tparam number top +Number of pixels from window's top edge. + +@tparam[opt] number width +Width of the rectangle to place layout outside of, defaults to 0. + +@tparam[opt] number height +Height of the rectangle to place layout outside of, defaults to 0. + +@treturn Layout +Return this layout for chaining. +--]]-- +function Layout:placeNear (left, top, width, height) + width, height = width or 0, height or 0 + local root = self.root + -- place to the left if there's no room to the right + local layoutWidth = root:getWidth() + local windowWidth, windowHeight = Backend.getWindowSize() + if left + width + layoutWidth > windowWidth then + left = left - layoutWidth - width + else + left = left + width + end + -- place above if there's no room below + local layoutHeight = root:getHeight() + if top + height + layoutHeight > windowHeight then + top = top - layoutHeight - height + else + top = top + height + end + root.left = left + root.top = top +end + + +-- Add handlers for keyboard shortcuts, tab focus, and mouse wheel scroll +function Layout:addDefaultHandlers () + self.shortcuts = {} + + for i = 0, 15 do + self.shortcuts[i] = {} + end + + self.behavior = {} + + local function createBehavior (name, hooks) + self.behavior[name] = hooks + function hooks.destroy () + for _, hook in ipairs(hooks) do + hook:unhook() + end + self.behavior[name] = nil + end + end + + createBehavior('context', { + self:onPressStart(function (event) + -- show context menu on right click + if event.button ~= 'right' then return end + local menu = event.target.context + if not menu then return end + menu:bubbleEvent('PressStart', event) + -- make sure it fits in the window + -- TODO: open in a new borderless window under SDL? + menu.menuLayout:placeNear(event.x - 1, event.y - 1, 2, 2) + return false + end) + }) + + createBehavior('shortcut', { + self:onKeyPress(function (event) + local entry = self.shortcuts[event.modifierFlags] + local widget = entry and entry[event.key] + if not widget then return end + widget.hovered = true + self.input:handlePressStart(self, 'left', event.x, event.y, + widget, widget.shortcut) + return false + end), + + self:onKeyRelease(function (event) + local entry = self.shortcuts[event.modifierFlags] + local widget = entry and entry[event.key] + if not widget then return end + widget.hovered = false + self.input:handlePressEnd(self, 'left', event.x, event.y, + widget, widget.shortcut) + return false + end) + }) + + createBehavior('navigate', { + self:onKeyPress(function (event) + -- tab/shift-tab cycles focused widget + if event.key == 'tab' then + if Backend.isKeyDown('lshift', 'rshift') then + self:focusPreviousWidget() + else + self:focusNextWidget() + end + return false + end + + -- space/enter presses focused widget + local widget = self.focusedWidget + if widget and event.key == 'space' or event.key == ' ' + or event.key == 'return' then + self.input:handlePressStart(self, 'left', event.x, event.y, + widget, event.key) + return false + end + end), + + self:onKeyRelease(function (event) + -- space / enter presses focused widget + local widget = self.focusedWidget + if widget and event.key == 'space' or event.key == ' ' + or event.key == 'return' then + self.input:handlePressEnd(self, 'left', event.x, event.y, + widget, event.key) + return false + end + end) + }) + + createBehavior('scroll', { + self:onWheelMove(function (event) + if not event.hit then return end + local amount = event.scrollY ~= 0 and event.scrollY or event.scrollX + for widget in event.target:eachAncestor(true) do + if widget:scrollBy(amount) then return false end + end -- ancestor loop + return false + end) -- wheel move + }) + + createBehavior('status', { + self:onEnter(function (event) + local statusWidget = (self.master or self).statusWidget + if not statusWidget then return end + + statusWidget.text = event.target.status + return false + end) + }) + +end + +Event.injectBinders(Layout) + +return Layout diff --git a/src/lib/luigi/mosaic.lua b/src/lib/luigi/mosaic.lua new file mode 100644 index 0000000..f01e688 --- /dev/null +++ b/src/lib/luigi/mosaic.lua @@ -0,0 +1,110 @@ +--[[-- +Mosiac, drawable class for 9-slice images. + +@classmod Mosiac +--]]-- +local ROOT = (...):gsub('[^.]*$', '') + +local Backend = require(ROOT .. 'backend') +local Base = require(ROOT .. 'base') + +local Mosaic = Base:extend() + +local imageCache = {} +local sliceCache = {} + +local function loadImage (path) + if not imageCache[path] then + imageCache[path] = Backend.Image(path) + end + + return imageCache[path] +end + +function Mosaic.fromWidget (widget) + local mosaic = widget.mosaic + if mosaic and mosaic.slicePath == widget.slices then + return mosaic + end + if widget.slices then + widget.mosaic = Mosaic(widget.slices) + return widget.mosaic + end +end + +function Mosaic:constructor (path) + local slices = self:loadSlices(path) + self.batch = Backend.SpriteBatch(slices.image) + self.slices = slices + self.slicePath = path +end + +function Mosaic:setRectangle (x, y, w, h) + if self.x == x and self.y == y and self.width == w and self.height == h then + self.needsRefresh = false + return + end + self.needsRefresh = true + self.x, self.y, self.width, self.height = x, y, w, h +end + +function Mosaic:loadSlices (path) + local slices = sliceCache[path] + + if not slices then + slices = {} + sliceCache[path] = slices + local image = loadImage(path) + local iw, ih = image:getWidth(), image:getHeight() + local w, h = math.floor(iw / 3), math.floor(ih / 3) + local Quad = Backend.Quad + + slices.image = image + slices.width = w + slices.height = h + + slices.topLeft = Quad(0, 0, w, h, iw, ih) + slices.topCenter = Quad(w, 0, w, h, iw, ih) + slices.topRight = Quad(iw - w, 0, w, h, iw, ih) + slices.middleLeft = Quad(0, h, w, h, iw, ih) + slices.middleCenter = Quad(w, h, w, h, iw, ih) + slices.middleRight = Quad(iw - w, h, w, h, iw, ih) + slices.bottomLeft = Quad(0, ih - h, w, h, iw, ih) + slices.bottomCenter = Quad(w, ih - h, w, h, iw, ih) + slices.bottomRight = Quad(iw - w, ih - h, w, h, iw, ih) + end + + return slices +end + +function Mosaic:draw () + local batch = self.batch + + if not self.needsRefresh then + Backend.draw(batch) + return + end + self.needsRefresh = false + + local x, y, w, h = self.x, self.y, self.width, self.height + local slices = self.slices + local sw, sh = slices.width, slices.height + local xs = (w - sw * 2) / sw -- x scale + local ys = (h - sh * 2) / sh -- y scale + + batch:clear() + + batch:add(slices.middleCenter, x + sw, y + sh, 0, xs, ys) + batch:add(slices.topCenter, x + sw, y, 0, xs, 1) + batch:add(slices.bottomCenter, x + sw, y + h - sh, 0, xs, 1) + batch:add(slices.middleLeft, x, y + sh, 0, 1, ys) + batch:add(slices.middleRight, x + w - sw, y + sh, 0, 1, ys) + batch:add(slices.topLeft, x, y) + batch:add(slices.topRight, x + w - sw, y) + batch:add(slices.bottomLeft, x, y + h - sh) + batch:add(slices.bottomRight, x + w - sw, y + h - sh) + + Backend.draw(batch) +end + +return Mosaic diff --git a/src/lib/luigi/multiline.lua b/src/lib/luigi/multiline.lua new file mode 100644 index 0000000..9340b76 --- /dev/null +++ b/src/lib/luigi/multiline.lua @@ -0,0 +1,57 @@ +local Multiline = {} + +function Multiline.wrap (font, text, limit) + local lines = {{ width = 0 }} + local advance = 0 + local lastSpaceAdvance = 0 + + local function append (word, space) + local wordAdvance = font:getAdvance(word) + local spaceAdvance = font:getAdvance(space) + local words = lines[#lines] + if advance + wordAdvance > limit then + words.width = (words.width or 0) - lastSpaceAdvance + advance = wordAdvance + spaceAdvance + lines[#lines + 1] = { width = advance, word, space } + else + advance = advance + wordAdvance + spaceAdvance + words.width = advance + words[#words + 1] = word + words[#words + 1] = space + end + lastSpaceAdvance = spaceAdvance + end + + local function appendFrag (frag, isFirst) + if isFirst then + append(frag, '') + else + local wordAdvance = font:getAdvance(frag) + lines[#lines + 1] = { width = wordAdvance, frag } + advance = wordAdvance + end + end + + local leadSpace = text:match '^ +' + + if leadSpace then + append('', leadSpace) + end + + for word, space in text:gmatch '([^ ]+)( *)' do + if word:match '\n' then + local isFirst = true + for frag in (word .. '\n'):gmatch '([^\n]*)\n' do + appendFrag(frag, isFirst) + isFirst = false + end + append('', space) + else + append(word, space) + end + end + + return lines +end + +return Multiline diff --git a/src/lib/luigi/painter.lua b/src/lib/luigi/painter.lua new file mode 100644 index 0000000..c6281de --- /dev/null +++ b/src/lib/luigi/painter.lua @@ -0,0 +1,237 @@ +local ROOT = (...):gsub('[^.]*$', '') + +local Backend = require(ROOT .. 'backend') +local Base = require(ROOT .. 'base') +local Event = require(ROOT .. 'event') +local Mosaic = require(ROOT .. 'mosaic') +local Text = Backend.Text + +local Painter = Base:extend() + +local imageCache = {} +-- local sliceCache = {} + +function Painter:constructor (widget) + self.widget = widget +end + +function Painter:loadImage (path) + if not imageCache[path] then + imageCache[path] = Backend.Image(path) + end + + return imageCache[path] +end + +function Painter:paintSlices () + local widget = self.widget + local mosaic = Mosaic.fromWidget(widget) + if not mosaic then return end + local x, y, w, h = widget:getRectangle(true) + mosaic:setRectangle(x, y, w, h) + mosaic:draw() +end + +function Painter:paintBackground () + local widget = self.widget + if not widget.background then return end + local x, y, w, h = widget:getRectangle(true) + + Backend.push() + Backend.setColor(widget.background) + Backend.drawRectangle('fill', x, y, w, h) + Backend.pop() +end + +function Painter:paintOutline () + local widget = self.widget + if not widget.outline then return end + local x, y, w, h = widget:getRectangle(true) + + Backend.push() + Backend.setColor(widget.outline) + Backend.drawRectangle('line', x + 0.5, y + 0.5, w, h) + Backend.pop() +end + +-- returns icon coordinates and rectangle with remaining space +function Painter:positionIcon (x1, y1, x2, y2) + local widget = self.widget + if not widget.icon then + return nil, nil, x1, y1, x2, y2 + end + + local icon = self:loadImage(widget.icon) + local iconWidth, iconHeight = icon:getWidth(), icon:getHeight() + local align = widget.align or '' + local padding = widget.padding or 0 + local x, y + + -- horizontal alignment + if align:find('right') then + x = x2 - iconWidth + x2 = x2 - iconWidth - padding + elseif align:find('center') then + x = x1 + (x2 - x1) / 2 - iconWidth / 2 + else -- if align:find('left') then + x = x1 + x1 = x1 + iconWidth + padding + end + + -- vertical alignment + if align:find('bottom') then + y = y2 - iconHeight + elseif align:find('middle') then + y = y1 + (y2 - y1) / 2 - iconHeight / 2 + else -- if align:find('top') then + y = y1 + end + + return x, y, x1, y1, x2, y2 +end + +-- returns text coordinates +function Painter:positionText (x1, y1, x2, y2) + local widget = self.widget + if not widget.text or x1 >= x2 then + return nil, nil, x1, y1, x2, y2 + end + + local font = widget:getFont() + local align = widget.align or '' + local horizontal = 'left' + + -- horizontal alignment + if align:find 'right' then + horizontal = 'right' + elseif align:find 'center' then + horizontal = 'center' + elseif align:find 'justify' then + horizontal = 'justify' + end + + if not widget.textData then + local limit = widget.wrap and x2 - x1 or nil + widget.textData = Text( + font, widget.text, widget.color, horizontal, limit) + end + + local textHeight = widget.textData:getHeight() + local y + + -- vertical alignment + if align:find('bottom') then + y = y2 - textHeight + elseif align:find('middle') then + y = y2 - (y2 - y1) / 2 - textHeight / 2 + else -- if align:find('top') then + y = y1 + end + + return font, x1, y +end + +function Painter:paintIconAndText () + local widget = self.widget + if not (widget.icon or widget.text) then return end + local x, y, w, h = widget:getRectangle(true, true) + if w < 1 or h < 1 then return end + + -- calculate position for icon and text based on alignment and padding + local iconX, iconY, x1, y1, x2, y2 = self:positionIcon(x, y, x + w, y + h) + local font, textX, textY = self:positionText(x1, y1, x2, y2) + + local icon = widget.icon and self:loadImage(widget.icon) + local text = widget.text + local align = widget.align or '' + local padding = widget.padding or 0 + + -- if aligned center, icon displays above the text + -- reposition icon and text for proper vertical alignment + if icon and text and align:find('center') then + local iconHeight = icon:getHeight() + + if align:find 'middle' then + local textHeight = widget.textData:getHeight() + local contentHeight = textHeight + padding + iconHeight + local offset = (h - contentHeight) / 2 + iconY = y + offset + textY = y + offset + padding + iconHeight + elseif align:find 'top' then + iconY = y + textY = y + padding + iconHeight + else -- if align:find 'bottom' + local textHeight = widget.textData:getHeight() + textY = y + h - textHeight + iconY = textY - padding - iconHeight + end + end + + -- horizontal alignment for non-wrapped text + -- TODO: handle this in Backend.Text + if text and not widget.wrap then + if align:find 'right' then + textX = textX + (w - widget.textData:getWidth()) + elseif align:find 'center' then + textX = textX + (w - widget.textData:getWidth()) / 2 + end + end + + Backend.push() + + Backend.intersectScissor(x, y, w, h) + + -- draw the icon + if icon then + iconX, iconY = math.floor(iconX), math.floor(iconY) + Backend.draw(icon, iconX, iconY) + end + + -- draw the text + if text and textX and textY and w > 1 then + widget.innerHeight = textY - y + widget.textData:getHeight() + widget.innerWidth = textX - x + widget.textData:getWidth() + textX = math.floor(textX - (widget.scrollX or 0)) + textY = math.floor(textY - (widget.scrollY or 0)) + Backend.draw(widget.textData, textX, textY) + end + + Backend.pop() +end + +function Painter:paintChildren () + for i, child in ipairs(self.widget) do + child:paint() + end +end + +function Painter:paint () + local widget = self.widget + local x, y, w, h = widget:getRectangle() + + -- if the drawable area has no width or height, don't paint + if w < 1 or h < 1 then return end + + Event.PreDisplay:emit(widget, { target = widget }, function() + + Backend.push() + + if widget.parent then + Backend.intersectScissor(x, y, w, h) + else + Backend.setScissor() + end + + self:paintBackground() + self:paintOutline() + self:paintSlices() + self:paintIconAndText() + self:paintChildren() + + Backend.pop() + + end) + Event.Display:emit(widget, { target = widget }) +end + +return Painter diff --git a/src/lib/luigi/shortcut.lua b/src/lib/luigi/shortcut.lua new file mode 100644 index 0000000..6fa8b32 --- /dev/null +++ b/src/lib/luigi/shortcut.lua @@ -0,0 +1,99 @@ +--[[-- +Keyboard shortcut module. +--]]-- + +local ROOT = (...):gsub('[^.]*$', '') + +local Backend = require(ROOT .. 'backend') + +local Shortcut = {} + +local isMac = Backend.isMac() + +local ALT = 1 +local CTRL = 2 +local SHIFT = 4 +local GUI = 8 + +function Shortcut.appliesToPlatform (value) + if isMac and value:match '%f[%a]win%-' + or not isMac and value:match '%f[%a]mac%-' then + return false + end + return true +end + +function Shortcut.expandAliases (value) + return value + :gsub('%f[%a]cmd%-', 'mac-gui-') + :gsub('%f[%a]command%-', 'mac-gui-') + :gsub('%f[%a]option%-', 'mac-alt-') +end + +function Shortcut.parseKeyCombo (value) + -- expand command- and option- aliases + value = Shortcut.expandAliases(value) + + -- exit early if shortcut is for different platform + if not Shortcut.appliesToPlatform(value) then return end + + -- expand c- special modifier + if isMac then + value = value:gsub('%f[%a]c%-', 'gui-') + else + value = value:gsub('%f[%a]c%-', 'ctrl-') + end + + -- extract main key + local mainKey = value:match '[^%-]*%-?$' + + -- extract modifiers + local alt = value:match '%f[%a]alt%-' and ALT or 0 + local ctrl = value:match '%f[%a]ctrl%-' and CTRL or 0 + local shift = value:match '%f[%a]shift%-' and SHIFT or 0 + local gui = value:match '%f[%a]gui%-' and GUI or 0 + + return mainKey, alt + ctrl + shift + gui +end + +function Shortcut.getModifierFlags () + local alt = Backend.isKeyDown('lalt', 'ralt') and ALT or 0 + local ctrl = Backend.isKeyDown('lctrl', 'rctrl') and CTRL or 0 + local shift = Backend.isKeyDown('lshift', 'rshift') and SHIFT or 0 + local gui = Backend.isKeyDown('lgui', 'rgui') and GUI or 0 + return alt + ctrl + shift + gui +end + +function Shortcut.stringify (shortcut) + if type(shortcut) ~= 'table' then + shortcut = { shortcut } + end + for _, value in ipairs(shortcut) do + value = Shortcut.expandAliases(value) + if Shortcut.appliesToPlatform(value) then + if isMac then + value = value + :gsub('%f[%a]c%-', 'cmd-') + :gsub('%f[%a]gui%-', 'cmd-') + :gsub('%f[%a]alt%-', 'option-') + -- Have Love backend default to DejaVuSans + -- so we can use these instead of the above + --[[ + :gsub('%f[%a]c%-', '⌘') + :gsub('%f[%a]gui%-', '⌘') + :gsub('%f[%a]alt%-', '⌥') + :gsub('%f[%a]shift%-', '⇧') + ]] + else + value = value + :gsub('%f[%a]c%-', 'ctrl-') + :gsub('%f[%a]gui%-', 'windows-') + end + value = value:gsub('%f[%a]win%-', ''):gsub('%f[%a]mac%-', '') + value = value:gsub('%f[%w].', string.upper) + return value + end + end +end + +return Shortcut diff --git a/src/lib/luigi/style.lua b/src/lib/luigi/style.lua new file mode 100644 index 0000000..65caa07 --- /dev/null +++ b/src/lib/luigi/style.lua @@ -0,0 +1,40 @@ +local ROOT = (...):gsub('[^.]*$', '') + +local Base = require(ROOT .. 'base') + +local Style = Base:extend() + +function Style:constructor (rules, lookupNames) + self.rules = rules + self.lookupNames = lookupNames +end + +function Style:getProperty (object, property, original) + local value = rawget(object, property) + if value ~= nil then return value end + + local rules = self.rules + original = original or object + + for _, lookupName in ipairs(self.lookupNames) do + local lookup = rawget(object, lookupName) + or object.attributes and rawget(object.attributes, lookupName) + if lookup then + if type(lookup) ~= 'table' then + lookup = { lookup } + end + for _, lookupValue in ipairs(lookup) do + local rule = rules[lookupValue] + if rule then + local value = self:getProperty(rule, property, original) + if type(value) == 'function' then + value = value(original) + end + if value ~= nil then return value end + end + end -- lookup values + end -- if lookup + end -- lookup names +end + +return Style diff --git a/src/lib/luigi/theme/dark.lua b/src/lib/luigi/theme/dark.lua new file mode 100644 index 0000000..3ea40d9 --- /dev/null +++ b/src/lib/luigi/theme/dark.lua @@ -0,0 +1,12 @@ +local RESOURCE = (...):gsub('%.', '/') .. '/' +local ROOT = (...):gsub('[^.]*.[^.]*$', '') + +return function (config) + config = config or {} + config.resources = config.resources or RESOURCE + config.backColor = config.backColor or { 40, 40, 40 } + config.lineColor = config.lineColor or { 60, 60, 60 } + config.textColor = config.textColor or { 240, 240, 240 } + config.highlight = config.highlight or { 0x00, 0x5c, 0x94 } + return require(ROOT .. 'engine.alpha')(config) +end diff --git a/src/lib/luigi/theme/dark/button.png b/src/lib/luigi/theme/dark/button.png new file mode 100644 index 0000000..79674d4 Binary files /dev/null and b/src/lib/luigi/theme/dark/button.png differ diff --git a/src/lib/luigi/theme/dark/button_focused.png b/src/lib/luigi/theme/dark/button_focused.png new file mode 100644 index 0000000..fbf3b2f Binary files /dev/null and b/src/lib/luigi/theme/dark/button_focused.png differ diff --git a/src/lib/luigi/theme/dark/button_hovered.png b/src/lib/luigi/theme/dark/button_hovered.png new file mode 100644 index 0000000..3d4a85c Binary files /dev/null and b/src/lib/luigi/theme/dark/button_hovered.png differ diff --git a/src/lib/luigi/theme/dark/button_pressed.png b/src/lib/luigi/theme/dark/button_pressed.png new file mode 100644 index 0000000..15df992 Binary files /dev/null and b/src/lib/luigi/theme/dark/button_pressed.png differ diff --git a/src/lib/luigi/theme/dark/check_checked.png b/src/lib/luigi/theme/dark/check_checked.png new file mode 100644 index 0000000..bb8ca09 Binary files /dev/null and b/src/lib/luigi/theme/dark/check_checked.png differ diff --git a/src/lib/luigi/theme/dark/check_checked_focused.png b/src/lib/luigi/theme/dark/check_checked_focused.png new file mode 100644 index 0000000..987659a Binary files /dev/null and b/src/lib/luigi/theme/dark/check_checked_focused.png differ diff --git a/src/lib/luigi/theme/dark/check_checked_pressed.png b/src/lib/luigi/theme/dark/check_checked_pressed.png new file mode 100644 index 0000000..b99de82 Binary files /dev/null and b/src/lib/luigi/theme/dark/check_checked_pressed.png differ diff --git a/src/lib/luigi/theme/dark/check_unchecked.png b/src/lib/luigi/theme/dark/check_unchecked.png new file mode 100644 index 0000000..a71bb54 Binary files /dev/null and b/src/lib/luigi/theme/dark/check_unchecked.png differ diff --git a/src/lib/luigi/theme/dark/check_unchecked_focused.png b/src/lib/luigi/theme/dark/check_unchecked_focused.png new file mode 100644 index 0000000..6e7a77a Binary files /dev/null and b/src/lib/luigi/theme/dark/check_unchecked_focused.png differ diff --git a/src/lib/luigi/theme/dark/check_unchecked_pressed.png b/src/lib/luigi/theme/dark/check_unchecked_pressed.png new file mode 100644 index 0000000..c453ff5 Binary files /dev/null and b/src/lib/luigi/theme/dark/check_unchecked_pressed.png differ diff --git a/src/lib/luigi/theme/dark/progress.png b/src/lib/luigi/theme/dark/progress.png new file mode 100644 index 0000000..082c868 Binary files /dev/null and b/src/lib/luigi/theme/dark/progress.png differ diff --git a/src/lib/luigi/theme/dark/radio_checked.png b/src/lib/luigi/theme/dark/radio_checked.png new file mode 100644 index 0000000..2bda46b Binary files /dev/null and b/src/lib/luigi/theme/dark/radio_checked.png differ diff --git a/src/lib/luigi/theme/dark/radio_checked_focused.png b/src/lib/luigi/theme/dark/radio_checked_focused.png new file mode 100644 index 0000000..45d42aa Binary files /dev/null and b/src/lib/luigi/theme/dark/radio_checked_focused.png differ diff --git a/src/lib/luigi/theme/dark/radio_checked_pressed.png b/src/lib/luigi/theme/dark/radio_checked_pressed.png new file mode 100644 index 0000000..c80426c Binary files /dev/null and b/src/lib/luigi/theme/dark/radio_checked_pressed.png differ diff --git a/src/lib/luigi/theme/dark/radio_unchecked.png b/src/lib/luigi/theme/dark/radio_unchecked.png new file mode 100644 index 0000000..72f2bfe Binary files /dev/null and b/src/lib/luigi/theme/dark/radio_unchecked.png differ diff --git a/src/lib/luigi/theme/dark/radio_unchecked_focused.png b/src/lib/luigi/theme/dark/radio_unchecked_focused.png new file mode 100644 index 0000000..08ef37f Binary files /dev/null and b/src/lib/luigi/theme/dark/radio_unchecked_focused.png differ diff --git a/src/lib/luigi/theme/dark/radio_unchecked_pressed.png b/src/lib/luigi/theme/dark/radio_unchecked_pressed.png new file mode 100644 index 0000000..60511f8 Binary files /dev/null and b/src/lib/luigi/theme/dark/radio_unchecked_pressed.png differ diff --git a/src/lib/luigi/theme/dark/submenu.png b/src/lib/luigi/theme/dark/submenu.png new file mode 100644 index 0000000..585b632 Binary files /dev/null and b/src/lib/luigi/theme/dark/submenu.png differ diff --git a/src/lib/luigi/theme/dark/text.png b/src/lib/luigi/theme/dark/text.png new file mode 100644 index 0000000..15df992 Binary files /dev/null and b/src/lib/luigi/theme/dark/text.png differ diff --git a/src/lib/luigi/theme/dark/text_focused.png b/src/lib/luigi/theme/dark/text_focused.png new file mode 100644 index 0000000..d85bc4c Binary files /dev/null and b/src/lib/luigi/theme/dark/text_focused.png differ diff --git a/src/lib/luigi/theme/dark/triangle_down.png b/src/lib/luigi/theme/dark/triangle_down.png new file mode 100644 index 0000000..6180e28 Binary files /dev/null and b/src/lib/luigi/theme/dark/triangle_down.png differ diff --git a/src/lib/luigi/theme/dark/triangle_left.png b/src/lib/luigi/theme/dark/triangle_left.png new file mode 100644 index 0000000..f4e4f7f Binary files /dev/null and b/src/lib/luigi/theme/dark/triangle_left.png differ diff --git a/src/lib/luigi/theme/dark/triangle_right.png b/src/lib/luigi/theme/dark/triangle_right.png new file mode 100644 index 0000000..6b67254 Binary files /dev/null and b/src/lib/luigi/theme/dark/triangle_right.png differ diff --git a/src/lib/luigi/theme/dark/triangle_up.png b/src/lib/luigi/theme/dark/triangle_up.png new file mode 100644 index 0000000..8b20960 Binary files /dev/null and b/src/lib/luigi/theme/dark/triangle_up.png differ diff --git a/src/lib/luigi/theme/light-big.lua b/src/lib/luigi/theme/light-big.lua new file mode 100644 index 0000000..7f84fac --- /dev/null +++ b/src/lib/luigi/theme/light-big.lua @@ -0,0 +1,13 @@ +local RESOURCE = (...):gsub('%.', '/') .. '/' +local ROOT = (...):gsub('[^.]*.[^.]*$', '') + +return function (config) + local theme = require(ROOT .. 'theme.light')() + theme.Control._defaultDimension = 44 + theme.Line._defaultDimension = 32 + theme.menu.height = 32 + theme['menu.item'].height = 32 + theme['menu.item'].padding = 8 + theme.panel.padding = 8 + return theme +end diff --git a/src/lib/luigi/theme/light.lua b/src/lib/luigi/theme/light.lua new file mode 100644 index 0000000..d285caf --- /dev/null +++ b/src/lib/luigi/theme/light.lua @@ -0,0 +1,12 @@ +local RESOURCE = (...):gsub('%.', '/') .. '/' +local ROOT = (...):gsub('[^.]*.[^.]*$', '') + +return function (config) + config = config or {} + config.resources = config.resources or RESOURCE + config.backColor = config.backColor or { 240, 240, 240 } + config.lineColor = config.lineColor or { 220, 220, 220 } + config.textColor = config.textColor or { 0, 0, 0 } + config.highlight = config.highlight or { 0x19, 0xAE, 0xFF } + return require(ROOT .. 'engine.alpha')(config) +end diff --git a/src/lib/luigi/theme/light/button.png b/src/lib/luigi/theme/light/button.png new file mode 100644 index 0000000..cafb4cf Binary files /dev/null and b/src/lib/luigi/theme/light/button.png differ diff --git a/src/lib/luigi/theme/light/button_disabled.png b/src/lib/luigi/theme/light/button_disabled.png new file mode 100644 index 0000000..962ef06 Binary files /dev/null and b/src/lib/luigi/theme/light/button_disabled.png differ diff --git a/src/lib/luigi/theme/light/button_focused.png b/src/lib/luigi/theme/light/button_focused.png new file mode 100644 index 0000000..4339ce1 Binary files /dev/null and b/src/lib/luigi/theme/light/button_focused.png differ diff --git a/src/lib/luigi/theme/light/button_hovered.png b/src/lib/luigi/theme/light/button_hovered.png new file mode 100644 index 0000000..b13fb2d Binary files /dev/null and b/src/lib/luigi/theme/light/button_hovered.png differ diff --git a/src/lib/luigi/theme/light/button_pressed.png b/src/lib/luigi/theme/light/button_pressed.png new file mode 100644 index 0000000..d80b08f Binary files /dev/null and b/src/lib/luigi/theme/light/button_pressed.png differ diff --git a/src/lib/luigi/theme/light/check_checked.png b/src/lib/luigi/theme/light/check_checked.png new file mode 100644 index 0000000..b43de3e Binary files /dev/null and b/src/lib/luigi/theme/light/check_checked.png differ diff --git a/src/lib/luigi/theme/light/check_checked_focused.png b/src/lib/luigi/theme/light/check_checked_focused.png new file mode 100644 index 0000000..4bebade Binary files /dev/null and b/src/lib/luigi/theme/light/check_checked_focused.png differ diff --git a/src/lib/luigi/theme/light/check_checked_pressed.png b/src/lib/luigi/theme/light/check_checked_pressed.png new file mode 100644 index 0000000..3ee0391 Binary files /dev/null and b/src/lib/luigi/theme/light/check_checked_pressed.png differ diff --git a/src/lib/luigi/theme/light/check_unchecked.png b/src/lib/luigi/theme/light/check_unchecked.png new file mode 100644 index 0000000..fb0f904 Binary files /dev/null and b/src/lib/luigi/theme/light/check_unchecked.png differ diff --git a/src/lib/luigi/theme/light/check_unchecked_focused.png b/src/lib/luigi/theme/light/check_unchecked_focused.png new file mode 100644 index 0000000..98ceede Binary files /dev/null and b/src/lib/luigi/theme/light/check_unchecked_focused.png differ diff --git a/src/lib/luigi/theme/light/check_unchecked_pressed.png b/src/lib/luigi/theme/light/check_unchecked_pressed.png new file mode 100644 index 0000000..999b3c7 Binary files /dev/null and b/src/lib/luigi/theme/light/check_unchecked_pressed.png differ diff --git a/src/lib/luigi/theme/light/light_theme.svgz b/src/lib/luigi/theme/light/light_theme.svgz new file mode 100644 index 0000000..6d5591a Binary files /dev/null and b/src/lib/luigi/theme/light/light_theme.svgz differ diff --git a/src/lib/luigi/theme/light/progress.png b/src/lib/luigi/theme/light/progress.png new file mode 100644 index 0000000..0f02229 Binary files /dev/null and b/src/lib/luigi/theme/light/progress.png differ diff --git a/src/lib/luigi/theme/light/radio_checked.png b/src/lib/luigi/theme/light/radio_checked.png new file mode 100644 index 0000000..c453a05 Binary files /dev/null and b/src/lib/luigi/theme/light/radio_checked.png differ diff --git a/src/lib/luigi/theme/light/radio_checked_focused.png b/src/lib/luigi/theme/light/radio_checked_focused.png new file mode 100644 index 0000000..4c166f8 Binary files /dev/null and b/src/lib/luigi/theme/light/radio_checked_focused.png differ diff --git a/src/lib/luigi/theme/light/radio_checked_pressed.png b/src/lib/luigi/theme/light/radio_checked_pressed.png new file mode 100644 index 0000000..7742011 Binary files /dev/null and b/src/lib/luigi/theme/light/radio_checked_pressed.png differ diff --git a/src/lib/luigi/theme/light/radio_unchecked.png b/src/lib/luigi/theme/light/radio_unchecked.png new file mode 100644 index 0000000..d10a56a Binary files /dev/null and b/src/lib/luigi/theme/light/radio_unchecked.png differ diff --git a/src/lib/luigi/theme/light/radio_unchecked_focused.png b/src/lib/luigi/theme/light/radio_unchecked_focused.png new file mode 100644 index 0000000..3b670e6 Binary files /dev/null and b/src/lib/luigi/theme/light/radio_unchecked_focused.png differ diff --git a/src/lib/luigi/theme/light/radio_unchecked_pressed.png b/src/lib/luigi/theme/light/radio_unchecked_pressed.png new file mode 100644 index 0000000..983a78f Binary files /dev/null and b/src/lib/luigi/theme/light/radio_unchecked_pressed.png differ diff --git a/src/lib/luigi/theme/light/submenu.png b/src/lib/luigi/theme/light/submenu.png new file mode 100644 index 0000000..7f37f94 Binary files /dev/null and b/src/lib/luigi/theme/light/submenu.png differ diff --git a/src/lib/luigi/theme/light/text.png b/src/lib/luigi/theme/light/text.png new file mode 100644 index 0000000..d137fcd Binary files /dev/null and b/src/lib/luigi/theme/light/text.png differ diff --git a/src/lib/luigi/theme/light/text_focused.png b/src/lib/luigi/theme/light/text_focused.png new file mode 100644 index 0000000..87ff79b Binary files /dev/null and b/src/lib/luigi/theme/light/text_focused.png differ diff --git a/src/lib/luigi/theme/light/triangle_down.png b/src/lib/luigi/theme/light/triangle_down.png new file mode 100644 index 0000000..87c28c7 Binary files /dev/null and b/src/lib/luigi/theme/light/triangle_down.png differ diff --git a/src/lib/luigi/theme/light/triangle_left.png b/src/lib/luigi/theme/light/triangle_left.png new file mode 100644 index 0000000..c5659ff Binary files /dev/null and b/src/lib/luigi/theme/light/triangle_left.png differ diff --git a/src/lib/luigi/theme/light/triangle_right.png b/src/lib/luigi/theme/light/triangle_right.png new file mode 100644 index 0000000..71cad44 Binary files /dev/null and b/src/lib/luigi/theme/light/triangle_right.png differ diff --git a/src/lib/luigi/theme/light/triangle_up.png b/src/lib/luigi/theme/light/triangle_up.png new file mode 100644 index 0000000..193963e Binary files /dev/null and b/src/lib/luigi/theme/light/triangle_up.png differ diff --git a/src/lib/luigi/utf8.lua b/src/lib/luigi/utf8.lua new file mode 100644 index 0000000..66e878f --- /dev/null +++ b/src/lib/luigi/utf8.lua @@ -0,0 +1,339 @@ +-- modified for partial compatibility with Lua 5.3 + +--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') + return #s + 1 +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 + 1 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 + return ci + 1 + -- 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 '�' --\uFFFD + return utf8.replace(s, replace_invalid, repl_char) +end + +-- Returns the position (in bytes) where the encoding of the n-th character +-- of s (counting from position i) starts. +function utf8.offset(s, n, i) + + -- The default for i is 1 when n is non-negative and #s + 1 otherwise + if not i then + i = n < 0 and #s + 1 or 1 + end + + local ci = utf8.char_index(s, i) + + -- As a special case, when n is 0 the function returns the start of + -- the encoding of the character that contains the i-th byte of s. + if n == 0 then + return ci + end + + if n > 0 then + n = n - 1 + end + + return utf8.byte_index(s, ci + n) +end + +utf8.codes = utf8.byte_indices + +return utf8 diff --git a/src/lib/luigi/widget.lua b/src/lib/luigi/widget.lua new file mode 100644 index 0000000..1d8474f --- /dev/null +++ b/src/lib/luigi/widget.lua @@ -0,0 +1,830 @@ +--[[-- +Widget class. + +@classmod Widget +--]]-- + +local STRICT = false +local ROOT = (...):gsub('[^.]*$', '') + +local Backend = require(ROOT .. 'backend') +local Event = require(ROOT .. 'event') +local Attribute = require(ROOT .. 'attribute') +local Painter = require(ROOT .. 'painter') +local Font = Backend.Font + +local Widget = {} + +Event.injectBinders(Widget) + +--[[-- +API Properties + +These properties may be useful when creating user interfaces, +and are a formal part of the API. + +@section api +--]]-- + +--[[-- +Whether this widget has keyboard focus. + +Can be used by styles and themes. This value is automatically set by +the `Input` class, and should generally be treated as read-only. +--]]-- +Widget.focused = false + +--[[-- +Whether the pointer is within this widget. + +Can be used by styles and themes. This value is automatically set by +the `Input` class, and should generally be treated as read-only. +--]]-- +Widget.hovered = false + +--[[-- +Table of mouse buttons pressed on this widget and not yet released, +keyed by mouse button name with booleans as values. + +Can be used by styles and themes. Values are automatically set by +the `Input` class, and should generally be treated as read-only. +--]]-- +Widget.pressed = nil + +--[[-- +Internal Properties + +These properties are used internally, but are not likely to be useful +when creating user interfaces; they are not a formal part of the API +and may change at any time. + +@section internal +--]]-- + +--[[-- +Identifies this object as a widget. + +Can be used to determine whether an unknown object is a widget. +--]]-- +Widget.isWidget = true + +--[[-- +Whether the widget is currently being reshaped. + +Used internally by `reshape` to prevent stack overflows when handling +`Reshape` events. +--]]-- +Widget.isReshaping = false + +--[[-- +Whether this widget has a type. + +Used by the @{attribute.type|type} attribute to determine whether to +run the type initializer when the widget's type is set. After a type +initializer has run, `hasType` becomes `true` and no other type +initializers should run on the widget. +--]]-- +Widget.hasType = false + +--[[-- +The `Font` object associated with the widget. +--]]-- +Widget.fontData = nil + +--[[-- +The `Text` object associated with the widget. +--]]-- +Widget.textData = nil + + +--[[-- +@section end +--]]-- + +Widget.typeDecorators = { + button = require(ROOT .. 'widget.button'), + check = require(ROOT .. 'widget.check'), + menu = require(ROOT .. 'widget.menu'), + ['menu.item'] = require(ROOT .. 'widget.menu.item'), + progress = require(ROOT .. 'widget.progress'), + radio = require(ROOT .. 'widget.radio'), + sash = require(ROOT .. 'widget.sash'), + slider = require(ROOT .. 'widget.slider'), + status = require(ROOT .. 'widget.status'), + stepper = require(ROOT .. 'widget.stepper'), + text = require(ROOT .. 'widget.text'), + window = require(ROOT .. 'widget.window'), +} + +--[[-- +Static Functions + +@section static +--]]-- + +--[[-- +Register a custom widget type. + +@static + +@tparam string name +A unique name for this type of widget. + +@tparam function(Widget) decorator +An initialization function for this type of widget. +--]]-- +function Widget.register (name, decorator) + Widget.typeDecorators[name] = decorator +end + +--[[-- +@section end +--]]-- + +-- look for properties in attributes, Widget, style, and theme +local function metaIndex (self, property) + -- look in widget's own attributes + local A = self.attributeDescriptors[property] or Attribute[property] + if A then + local value = A.get and A.get(self, property) + or self.attributes[property] + if type(value) == 'function' then value = value(self) end + if value ~= nil then return value end + end + + -- look in Widget class properties + local value = Widget[property] + if value ~= nil then return value end + + -- look in style + local layout = self.layout + value = layout:getStyle():getProperty(self, property) + if value ~= nil then return value end + + -- look in theme + return layout:getTheme():getProperty(self, property) +end + +-- setting attributes triggers special behavior +local function metaNewIndex (self, property, value) + local A = self.attributeDescriptors[property] or Attribute[property] + if A then + if A.set then + A.set(self, value, property) + else + self.attributes[property] = value + end + else + if STRICT and Widget[property] == nil then + error(property .. ' is not a valid widget property.') + else + rawset(self, property, value) + end + end +end + +local attributeNames = {} + +for name in pairs(Attribute) do + if name ~= 'type' then -- type must be handled last + attributeNames[#attributeNames + 1] = name + end +end + +attributeNames[#attributeNames + 1] = 'type' + +--[[-- +Widget pseudo-constructor. + +@function Luigi.Widget + +@within Constructor + +@tparam Layout layout +The layout this widget belongs to. + +@tparam[opt] table data +The data definition table for this widget. +This table is identical to the constructed widget. + +@treturn Widget +A Widget instance. +--]]-- +local function metaCall (Widget, layout, self) + self = self or {} + self.layout = layout + self.position = { x = nil, y = nil } + self.dimensions = { width = nil, height = nil } + self.attributes = {} + self.attributeDescriptors = {} + self.pressed = {} + self.painter = Painter(self) + + setmetatable(self, { __index = metaIndex, __newindex = metaNewIndex }) + + for _, property in ipairs(attributeNames) do + local value = rawget(self, property) + rawset(self, property, nil) + self[property] = value + end + + for k, v in ipairs(self) do + self[k] = v.isWidget and v or metaCall(Widget, self.layout, v) + self[k].parent = self + end + + return self +end + +function Widget:getMasterLayout () + return self.layout.master or self.layout +end + +--[[-- +Define a custom attribute for this widget. + +When an attribute is defined, the current value is stored locally and +removed from the widget's own properties and its attributes collection. +Then, the newly-defined setter is called with the stored value. + +@tparam string name +The name of the attribute. + +@tparam table descriptor +A table, optionally containing `get` and `set` functions (see `Attribute`). + +@treturn Widget +Return this widget for chaining. +--]]-- +function Widget:defineAttribute (name, descriptor) + local value = rawget(self, name) + if value == nil then value = self.attributes[name] end + self.attributeDescriptors[name] = descriptor or {} + rawset(self, name, nil) + self.attributes[name] = nil + self[name] = value + return self +end + +--[[-- +Fire an event on this widget and each ancestor. + +If any event handler returns non-nil, stop the event from propagating. + +@tparam string eventName +The name of the Event. + +@tparam[opt] table data +Information about the event to send to handlers. + +@treturn mixed +The first value returned by an event handler. +--]]-- +function Widget:bubbleEvent (eventName, data) + local event = Event[eventName] + data = data or {} + data.target = self + for ancestor in self:eachAncestor(true) do + local result = event:emit(ancestor, data) + if result ~= nil then return result end + end + return event:emit(self.layout, data) +end + +--[[-- +Get widget's previous sibling. + +@treturn Widget|nil +The widget's previous sibling, if any. +--]]-- +function Widget:getPreviousSibling () + local parent = self.parent + if not parent then return end + for i, widget in ipairs(parent) do + if widget == self then return parent[i - 1] end + end +end + +--[[-- +Get widget's next sibling. + +@treturn Widget|nil +The widget's next sibling, if any. +--]]-- +function Widget:getNextSibling () + local parent = self.parent + if not parent then return end + for i, widget in ipairs(parent) do + if widget == self then return parent[i + 1] end + end +end + +--[[-- +Attempt to focus the widget. + +Unfocus currently focused widget, and focus this widget if it's focusable. + +@treturn boolean +true if this widget was focused, else false. +--]]-- +function Widget:focus () + local layout = self.layout + + if layout.focusedWidget == self then + return true + end + + if layout.focusedWidget then + layout.focusedWidget.focused = nil + Event.Blur:emit(self.layout, layout.focusedWidget) + layout.focusedWidget = nil + end + + if self.focusable then + self.focused = true + layout.focusedWidget = self + Event.Focus:emit(self.layout, self) + return true + end + + return false +end + +--[[-- +Get the next widget, depth-first. + +If the widget has children, returns the first child. +Otherwise, returns the next sibling of the nearest possible ancestor. +Cycles back around to the layout root from the last widget in the tree. + +@treturn Widget +The next widget in the tree. +--]]-- +function Widget:getNextNeighbor () + if #self > 0 then + return self[1] + end + for ancestor in self:eachAncestor(true) do + local nextWidget = ancestor:getNextSibling() + if nextWidget then return nextWidget end + end + return self.layout.root +end + +-- get the last child of the last child of the last child of the... +local function getGreatestDescendant (widget) + while #widget > 0 do + widget = widget[#widget] + end + return widget +end + +--[[-- +Get the previous widget, depth-first. + +Uses the reverse of the traversal order used by `getNextNeighbor`. +Cycles back around to the last widget in the tree from the layout root. + +@treturn Widget +The previous widget in the tree. +--]]-- +function Widget:getPreviousNeighbor () + local layout = self.layout + + if self == layout.root then + return getGreatestDescendant(self) + end + + for ancestor in self:eachAncestor(true) do + local previousWidget = ancestor:getPreviousSibling() + if previousWidget then + return getGreatestDescendant(previousWidget) + end + if ancestor ~= self then return ancestor end + end + + return layout.root +end + +--[[-- +Add a child to this widget. + +@tparam Widget|table data +A widget or definition table representing a widget. + +@treturn Widget +The newly added child widget. +--]]-- +function Widget:addChild (data) + local layout = self.layout + local child = data and data.isWidget and data or Widget(layout, data or {}) + + self[#self + 1] = child + child.parent = self + child.layout = self.layout + + return child +end + +function Widget:calculateDimension (name) + -- If dimensions are already calculated, return them. + if self.dimensions[name] then + return self.dimensions[name] + end + + -- Get minimum width/height from attributes. + local min = (name == 'width') and (self.minwidth or 0) + or (self.minheight or 0) + + -- If width/height attribute is found (in widget, style or theme) + if self[name] then + -- and if width/height is "auto" then shrink to fit content + if self[name] == 'auto' then + self.dimensions[name] = self:calculateDimensionMinimum(name) + return self.dimensions[name] + end + -- else width/height should be a number; use that value, + -- clamped to minimum. + self.dimensions[name] = math.max(self[name], min) + return self.dimensions[name] + end + + -- If the widget is a layout root (and has no width/height), + -- it's the same size as the window. + local parent = self.parent + if not parent then + local windowWidth, windowHeight = Backend.getWindowSize() + local size = name == 'width' and windowWidth or windowHeight + self.dimensions[name] = size + return self.dimensions[name] + end + + -- Widgets expand to fit their parents when no width/height is specified. + local parentDimension = parent:calculateDimension(name) + parentDimension = parentDimension - (parent.margin or 0) * 2 + parentDimension = parentDimension - (parent.padding or 0) * 2 + + -- If the dimension is in the opposite direction of the parent flow + -- (for example if parent.flow is 'x' and the dimension is 'height'), + -- then return the parent dimension. + local parentFlow = parent.flow or 'y' + if (parentFlow ~= 'x' and name == 'width') + or (parentFlow == 'x' and name == 'height') then + self.dimensions[name] = math.max(parentDimension, min) + return self.dimensions[name] + end + + -- If the dimension is in the same direction as the parent flow + -- (for example if parent.flow is 'x' and the dimension is 'width'), + -- then return an equal portion of the unclaimed space in the parent. + local claimed = 0 + local unsized = 1 + for i, widget in ipairs(self.parent) do + if widget ~= self then + local value = widget[name] + if value == 'auto' then + if not widget.dimensions[name] then + widget.dimensions[name] = widget:calculateDimensionMinimum(name) + end + claimed = claimed + widget.dimensions[name] + elseif value then + local min = (name == 'width') and (widget.minwidth or 0) + or (widget.minheight or 0) + claimed = claimed + math.max(value, min) + else + unsized = unsized + 1 + end + end + end + local size = (parentDimension - claimed) / unsized + + size = math.max(size, min) + self.dimensions[name] = size + return size +end + +function Widget:calculateRootPosition (axis) + local value = (axis == 'x' and self.left) or (axis ~= 'x' and self.top) + + if value then + self.position[axis] = value + return value + end + + local ww, wh = Backend.getWindowSize() + + if axis == 'x' and type(self.width) == 'number' then + value = (ww - self.width) / 2 + elseif axis ~= 'x' and type(self.height) == 'number' then + value = (wh - self.height) / 2 + else + value = 0 + end + + self.position[axis] = value + return value +end + +function Widget:calculatePosition (axis) + if self.position[axis] then + return self.position[axis] + end + local parent = self.parent + local scroll = 0 + if not parent then + return self:calculateRootPosition(axis) + else + scroll = axis == 'x' and (parent.scrollX or 0) + or axis ~= 'x' and (parent.scrollY or 0) + end + local parentPos = parent:calculatePosition(axis) + local p = parentPos - scroll + (parent.margin or 0) + (parent.padding or 0) + local parentFlow = parent.flow or 'y' + for i, widget in ipairs(parent) do + if widget == self then + self.position[axis] = p + return p + end + if parentFlow == axis then + local dimension = (axis == 'x') and 'width' or 'height' + p = p + widget:calculateDimension(dimension) + end + end + self.position[axis] = 0 + return 0 +end + +function Widget:calculateDimensionMinimum (name) + local dim = self[name] + local min = (name == 'width') and (self.minwidth or 0) + or (self.minheight or 0) + + if type(dim) == 'number' then + return math.max(dim, min) + end + + local value = 0 + + for _, child in ipairs(self) do + if (name == 'width' and self.flow == 'x') + or (name == 'height' and self.flow ~= 'x') then + value = value + child:calculateDimensionMinimum(name) + else + value = math.max(value, child:calculateDimensionMinimum(name)) + end + end + + if value > 0 then + local space = (self.margin or 0) * 2 + (self.padding or 0) * 2 + value = value + space + end + + return math.max(value, min) +end + +--[[-- +Get the widget's X coordinate. + +@treturn number +The widget's X coordinate. +--]]-- +function Widget:getX () + return self:calculatePosition('x') +end + +--[[-- +Get the widget's Y coordinate. + +@treturn number +The widget's Y coordinate. +--]]-- +function Widget:getY () + return self:calculatePosition('y') +end + +--[[-- +Get the widget's calculated width. + +@treturn number +The widget's calculated width. +--]]-- +function Widget:getWidth () + return self:calculateDimension('width') +end + +--[[-- +Get the widget's calculated height. + +@treturn number +The widget's calculated height. +--]]-- +function Widget:getHeight () + return self:calculateDimension('height') +end + +--[[-- +Get the content width. + +Gets the combined width of the widget's children. + +@treturn number +The content width. +--]]-- +function Widget:getContentWidth () + if not self.layout.isReady then return 0 end + local width = 0 + if self.flow == 'x' then + for _, child in ipairs(self) do + width = width + child:getWidth() + end + else + for _, child in ipairs(self) do + width = math.max(width, child:getWidth()) + end + end + return width +end + +--[[-- +Get the content height. + +Gets the combined height of the widget's children. + +@treturn number +The content height. +--]]-- +function Widget:getContentHeight () + if not self.layout.isReady then return 0 end + local height = 0 + if self.flow ~= 'x' then + for _, child in ipairs(self) do + height = height + child:getHeight() + end + else + for _, child in ipairs(self) do + height = math.max(height, child:getHeight()) + end + end + return height +end + +function Widget:getFont () + if not self.fontData then + self.fontData = Font(self.font, self.size) + end + return self.fontData +end + +--[[-- +Get x/y/width/height values describing a rectangle within the widget. + +@tparam boolean useMargin +Whether to adjust the rectangle based on the widget's margin. + +@tparam boolean usePadding +Whether to adjust the rectangle based on the widget's padding. + +@treturn number +The upper left corner's X position. + +@treturn number +The upper left corner's Y position. + +@treturn number +The rectangle's width + +@treturn number +The rectangle's height +--]]-- +function Widget:getRectangle (useMargin, usePadding) + local x, y = self:getX(), self:getY() + local w, h = self:getWidth(), self:getHeight() + local function shrink(amount) + x = x + amount + y = y + amount + w = w - amount * 2 + h = h - amount * 2 + end + if useMargin then + shrink(self.margin or 0) + end + if usePadding then + shrink(self.padding or 0) + end + return math.floor(x), math.floor(y), math.floor(w), math.floor(h) +end + +--[[-- +Determine whether a point is within a widget. + +@tparam number x +The point's X coordinate. + +@tparam number y +The point's Y coordinate. + +@treturn boolean +true if the point is within the widget, else false. +--]]-- +function Widget:isAt (x, y) + local x1, y1, w, h = self:getRectangle() + local x2, y2 = x1 + w, y1 + h + return (x1 <= x) and (x2 >= x) and (y1 <= y) and (y2 >= y) +end + +--[[-- +Iterate widget's ancestors. + +@tparam boolean includeSelf +Whether to include this widget as the first result. + +@treturn function +Returns an iterator function that returns widgets. + +@usage +for ancestor in myWidget:eachAncestor(true) do + print(widget.type or 'generic') +end +--]]-- +function Widget:eachAncestor (includeSelf) + local instance = includeSelf and self or self.parent + return function() + local widget = instance + if not widget then return end + instance = widget.parent + return widget + end +end + +function Widget:paint () + return self.painter:paint() +end + +--[[-- +Reshape the widget. + +Clears calculated widget dimensions, allowing them to be recalculated, and +fires a Reshape event (does not bubble). Called recursively for each child. + +When setting a widget's width or height, this function is automatically called +on the parent widget. +--]]-- +function Widget:reshape () + if self.isReshaping then return end + self.isReshaping = true + + self:scrollBy(0, 0) + + self.position = {} + self.dimensions = {} + + self.textData = nil + + Event.Reshape:emit(self, { target = self }) + for _, child in ipairs(self) do + if child.reshape then + child:reshape() + end + end + local items = self.items + if items then + for _, child in ipairs(items) do + if child.reshape then + child:reshape() + end + end + end + self.isReshaping = nil +end + +function Widget:scrollBy (amount) + if not self.scroll then return end + --TODO: eliminate redundancy + if self.flow == 'x' then + if not self.scrollX then self.scrollX = 0 end + local scrollX = self.scrollX - amount * 10 + local inner = math.max(self:getContentWidth(), self.innerWidth or 0) + local maxX = inner - self:getWidth() + + (self.padding or 0) * 2 + (self.margin or 0) * 2 + scrollX = math.max(math.min(scrollX, maxX), 0) + if scrollX ~= self.scrollX then + self.scrollX = scrollX + self:reshape() + return true + end + else + if not self.scrollY then self.scrollY = 0 end + local scrollY = self.scrollY - amount * 10 + local inner = math.max(self:getContentHeight(), self.innerHeight or 0) + local maxY = inner - self:getHeight() + + (self.padding or 0) * 2 + (self.margin or 0) * 2 + scrollY = math.max(math.min(scrollY, maxY), 0) + if scrollY ~= self.scrollY then + self.scrollY = scrollY + self:reshape() + return true + end + end +end + +return setmetatable(Widget, { __call = metaCall }) diff --git a/src/lib/luigi/widget/button.lua b/src/lib/luigi/widget/button.lua new file mode 100644 index 0000000..db9814f --- /dev/null +++ b/src/lib/luigi/widget/button.lua @@ -0,0 +1,30 @@ +--[[-- +A button. + +Buttons have no special behavior beyond that of generic widgets, +but themes should give buttons an appropriate appearance. + +@usage +-- create a layout containing only a button +local layout = Layout { + type = 'button', + id = 'exampleButton', + text = 'Press me', + width = 100, + height = 32, +} + +-- handle Press events +layout.exampleButton:onPress(function (event) + print 'You pressed the button.' +end) + +-- show the layout +layout:show() + +@widget button +--]]-- + +return function (self) + +end diff --git a/src/lib/luigi/widget/check.lua b/src/lib/luigi/widget/check.lua new file mode 100644 index 0000000..ed58e99 --- /dev/null +++ b/src/lib/luigi/widget/check.lua @@ -0,0 +1,22 @@ +--[[-- +A check box. + +Check boxes toggle their @{attribute.value|value} attribute between +`true` and `false` when pressed. + +Changing the value of a check box causes it to change its appearance to +indicate its value. The standard themes use the @{attribute.icon|icon} +attribute for this purpose. If a custom icon is provided when using the +standard themes, the widget's value should be indicated in some other way. + +@widget check +--]]-- + +return function (self) + self:onPress(function (event) + if event.button ~= 'left' then return end + self.value = not self.value + end) + + self.value = not not self.value +end diff --git a/src/lib/luigi/widget/menu.lua b/src/lib/luigi/widget/menu.lua new file mode 100644 index 0000000..fa8ae12 --- /dev/null +++ b/src/lib/luigi/widget/menu.lua @@ -0,0 +1,15 @@ +--[[-- +A menu bar. + +@widget menu +--]]-- + +return function (self) + + for index, child in ipairs(self) do + child.type = child.type or 'menu.item' + child.parentMenu = self + child.rootMenu = self + end + +end diff --git a/src/lib/luigi/widget/menu/item.lua b/src/lib/luigi/widget/menu/item.lua new file mode 100644 index 0000000..bc6509d --- /dev/null +++ b/src/lib/luigi/widget/menu/item.lua @@ -0,0 +1,243 @@ +--[[-- +A menu item. + +When a `menu` is created, any sub-items not having a specified type +are automatically given a type of `'menu.item'`. These widgets should +not be explicitly created. + +@widget menu.item +--]]-- +local ROOT = (...):gsub('[^.]*.[^.]*.[^.]*$', '') + +local Backend = require(ROOT .. 'backend') +local Shortcut = require(ROOT .. 'shortcut') + +local Layout, Event + +local function checkMouseButton (self, event) + local button = event.button + if not button then return false end + if self.isContextMenu then + return button == 'left' or button == 'right' + end + return button == 'left' +end + +local function addLayoutChildren (self) + local root = self.menuLayout.root + local textWidth = 0 + local keyWidth = 0 + local height = 0 + + while #root > 0 do rawset(root, #root, nil) end + + root.height = 0 + root.width = 0 + + for index, child in ipairs(self.items) do + child.type = child.type or 'menu.item' + root:addChild(child) + local childHeight = child:getHeight() + height = height + childHeight + if child.type == 'menu.item' then + local font = child:getFont() + local pad = child.padding or 0 + local tw = font:getAdvance(child[2].text) + + pad * 2 + childHeight + local kw = font:getAdvance(child[3].text) + + pad * 2 + childHeight + textWidth = math.max(textWidth, tw) + keyWidth = math.max(keyWidth, kw) + end + end + + root.height = height + root.width = textWidth + keyWidth + (root.padding or 0) + + local isSubmenu = self.parentMenu and self.parentMenu.parentMenu + local w = isSubmenu and self:getWidth() or 0 + local h = isSubmenu and 0 or self:getHeight() + self.menuLayout:placeNear(self:getX(), self:getY(), w, h) +end + +local function show (self) + if not self.items or #self.items < 1 then return end + + addLayoutChildren(self) + self.menuLayout:show() +end + +local function deactivateSiblings (target) + local sibling = target.parent and target.parent[1] + local wasSiblingOpen + + if not sibling then + return + end + + while sibling do + local layout = sibling.menuLayout + local items = sibling.items + + sibling.active = nil + + if layout and layout.isShown then + wasSiblingOpen = true + layout:hide() + end + + if items and items[1] then + deactivateSiblings(items[1]) + end + + sibling = sibling:getNextSibling() + end + + return wasSiblingOpen +end + +local function activate (event, ignoreIfNoneOpen) + -- if event.button and event.button ~= 'left' then return end + local target = event.target + + while target.parent + and target.parent.type ~= 'menu' and target.parent.type ~= 'submenu' do + target = target.parent + if not target then + return + end + end + + -- if not checkMouseButton(event) then return end + + local wasSiblingOpen = deactivateSiblings(target) + local ignore = ignoreIfNoneOpen and not wasSiblingOpen + + if not ignore then + show(target) + target.active = true + end +end + +local function registerLayoutEvents (self) + local menuLayout = self.menuLayout + + menuLayout:onReshape(function (event) + menuLayout:hide() + deactivateSiblings(self.rootMenu[1]) + end) + + menuLayout:onPressStart(function (event) + if not event.hit then + menuLayout:hide() + if self.parentMenu == self.rootMenu then + deactivateSiblings(self.rootMenu[1]) + end + elseif checkMouseButton(self, event) then + activate(event) + end + end) + + menuLayout:onPress(function (event) + -- if event.button ~= 'left' then return end + if not checkMouseButton(self, event) then return end + for widget in event.target:eachAncestor(true) do + if widget.type == 'menu.item' and #widget.items == 0 then + menuLayout:hide() + deactivateSiblings(self.rootMenu[1]) + end + end + end) + + menuLayout:onPressEnd(function (event) + -- if event.button ~= 'left' then return end + if not checkMouseButton(self, event) then return end + for widget in event.target:eachAncestor(true) do + if widget.type == 'menu.item' and #widget.items == 0 + and event.target ~= event.origin then + widget:bubbleEvent('Press', event) + end + end + end) + + menuLayout:onEnter(activate) + menuLayout:onPressEnter(activate) +end + +local function initialize (self) + local font = self:getFont() + local pad = self.padding or 0 + local isSubmenu = self.parentMenu and self.parentMenu.parentMenu + local text, shortcut, icon = self.text or '', self.shortcut or '', self.icon + local textWidth = font:getAdvance(text) + pad * 2 + + if isSubmenu then + local edgeType + if #self.items > 0 then + shortcut = ' ' + edgeType = 'menu.expander' + else + shortcut = Shortcut.stringify(shortcut) + end + self.flow = 'x' + self:addChild { icon = icon, width = self.height } + self:addChild { text = text, width = textWidth } + self:addChild { + type = edgeType, + text = shortcut, + align = 'middle right', + minwidth = self.height, + color = function () + local c = self.color or { 0, 0, 0 } + return { c[1], c[2], c[3], (c[4] or 256) / 2 } + end + } + + self.icon = nil + self.text = nil + else + -- top level menu + self.width = textWidth + pad * 2 + self.align = 'middle center' + end +end + +local function extractChildren (self) + self.items = {} + for index, child in ipairs(self) do + self[index] = nil + self.items[#self.items + 1] = child + child.parentMenu = self + child.rootMenu = self.rootMenu + child.type = child.type or 'menu.item' + end +end + +local function registerEvents (self) + self:onPressStart(activate) + + self:onEnter(function (event) + activate(event, true) + end) + + self:onPressEnter(function (event) + activate(event, true) + end) +end + +local function createLayout (self) + Layout = Layout or require(ROOT .. 'layout') + + self.menuLayout = Layout({ type = 'submenu' }, self.rootMenu.layout) +end + +return function (self) + extractChildren(self) + initialize(self) + registerEvents(self) + + if not self.items or #self.items < 1 then return end + createLayout(self) + registerLayoutEvents(self) + addLayoutChildren(self) +end diff --git a/src/lib/luigi/widget/progress.lua b/src/lib/luigi/widget/progress.lua new file mode 100644 index 0000000..e54ca75 --- /dev/null +++ b/src/lib/luigi/widget/progress.lua @@ -0,0 +1,41 @@ +--[[-- +A progress bar. + +Set the widget's `value` property to a decimal value +between 0 and 1 (inclusive) to change the width of the bar. + +@widget progress +--]]-- + +return function (self) + self.value = self.value or 0 + + local pad = self:addChild { + width = 0, + } + local bar = self:addChild { + type = 'progress.bar', + } + + self:onChange(function () + self:reshape() + end) + + self:onReshape(function () + local x1, y1, w, h = self:getRectangle(true, true) + local x2, y2 = x1 + w, y1 + h + if self.flow == 'x' then + local min = bar.minwidth or 0 + x1 = x1 + min + bar.width = self.value * (x2 - x1) + min + bar.height = false + pad.height = 0 + else + local min = bar.minheight or 0 + y1 = y1 + min + bar.width = false + bar.height = false + pad.height = math.ceil(h - (self.value * (y2 - y1) + min)) + end + end) +end diff --git a/src/lib/luigi/widget/radio.lua b/src/lib/luigi/widget/radio.lua new file mode 100644 index 0000000..74a9e08 --- /dev/null +++ b/src/lib/luigi/widget/radio.lua @@ -0,0 +1,94 @@ +--[[-- +A radio widget. + +When pressed, a radio widget's @{attribute.value|value} changes to +`true`, and the values of other radio widgets in the same `group` +change to `false`. + +Changing the value of a radio button causes it to change its appearance to +indicate its value. The standard themes use the @{attribute.icon|icon} +attribute for this purpose. If a custom icon is provided when using the +standard themes, the widget's value should be indicated in some other way. + +@widget radio +--]]-- + +local groups = {} + +local function remove (t, value) + for i, v in t do + if v == value then return table.remove(t, i) end + end +end + +local function setGroup (self, value) + -- remove the widget from the old group + local oldValue = self.attributes.group + local oldGroup = oldValue and groups[oldValue] + if oldGroup then + remove(oldGroup, self) + -- TODO: is it safe to remove these? + if #oldGroup < 1 then groups[oldValue] = nil end + end + -- add the widget to the new group, or 'defaultGroup' if no group specified + value = value or 'defaultGroup' + if not groups[value] then + groups[value] = {} + end + local group = groups[value] + group[#group + 1] = self + self.attributes.group = value + local layout = self:getMasterLayout() + if not layout[value] then + layout:createWidget { id = value, items = group } + end + self.groupWidget = layout[value] +end + +return function (self) + +--[[-- +Special Attributes + +@section special +--]]-- + +--[[-- +Widget group. + +Should contain a string identifying the widget's group. +If not defined, defaults to the string `'default'`. + +When a radio widget is pressed, the values of other radio widgets +in the same group change to `false`. + +@attrib group +--]]-- + self:defineAttribute('group', { set = setGroup }) +--[[-- +@section end +--]]-- + + -- when we bubble events, send them to the group widget + local bubbleEvent = self.bubbleEvent + function self:bubbleEvent (...) + local result = bubbleEvent(self, ...) + if result ~= nil then return result end + return self.groupWidget:bubbleEvent(...) + end + + self:onPress(function (event) + if event.button ~= 'left' then return end + for _, widget in ipairs(groups[self.group]) do + widget.value = widget == self + end + end) + + self:onChange(function (event) + -- change event is only sent to group widget once. + if not self.value then return false end + self.groupWidget.selected = self + end) + + self.value = not not self.value +end diff --git a/src/lib/luigi/widget/sash.lua b/src/lib/luigi/widget/sash.lua new file mode 100644 index 0000000..b0210f2 --- /dev/null +++ b/src/lib/luigi/widget/sash.lua @@ -0,0 +1,92 @@ +--[[-- +A sash. + +Dragging this widget resizes the widgets adjacent to it. +A sash should be adjacent to a widget with a specified size +in the same direction as the parent element's @{attribute.flow|flow}. + +For example, if the parent of the sash is `flow = 'x'` +then either or both of the siblings adjacent to the sash +should have a specified @{attribute.width|width} attribute. + +@usage +-- create a layout containing two widgets separated by a sash +local layout = Layout { + type = 'panel', flow = 'x', + { text = 'This is the left side', wrap = true, width = 100 }, + { type = 'sash' }, + { text = 'This is the right side', wrap = true }, +} + +-- show the layout +layout:show() + +@widget sash +--]]-- + +local function setDimension (widget, name, size) + if not widget.parent then + widget[name] = size + return + end + local parentDimension = widget.parent:calculateDimension(name) + local claimed = 0 + for i, sibling in ipairs(widget.parent) do + local value = sibling[name] + if sibling ~= widget and value then + if value == 'auto' then + value = sibling:calculateDimensionMinimum(name) + end + claimed = claimed + value + end + end + if claimed + size > parentDimension then + size = parentDimension - claimed + end + + local min = (name == 'width') and (widget.minwidth or 0) + or (widget.minheight or 0) + + widget[name] = math.max(size, min) +end + + +return function (self) + + self:onEnter(function (event) + local axis = self.parent.flow + if axis == 'x' then + self.cursor = 'sizewe' + else + self.cursor = 'sizens' + end + end) + + self:onPressDrag(function (event) + if event.button ~= 'left' then return end + local axis = self.parent.flow + if axis == 'x' then + dimension = 'width' + else + axis = 'y' + dimension = 'height' + end + local prevSibling = self:getPreviousSibling() + local nextSibling = self:getNextSibling() + local prevSize = prevSibling and prevSibling[dimension] + local nextSize = nextSibling and nextSibling[dimension] + + if prevSize or not nextSize then + setDimension(prevSibling, dimension, + event[axis] - prevSibling:calculatePosition(axis)) + elseif nextSize then + if nextSize == 'auto' then + nextSize = nextSibling:calculateDimensionMinimum(dimension) + end + setDimension(nextSibling, dimension, + nextSibling:calculatePosition(axis) + nextSize - event[axis]) + end + self.parent:reshape() + end) + +end diff --git a/src/lib/luigi/widget/slider.lua b/src/lib/luigi/widget/slider.lua new file mode 100644 index 0000000..c4b68e9 --- /dev/null +++ b/src/lib/luigi/widget/slider.lua @@ -0,0 +1,88 @@ +--[[-- +A slider. + +Dragging this widget changes its `value` property to a +number between 0 and 1, inclusive. + +@widget slider +--]]-- + +return function (self) + local function clamp (value) + return value < 0 and 0 or value > 1 and 1 or value + end + + self.value = clamp(self.value or 0) + self.step = self.step or 0.01 + + local spacer = self:addChild() + + local thumb = self:addChild { + type = 'slider.thumb', + } + + local function unpress (event) + if event.button ~= 'left' then return end + thumb.pressed.left = nil -- don't make the thumb appear pushed in + return false -- don't press thumb on focused keyboard activation + end + + thumb:onPressStart(unpress) + thumb:onPressEnter(unpress) + + thumb:onKeyPress(function (event) + local key = event.key + if key == 'left' or key == 'down' then + self.value = clamp(self.value - self.step) + elseif key == 'right' or key == 'up' then + self.value = clamp(self.value + self.step) + end + end) + + local function press (event) + if event.button ~= 'left' then return end + local x1, y1, w, h = self:getRectangle(true, true) + local x2, y2 = x1 + w, y1 + h + if self.flow == 'x' then + local halfThumb = thumb:getWidth() / 2 + x1, x2 = x1 + halfThumb, x2 - halfThumb + self.value = clamp((event.x - x1) / (x2 - x1)) + else + local halfThumb = thumb:getHeight() / 2 + y1, y2 = y1 + halfThumb, y2 - halfThumb + self.value = 1 - clamp((event.y - y1) / (y2 - y1)) + end + thumb:focus() + end + + self:onPressStart(press) + self:onPressDrag(press) + + self:onEnter(function (event) + thumb.hovered = true + end) + + self:onLeave(function (event) + thumb.hovered = false + end) + + self:onChange(function (event) + self:reshape() + end) + + self:onReshape(function (event) + local x1, y1, w, h = self:getRectangle(true, true) + local x2, y2 = x1 + w, y1 + h + if self.flow == 'x' then + local halfThumb = thumb:getWidth() / 2 + x1, x2 = x1 + halfThumb, x2 - halfThumb + spacer.width = self.value * (x2 - x1) + spacer.height = false + else + local halfThumb = thumb:getHeight() / 2 + y1, y2 = y1 + halfThumb, y2 - halfThumb + spacer.width = false + spacer.height = (1 - self.value) * (y2 - y1) + end + end) +end diff --git a/src/lib/luigi/widget/status.lua b/src/lib/luigi/widget/status.lua new file mode 100644 index 0000000..2094a47 --- /dev/null +++ b/src/lib/luigi/widget/status.lua @@ -0,0 +1,26 @@ +--[[-- +A status bar. + +This widget will display the @{attribute.status|status} attribute of the +hovered widget. Only one status widget should exist per layout. If multiple +status widgets exist in the same layout, only the last one created will +display status messages. + +@usage +-- create a layout containing some buttons and a status bar +local layout = Layout { + { type = 'panel', flow = 'x', + { text = 'Do stuff', status = 'Press to do stuff' }, + { text = 'Quit', status = 'Press to quit' }, + }, + { type = 'status', height = 24 }, +} + +-- show the layout +layout:show() + +@widget status +--]]-- +return function (self) + self.layout.statusWidget = self +end diff --git a/src/lib/luigi/widget/stepper.lua b/src/lib/luigi/widget/stepper.lua new file mode 100644 index 0000000..40b8ab2 --- /dev/null +++ b/src/lib/luigi/widget/stepper.lua @@ -0,0 +1,106 @@ +--[[-- +A stepper. + +This widget is composed of two buttons and a content area. +Upon creation, this widget's children are moved into the +`items` attribute. The items are displayed one at a time in +the content area. Pressing the buttons cycles through the +item displayed in the content area. + +@widget stepper +--]]-- + +return function (self) + +--[[-- +Special Attributes + +@section special +--]]-- + +--[[-- +Content items. + +Contains an array of child widgets to be displayed. + +@attrib items +--]]-- + self:defineAttribute('items', {}) + +--[[-- +Child item index. + +Contains the index in `items` of the item being displayed. + +@attrib index +--]]-- + self:defineAttribute('index', {}) +--[[-- +@section end +--]]-- + + self.items = {} + self.index = 1 + + for index, child in ipairs(self) do + child.type = child.type or 'stepper.item' + self.items[index] = child + self[index] = nil + end + + local before = self:addChild { type = 'stepper.before' } + local view = self:addChild { type = 'stepper.view' } + local after = self:addChild { type = 'stepper.after' } + + self:onReshape(function (event) + if self.flow == 'x' then + before.height = false + after.height = false + before.width = 0 + after.width = 0 + else + before.width = false + after.width = false + before.height = 0 + after.height = 0 + end + end) + + local function updateValue () + local item = self.items[self.index] + self.value = item.value + view[1] = nil + view:addChild(item) + view:reshape() + end + + local function decrement () + if not self.items then return end + self.index = self.index - 1 + if self.index < 1 then + self.index = #self.items + end + updateValue() + end + + local function increment () + if not self.items then return end + self.index = self.index + 1 + if self.index > #self.items then + self.index = 1 + end + updateValue() + end + + before:onPress(function (event) + if event.button ~= 'left' then return end + if self.flow == 'x' then decrement() else increment() end + end) + + after:onPress(function (event) + if event.button ~= 'left' then return end + if self.flow == 'x' then increment() else decrement() end + end) + + updateValue() +end diff --git a/src/lib/luigi/widget/text.lua b/src/lib/luigi/widget/text.lua new file mode 100644 index 0000000..1d542c8 --- /dev/null +++ b/src/lib/luigi/widget/text.lua @@ -0,0 +1,441 @@ +--[[-- +A text entry area. + +@widget text +--]]-- +local ROOT = (...):gsub('[^.]*.[^.]*$', '') + +local utf8 = require(ROOT .. 'utf8') +local Backend = require(ROOT .. 'backend') + +-- make sure selection range doesn't extend past EOT +local function trimRange (self) + local max = #self.value + if self.startIndex > max then self.startIndex = max end + if self.endIndex > max then self.endIndex = max end +end + +local function updateHighlight (self) + local value = self.value + local font = self:getFont() + local startIndex, endIndex = self.startIndex, self.endIndex + local offset = self:getRectangle(true, true) - self.scrollX + self.startX = font:getAdvance(value:sub(1, startIndex)) + offset + self.endX = font:getAdvance(value:sub(1, endIndex)) + offset +end + +local function scrollToCaret (self) + updateHighlight(self) + local x1, y1, w, h = self:getRectangle(true, true) + local x2, y2 = x1 + w, y1 + h + local oldX = self.endX or x1 + + if oldX <= x1 then + self.scrollX = self.scrollX - (x1 - oldX) + updateHighlight(self) + elseif oldX >= x2 then + self.scrollX = self.scrollX + (oldX - x2 + 1) + updateHighlight(self) + end +end + +local function selectRange (self, startIndex, endIndex) + if startIndex then self.startIndex = startIndex end + if endIndex then self.endIndex = endIndex end + trimRange(self) + + scrollToCaret(self) +end + +-- return caret index +local function findIndexFromPoint (self, x, y) + local x1 = self:getRectangle(true, true) + + local font = self.fontData + local width = 0 + local lastPosition = 0 + + local function checkPosition (position) + local text = self.value:sub(1, position - 1) + width = font:getAdvance(text) + if width > x + self.scrollX - x1 then + if position == 1 then + return 0 + end + return lastPosition + end + lastPosition = position - 1 + end + + for position in utf8.codes(self.value) do + local index = checkPosition(position) + if index then return index end + end + + local index = checkPosition(#self.value + 1) + if index then return index end + + return #self.value +end + +-- move the caret or end of selection range one character to the left +local function moveCharLeft (self, alterRange) + trimRange(self) + local text, endIndex = self.value, self.endIndex + + -- clamp caret to beginning + if endIndex < 1 then endIndex = 1 end + + -- move left + local index = (utf8.offset(text, -1, endIndex + 1) or 0) - 1 + selectRange(self, not alterRange and index, index) +end + +-- move caret or end of selection range one word to the left +local function moveWordLeft (self, alterRange) + trimRange(self) + local text = self.value:sub(1, self.endIndex) + local pos = text:find('%s[^%s]+%s*$') or 0 + selectRange(self, not alterRange and pos, pos) +end + +-- move the caret or end of selection range to the beginning of the line +local function moveLineLeft (self, alterRange) + trimRange(self) + selectRange(self, not alterRange and 0, 0) +end + +-- move caret or end of selection range one character to the right +local function moveCharRight (self, alterRange) + trimRange(self) + local text, endIndex = self.value, self.endIndex + + -- clamp caret to end + if endIndex >= #text then endIndex = #text - 1 end + + -- move right + local index = (utf8.offset(text, 2, endIndex + 1) or #text) - 1 + selectRange(self, not alterRange and index, index) +end + +-- move caret or end of selection range one word to the right +local function moveWordRight (self, alterRange) + trimRange(self) + local text = self.value + local _, pos = text:find('^%s*[^%s]+', self.endIndex + 1) + pos = pos or #text + 1 + selectRange(self, not alterRange and pos, pos) +end + +-- move caret or end of selection range to the end of the line +local function moveLineRight (self, alterRange) + trimRange(self) + local text = self.value + selectRange(self, not alterRange and #text, #text) +end + +local function getRange (self) + trimRange(self) + if self.startIndex <= self.endIndex then + return self.startIndex, self.endIndex + end + + return self.endIndex, self.startIndex +end + +local function deleteRange (self) + trimRange(self) + local text = self.value + local first, last = getRange(self) + + -- if expanded range is selected, delete text in range + if first ~= last then + local left = text:sub(1, first) + local index = #left + self.value = left .. text:sub(last + 1) + selectRange(self, index, index) + return true + end +end + +local function deleteCharacterLeft (self) + trimRange(self) + local text = self.value + local first, last = getRange(self) + + -- if cursor is at beginning, do nothing + if first < 1 then + return + end + + -- delete character to the left + local offset = utf8.offset(text, -1, last + 1) or 0 + local left = text:sub(1, offset - 1) + local index = #left + self.value = left .. text:sub(first + 1) + selectRange(self, index, index) +end + +local function deleteCharacterRight (self) + trimRange(self) + local text = self.value + local first, last = getRange(self) + + -- if cursor is at end, do nothing + if first == #text then + return + end + + -- delete character to the right + local offset = utf8.offset(text, 2, last + 1) or 0 + local left = text:sub(1, first) + local index = #left + self.value = left .. text:sub(offset) + selectRange(self, index, index) +end + +local function copyRangeToClipboard (self) + trimRange(self) + local text = self.value + local first, last = getRange(self) + if last >= first + 1 then + Backend.setClipboardText(text:sub(first + 1, last)) + end +end + +local function pasteFromClipboard (self) + trimRange(self) + local text = self.value + local pasted = Backend.getClipboardText() or '' + local first, last = getRange(self) + local left = text:sub(1, first) .. pasted + local index = #left + self.value = left .. text:sub(last + 1) + selectRange(self, index, index) +end + +local function insertText (self, newText) + trimRange(self) + local text = self.value + local first, last = getRange(self) + local left = text:sub(1, first) .. newText + local index = #left + self.value = left .. text:sub(last + 1) + selectRange(self, index, index) +end + +local function isShiftPressed () + return Backend.isKeyDown('lshift', 'rshift') +end + +-- check command (gui) key, only on Mac. +local isCommandPressed +if Backend.isMac() then + isCommandPressed = function () + return Backend.isKeyDown('lgui', 'rgui') + end +else + isCommandPressed = function () + return false + end +end + +-- check command (gui) key on Mac and ctrl key everywhere else. +local isCommandOrCtrlPressed +if Backend.isMac() then + isCommandOrCtrlPressed = function () + return Backend.isKeyDown('lgui', 'rgui') + end +else + isCommandOrCtrlPressed = function () + return Backend.isKeyDown('lctrl', 'rctrl') + end +end + +-- check option (alt) key on Mac and ctrl key everywhere else. +local isOptionOrCtrlPressed +if Backend.isMac() then + isOptionOrCtrlPressed = function () + return Backend.isKeyDown('lalt', 'ralt') + end +else + isOptionOrCtrlPressed = function () + return Backend.isKeyDown('lctrl', 'rctrl') + end +end + +-- Special keys. +local function createDefaultKeyActions (self) + return { + -- let tab press propagate + ['tab'] = function () return true end, + ['backspace'] = function () + if not deleteRange(self) then + deleteCharacterLeft(self) + end + end, + ['delete'] = function () + if not deleteRange(self) then + deleteCharacterRight(self) + end + end, + ['left'] = function () + if isOptionOrCtrlPressed() then + moveWordLeft(self, isShiftPressed()) + elseif isCommandPressed() then + moveLineLeft(self, isShiftPressed()) + else + moveCharLeft(self, isShiftPressed()) + end + end, + ['right'] = function () + if isOptionOrCtrlPressed() then + moveWordRight(self, isShiftPressed()) + elseif isCommandPressed() then + moveLineRight(self, isShiftPressed()) + else + moveCharRight(self, isShiftPressed()) + end + end, + ['home'] = function () + moveLineLeft(self, isShiftPressed()) + end, + ['end'] = function () + moveLineRight(self, isShiftPressed()) + end, + ['x'] = function () + if isCommandOrCtrlPressed() then + copyRangeToClipboard(self) + deleteRange(self) + end + end, + ['c'] = function () + if isCommandOrCtrlPressed() then + copyRangeToClipboard(self) + end + end, + ['v'] = function () + if isCommandOrCtrlPressed() then + pasteFromClipboard(self) + end + end, + ['a'] = function () + if isCommandOrCtrlPressed() then + selectRange(self, 0, #self.value) + end + end, + } +end + +local textInputKeys = { + ['space'] = true, + ['return'] = true, + ['kp00'] = true, + ['kp000'] = true, + ['kp&&'] = true, + ['kp||'] = true, +} + +local function isKeyTextInput (key) + if textInputKeys[key] or #key == 1 + or (#key == 3 and key:sub(1, 2) == "kp") then + return not Backend.isKeyDown( + 'lalt', 'ralt', 'lctrl', 'rctrl', 'lgui', 'rgui') + end + return false +end + +return function (self) + self.startIndex, self.endIndex = 0, 0 + self.startX, self.endX = -1, -1 + self.scrollX = 0 + self.value = tostring(self.value or self.text or '') + self.text = '' + self.keyActions = createDefaultKeyActions(self) + +--[[-- +Special Attributes + +@section special +--]]-- + +--[[-- +Highlight color. + +Should contain an array with 3 or 4 values (RGB or RGBA) from 0 to 255. + +This color is used to indicate the selected range of text. + +@attrib highlight +--]]-- + + self:defineAttribute('highlight') + local defaultHighlight = { 0x80, 0x80, 0x80, 0x80 } +--[[-- +@section end +--]]-- + + self:onPressStart(function (event) + if event.button ~= 'left' then return end + self.startIndex = findIndexFromPoint(self, event.x) + self.endIndex = self.startIndex + scrollToCaret(self) + end) + + self:onPressDrag(function (event) + if event.button ~= 'left' then return end + self.endIndex = findIndexFromPoint(self, event.x) + scrollToCaret(self) + end) + + self:onTextInput(function (event) + insertText(self, event.text) + end) + + self:onKeyPress(function (event) + local key = event.key + local act = self.keyActions[key] + if act then + if act() then return end + end + return isKeyTextInput(key) or nil + end) + + self:onDisplay(function (event) + local startX, endX = self.startX, self.endX + local x, y, w, h = self:getRectangle(true, true) + local width, height = endX - startX, h + local font = self:getFont() + local color = self.color or { 0, 0, 0, 255 } + local textTop = math.floor(y + (h - font:getLineHeight()) / 2) + + Backend.push() + Backend.intersectScissor(x, y, w, h) + Backend.setFont(font) + + if self.focused then + -- draw highlighted selection + Backend.setColor(self.highlight or defaultHighlight) + Backend.drawRectangle('fill', startX, y, width, height) + -- draw cursor selection + if Backend.getTime() % 2 < 1.75 then + Backend.setColor(color) + Backend.drawRectangle('fill', endX, y, 1, height) + end + else + Backend.setColor { color[1], color[2], color[3], + (color[4] or 256) / 8 } + Backend.drawRectangle('fill', startX, y, width, height) + end + + -- draw text + Backend.setColor(color) + Backend.print(self.value, x - self.scrollX, textTop) + + Backend.pop() + end) + + self:onReshape(function () + updateHighlight(self) + end) +end diff --git a/src/lib/luigi/widget/window.lua b/src/lib/luigi/widget/window.lua new file mode 100644 index 0000000..237edd4 --- /dev/null +++ b/src/lib/luigi/widget/window.lua @@ -0,0 +1,265 @@ +--[[-- +Window widget. + +Set properties of the window with this widget's attributes. +This widget should only be used as the root widget of a layout. + +@usage +-- create a new window +local window = Layout { + type = 'window', + icon = 'logo.png', + text = 'Window Example', + width = 800, + height = 600, + { icon = 'logo.png', text = 'Window Example', align = 'middle center' }, + { type = 'panel', flow = 'x', height = 'auto', + {}, -- spacer + { type = 'button', id = 'quitButton', text = 'Quit' } + } +} + +-- handle quit button +window.quitButton:onPress(function () + os.exit() +end) + +-- show the window +window:show() + +@widget window +--]]-- + +local ROOT = (...):gsub('[^.]*.[^.]*$', '') + +local Backend = require(ROOT .. 'backend') + +return function (self) + + function self:calculateRootPosition (axis) + self.position[axis] = 0 + return 0 + end + + function self.painter:paintIconAndText () end + +--[[-- +Special Attributes + +@section special +--]]-- + +--[[-- +Maximized. Set to `true` to make the window as large as possible. +Set to `false` to restore the size and position. + +@attrib maximized +--]]-- + self:defineAttribute('maximized', { + set = function (_, value) + if value == nil then return end + Backend.setWindowMaximized(value) + self.layout.root:reshape() + end, + get = Backend.getWindowMaximized + }) + +--[[-- +Minimized. Set to `true` to minimize the window to an iconic representation. +Set to `false` to restore the size and position. + +@attrib minimized +--]]-- + self:defineAttribute('minimized', { + set = function (_, value) + if value == nil then return end + Backend.setWindowMinimized(value) + end, + get = Backend.getWindowMinimized + }) + +--[[-- +Borderless. Set to `true` or `false` to change the border state of the window. +You can't change the border state of a fullscreen window. + +@attrib borderless +--]]-- + self:defineAttribute('borderless', { + set = function (_, value) + if value == nil then return end + Backend.setWindowBorderless(value) + self.layout.root:reshape() + end, + get = Backend.getWindowBorderless + }) + +--[[-- +Fullscreen. Set to `true` or `false` to change the fullscreen state +of the window. + +@attrib fullscreen +--]]-- + self:defineAttribute('fullscreen', { + set = function (_, value) + if value == nil then return end + Backend.setWindowFullscreen(value) + self.layout.root:reshape() + end, + get = Backend.getWindowFullscreen + }) + +--[[-- +Mouse grab. Set to `true` or `false` to change the window's input grab mode. +When input is grabbed the mouse is confined to the window. + +If the caller enables a grab while another window is currently grabbed, +the other window loses its grab in favor of the caller's window. + +@attrib grab +--]]-- + self:defineAttribute('grab', { + set = function (_, value) + if value == nil then return end + Backend.setWindowGrab(value) + end, + get = Backend.getWindowGrab + }) + +--[[-- +Window icon. Should be a string containing a path to an image. + +@attrib icon +--]]-- + local icon + + self:defineAttribute('icon', { + set = function (_, value) + if value == nil then return end + icon = value + Backend.setWindowIcon(value) + end, + get = function () return icon end + }) +--[[-- +Maximum width of the window's client area. + +@attrib maxwidth +--]]-- + self:defineAttribute('maxwidth', { + set = function (_, value) + if value == nil then return end + Backend.setWindowMaxwidth(value) + end, + get = Backend.getWindowMaxwidth + }) + +--[[-- +Maximum height of the window's client area. + +@attrib maxheight +--]]-- + self:defineAttribute('maxheight', { + set = function (_, value) + if value == nil then return end + Backend.setWindowMaxheight(value) + end, + get = Backend.getWindowMaxheight + }) + +--[[-- +Minimum width of the window's client area. + +@attrib minwidth +--]]-- + self:defineAttribute('minwidth', { + set = function (_, value) + if value == nil then return end + Backend.setWindowMinwidth(value) + end, + get = Backend.getWindowMinwidth + }) + +--[[-- +Minimum height of the window's client area. + +@attrib minheight +--]]-- + self:defineAttribute('minheight', { + set = function (_, value) + if value == nil then return end + Backend.setWindowMinheight(value) + end, + get = Backend.getWindowMinheight + }) + +--[[-- +Position of the window's top edge. + +@attrib top +--]]-- + self:defineAttribute('top', { + set = function (_, value) + if value == nil then return end + Backend.setWindowTop(value) + end, + get = Backend.getWindowTop + }) + +--[[-- +Position of the window's left edge. + +@attrib left +--]]-- + self:defineAttribute('left', { + set = function (_, value) + if value == nil then return end + Backend.setWindowLeft(value) + end, + get = Backend.getWindowLeft + }) + +--[[-- +Width of the window's content area. + +@attrib width +--]]-- + self:defineAttribute('width', { + set = function (_, value) + if value == nil then return end + Backend.setWindowWidth(value) + self.layout.root:reshape() + end, + get = Backend.getWindowWidth + }) + +--[[-- +Height of the window's content area. + +@attrib height +--]]-- + self:defineAttribute('height', { + set = function (_, value) + if value == nil then return end + Backend.setWindowHeight(value) + self.layout.root:reshape() + end, + get = Backend.getWindowHeight + }) + +--[[-- +Title of the window. + +@attrib title +--]]-- + self:defineAttribute('title', { + set = function (_, value) + if value == nil then return end + Backend.setWindowTitle(value) + end, + get = Backend.getWindowTitle + }) + +--[[-- +@section end +--]]-- +end