Knu.spoon icon indicating copy to clipboard operation
Knu.spoon copied to clipboard

Overlapping set of keychords

Open kiryph opened this issue 3 years ago • 3 comments

I try to adapt Key chords to apply commonly used styles to Inkscape objects on macOS.

The list of key chords are shown in this graphic

default-styles-names2

The author has published his key chord setup on github https://github.com/gillescastel/inkscape-shortcut-manager for linux.

I have already managed to adapt the creation of svg snippets for pasting. The remaining issue is that defining a large overlapping set of key chords using https://github.com/knu/hs-knu/blob/master/chord.lua does not work as expected. Only a subset works.

knu = require("knu")

-- Function to guard a given object from GC
guard = knu.runtime.guard

-- Keychords (48x)
keychords = {}
-- work
keychords[1]  = {"f", "space"}
keychords[2]  = {"w", "space"}
keychords[3]  = {"b", "space"}
keychords[4]  = {"s", "space"}
keychords[5]  = {"d", "space"}
keychords[6]  = {"e", "space"}
-- do not work
keychords[7]  = {"s", "f"}
keychords[8]  = {"d", "f"}
keychords[9]  = {"e", "f"}
keychords[10] = {"s", "w"}
keychords[11] = {"d", "w"}
keychords[12] = {"e", "w"}
keychords[13] = {"s", "g"}
keychords[14] = {"d", "g"}
keychords[15] = {"e", "g"}
keychords[16] = {"s", "f", "g"}
keychords[17] = {"d", "f", "g"}
keychords[18] = {"e", "f", "g"}
keychords[19] = {"s", "w", "g"}
keychords[20] = {"d", "w", "g"}
keychords[21] = {"e", "w", "g"}
keychords[22] = {"s", "v"}
keychords[23] = {"d", "v"}
keychords[24] = {"e", "v"}
keychords[25] = {"s", "f", "v"}
keychords[26] = {"d", "f", "v"}
keychords[27] = {"e", "f", "v"}
keychords[28] = {"s", "w", "v"}
keychords[29] = {"d", "w", "v"}
keychords[30] = {"e", "w", "v"}
keychords[31] = {"s", "a"}
keychords[32] = {"d", "a"}
keychords[33] = {"e", "a"}
keychords[34] = {"s", "a", "v"}
keychords[35] = {"d", "a", "v"}
keychords[36] = {"e", "a", "v"}
keychords[37] = {"s", "a", "g"}
keychords[38] = {"d", "a", "g"}
keychords[39] = {"e", "a", "g"}
keychords[40] = {"s", "x"}
keychords[41] = {"d", "x"}
keychords[42] = {"e", "x"}
keychords[43] = {"s", "x", "v"}
keychords[44] = {"d", "x", "v"}
keychords[45] = {"e", "x", "v"}
keychords[46] = {"s", "x", "g"}
keychords[47] = {"d", "x", "g"}
keychords[48] = {"e", "x", "g"}

knuchords = {}
for k, v in pairs(keychords) do
    knuchords[k] = knu.chord.new({}, v, function() print(hs.inspect.inspect(v)); end, 0.5)
end

local InkscapeWF = hs.window.filter.new("Inkscape")

InkscapeWF
    :subscribe(hs.window.filter.windowFocused, function()
        print("starting keychords")
        for k,v in pairs(knuchords) do
            guard(v:start())
        end
    end)
    :subscribe(hs.window.filter.windowUnfocused, function()
        print("stopping keychords")
        for k,v in pairs(knuchords) do
            guard(v:stop())
        end
    end)

@knu Do you know where the issue is located and how one could resolve it?

kiryph avatar Jan 07 '22 09:01 kiryph

That's a massive use case of key chords! I can imagine too many active event taps could lead to an unreliable state because the event loop of the operating system has strict time constraints. Under the hood knu.chord creates one event tap for each chord, so it may not scale well.

Instead of using knu.chord, I'd suggest you use Karabiner-Elements for the chord part, mapping simultaneous non-modifier key combinations to modifiers + single keys, and then define them as hotkeys on Hammerspoon.

knu avatar Jan 08 '22 01:01 knu

Apart from that, I cannot deny an overlapping set of key chords could not work well, depending on the order of definitions. Each event tap created for a key chord holds key events for a certain amount of time, and when those are accumulated to be more than a timeout of the later chord, it will always fail to form.

This could be fixed by making a giant event tap that would check all defined chords, but that's going to be a lot of work.

knu avatar Jan 08 '22 02:01 knu

Thanks for your comments.

I can imagine too many active event taps could lead to an unreliable state because the event loop of the operating system has strict time constraints.

I also suspect this.

Karabiner Elements instead of knu.chord

Instead of using knu.chord, I'd suggest you use Karabiner-Elements for the chord part, mapping simultaneous non-modifier key combinations to modifiers + single keys, and then define them as hotkeys on Hammerspoon.

I have tried Karabiner Elements. Karabiner Elements actually offers also to.shell_command so I do not have to remap to modifier + single key. Since I have written the function create_svg_and_paste(keys) for hammerspoon:

~/.hammerspoon/init.lua
-- Helper function
local function intersect(m,n)
 local r={}
 for i,v1 in ipairs(m) do
  for k,v2 in pairs(n) do
   if (v1==v2) then
    return true
   end
  end
 end
 return false
end

-- Helper function
local function has_value (tab, val)
    for index, value in ipairs(tab) do
        if value == val then
            return true
        end
    end
    return false
end

function create_svg_and_paste(keys)

    print(hs.inspect.inspect(keys))

    pt = 1.327 -- pixels
    w = 1 * pt
    thick_width = 4 * pt
    very_thick_width = 8 * pt

    style = {}
    style["stroke-opacity"] = 1

    if intersect({"s", "a", "d", "g", "h", "x", "e"}, keys)
    then
        style["stroke"] = "black"
        style["stroke-width"] = w
        style["marker-end"] = "none"
        style["marker-start"] = "none"
        style["stroke-dasharray"] = "none"
    else
        style["stroke"] = "none"
    end

    if has_value(keys, "g")
    then
        w = thick_width
        style["stroke-width"] = w
    end

    if has_value(keys, "h")
    then
        w = very_thick_width
        style["stroke-width"] = w
    end

    if has_value(keys, "a")
    then
        style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
    end

    if has_value(keys, "x")
    then
        style['marker-start'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
        style['marker-end'] = 'url(#marker-arrow-' .. tostring(w) .. ')'
    end

    if has_value(keys, "d")
    then
        style['stroke-dasharray'] = tostring(w) .. ',' .. tostring(2*pt)
    end

    if has_value(keys, "e")
    then
        style['stroke-dasharray'] = tostring(3*pt) .. ',' .. tostring(3*pt)
    end

    if has_value(keys, "f")
    then
        style['fill'] = 'black'
        style['fill-opacity'] = 0.12
    end

    if has_value(keys, "b")
    then
        style['fill'] = 'black'
        style['fill-opacity'] = 1
    end

    if has_value(keys, "w")
    then
        style['fill'] = 'white'
        style['fill-opacity'] = 1
    end

    if intersect(keys, {"f", "b", "w"})
    then
        style['marker-end'] = 'none'
        style['marker-start'] = 'none'
    end

    if not intersect(keys, {"f", "b", "w"})
    then
        style['fill'] = 'none'
        style['fill-opacity'] = 1
    end

    svg = [[
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg>
]]

    print(hs.inspect.inspect(style)) -- ENABLE ONLY FOR DEBUGGING

    if (style['marker-end'] ~= nil and style['marker-end'] ~= 'none') or
       (style['marker-start'] ~= nil and style['marker-start'] ~= 'none')
    then
        svgtemp = [[
<defs id="marker-defs">
<marker
]]
        svgtemp = svgtemp .. 'id="marker-arrow-' .. tostring(w) .. "\"\n"
        svgtemp = svgtemp .. 'orient="auto-start-reverse"' .. "\n"
        svgtemp = svgtemp .. 'refY="0" refX="0"' .. "\n"
        svgtemp = svgtemp .. 'markerHeight="3" markerWidth="2">' .. "\n"

        svgtemp = svgtemp .. '    <g transform="scale('.. tostring((2.40 * w + 3.87)/(4.5*w)) .. ')">' .. "\n"
        svg = svg .. svgtemp
        svgtemp = [[
    <path
       d="M -1.55415,2.0722 C -1.42464,1.29512 0,0.1295 0.38852,0 0,-0.1295 -1.42464,-1.29512 -1.55415,-2.0722"
       style="fill:none;stroke:#000000;stroke-width:{0.6};stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;stroke-dasharray:none;stroke-opacity:1
       inkscape:connector-curvature="0" />
   </g>
</marker>
</defs>
]]
        svg = svg .. svgtemp
    end
 
    style_string = ''
    for key, value in pairs(style) do
        -- skips nil?
        style_string = style_string .. key .. ":" .. " " .. value .. ";"
    end

    svg = svg .. '<inkscape:clipboard style="' .. style_string .. '" />' .. "\n</svg>"

    print(svg) -- ENABLE ONLY FOR DEBUGGING

    hs.pasteboard.writeDataForUTI("dyn.ah62d4rv4gu80w5pbq7ww88brrf1g065dqf2gnppxs3xu", svg)
    -- get UTI via https://github.com/sindresorhus/Pasteboard-Viewer
    hs.eventtap.keyStroke({"shift", "cmd"}, "v")
end

I will simply call the lua-hammerspoon function from the cli

"shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+␣\");create_svg_and_paste({\"e\",\"space\"});'"

~/.config/karabiner/karabiner.json for the 30 shape key chords
      "complex_modifications": {
        "parameters": {
          "basic.simultaneous_threshold_milliseconds": 200,
          "basic.to_delayed_action_delay_milliseconds": 500,
          "basic.to_if_alone_timeout_milliseconds": 1000,
          "basic.to_if_held_down_threshold_milliseconds": 500,
          "mouse_motion_to_scroll.speed": 100
        },
        "rules": [
          {
            "description": "s+space",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "spacebar" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+␣\");create_svg_and_paste({\"s\",\"space\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+space",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "spacebar" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+␣\");create_svg_and_paste({\"d\",\"space\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+space",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "spacebar" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+␣\");create_svg_and_paste({\"e\",\"space\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "f+space",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "f" }, { "key_code": "spacebar" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"f+␣\");create_svg_and_paste({\"f\",\"space\"});'" } ],
                "conditions": [ { "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] } ]
              }
            ]
          },
          {
            "description": "w+space",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "w" }, { "key_code": "spacebar" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"w+␣\");create_svg_and_paste({\"w\",\"space\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "b+space",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "b" }, { "key_code": "spacebar" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"b+␣\");create_svg_and_paste({\"b\",\"space\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+f",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "f" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+f\");create_svg_and_paste({\"s\",\"f\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+f",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "f" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+f\");create_svg_and_paste({\"d\",\"f\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+f",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "f" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+f\");create_svg_and_paste({\"e\",\"f\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+w",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "w" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+w\");create_svg_and_paste({\"d\",\"w\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+w",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "b" }, { "key_code": "space" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"b+␣\");create_svg_and_paste({\"b\",\"space\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+w",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "w" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+w\");create_svg_and_paste({\"e\",\"w\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+g\");create_svg_and_paste({\"s\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+g\");create_svg_and_paste({\"d\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+g\");create_svg_and_paste({\"e\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+f+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "f" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+f+g\");create_svg_and_paste({\"s\",\"f\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+f+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "f" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+f+g\");create_svg_and_paste({\"d\",\"f\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+f+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "f" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+f+g\");create_svg_and_paste({\"e\",\"f\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+w+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "w" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+w+g\");create_svg_and_paste({\"s\",\"w\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+w+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "w" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+w+g\");create_svg_and_paste({\"d\",\"w\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+w+g",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "w" }, { "key_code": "g" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+w+g\");create_svg_and_paste({\"e\",\"w\",\"g\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+h\");create_svg_and_paste({\"s\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+h\");create_svg_and_paste({\"d\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+h\");create_svg_and_paste({\"e\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+f+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "f" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+f+h\");create_svg_and_paste({\"s\",\"f\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+f+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "f" }, { "key_code": "h" } ] },
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+f+h\");create_svg_and_paste({\"d\",\"f\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+f+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "f" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+f+h\");create_svg_and_paste({\"e\",\"f\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "s+w+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "s" }, { "key_code": "w" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"s+w+h\");create_svg_and_paste({\"s\",\"w\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "d+w+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "d" }, { "key_code": "w" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"d+w+h\");create_svg_and_paste({\"d\",\"w\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          },
          {
            "description": "e+w+h",
            "manipulators": [
              {
                "type": "basic",
                "from": { "simultaneous": [ { "key_code": "e" }, { "key_code": "w" }, { "key_code": "h" } ] }, 
                "to_if_alone": [ { "shell_command": "/usr/local/bin/hs -c 'hs.alert(\"e+w+h\");create_svg_and_paste({\"e\",\"w\",\"h\"});'" } ],
                "conditions": [{ "type": "frontmost_application_if", "bundle_identifiers": [ "org.inkscape.Inkscape" ] }]
              }
            ]
          }
        ]
      },
...

This uses

This is an improvement. So all two-letter combinations work. However, the three-letter combinations do not yet. There are a few options I have not tested comprehensively

          "basic.simultaneous_threshold_milliseconds": 200,
          "basic.to_if_alone_timeout_milliseconds": 1000,

and the simultaneous_options detect_key_down_uninterruptedly.

Single hs.eventtap for all key chords

This could be fixed by making a giant event tap that would check all defined chords, but that's going to be a lot of work.

I think this is what the original author did. And I guess this could also be possible with hammerspoon eventtap.

kiryph avatar Jan 09 '22 09:01 kiryph