diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e38d45..336af5d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/internal/connect/x11_keymap.go b/internal/connect/x11_keymap.go index 1e3ed0f..419ea04 100644 --- a/internal/connect/x11_keymap.go +++ b/internal/connect/x11_keymap.go @@ -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 { diff --git a/internal/connect/x11_keymap_test.go b/internal/connect/x11_keymap_test.go index 5f8bebd..6de3e6a 100644 --- a/internal/connect/x11_keymap_test.go +++ b/internal/connect/x11_keymap_test.go @@ -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") } }