Skip to content

Commit fead06b

Browse files
committed
InputSourceSwitch: add application watcher support for apps window filter can't capture
Some applications (like kitty-quick-access) cannot be detected by window filter. This adds an optional application watcher mode that users can configure per-app. Also adds a stop() method and bumps version to 1.1.
1 parent e5b8712 commit fead06b

File tree

1 file changed

+53
-19
lines changed

1 file changed

+53
-19
lines changed

Source/InputSourceSwitch.spoon/init.lua

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
---
99
--- spoon.InputSourceSwitch:setApplications({
1010
--- ["WeChat"] = "Pinyin - Simplified",
11-
--- ["Mail"] = "ABC"
11+
--- ["Mail"] = "ABC",
12+
--- -- Use application watcher for apps that window filter can't capture
13+
--- ["kitty-quick-access"] = { source = "ABC", watcher = "application" }
1214
--- })
1315
---
1416
--- spoon.InputSourceSwitch:start()
@@ -21,7 +23,7 @@ obj.__index = obj
2123

2224
-- Metadata
2325
obj.name = "InputSourceSwitch"
24-
obj.version = "1.0"
26+
obj.version = "1.1"
2527
obj.author = "eks5115 <eks5115@gmail.com>"
2628
obj.homepage = "https://github.com/Hammerspoon/Spoons"
2729
obj.license = "MIT - https://opensource.org/licenses/MIT"
@@ -52,24 +54,38 @@ local function isMethod(methodName)
5254
return false
5355
end
5456

55-
local function setAppInputSource(appName, sourceName, event)
57+
local function switchInputSource(sourceName, appName)
58+
local r = true
59+
60+
if (isLayout(sourceName)) then
61+
r = hs.keycodes.setLayout(sourceName)
62+
elseif isMethod(sourceName) then
63+
r = hs.keycodes.setMethod(sourceName)
64+
else
65+
hs.alert.show(string.format('sourceName: %s is not layout or method', sourceName))
66+
end
67+
68+
if (not r) then
69+
hs.alert.show(string.format('set %s to %s failure', appName, sourceName))
70+
end
71+
end
72+
73+
local function setAppInputSourceWithWindowFilter(appName, sourceName, event)
5674
event = event or hs.window.filter.windowFocused
5775

5876
hs.window.filter.new(appName):subscribe(event, function()
59-
local r = true
60-
61-
if (isLayout(sourceName)) then
62-
r = hs.keycodes.setLayout(sourceName)
63-
elseif isMethod(sourceName) then
64-
r = hs.keycodes.setMethod(sourceName)
65-
else
66-
hs.alert.show(string.format('sourceName: %s is not layout or method', sourceName))
67-
end
77+
switchInputSource(sourceName, appName)
78+
end)
79+
end
6880

69-
if (not r) then
70-
hs.alert.show(string.format('set %s to %s failure', appName, sourceName))
81+
local function setAppInputSourceWithAppWatcher(appName, sourceName)
82+
local watcher = hs.application.watcher.new(function(name, eventType, appObj)
83+
if eventType == hs.application.watcher.activated and name == appName then
84+
switchInputSource(sourceName, appName)
7185
end
72-
end)
86+
end)
87+
watcher:start()
88+
return watcher
7389
end
7490

7591
--- InputSourceSwitch.applicationMap
@@ -83,12 +99,16 @@ obj.applicationsMap = {}
8399
---
84100
--- Parameters:
85101
--- * applications - A table containing that mapping the application name to the input source
86-
--- key is the application name and value is the input source name
102+
--- key is the application name
103+
--- value can be:
104+
--- - a string: the input source name (uses window filter, default)
105+
--- - a table: { source = "input source name", watcher = "window" | "application" }
87106
--- example:
88107
--- ```
89108
--- {
90109
--- ["WeChat"] = "Pinyin - Simplified",
91-
--- ["Mail"] = "ABC"
110+
--- ["Mail"] = "ABC",
111+
--- ["kitty-quick-access"] = { source = "ABC", watcher = "application" }
92112
--- }
93113
--- ```
94114
function obj:setApplications(applications)
@@ -104,8 +124,22 @@ end
104124
--- Parameters:
105125
--- * None
106126
function obj:start()
107-
for k,v in pairs(self.applicationsMap) do
108-
setAppInputSource(k, v)
127+
for appName, config in pairs(self.applicationsMap) do
128+
local sourceName, watcherType
129+
130+
if type(config) == "string" then
131+
sourceName = config
132+
watcherType = "window"
133+
else
134+
sourceName = config.source
135+
watcherType = config.watcher or "window"
136+
end
137+
138+
if watcherType == "application" then
139+
setAppInputSourceWithAppWatcher(appName, sourceName)
140+
else
141+
setAppInputSourceWithWindowFilter(appName, sourceName)
142+
end
109143
end
110144
return self
111145
end

0 commit comments

Comments
 (0)