1- /*
2- * Shortcut Sage Event Monitor - KWin Script
3- * Monitors KDE Plasma events and sends them to Shortcut Sage daemon
1+ /**
2+ * Shortcut Sage - KWin Event Monitor
3+ *
4+ * Monitors desktop events and sends them to the Shortcut Sage daemon via DBus.
5+ *
6+ * Events monitored:
7+ * - Desktop/workspace switches
8+ * - Window focus changes
9+ * - Show desktop state changes
10+ *
11+ * Dev shortcut: Meta+Shift+S sends a test event
412 */
513
6- // Configuration
7- const DAEMON_SERVICE = "org.shortcutsage.Daemon" ;
8- const DAEMON_PATH = "/org/shortcutsage/Daemon" ;
14+ // DBus connection to Shortcut Sage daemon
15+ const BUS_NAME = "org.shortcutsage.Daemon" ;
16+ const OBJECT_PATH = "/org/shortcutsage/Daemon" ;
17+ const INTERFACE = "org.shortcutsage.Daemon" ;
918
10- // Initialize DBus interface
11- function initDBus ( ) {
12- try {
13- var dbusInterface = workspace . knownInterfaces [ DAEMON_SERVICE ] ;
14- if ( dbusInterface ) {
15- print ( "Found Shortcut Sage daemon interface" ) ;
16- return true ;
17- } else {
18- print ( "Shortcut Sage daemon not available" ) ;
19- return false ;
20- }
21- } catch ( error ) {
22- print ( "Failed to connect to Shortcut Sage daemon: " + error ) ;
23- return false ;
19+ // Logging configuration
20+ const DEBUG = true ; // Set to false in production
21+ const LOG_PREFIX = "[ShortcutSage]" ;
22+
23+ // Helper function for logging
24+ function log ( message ) {
25+ if ( DEBUG ) {
26+ console . log ( LOG_PREFIX + " " + message ) ;
2427 }
2528}
2629
27- // Function to send event to daemon via DBus
30+ function logError ( message ) {
31+ console . error ( LOG_PREFIX + " ERROR: " + message ) ;
32+ }
33+
34+ // Initialize the script
35+ log ( "Initializing KWin Event Monitor" ) ;
36+
37+ /**
38+ * Send an event to the daemon via DBus
39+ * @param {string } type - Event type (e.g., "window_focus", "desktop_switch")
40+ * @param {string } action - Action name (e.g., "show_desktop", "tile_left")
41+ * @param {Object } metadata - Additional metadata (optional)
42+ */
2843function sendEvent ( type , action , metadata ) {
29- // Using DBus to call the daemon's SendEvent method
30- callDBus (
31- DAEMON_SERVICE ,
32- DAEMON_PATH ,
33- DAEMON_SERVICE ,
34- "SendEvent" ,
35- JSON . stringify ( {
44+ try {
45+ // Build event object
46+ const event = {
3647 timestamp : new Date ( ) . toISOString ( ) ,
3748 type : type ,
3849 action : action ,
3950 metadata : metadata || { }
40- } )
41- ) ;
42- }
51+ } ;
4352
44- // Monitor workspace events
45- function setupEventListeners ( ) {
46- // Desktop switch events
47- workspace . clientDesktopChanged . connect ( function ( client , desktop ) {
48- sendEvent ( "desktop_switch" , "switch_desktop" , {
49- window : client ? client . caption : "unknown" ,
50- desktop : desktop
51- } ) ;
52- } ) ;
53-
54- // Window focus events
55- workspace . clientActivated . connect ( function ( client ) {
56- if ( client ) {
57- sendEvent ( "window_focus" , "window_focus" , {
58- window : client . caption ,
59- app : client . resourceClass ? client . resourceClass . toString ( ) : "unknown"
60- } ) ;
61- }
62- } ) ;
63-
64- // Screen edge activation (overview, etc.)
65- workspace . screenEdgeActivated . connect ( function ( edge , desktop ) {
66- var action = "unknown" ;
67- if ( edge === 0 ) action = "overview" ; // Top edge usually shows overview
68- else if ( edge === 2 ) action = "application_launcher" ; // Bottom edge
69- else action = "screen_edge" ;
70-
71- sendEvent ( "desktop_state" , action , {
72- edge : edge ,
73- desktop : desktop
74- } ) ;
75- } ) ;
76-
77- // Window geometry changes (for tiling, maximizing, etc.)
78- workspace . clientStepUserMovedResized . connect ( function ( client , step ) {
79- if ( client && step ) {
80- var action = "window_move" ;
81- if ( client . maximizedHorizontally && client . maximizedVertically ) {
82- action = "maximize" ;
83- } else if ( ! client . maximizedHorizontally && ! client . maximizedVertically ) {
84- action = "window_move" ;
85- }
86-
87- sendEvent ( "window_state" , action , {
88- window : client . caption ,
89- maximized : client . maximizedHorizontally && client . maximizedVertically
90- } ) ;
91- }
92- } ) ;
53+ const eventJson = JSON . stringify ( event ) ;
54+ log ( "Sending event: " + eventJson ) ;
55+
56+ // Call DBus method
57+ callDBus (
58+ BUS_NAME ,
59+ OBJECT_PATH ,
60+ INTERFACE ,
61+ "SendEvent" ,
62+ eventJson
63+ ) ;
64+ } catch ( error ) {
65+ logError ( "Failed to send event: " + error ) ;
66+ }
9367}
9468
95- // Register a test shortcut for development
96- function setupTestShortcut ( ) {
97- registerShortcut (
98- "Shortcut Sage Test" ,
99- "Test shortcut for Shortcut Sage development" ,
100- "Ctrl+Alt+S" ,
101- function ( ) {
102- sendEvent ( "test" , "test_shortcut" , {
103- source : "kwin_script"
104- } ) ;
105- }
106- ) ;
69+ /**
70+ * Ping the daemon to check if it's alive
71+ */
72+ function pingDaemon ( ) {
73+ try {
74+ const result = callDBus (
75+ BUS_NAME ,
76+ OBJECT_PATH ,
77+ INTERFACE ,
78+ "Ping"
79+ ) ;
80+ log ( "Ping result: " + result ) ;
81+ return result === "pong" ;
82+ } catch ( error ) {
83+ logError ( "Daemon not responding to ping: " + error ) ;
84+ return false ;
85+ }
10786}
10887
109- // Initialize when script loads
110- function init ( ) {
111- print ( "Shortcut Sage KWin script initializing..." ) ;
112-
113- if ( initDBus ( ) ) {
114- setupEventListeners ( ) ;
115- setupTestShortcut ( ) ;
116- print ( "Shortcut Sage KWin script initialized successfully" ) ;
117- } else {
118- print ( "Shortcut Sage KWin script initialized in fallback mode - daemon not available" ) ;
119- // Still set up events but with fallback behavior if needed
120- setupTestShortcut ( ) ;
88+ // Track previous state to detect changes
89+ let previousDesktop = workspace . currentDesktop ;
90+ let showingDesktop = workspace . showingDesktop ;
91+
92+ /**
93+ * Monitor desktop/workspace switches
94+ */
95+ workspace . currentDesktopChanged . connect ( function ( desktop , client ) {
96+ if ( desktop !== previousDesktop ) {
97+ log ( "Desktop switched: " + previousDesktop + " -> " + desktop ) ;
98+ sendEvent (
99+ "desktop_switch" ,
100+ "switch_desktop" ,
101+ {
102+ from : previousDesktop ,
103+ to : desktop
104+ }
105+ ) ;
106+ previousDesktop = desktop ;
107+ }
108+ } ) ;
109+
110+ /**
111+ * Monitor "Show Desktop" state changes
112+ */
113+ workspace . showingDesktopChanged . connect ( function ( showing ) {
114+ if ( showing !== showingDesktop ) {
115+ log ( "Show desktop changed: " + showing ) ;
116+ const action = showing ? "show_desktop" : "hide_desktop" ;
117+ sendEvent (
118+ "show_desktop" ,
119+ action ,
120+ { showing : showing }
121+ ) ;
122+ showingDesktop = showing ;
123+ }
124+ } ) ;
125+
126+ /**
127+ * Monitor active window (focus) changes
128+ */
129+ workspace . clientActivated . connect ( function ( client ) {
130+ if ( client ) {
131+ log ( "Window activated: " + client . caption ) ;
132+ sendEvent (
133+ "window_focus" ,
134+ "window_activated" ,
135+ {
136+ // Don't include window title for privacy
137+ // caption: client.caption, // Disabled by default
138+ resourceClass : client . resourceClass || "unknown"
139+ }
140+ ) ;
141+ }
142+ } ) ;
143+
144+ /**
145+ * Dev shortcut: Meta+Shift+S to send a test event
146+ */
147+ registerShortcut (
148+ "ShortcutSage: Test Event" ,
149+ "ShortcutSage: Send Test Event (Meta+Shift+S)" ,
150+ "Meta+Shift+S" ,
151+ function ( ) {
152+ log ( "Test event triggered" ) ;
153+ sendEvent (
154+ "test" ,
155+ "test_event" ,
156+ { source : "dev_shortcut" }
157+ ) ;
121158 }
159+ ) ;
160+
161+ // Ping daemon on startup to verify connection
162+ log ( "Pinging daemon..." ) ;
163+ if ( pingDaemon ( ) ) {
164+ log ( "Successfully connected to daemon" ) ;
165+ } else {
166+ logError ( "Could not connect to daemon - is it running?" ) ;
167+ logError ( "Start the daemon with: shortcut-sage daemon <config_dir>" ) ;
122168}
123169
124- // Run initialization
125- init ( ) ;
170+ log ( "KWin Event Monitor initialized successfully" ) ;
0 commit comments