Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions images/chromium-headful/client/src/utils/guacamole-keyboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,6 @@ Guacamole.Keyboard = function Keyboard(element) {
if (this.modifiers.meta && this.keysym !== 0xFFE7 && this.keysym !== 0xFFE8)
this.keyupReliable = false;

// We cannot rely on receiving keyup for Caps Lock on certain platforms
else if (this.keysym === 0xFFE5 && quirks.capsLockKeyupUnreliable)
this.keyupReliable = false;

// Determine whether default action for Alt+combinations must be prevented
var prevent_alt = !this.modifiers.ctrl && !quirks.altIsTypableOnly;

Expand Down Expand Up @@ -1135,6 +1131,14 @@ Guacamole.Keyboard = function Keyboard(element) {
if (!first)
return null;

// macOS sends Caps Lock keydown only when enabling it and keyup only
// when disabling it, so each event is one full press of the key
if (first.keysym === 0xFFE5 && quirks.capsLockKeyupUnreliable) {
first.defaultPrevented = !guac_keyboard.press(0xFFE5);
guac_keyboard.release(0xFFE5);
return eventLog.shift();
}

// Keydown event
if (first instanceof KeydownEvent) {

Expand Down
44 changes: 43 additions & 1 deletion images/chromium-headful/client/tests/guacamole-keyboard.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, test } from 'bun:test'
import { afterEach, describe, expect, test } from 'bun:test'

import createKeyboard from '../src/utils/guacamole-keyboard'

Expand Down Expand Up @@ -70,3 +70,45 @@ describe('GuacamoleKeyboard repeat handling', () => {
expect(events).toEqual(['down:111', 'up:111', 'down:111', 'up:111'])
})
})

describe('GuacamoleKeyboard Caps Lock handling', () => {
const platform = Object.getOwnPropertyDescriptor(navigator, 'platform')!

afterEach(() => {
Object.defineProperty(navigator, 'platform', platform)
})

function keyboardOn(platformName: string) {
Object.defineProperty(navigator, 'platform', { value: platformName, configurable: true })
const target = new KeyboardTarget()
const keyboard = createKeyboard(target as unknown as Element)
const events: string[] = []
keyboard.onkeydown = (key) => {
events.push(`down:${key}`)
return false
}
keyboard.onkeyup = (key) => events.push(`up:${key}`)
return { target, events }
}

test('forwards each macOS Caps Lock keydown and keyup as a full press', () => {
const { target, events } = keyboardOn('MacIntel')

target.dispatch('keydown', 'CapsLock', 20)
target.dispatch('keydown', 'a', 65)
target.dispatch('keypress', 'A', 65)
target.dispatch('keyup', 'a', 65)
target.dispatch('keyup', 'CapsLock', 20)

expect(events).toEqual(['down:65509', 'up:65509', 'down:65', 'up:65', 'down:65509', 'up:65509'])
})

test('keeps Caps Lock as a key press on other platforms', () => {
const { target, events } = keyboardOn('Win32')

target.dispatch('keydown', 'CapsLock', 20)
target.dispatch('keyup', 'CapsLock', 20)

expect(events).toEqual(['down:65509', 'up:65509'])
})
})
Loading