@@ -2,15 +2,10 @@ package app
22
33import (
44"context"
5- "encoding/json"
65"fmt"
76"os"
8- "os/exec"
97"os/signal"
10- "regexp"
11- "strings"
128"syscall"
13- "time"
149
1510"hypr-input-switcher/internal/config"
1611"hypr-input-switcher/internal/inputmethod"
@@ -22,8 +17,6 @@ import (
2217type Application struct {
2318config * config.Config
2419configManager * config.Manager
25- currentClient string
26- currentIM string
2720switcher * inputmethod.Switcher
2821notifier * notification.Notifier
2922watchConfig bool
@@ -54,6 +47,9 @@ func (app *Application) Run(configPath string, watchConfig bool) error {
5447app .switcher = inputmethod .NewSwitcher (cfg )
5548app .notifier = notification .NewNotifier (cfg )
5649
50+ // Set notifier for switcher
51+ app .switcher .SetNotifier (app .notifier )
52+
5753// Register config change callback
5854app .configManager .AddCallback (app .onConfigChanged )
5955
@@ -91,8 +87,8 @@ func (app *Application) Run(configPath string, watchConfig bool) error {
9187cancel ()
9288}()
9389
94- // Start monitoring
95- return app .monitorAndSwitch (ctx )
90+ // Use switcher's monitoring loop instead of our own
91+ return app .switcher . MonitorAndSwitch (ctx )
9692}
9793
9894// onConfigChanged handles configuration changes
@@ -108,124 +104,8 @@ func (app *Application) onConfigChanged(newConfig *config.Config) {
108104// Recreate notifier with new config
109105app .notifier = notification .NewNotifier (newConfig )
110106
111- // Reset current client to force re-evaluation
112- app .currentClient = ""
107+ // Set notifier for switcher
108+ app .switcher . SetNotifier ( app . notifier )
113109
114110logger .Info ("Configuration applied successfully" )
115111}
116-
117- // getCurrentClient gets current active window information
118- func (app * Application ) getCurrentClient () (* config.WindowInfo , error ) {
119- cmd := exec .Command ("hyprctl" , "activewindow" , "-j" )
120- output , err := cmd .Output ()
121- if err != nil {
122- return nil , fmt .Errorf ("failed to get active window: %w" , err )
123- }
124-
125- var windowInfo config.WindowInfo
126- if err := json .Unmarshal (output , & windowInfo ); err != nil {
127- return nil , fmt .Errorf ("failed to parse window info: %w" , err )
128- }
129-
130- return & windowInfo , nil
131- }
132-
133- // getTargetInputMethod determines target input method based on client information
134- func (app * Application ) getTargetInputMethod (clientInfo * config.WindowInfo ) string {
135- // Get current config (thread-safe)
136- currentConfig := app .configManager .GetConfig ()
137-
138- if clientInfo == nil {
139- return currentConfig .DefaultInputMethod
140- }
141-
142- className := clientInfo .Class
143- title := clientInfo .Title
144-
145- // Check client rules
146- for _ , rule := range currentConfig .ClientRules {
147- // Match class (required)
148- if rule .Class == "" || ! app .matchPattern (rule .Class , className ) {
149- continue
150- }
151-
152- // If title is empty or not specified, class match is enough
153- if rule .Title == "" {
154- return rule .InputMethod
155- }
156-
157- // If title is specified, both class and title must match
158- if app .matchPattern (rule .Title , title ) {
159- return rule .InputMethod
160- }
161- }
162-
163- return currentConfig .DefaultInputMethod
164- }
165-
166- // matchPattern matches pattern, supporting regex and string matching
167- func (app * Application ) matchPattern (pattern , text string ) bool {
168- if pattern == "" || text == "" {
169- return false
170- }
171-
172- // Try as regex first
173- if matched , err := regexp .MatchString (pattern , text ); err == nil {
174- return matched
175- }
176-
177- // Fallback to string contains matching
178- return strings .Contains (strings .ToLower (text ), strings .ToLower (pattern ))
179- }
180-
181- // monitorAndSwitch main monitoring loop
182- func (app * Application ) monitorAndSwitch (ctx context.Context ) error {
183- ticker := time .NewTicker (200 * time .Millisecond )
184- defer ticker .Stop ()
185-
186- for {
187- select {
188- case <- ctx .Done ():
189- return nil
190-
191- case <- ticker .C :
192- // Get current active window
193- clientInfo , err := app .getCurrentClient ()
194- if err != nil {
195- logger .Debugf ("Failed to get current client: %v" , err )
196- continue
197- }
198-
199- currentClient := fmt .Sprintf ("%s:%s" , clientInfo .Class , clientInfo .Title )
200-
201- // If window changed
202- if currentClient != app .currentClient {
203- app .currentClient = currentClient
204-
205- // Get current input method status
206- currentIM := app .switcher .GetCurrent ()
207-
208- // Determine target input method
209- targetIM := app .getTargetInputMethod (clientInfo )
210-
211- logger .Infof ("Window changed: %s - %s" , clientInfo .Class , clientInfo .Title )
212- logger .Infof ("Current IM: %s -> Target IM: %s" , currentIM , targetIM )
213-
214- // If input method needs to be switched
215- if currentIM != targetIM && currentIM != "unknown" {
216- if err := app .switcher .Switch (targetIM ); err != nil {
217- logger .Errorf ("Failed to switch input method to %s: %v" , targetIM , err )
218- } else {
219- logger .Infof ("Switched input method to: %s" , targetIM )
220-
221- // Show notification (use current config)
222- currentConfig := app .configManager .GetConfig ()
223- if currentConfig .Notifications .ShowOnSwitch {
224- app .notifier .ShowInputMethodSwitch (targetIM , clientInfo )
225- }
226- }
227- }
228- }
229- }
230- }
231- }
0 commit comments