Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- dkeylib = {
- -- Feel free to edit the below settings
- layoutName = "US", -- Keyboard layout
- repeatDelay = 0.5, -- Delay before starting key repeating (In seconds)
- repeatInterval = 0.02, -- Delay between each repeated key (In seconds)
- -- end of editable settings --
- version = 1, -- Version Number
- versionMinor = 3, -- Minor version number
- loaded = false, -- true if dkeylib has been loaded. false otherwise
- layouts = {}, -- Layouts for various keyboards
- layout = {}, -- Current layout
- processors = {}, -- Various extra processing functions you may choose to use
- pressedvkeys = {}, -- Current virtual keys that have been pressed
- bind = {}, -- Namespace for bind related functions
- allbinds = {}, -- Current keyboard binds
- key = {}, -- Last pressed key
- }
- --[[
- -- Notes for lua script writers developer --
- It's recommended you do not modify anything marked as readonly,
- as the behaviour of doing so is not defined.
- Bind interface
- dkeylib:newbind(name [, priority])
- name string The name of the bind (This must be unique)
- priority int The priority of the bind (higher goes first) default = 0
- returns: bind object see bindobj
- Create a new bind object with the name and priority specified
- dkeylib:getbind(name)
- name string The name of the bind to get
- returns: bind object or nil if the bind does not exist
- Get the bind object with the name name
- dkeylib:isvkeydown(vkeys)
- vkeys multiple A single vkey, or a table of vkeys to test
- returns: true if all the specified vkeys are currently down, false otherwise
- Test if a vkey is being pressed (search for [MAPPINGS])
- --------------------------------------------------------------------------------
- bindobj = {
- enabled if true, the bind is enabled and bound functions are called
- name readonly. The name passed into newbind when this bind was created
- priority readonly. The priority of the bind object (higher goes first)
- }
- bindobj:bind(f [, key, modifiers, userdata])
- f function The handler for this bind
- key multiple The key to bind the handler to default = nil
- modifiers multiple The key modifiers (shift etc.) default = -1
- userdata multiple Extra user specified data to pass to f default = nil
- returns: nil
- Bind the function f.
- If key is nil then f becomes the global key handler
- modifiers has no effect
- Otherwise f is bound to key specified
- modifiers can be a number or a table of modifiers (see vkeys)
- -1 will bind f to any combination of modifiers
- The function you should pass into bind should look like the following
- function bind( key, userdata )
- [or]
- function bind( key )
- Search for [KEY] to get an idea of what key is.
- Binds are called in the following order
- start at highest bind
- call specific modifier bind (eg. shift+r)
- call key handler
- call global key handler
- move to lower priority bind and repeat
- If any bind at any point returns true then no more binds will be processed
- and the event will be eaten and not passed to powder toy.
- ]]--
- ------------------------------------------------
- -- LAYOUTS -------------------------------------
- ------------------------------------------------
- dkeylib.layouts.US = function(self, layout)
- self:addprocessor(layout, "shiftdef")
- self:addprocessor(layout, "shiftmap")
- self:addprocessor(layout, "special")
- -- Shifted Numbers Map
- layout.numbers = {")", "!", "@", "#", "$", "%", "^", "&", "*", "("}
- -- Shifted Symbols Map
- layout.shift["-"] = "_"
- layout.shift["="] = "+"
- layout.shift["["] = "{"
- layout.shift["]"] = "}"
- layout.shift[";"] = ":"
- layout.shift["'"] = "\""
- layout.shift[","] = "<"
- layout.shift["."] = ">"
- layout.shift["/"] = "?"
- layout.shift["\\"] = "|"
- -- Modifier key map
- layout.modifiers = {
- { 1, self.vkey.shift, self.vkey.shift_left},
- { 2, self.vkey.shift, self.vkey.shift_right},
- { 64, self.vkey.ctrl , self.vkey.ctrl_left},
- {128, self.vkey.ctrl , self.vkey.ctrl_right},
- {256, self.vkey.alt , self.vkey.alt_left},
- {512, self.vkey.alt , self.vkey.alt_left},
- {8192, self.vkey.capslock}
- }
- layout.vkey[96] = self.vkey.console
- return layout
- end
- dkeylib.layouts.default = dkeylib.layouts.US
- dkeylib.layouts.ISO = function(self, layout)
- self:addprocessor(layout, "shiftdef")
- self:addprocessor(layout, "shiftmap")
- self:addprocessor(layout, "special")
- -- Shifted Numbers Map
- layout.numbers = {")", "!", "@", "#", "$", "%", "^", "&", "*", "("}
- -- Shifted Symbols Map
- layout.shift["-"] = "_"
- layout.shift["="] = "+"
- layout.shift["["] = "{"
- layout.shift["]"] = "}"
- layout.shift[";"] = ":"
- layout.shift["'"] = "\""
- layout.shift[","] = "<"
- layout.shift["."] = ">"
- layout.shift["/"] = "?"
- layout.shift["\\"] = "|"
- -- Modifier key map
- layout.modifiers = {
- { 1, self.vkey.shift, self.vkey.shift_left},
- { 2, self.vkey.shift, self.vkey.shift_right},
- { 64, self.vkey.ctrl , self.vkey.ctrl_left},
- {128, self.vkey.ctrl , self.vkey.ctrl_right},
- {256, self.vkey.alt , self.vkey.alt_left},
- {512, self.vkey.alt , self.vkey.alt_left},
- {8192, self.vkey.caps_lock}
- }
- layout.vkey[96] = self.vkey.console
- return layout
- end
- ------------------------------------------------
- -- PROCESSORS ----------------------------------
- ------------------------------------------------
- dkeylib.processors.shiftdef = function(self, layout, key)
- local m = key.modifier
- local v = key.value
- local s = self:isvkeydown(self.vkey.shift)
- local c = self:isvkeydown(self.vkey.capslock)
- -- Mappings for shifted keys
- if s then
- if v >= 48 and v <= 57 then
- key.pstr = layout.numbers[v - 47]
- end
- end
- if s or c then
- if s ~= c then
- if v >= 97 and v <= 122 then
- key.pstr = string.char(v - 32)
- end
- end
- end
- end
- dkeylib.processors.special = function(self, layout, key)
- if key.value >= 256 or key.value < 32 then
- key.pstr = "[S]"
- end
- end
- dkeylib.processors.shiftmap = function(self, layout, key)
- -- Shift map
- local s = self:isvkeydown(self.vkey.shift)
- if s then
- local temp = layout.shift[key.str]
- if temp ~= nil then
- key.pstr = temp
- end
- end
- end
- ------------------------------------------------
- -- [MAPPINGS] ----------------------------------
- ------------------------------------------------
- dkeylib.vkey = {
- shift = 1,
- shift_left = 2,
- shift_right = 3,
- ctrl = 4,
- ctrl_left = 5,
- ctrl_right = 6,
- alt = 7,
- alt_left = 8,
- alt_right = 9,
- capslock = 10,
- shiftlock = 11,
- numlock = 12,
- }
- ------------------------------------------------
- -- [KEY] ---------------------------------------
- ------------------------------------------------
- dkeylib.key = {
- value = 0, -- Value of last key pressed
- modifier = 0, -- Modifier value of last key pressed
- str = 0, -- Key string
- pstr = 0, -- Processed key string ([S] for special key)
- event = 0, -- 1 = Pressed, 2 = Released, 3 = Held
- -- Used internally for repeating (don't use these if possible)
- downstart = 0, -- Time when key was pressed
- down = 0, -- 0 = Up, 1 = Down, 2 = Pressed
- }
- ------------------------------------------------
- -- CORE FUNCTIONS ------------------------------
- ------------------------------------------------
- table.compare = function(a, b)
- if #a ~= #b then
- return false
- end
- local ai, as, ab = pairs(a)
- local bi, bs, bc = pairs(b)
- for i=1,#a do
- ak, av = ai(as, ac)
- bk, bv = bi(bs, bc)
- if ak ~= bk or av ~= bv then
- return false
- end
- end
- return true
- end
- dkeylib.isvkeydown = function(self, search)
- if type(search) == "table" then
- matched = 0
- for i=1,#search do
- if self.pressedvkeys[search[i]] == true then
- matched = matched + 1
- end
- end
- return matched == #search
- end
- return self.pressedvkeys[search] == true
- end
- dkeylib.addprocessor = function(self, layout, processor)
- -- Lookup processor
- local p = self.processors[processor]
- if p == nil then
- error("A processor by the name '" .. processor .. "' does not exist")
- return
- end
- table.insert(layout.process, p)
- end
- dkeylib.newlayout = function(self)
- local layout = {
- process = {}, -- Functions to process input
- shift = {}, -- Shifted symbols map (run before process)
- modifiers = {}, -- Map of modifiers to their vkey counterparts
- vkey = {}, -- Map of other keys to their vkey counterparts
- }
- return layout
- end
- -- set the layout to the specified layout
- dkeylib.setlayout = function(self, layout)
- local l = dkeylib.layouts[layout]
- if l == nil then
- -- Set the default layout and throw an error
- dkeylib.layout = dkeylib.layouts.default(dkeylib, dkeylib:newlayout())
- error("A keyboard layout with the name " .. tostring(layout) .. " does not exist")
- end
- dkeylib.layout = l(dkeylib, dkeylib:newlayout())
- end
- ------------------------------------------------
- -- BINDS ---------------------------------------
- ------------------------------------------------
- dkeylib.newbind = function(self, name, priority)
- if priority == nil or type(priority) ~= "number" then
- priority = 0
- end
- if self:getbind(name) ~= nil then
- error("A bind already exists with that name")
- end
- local temp = {
- enabled = true,
- name = name,
- priority = priority,
- binds = {},
- handler = nil,
- --
- bind = dkeylib.bind.bind,
- }
- table.insert(self.allbinds, temp)
- table.sort(self.allbinds, dkeylib.bind.sort)
- return temp
- end
- dkeylib.getbind = function(self, name)
- for i,v in pairs(self.allbinds) do
- if v.name == name then
- return v
- end
- end
- return nil
- end
- dkeylib.bind.sort = function(a, b)
- return a.priority > b.priority
- end
- dkeylib.bind.bind = function(self, f, key, modifiers, userdata)
- if type(f) ~= "function" then
- error("f must be a function")
- end
- if key == nil then
- if self.handler ~= nil then
- error("A handler has already been defined for the bind group " .. self.name)
- return
- end
- self.handler = { f = f, userdata = userdata }
- return
- end
- if type(key) == "string" then
- if #key ~= 1 then
- error("Bind target is longer than a single character '" .. key .. "'")
- end
- key = string.byte(key)
- end
- if type(key) == "number" then
- if self.binds[key] == nil then
- self.binds[key] = {}
- end
- if modifiers == nil or modifiers == -1 then
- if self.binds[key][-1] ~= nil then
- error("A handler for the bind '" .. tostring(key) .. "' has already been defined.")
- end
- self.binds[key][-1] = { f = f, userdata = userdata }
- return
- end
- if type(modifiers) == "number" then
- modifiers = {modifiers}
- end
- for i,v in pairs(self.binds[key]) do
- if table.compare(i, modifiers) then
- error("A handler for the bind '" .. tostring(key) .. "' has already been defined.")
- end
- end
- self.binds[key][modifiers] = { f = f, userdata = userdata }
- return
- end
- error("Unable to bind function, unknown key '" .. tostring(key) .. "' to bind group " .. self.name)
- end
- function pobj(obj)
- o = ""
- for i,v in pairs(obj) do
- o = o .. tostring(i) .. ": " .. tostring(v) .. "\n"
- end
- return o
- end
- dkeylib.callbind = function(self, bind, key)
- if bind.enabled ~= true then
- return
- end
- if bind.binds[key.value] == nil then
- if bind.handler ~= nil then
- return ecall(bind.handler.f, key, bind.handler.userdata)
- end
- return
- end
- for i,v in pairs(bind.binds[key.value]) do
- if self:isvkeydown(i) then
- if ecall(v.f, key, v.userdata) == true then
- return true
- end
- break
- end
- end
- local v = bind.binds[key.value][-1]
- if v ~= nil then
- if ecall(v.f, key, v.userdata) == true then
- return true
- end
- end
- if bind.handler ~= nil then
- return ecall(bind.handler.f, key, bind.handler.userdata)
- end
- return false
- end
- ------------------------------------------------
- -- HOOKS & INITIALIZATION ----------------------
- ------------------------------------------------
- -- Handle held keys
- dkeylib.hookStep = function()
- local key = dkeylib.key
- if key.down == 1 then
- if os.clock() - key.downstart > dkeylib.repeatDelay then
- key.down = 2
- end
- end
- if key.down == 2 and (os.clock() - key.downstart) > dkeylib.repeatInterval then
- dkeylib.hookKey(key.str, key.value, key.modifier, 3)
- end
- end
- -- Main key processing function
- dkeylib.hookKey = function(keyString, value, modifier, event)
- local key = dkeylib.key
- local layout = dkeylib.layout
- key.downstart = os.clock()
- key.event = event
- if event ~= 3 then
- key.down = 2 - event
- end
- -- Update modifier keys
- if key.modifier ~= modifier then
- dkeylib.pressedvkeys = {}
- if modifier ~= 0 then
- for i=1,#layout.modifiers do
- m = layout.modifiers[i]
- f = m[1]
- if modifier % (2*f) >= f then
- for ii=2,#m do
- if dkeylib.pressedvkeys[m[ii]] ~= true then
- dkeylib.pressedvkeys[m[ii]] = true
- end
- end
- end
- end
- end
- -- Force dkeylib to reprocess this key
- key.value = 0
- key.modifier = modifier
- end
- -- Update current pressed key & Reaquire current binds
- if key.value ~= value then
- key.value = value
- key.str = keyString
- key.pstr = keyString
- -- Run current layout processors
- for i=1,#layout.process do
- local temp = layout.process[i]
- if temp(dkeylib, layout, key) == true then
- break
- end
- end
- end
- -- Run all binds
- for k,bind in pairs(dkeylib.allbinds) do
- if dkeylib:callbind(bind, key) == true then
- return false
- end
- end
- return true
- end
- dkeylib.load = function()
- tpt.register_keypress(dkeylib.hookKey)
- tpt.register_step(dkeylib.hookStep)
- dkeylib:setlayout(dkeylib.layoutName)
- tpt.log(string.format("Loaded dkeylib V%d.%d", dkeylib.version, dkeylib.versionMinor))
- dkeylib.loaded = true
- end
- dkeylib.unload = function()
- tpt.unregister_step(dkeylib.hookStep)
- tpt.unregister_keypress(dkeylib.hookKey)
- dkeylib.loaded = false
- end
- ------------------------ DKEYLIB TEST CODE ------------------------
- dkeylib.load()
- -- Call cmd & automatically call error (w/ error message) upon error
- function ecall(cmd, ...)
- _, result = pcall(cmd, unpack(arg))
- if _ == false then
- error(result)
- end
- return result, _
- end
- local _e = "No key has been pressed - Extra: None"
- local function draw()
- tpt.drawtext(0, 10, "DKEYLIB TESTMODE Version " .. dkeylib.version .. "." .. dkeylib.versionMinor)
- tpt.drawtext(0, 20, "Please test the following keys. a, f, shift+r, shift+ctrl+r")
- tpt.drawtext(0, 30, "Extra should be 12345 for a, override for f, shift+r for shift+r and shift+ctrl+r for shift+ctrl+r")
- tpt.drawtext(0, 40, "Try pressing and holding a key, E should change to 3")
- tpt.drawtext(0, 60, "Key Pressed: " .. _e)
- tpt.hud(0)
- end
- tpt.register_step(draw)
- local function test(key, userdata)
- _e = string.format("S: %s E: %d V: %d - Extra: %s", key.pstr, key.event, key.value, tostring(userdata))
- return true
- end
- -- You do not need to worry about userdata
- local function testany(key)
- _e = string.format("S: %s E: %d V: %d - Extra: None", key.pstr, key.event, key.value)
- return true
- end
- ----- Overriding
- -- override bind has a higher priority than test so its binds will get called first
- local b2 = dkeylib:newbind("override bind", 1000)
- b2:bind(test, 'f', -1, "override")
- local b = dkeylib:newbind("test bind") -- Create a new bind with default priority, called test
- -- Bind to any key press of 'a', userdata can be anything
- b:bind(test, 'a', -1, 12345)
- -- Bind to shift+ctrl+r
- b:bind(test, 'r', {dkeylib.vkey.shift, dkeylib.vkey.ctrl}, "shift+ctrl+r" )
- -- Bind to only shift+r
- b:bind(test, 'r', dkeylib.vkey.shift, "shift+r" )
- -- Bind to every other key press
- b:bind(testany)
- -- If you wanted to pass userdata to testany
- -- b:bind(testany, nil, nil, "User data")
Advertisement
Add Comment
Please, Sign In to add comment