Skip to content

Commit 47ce911

Browse files
authored
feat: add support for hotkeys (#1984)
1 parent 54aaeea commit 47ce911

1 file changed

Lines changed: 158 additions & 1 deletion

File tree

kvm/src/kvm.component.ts

Lines changed: 158 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,10 @@ interface EncodingOption {
3434
value: number
3535
viewValue: string
3636
}
37+
interface KeyObject {
38+
code: number
39+
down: boolean
40+
}
3741

3842
@Component({
3943
selector: 'amt-kvm',
@@ -43,7 +47,7 @@ interface EncodingOption {
4347
export class KVMComponent implements OnDestroy {
4448
private readonly renderer = inject(Renderer2)
4549
private readonly destroyRef = inject(DestroyRef)
46-
50+
private KEY_SEQUENCE_DELAY_MS = 10
4751
readonly canvas = viewChild<ElementRef>('canvas')
4852
readonly device = viewChild.required<string>('device')
4953

@@ -62,6 +66,7 @@ export class KVMComponent implements OnDestroy {
6266
readonly deviceStatus = output<number>()
6367
readonly deviceConnection = input<boolean>(false)
6468
readonly selectedEncoding = input<number>(1)
69+
readonly selectedHotkey = input<string>('')
6570

6671
// Component state
6772
module: AMTDesktop | null = null
@@ -102,6 +107,15 @@ export class KVMComponent implements OnDestroy {
102107
this.selected.set(encoding)
103108
this.onEncodingChange()
104109
})
110+
111+
// React to selectedHotkey changes
112+
effect(() => {
113+
const hotkey = this.selectedHotkey()
114+
if (hotkey && this.deviceConnection() && this.keyboardHelper) {
115+
console.log('KVMComponent: Hotkey triggered:', hotkey)
116+
this.sendHotkey(hotkey)
117+
}
118+
})
105119
}
106120

107121
toggleFullscreen(): void {
@@ -216,6 +230,149 @@ export class KVMComponent implements OnDestroy {
216230
this.mouseHelper?.mousemove(event)
217231
}
218232

233+
sendHotkey(hotKeyType: string): void {
234+
if (!this.keyboardHelper) {
235+
console.warn('KVMComponent: Cannot send hotkey - keyboard helper not available')
236+
return
237+
}
238+
239+
const keySequence = this.getKeySequence(hotKeyType)
240+
if (keySequence?.keys) {
241+
console.log('KVMComponent: Sending hotkey sequence for', hotKeyType)
242+
// Send each key in the sequence with a small delay
243+
keySequence.keys.forEach((key: KeyObject, index: number) => {
244+
setTimeout(() => {
245+
// Use the KeyboardHelper's existing handleKeyEvent method
246+
// 0 = Up, 1 = Down (from the UpDown enum)
247+
const upDown = key.down ? 1 : 0
248+
249+
// Create a mock keyboard event with the key code
250+
const mockEvent = {
251+
keyCode: key.code,
252+
preventDefault: () => {},
253+
stopPropagation: () => {}
254+
} as KeyboardEvent
255+
256+
// Use the existing handleKeyEvent method
257+
this.keyboardHelper!.handleKeyEvent(upDown, mockEvent)
258+
}, index * this.KEY_SEQUENCE_DELAY_MS) // delay between keys
259+
})
260+
}
261+
}
262+
263+
private getKeySequence(hotKeyType: string): { keys: KeyObject[] } | null {
264+
// Based on the KeyBoardHelper, these are the key codes for various combinations
265+
switch (hotKeyType) {
266+
case 'ctrl-alt-del':
267+
return {
268+
keys: [
269+
{ code: 17, down: true }, // Ctrl down (JS keyCode)
270+
{ code: 18, down: true }, // Alt down (JS keyCode)
271+
{ code: 46, down: true }, // Delete down (JS keyCode)
272+
{ code: 46, down: false }, // Delete up (JS keyCode)
273+
{ code: 18, down: false }, // Alt up (JS keyCode)
274+
{ code: 17, down: false } // Ctrl up (JS keyCode)
275+
]
276+
}
277+
case 'alt-tab':
278+
return {
279+
keys: [
280+
{ code: 18, down: true }, // Alt down (JS keyCode)
281+
{ code: 9, down: true }, // Tab down (JS keyCode)
282+
{ code: 9, down: false }, // Tab up (JS keyCode)
283+
{ code: 18, down: false } // Alt up (JS keyCode)
284+
]
285+
}
286+
case 'alt-release':
287+
return {
288+
keys: [
289+
{ code: 18, down: false } // Alt up (JS keyCode)
290+
]
291+
}
292+
case 'windows':
293+
return {
294+
keys: [
295+
{ code: 0xffe7, down: true }, // Windows key down
296+
{ code: 0xffe7, down: false } // Windows key up
297+
]
298+
}
299+
case 'windows-l':
300+
return {
301+
keys: [
302+
{ code: 0xffe7, down: true }, // Windows down
303+
{ code: 108, down: true }, // L down
304+
{ code: 108, down: false }, // L up
305+
{ code: 0xffe7, down: false } // Windows up
306+
]
307+
}
308+
case 'windows-r':
309+
return {
310+
keys: [
311+
{ code: 0xffe7, down: true }, // Windows down
312+
{ code: 82, down: true }, // R down
313+
{ code: 82, down: false }, // R up
314+
{ code: 0xffe7, down: false } // Windows up
315+
]
316+
}
317+
case 'windows-up':
318+
return {
319+
keys: [
320+
{ code: 0xffe7, down: true }, // Windows down
321+
{ code: 0xff52, down: true }, // Up arrow down
322+
{ code: 0xff52, down: false }, // Up arrow up
323+
{ code: 0xffe7, down: false } // Windows up
324+
]
325+
}
326+
case 'windows-down':
327+
return {
328+
keys: [
329+
{ code: 0xffe7, down: true }, // Windows down
330+
{ code: 0xff54, down: true }, // Down arrow down
331+
{ code: 0xff54, down: false }, // Down arrow up
332+
{ code: 0xffe7, down: false } // Windows up
333+
]
334+
}
335+
case 'windows-left':
336+
return {
337+
keys: [
338+
{ code: 0xffe7, down: true }, // Windows down
339+
{ code: 0xff51, down: true }, // Left arrow down
340+
{ code: 0xff51, down: false }, // Left arrow up
341+
{ code: 0xffe7, down: false } // Windows up
342+
]
343+
}
344+
case 'windows-right':
345+
return {
346+
keys: [
347+
{ code: 0xffe7, down: true }, // Windows down
348+
{ code: 0xff53, down: true }, // Right arrow down
349+
{ code: 0xff53, down: false }, // Right arrow up
350+
{ code: 0xffe7, down: false } // Windows up
351+
]
352+
}
353+
case 'alt-f4':
354+
return {
355+
keys: [
356+
{ code: 0xffe9, down: true }, // Alt down
357+
{ code: 0xffc1, down: true }, // F4 down (F1 = 0xffbe, so F4 = 0xffc1)
358+
{ code: 0xffc1, down: false }, // F4 up
359+
{ code: 0xffe9, down: false } // Alt up
360+
]
361+
}
362+
case 'ctrl-w':
363+
return {
364+
keys: [
365+
{ code: 0xffe3, down: true }, // Ctrl down
366+
{ code: 87, down: true }, // W down
367+
{ code: 87, down: false }, // W up
368+
{ code: 0xffe3, down: false } // Ctrl up
369+
]
370+
}
371+
default:
372+
return null
373+
}
374+
}
375+
219376
ngOnDestroy(): void {
220377
this.mouseMove$?.unsubscribe()
221378
this.stopKvm()

0 commit comments

Comments
 (0)