Skip to content
Merged
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Fix the Crabfleet Connect Linux X11 backend disabling capture entirely on keyboards with multiple XKB groups: bind only the primary group's base/shift levels so real screen capture and input injection work instead of silently falling back to the synthetic test pattern (validated on a headless Xvfb X server: real 1920x1080 Tight framebuffer + XTest pointer injection).

- Add opt-in single-folder sharing to Share This Mac with security-scoped bookmark persistence, negotiated `FSH1` browsing, bounded native and browser download/upload streams, strict realpath containment, 512 MiB file and 256 KiB chunk limits, root-local temporary uploads with atomic rename and teardown cleanup, and host-controlled remote writes.
- Add a pure-Go Windows Crabfleet Connect backend with synchronized GDI BitBlt primary-display capture, full-frame RGBA dirty updates, SendInput absolute pointer, wheel, and keyboard injection, active-layout shortcuts, Unicode and legacy X11 keysym text mapping, retry-safe teardown, and amd64/arm64 cross-build proof, while deferring DXGI, multi-monitor, per-monitor-DPI, packaging, and real-hardware validation.
- Add the Crabfleet Connect foundation with a shared Go RFB 3.8 host core, per-run VNC-DES authentication, Tight/JPEG and client-side cursor/input support, a CI-safe synthetic backend, and a Linux X11 MIT-SHM/XFixes/XTest backend plus cross-compiled CLI, while documenting deferred codecs, Wayland, ARD, audio, and hardware validation.
Expand Down
11 changes: 9 additions & 2 deletions internal/connect/x11_keymap.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,17 @@ func buildX11Keymap(
for keyOffset := 0; keyOffset < count; keyOffset++ {
keycode := byte(int(minimum) + keyOffset)
rawLevels := keysyms[keyOffset*perKeycode : (keyOffset+1)*perKeycode]
levels := normalizedX11Levels(rawLevels)
if !x11GroupsEquivalent(rawLevels) {
return x11Keymap{}, errors.New("X11 multiple keyboard groups require XKB support")
// Extra XKB keyboard groups (secondary layouts) cannot be
// disambiguated from AltGr levels through the core protocol, so bind
// only the primary group's base and shift levels. Standard typing
// and — critically — screen capture keep working instead of the
// whole backend falling back to the synthetic test pattern.
// (Active-group-aware XKB mapping is a follow-up.)
primary := normalizedX11Levels(rawLevels[:2])
levels = []uint32{primary[0], primary[1], primary[0], primary[1]}
}
levels := normalizedX11Levels(rawLevels)
symbolsByKeycode[keycode] = levels
for level, keysym := range levels[:2] {
if keysym == 0 {
Expand Down
30 changes: 23 additions & 7 deletions internal/connect/x11_keymap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,36 @@ func TestBuildX11KeymapPreservesRequiredLevels(t *testing.T) {
}
}

func TestBuildX11KeymapRejectsMultipleGroups(t *testing.T) {
func TestBuildX11KeymapBindsPrimaryGroupIgnoringSecondary(t *testing.T) {
t.Parallel()
if _, err := buildX11Keymap([]uint32{'a', 'A', 0x06c1, 0x06e1}, 20, 1, 4, x11ModifierMap{}); err == nil {
t.Fatal("accepted an XKB multi-group keymap")
// A second XKB group (Arabic here) must not disable the keymap; the primary
// group's base and shift bind, and the secondary group's keysyms are ignored.
keymap, err := buildX11Keymap([]uint32{'a', 'A', 0x06c1, 0x06e1}, 20, 1, 4, x11ModifierMap{})
if err != nil {
t.Fatalf("multi-group keymap must build: %v", err)
}
if b, ok := keymap.bindings['a']; !ok || b.keycode != 20 || b.shift {
t.Fatalf("primary base binding missing/wrong: %+v ok=%v", b, ok)
}
if _, ok := keymap.bindings[0x06c1]; ok {
t.Fatal("secondary-group keysym must not be bound")
}
}

func TestBuildX11KeymapRejectsThirdGroup(t *testing.T) {
func TestBuildX11KeymapIgnoresDifferingThirdGroup(t *testing.T) {
t.Parallel()
if _, err := buildX11Keymap(
keymap, err := buildX11Keymap(
[]uint32{'a', 'A', 'a', 'A', 0x06c1, 0x06e1},
20, 1, 6, x11ModifierMap{},
); err == nil {
t.Fatal("accepted a differing third XKB group")
)
if err != nil {
t.Fatalf("keymap with a differing third group must build: %v", err)
}
if _, ok := keymap.bindings['a']; !ok {
t.Fatal("primary base binding missing")
}
if _, ok := keymap.bindings[0x06c1]; ok {
t.Fatal("differing third-group keysym must not be bound")
}
}

Expand Down