Last active
January 10, 2020 19:04
-
-
Save caiwan/a713dc51b3baf366fd58e61111a5790e to your computer and use it in GitHub Desktop.
Caiwan's LMC macro framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- ------------------------------------------------------------------------------------ | |
-- * Caiwan's LMC macro framework | |
-- * Usage: | |
-- * Place this file to <LuaMacros>/lua/caiwan_macro_framework.lua | |
-- ------------------------------------------------------------------------------------ | |
-- Helpers | |
function bit(p) return bit32.lshift(1, p) end | |
function hasbit(x, p) return bit32.band(x, p) ~=0 end | |
function setbit(x, p) return bit32.bor(x, p) end | |
function clearbit(x, p) return bit32.band(x, bit32.bnot(p)) end | |
-- Simulates JS-like Dynamic scoping | |
-- e.g. injectin object into function | |
function bind(t, k) return function(...) return t[k](t, ...) end end | |
-- Modifier keys | |
SHIFT_KEY = 16 | |
CTRL_KEY = 17 | |
ALT_KEY = 18 | |
MODIFIER_KEY_BITS = { | |
[SHIFT_KEY] = bit(0), | |
[CTRL_KEY] = bit(1), | |
[ALT_KEY] = bit(2) | |
} | |
MODIFIER_OFFSET = bit(3) | |
-- ------------------------------------------------------------------------------------ | |
MacroDispatcher = {} | |
MacroDispatcher.__index = MacroDispatcher | |
setmetatable(MacroDispatcher, { | |
__call = function (cls, ...) | |
local self = setmetatable({}, cls) | |
self:_init(...) | |
return self | |
end, | |
}) | |
-- Ctor | |
function MacroDispatcher:_init(name, identifier) | |
setmetatable({}, self) | |
self.name = name | |
self.identifier = identifier or '0000AAA' | |
self._modifier_keys = 0 | |
self._keypress_map = {} | |
self._bindings = {} | |
self.ignore_unbound_keys = false | |
return self | |
end | |
-- Binds a functiuon to be executed | |
function MacroDispatcher:bind_fn(fn, key, ...) | |
local args = table.pack(...) | |
local modifiers = 0 | |
for i=1, args.n do | |
local key = args[i] | |
if (MODIFIER_KEY_BITS[key] ~= nil) then modifiers = setbit(modifiers, MODIFIER_KEY_BITS[key]) end | |
end | |
local id = MODIFIER_OFFSET * key + modifiers | |
self._bindings[id] = fn | |
end | |
-- assign logical name to macro keyboard | |
-- then sets up callback handlers | |
function MacroDispatcher:register_keyboard() | |
if self.identifier == '0000AAA' then | |
lmc_assign_keyboard(self.name); | |
else | |
lmc_device_set_name(self.name, self.identifier) | |
end | |
lmc_set_handler(self.name, bind(self, '_handler')) | |
end | |
-- Callback | |
function MacroDispatcher:_handler(button, direction) | |
-- 0 = up 1 = down | |
if (self._keypress_map[button] == 1 and direction == 1) then return end -- ignore repeat | |
self._keypress_map[button] = direction | |
-- print('beep') | |
if (MODIFIER_KEY_BITS[button] ~= nil) then | |
if direction == 1 then | |
self._modifier_keys = setbit(self._modifier_keys, MODIFIER_KEY_BITS[button]) | |
else | |
self._modifier_keys = clearbit(self._modifier_keys, MODIFIER_KEY_BITS[button]) | |
end | |
end | |
local id = MODIFIER_OFFSET * button + self._modifier_keys | |
if (self._bindings[id] ~= nil ) then | |
-- release all modifiers first | |
if direction == 1 then -- only invoke when pushing button down; there's no other support atm | |
if (not self.ignore_unbound_keys) then if self:_set_modifiers(0) then lmc_sleep(25) end end | |
-- Invoke callback | |
local fn = self._bindings[id] | |
fn(self, button, direction) | |
-- press all modifiers agaion | |
if (not self.ignore_unbound_keys) then | |
lmc_sleep(25) | |
self:_set_modifiers(1) | |
end | |
end | |
else | |
-- Pass through otherwise | |
if (not self.ignore_unbound_keys) then self:send_input(button, direction) end | |
end | |
print( self.name .. ' : ' .. button .. ', ' .. direction .. ' ' | |
.. 'Modifiers: ' .. self._modifier_keys .. ' ' | |
.. 'ID: ' .. id .. ' ' | |
.. (self._bindings[id] ~= nil and '*fn' or '') | |
) | |
end | |
function MacroDispatcher:_set_modifiers(direction) | |
local is_set = false | |
for k, v in pairs(MODIFIER_KEY_BITS) do if hasbit(self._modifier_keys, v) then | |
is_set = true | |
self:send_input(k, direction) | |
end end | |
return is_set | |
end | |
function MacroDispatcher:send_input(button, direction) | |
-- -- 0 = up 1 = down | |
-- -- https://docs.microsoft.com/en-us/windows/desktop/api/winuser/ns-winuser-tagkeybdinput | |
local a = 0 | |
if direction == 0 then a = 2 end | |
-- TODO: There is a logger inside LMC for this | |
print('Send input: ' .. button .. ', ' .. direction) | |
lmc_send_input(button, 0, a) | |
end | |
function MacroDispatcher:send_keys(stroke) | |
-- TODO: There is a logger inside LMC for this | |
print('Keys: ' .. stroke) | |
lmc_send_keys(stroke) | |
end | |
-- Bootstrap | |
-- TODO: Set up loggers, etc. here | |
function main (fn) fn() end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- Photoshop shortcuts | |
-- | |
clear() | |
require 'caiwan_macro_framework' | |
-- photoshop function keystroke definitions | |
ps = { | |
increase_brush_size = function(m) m:send_keys('[') end, | |
decrease_brush_size = function(m) m:send_keys(']') end, | |
increase_brush_hardness = function(m) m:send_keys('+[') end, | |
decrease_brush_hardness = function(m) m:send_keys('+]') end, | |
zoom_in = function(m) m:send_keys('^{NUMPLUS}') end, | |
zoom_out = function(m) m:send_keys('^{NUMMINUS}') end, | |
zoom_reset = function(m) m:send_keys('^0') end, | |
undo = function(m) m:send_keys('^z') end, | |
redo = function(m) m:send_keys('^+z') end, | |
history_step_back = function(m) m:send_keys('^%z') end, | |
is_brush_selected = false, | |
toggle_brush = function(m) | |
ps.is_brush_selected = not ps.is_brush_selected | |
if (ps.is_brush_selected) then m:send_keys('b') | |
else m:send_keys('e') end | |
end, | |
delete = function(m) m:send_keys('{DELETE}') end, | |
lasso_tool = function(m) m:send_keys('l') end, | |
deselect = function(m) m:send_keys('^d') end, | |
invert_selection = function(m) m:send_keys('^+i') end, | |
marquee_tool = function(m) m:send_keys('m') end, | |
move_tool = function(m) m:send_keys('v') end, | |
-- rotate = function() m:send_keys('r') end | |
is_rotation_selected = false, | |
rotate = function(m) | |
ps.is_rotation_selected = not ps.is_rotation_selected | |
if (ps.is_rotation_selected) then m:send_keys('r') | |
else if (ps.is_brush_selected) then m:send_keys('b') | |
else m:send_keys('e') end | |
end | |
end, | |
transformation_tool = function(m) m:send_keys('^t') end, | |
swap_colors = function(m) m:send_keys('x') end, | |
flip_canvas = function(m) m:send_keys('^+=') end, | |
switch_document_next = function(m) m:send_keys('^{TAB}') end, | |
switch_document_prev = function(m) m:send_keys('+^{TAB}') end, | |
new_layer = function(m) m:send_keys('^+n~') end, | |
merge_layer_down = function(m) m:send_keys('^e') end, | |
select_all = function(m) m:send_keys('^a') end, | |
cut = function(m) m:send_keys('^x') end, -- TODO Does not work when boudn to `xcv` and `s` | |
copy = function(m) m:send_keys('^c') end, | |
paste = function(m) m:send_keys('^v') end, | |
save = function(m) m:send_keys('^s') end, | |
save_as = function(m) m:send_keys('^+s') end, | |
open = function(m) m:send_keys('^o') end, | |
magic_wand = function(m) m:send_keys('w') end, | |
gradient_fill = function(m) m:send_keys('g') end, | |
hue_saturation = function(m) m:send_keys('^u') end, | |
desaturate = function(m) m:send_keys('+^u') end, | |
levels = function(m) m:send_keys('^l') end, | |
curves = function(m) m:send_keys('^m') end, | |
group = function(m) m:send_keys('^g') end, | |
toggle_clipping_mask = function(m) m:send_keys('^%g') end, | |
-- TODO: This gets ignored, switches to hand tool instead of zoom out-and-in | |
hand_tool = function(m, b, d) m:send_input(72, d) end, -- 72 = `H` | |
action_create_base = function(m) m:send_keys('^+{F3}') end, | |
action_extend_selection = function(m) m:send_keys('^+{F4}') end, | |
-- TODO This does not work bacause fw cannot unbind ctrl key and opens quickfind instead (`ctlr+f` instead of `f`) | |
toggle_fullscreen = function(m) m:send_keys('f') end | |
} | |
function key_passthrough(m, b, d) m:send_input(b, d) end | |
main(function() | |
clear() | |
print(_VERSION) | |
local macros = MacroDispatcher('MACROS') | |
macros.ignore_unbound_keys = false | |
-- esc | |
macros:bind_fn(key_passthrough, 27) | |
-- function key line | |
macros:bind_fn(ps.action_create_base, 112) -- G1 | |
macros:bind_fn(ps.action_extend_selection, 113) -- G2 | |
macros:bind_fn(ps.curves, 114) -- G3 | |
macros:bind_fn(ps.levels, 115) -- G4 | |
macros:bind_fn(ps.hue_saturation, 116) -- G5 | |
macros:bind_fn(ps.desaturate, 116, CTRL_KEY) -- CTRL + G5 | |
macros:bind_fn(ps.toggle_fullscreen, 116, CTRL_KEY, ALT_KEY) -- CTRL + ALT + G5 | |
-- `~123456` line | |
macros:bind_fn(ps.save, 223) -- ~ | |
macros:bind_fn(ps.save_as, 223, SHIFT_KEY) -- SHIFT + ~ | |
macros:bind_fn(ps.open, 223, CTRL_KEY) -- CTRL + ~ | |
macros:bind_fn(ps.new_layer, 49) -- 1 | |
macros:bind_fn(ps.group, 49, CTRL_KEY) -- CTRL + 1 | |
macros:bind_fn(ps.merge_down, 50) -- 2 | |
macros:bind_fn(ps.toggle_clipping_mask, 51) -- 3 | |
-- macros:bind_fn(52) -- 4 | |
-- macros:bind_fn(53) -- 5 | |
-- macros:bind_fn(54) -- 6 | |
-- `qwert` line | |
macros:bind_fn(ps.lasso_tool, 81) -- Q | |
macros:bind_fn(ps.magic_wand, 81, CTRL_KEY) -- CTRL + Q | |
macros:bind_fn(ps.marquee_tool, 81, ALT_KEY) -- ALT + Q | |
macros:bind_fn(ps.increase_brush_size, 87) -- W | |
macros:bind_fn(ps.increase_brush_hardness, 87, CTRL_KEY) -- CTRL + W | |
macros:bind_fn(ps.decrease_brush_size, 69) -- E | |
macros:bind_fn(ps.decrease_brush_hardness, 69, CTRL_KEY) -- CTRL + E | |
macros:bind_fn(ps.toggle_brush, 82) -- R | |
macros:bind_fn(ps.undo, 84) -- T | |
macros:bind_fn(ps.redo, 84, SHIFT_KEY) -- SHIFT + T | |
macros:bind_fn(ps.delete, 84, CTRL_KEY) -- CTRL + T | |
-- `asdfg` line | |
-- TODO: `asd` are unuded (65, 83, 68) | |
-- macros:bind_fn(65] = ctrl_switch(on_up(select_all), on_down(new_layer)) -- A | |
-- macros:bind_fn(83] = ctrl_switch(on_up(save), on_down(merge_layer_down)) -- S | |
-- macros:bind_fn(ps.move_tool, 68) -- D | |
macros:bind_fn(ps.deselect, 70) -- F | |
macros:bind_fn(ps.invert_selection, 70, CTRL_KEY) -- CTRL + F | |
macros:bind_fn(ps.zoom_in, 71) -- G | |
-- `zxcvb` line | |
macros:bind_fn(ps.rotate, 90) -- Z | |
macros:bind_fn(ps.swap_colors, 88) -- X | |
-- macros:bind_fn(ps.cut, 88, CTRL_KEY) -- CTRL + X | |
macros:bind_fn(ps.move_tool, 67) -- C | |
-- macros:bind_fn(ps.copy 67, CTRL_KEY) -- CTRL + C | |
macros:bind_fn(ps.transformation_tool, 86) -- V | |
-- macros:bind_fn(ps.paste, 86, CTRL_KEY) -- CTRL + V | |
macros:bind_fn(ps.zoom_out, 66) -- B | |
-- tab | |
macros:bind_fn(ps.flip_canvas, 9) | |
macros:bind_fn(ps.switch_document_next, 9, CTRL_KEY) | |
macros:bind_fn(ps.switch_document_prev, 9, CTRL_KEY, SHIFT_KEY) | |
-- space | |
macros:bind_fn(key_passthrough, 32) -- TODO: supposed to be the hand tool | |
macros:bind_fn(ps.zoom_reset, 32, CTRL_KEY) | |
macros:register_keyboard() | |
end) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment