mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-23 15:19:08 +02:00
Reset X11 button state on reconnect, read the Shift+AltGr console table, sync remote Caps Lock with macOS, and restore the FreeBSD stride test
This commit is contained in:
@@ -6,6 +6,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// fb_depth is colour depth, not storage: a KMS console is depth 24 stored as
|
||||
@@ -32,3 +33,33 @@ func TestFreebsdStorageBits(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// The row pitch is derived from the mapping, not computed from width and
|
||||
// depth: a KMS framebuffer pads its rows, and reading at the unpadded width
|
||||
// shears the image. A mapping too small for the reported geometry would have
|
||||
// rows read off its end, so it is refused rather than guessed at.
|
||||
func TestFreebsdFBStride(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fbt fbType
|
||||
want int
|
||||
wantErr bool
|
||||
}{
|
||||
{"unpadded 32-bit", fbType{FbWidth: 1024, FbHeight: 768, FbDepth: 32, FbSize: 1024 * 768 * 4}, 4096, false},
|
||||
{"padded rows", fbType{FbWidth: 1000, FbHeight: 768, FbDepth: 32, FbSize: 4096 * 768}, 4096, false},
|
||||
{"packed 24-bit", fbType{FbWidth: 1024, FbHeight: 768, FbDepth: 24, FbSize: 1024 * 768 * 3}, 3072, false},
|
||||
{"16-bit", fbType{FbWidth: 800, FbHeight: 600, FbDepth: 16, FbSize: 800 * 600 * 2}, 1600, false},
|
||||
{"size cannot hold the geometry", fbType{FbWidth: 1024, FbHeight: 768, FbDepth: 32, FbSize: 1024 * 768 * 2}, 0, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := freebsdFBStride(tc.fbt)
|
||||
if tc.wantErr {
|
||||
require.Error(t, err, "a mapping smaller than the geometry must be refused")
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tc.want, got, "row pitch in bytes")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@ var (
|
||||
// event, so the receiving app gets those characters whatever the active
|
||||
// keyboard layout would have produced for the keycode.
|
||||
cgEventKeyboardSetUnicodeString func(uintptr, uintptr, *uint16)
|
||||
// cgEventSourceFlagsState reads the modifier flags macOS currently holds
|
||||
// for an event-source state, which is where the real Caps Lock state is.
|
||||
cgEventSourceFlagsState func(int32) uint64
|
||||
|
||||
// CGEventCreateScrollWheelEvent is variadic, call via SyscallN.
|
||||
cgEventCreateScrollWheelEventAddr uintptr
|
||||
@@ -171,6 +174,9 @@ func initDarwinInput() {
|
||||
if sym, err := purego.Dlsym(cg, "CGEventKeyboardSetUnicodeString"); err == nil {
|
||||
purego.RegisterFunc(&cgEventKeyboardSetUnicodeString, sym)
|
||||
}
|
||||
if sym, err := purego.Dlsym(cg, "CGEventSourceFlagsState"); err == nil {
|
||||
purego.RegisterFunc(&cgEventSourceFlagsState, sym)
|
||||
}
|
||||
|
||||
sym, err := purego.Dlsym(cg, "CGEventCreateScrollWheelEvent")
|
||||
if err == nil {
|
||||
@@ -616,13 +622,22 @@ func (m *MacInputInjector) postModifier(src uintptr, keycode uint16, down bool,
|
||||
if capsLock && !down {
|
||||
return
|
||||
}
|
||||
// Toggle from the state macOS actually has, not from the one this injector
|
||||
// has built up: Caps Lock may already be on when the injector starts, or be
|
||||
// toggled at the local keyboard in the meantime, and toggling a stale copy
|
||||
// would post the state the system is already in instead of a transition.
|
||||
systemCaps, haveSystemCaps := systemCapsLock()
|
||||
|
||||
var flags uint64
|
||||
for {
|
||||
old := m.modifiers.Load()
|
||||
switch {
|
||||
case capsLock:
|
||||
flags = old ^ bit
|
||||
current := old & bit
|
||||
if haveSystemCaps {
|
||||
current = systemCaps
|
||||
}
|
||||
flags = (old &^ bit) | (current ^ bit)
|
||||
case down:
|
||||
flags = old | bit
|
||||
default:
|
||||
@@ -893,8 +908,11 @@ func (m *MacInputInjector) SetClipboard(text string) {
|
||||
// TypeText synthesizes the given text as keystrokes via Core Graphics.
|
||||
// Lets a client push host clipboard content to the focused remote app
|
||||
// even when the app doesn't honor pbpaste-style clipboard sync (e.g.
|
||||
// login screens, locked-down apps). ASCII printable runes only; others
|
||||
// are skipped.
|
||||
// login screens, locked-down apps). Printable runes, non-ASCII included, are
|
||||
// attached to the events as literal text, so they arrive as written whatever
|
||||
// the host's keyboard layout. Return and Tab go out as their keys. Where the
|
||||
// literal-text call is unavailable, ASCII falls back to US-layout keycodes and
|
||||
// anything else is skipped.
|
||||
func (m *MacInputInjector) TypeText(text string) {
|
||||
// Same permission the other injection paths need: without it the posted
|
||||
// events are swallowed, so asking here is what makes the prompt appear
|
||||
@@ -1149,3 +1167,12 @@ func (m *MacInputInjector) typeUnicodeRune(src uintptr, r rune) bool {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// systemCapsLock returns the Caps Lock bit macOS currently holds, and false
|
||||
// when that cannot be read.
|
||||
func systemCapsLock() (uint64, bool) {
|
||||
if cgEventSourceFlagsState == nil {
|
||||
return 0, false
|
||||
}
|
||||
return cgEventSourceFlagsState(kCGEventSourceStateCombinedSessionState) & kCGEventFlagMaskAlphaShift, true
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ const consoleTTY = "/dev/tty0"
|
||||
// kdgkbent is KDGKBENT from linux/kd.h, which reads one keymap entry.
|
||||
const kdgkbent = 0x4B46
|
||||
|
||||
// Keymap tables (linux/keyboard.h): the character a key produces unmodified,
|
||||
// with Shift, and with AltGr.
|
||||
// Keymap tables (linux/keyboard.h), indexed by the modifier bits held: the
|
||||
// character a key produces unmodified, with Shift (KG_SHIFT), with AltGr
|
||||
// (KG_ALTGR), and with both.
|
||||
const (
|
||||
kNormTab = 0
|
||||
kShiftTab = 1
|
||||
kAltGrTab = 2
|
||||
kNormTab = 0
|
||||
kShiftTab = 1
|
||||
kAltGrTab = 2
|
||||
kShiftAltGrTab = 3
|
||||
)
|
||||
|
||||
// Key types (linux/keyboard.h). KT_LATIN and KT_LETTER carry a Latin-1
|
||||
@@ -50,9 +52,10 @@ type consoleKey struct {
|
||||
}
|
||||
|
||||
// readConsoleKeymap reads the active console keymap and returns, for every
|
||||
// printable character it can type, the key that types it. The unmodified table
|
||||
// wins over the Shift one, and both over AltGr, so a character reachable more
|
||||
// than one way gets the simplest.
|
||||
// printable character it can type, the key that types it. Tables are read from
|
||||
// fewest modifiers to most (plain, Shift, AltGr, Shift+AltGr), and the first
|
||||
// one to produce a character wins, so a character reachable more than one way
|
||||
// gets the simplest.
|
||||
//
|
||||
// The kernel decodes uinput key codes with this same keymap, which is what makes
|
||||
// it the right source. A fixed US table types the wrong characters on any other
|
||||
@@ -71,6 +74,7 @@ func readConsoleKeymap(tty string) (map[rune]consoleKey, error) {
|
||||
{kNormTab, consoleKey{}},
|
||||
{kShiftTab, consoleKey{shift: true}},
|
||||
{kAltGrTab, consoleKey{altGr: true}},
|
||||
{kShiftAltGrTab, consoleKey{shift: true, altGr: true}},
|
||||
}
|
||||
|
||||
out := make(map[rune]consoleKey)
|
||||
|
||||
@@ -132,6 +132,10 @@ func (x *X11InputInjector) ensureConnLocked() {
|
||||
x.conn = conn
|
||||
x.root = screen.Root
|
||||
x.screen = &screen
|
||||
// The new server has no buttons down. Keeping the old mask would make a
|
||||
// button still held across the restart look already pressed, so it would
|
||||
// never get the ButtonPress it now needs.
|
||||
x.lastButtons = 0
|
||||
x.cacheKeyboardMapping()
|
||||
log.Infof("X11 input injector reconnected (display=%s)", x.display)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user