@@ -34,27 +34,89 @@ try {
3434 console . warn ( `[${ pluginUUID } ] WARNING: xdotool not found. Install it with: sudo apt install xdotool` )
3535}
3636
37+ /**
38+ * Find Discord's X11 window ID.
39+ * Returns the window ID string, or null if Discord is not running / not found.
40+ */
41+ function getDiscordWindowId ( ) {
42+ // Try by window class first (most reliable)
43+ const byClass = spawnSync ( 'xdotool' , [ 'search' , '--class' , 'discord' ] , { stdio : 'pipe' } )
44+ if ( byClass . status === 0 ) {
45+ const ids = byClass . stdout . toString ( ) . trim ( ) . split ( '\n' ) . filter ( Boolean )
46+ if ( ids . length > 0 ) return ids [ ids . length - 1 ]
47+ }
48+ // Fallback: search by window title
49+ const byName = spawnSync ( 'xdotool' , [ 'search' , '--name' , 'Discord' ] , { stdio : 'pipe' } )
50+ if ( byName . status === 0 ) {
51+ const ids = byName . stdout . toString ( ) . trim ( ) . split ( '\n' ) . filter ( Boolean )
52+ if ( ids . length > 0 ) return ids [ ids . length - 1 ]
53+ }
54+ console . warn ( `[${ pluginUUID } ] Could not find Discord window — sending key to focused window instead` )
55+ return null
56+ }
57+
58+ /**
59+ * Focus `winId`, run `action()`, then restore focus to the previously active
60+ * window. Uses XTestFakeKeyEvent (not XSendEvent) so Electron/Chromium treats
61+ * the event as a trusted hardware input (isTrusted=true in JavaScript).
62+ * xdotool key --window uses XSendEvent which Discord ignores.
63+ */
64+ function withDiscordFocus ( winId , action ) {
65+ const prevResult = spawnSync ( 'xdotool' , [ 'getactivewindow' ] , { stdio : 'pipe' } )
66+ const prevWinId = prevResult . status === 0 ? prevResult . stdout . toString ( ) . trim ( ) : null
67+
68+ spawnSync ( 'xdotool' , [ 'windowfocus' , '--sync' , winId ] , { stdio : 'pipe' } )
69+ action ( )
70+ if ( prevWinId && prevWinId !== winId ) {
71+ spawnSync ( 'xdotool' , [ 'windowfocus' , '--sync' , prevWinId ] , { stdio : 'pipe' } )
72+ }
73+ }
74+
3775function xdotoolKey ( hotkey ) {
3876 if ( ! xdotoolAvailable ) return
39- const result = spawnSync ( 'xdotool' , [ 'key' , hotkey ] , { stdio : 'pipe' } )
40- if ( result . status !== 0 ) {
41- console . warn ( `[${ pluginUUID } ] xdotool key failed for "${ hotkey } ":` , result . stderr ?. toString ( ) . trim ( ) )
77+ const winId = getDiscordWindowId ( )
78+ const args = [ 'key' , '--clearmodifiers' , hotkey ]
79+ console . log ( `[${ pluginUUID } ] xdotool${ winId ? ' (via Discord focus)' : '' } ${ args . join ( ' ' ) } ` )
80+ if ( winId ) {
81+ withDiscordFocus ( winId , ( ) => {
82+ const r = spawnSync ( 'xdotool' , args , { stdio : 'pipe' } )
83+ if ( r . status !== 0 ) console . warn ( `[${ pluginUUID } ] xdotool key failed:` , r . stderr ?. toString ( ) . trim ( ) )
84+ } )
85+ } else {
86+ const r = spawnSync ( 'xdotool' , args , { stdio : 'pipe' } )
87+ if ( r . status !== 0 ) console . warn ( `[${ pluginUUID } ] xdotool key failed:` , r . stderr ?. toString ( ) . trim ( ) )
4288 }
4389}
4490
4591function xdotoolKeyDown ( hotkey ) {
4692 if ( ! xdotoolAvailable ) return
47- const result = spawnSync ( 'xdotool' , [ 'keydown' , hotkey ] , { stdio : 'pipe' } )
48- if ( result . status !== 0 ) {
49- console . warn ( `[${ pluginUUID } ] xdotool keydown failed for "${ hotkey } ":` , result . stderr ?. toString ( ) . trim ( ) )
93+ const winId = getDiscordWindowId ( )
94+ const args = [ 'keydown' , '--clearmodifiers' , hotkey ]
95+ console . log ( `[${ pluginUUID } ] xdotool${ winId ? ' (via Discord focus)' : '' } ${ args . join ( ' ' ) } ` )
96+ if ( winId ) {
97+ withDiscordFocus ( winId , ( ) => {
98+ const r = spawnSync ( 'xdotool' , args , { stdio : 'pipe' } )
99+ if ( r . status !== 0 ) console . warn ( `[${ pluginUUID } ] xdotool keydown failed:` , r . stderr ?. toString ( ) . trim ( ) )
100+ } )
101+ } else {
102+ const r = spawnSync ( 'xdotool' , args , { stdio : 'pipe' } )
103+ if ( r . status !== 0 ) console . warn ( `[${ pluginUUID } ] xdotool keydown failed:` , r . stderr ?. toString ( ) . trim ( ) )
50104 }
51105}
52106
53107function xdotoolKeyUp ( hotkey ) {
54108 if ( ! xdotoolAvailable ) return
55- const result = spawnSync ( 'xdotool' , [ 'keyup' , hotkey ] , { stdio : 'pipe' } )
56- if ( result . status !== 0 ) {
57- console . warn ( `[${ pluginUUID } ] xdotool keyup failed for "${ hotkey } ":` , result . stderr ?. toString ( ) . trim ( ) )
109+ const winId = getDiscordWindowId ( )
110+ const args = [ 'keyup' , '--clearmodifiers' , hotkey ]
111+ console . log ( `[${ pluginUUID } ] xdotool${ winId ? ' (via Discord focus)' : '' } ${ args . join ( ' ' ) } ` )
112+ if ( winId ) {
113+ withDiscordFocus ( winId , ( ) => {
114+ const r = spawnSync ( 'xdotool' , args , { stdio : 'pipe' } )
115+ if ( r . status !== 0 ) console . warn ( `[${ pluginUUID } ] xdotool keyup failed:` , r . stderr ?. toString ( ) . trim ( ) )
116+ } )
117+ } else {
118+ const r = spawnSync ( 'xdotool' , args , { stdio : 'pipe' } )
119+ if ( r . status !== 0 ) console . warn ( `[${ pluginUUID } ] xdotool keyup failed:` , r . stderr ?. toString ( ) . trim ( ) )
58120 }
59121}
60122
0 commit comments