Documentation
¶
Index ¶
- Constants
- Variables
- func ApplySessionEnv(env *SessionEnv) error
- func CommandAsSessionUser(env *SessionEnv, name string, args ...string) (*exec.Cmd, error)
- func FormatLayout(spec LayoutSpec) string
- func GetKeyName(code uint16) string
- func HasKeyboardCapability(fd int) bool
- func Run(daemon bool, stderr io.Writer) int
- func RunDefault(daemon bool)
- func SetVersion(buildVersion string)
- type Action
- type BufferAction
- type Clipboard
- type Config
- type Converter
- func (c *Converter) BufferLen() int
- func (c *Converter) ClearBuffer()
- func (c *Converter) Convert(action Action) []KeyEvent
- func (c *Converter) GetBuffer() []KeyEvent
- func (c *Converter) GetBufferDump() string
- func (c *Converter) HasText() bool
- func (c *Converter) Process() Action
- func (c *Converter) Push(code uint16, value int32) bool
- func (c *Converter) SetDebugLogger(fn DebugLogger)
- type DebugLogger
- type DetectionOptions
- type DetectionResult
- type Device
- type DeviceEvent
- type DeviceManager
- type FileLogger
- type InputEvent
- type InputReader
- func (ir *InputReader) AddDevice(path string) (*Device, error)
- func (ir *InputReader) AddToBlacklist(uid string)
- func (ir *InputReader) Close()
- func (ir *InputReader) Count() int
- func (ir *InputReader) Flush()
- func (ir *InputReader) GetAllDevices() []*Device
- func (ir *InputReader) GetAllFDs() []int
- func (ir *InputReader) GetDevice(fd int) *Device
- func (ir *InputReader) GetDeviceByPath(path string) *Device
- func (ir *InputReader) GrabAll()
- func (ir *InputReader) HasEpoll() bool
- func (ir *InputReader) IsBlacklisted(uid string) bool
- func (ir *InputReader) ReadEvent(fd int) (*InputEvent, bool)
- func (ir *InputReader) RemoveDevice(path string) error
- func (ir *InputReader) UngrabAll()
- func (ir *InputReader) WaitForEvents(timeoutMs int) []int
- type KeyEvent
- type LayoutConverter
- type LayoutInfo
- type LayoutSpec
- type LogLevel
- type Pattern
- type SessionEnv
- type Switcher
- type UinputSetup
- type VirtualKeyboard
- type WaitConfig
- type X11Selection
Constants ¶
const ( K_UP = 0 K_DOWN = 1 K_REPEAT = 2 ANY_SHIFT = -1 // special placeholder for any shift key )
const ( LogDir = "/tmp/gswitch" LogFileName = "gswitch.log" LogMaxSize = 5 * 1024 * 1024 // 5 MB LogMaxBackups = 3 )
Log file location and rotation settings
const ( // EVIOCGID - get device ID EVIOCGID = 0x80084502 // EVIOCGNAME - get device name (with size 256) EVIOCGNAME = 0x81004506 // EVIOCGBIT - get event bits EVIOCGBIT_EV_KEY = 0x80604521 // EVIOCGBIT(EV_KEY, 96) )
ioctl constants for evdev
const ( ConfigFile = "/etc/gswitch/default.conf" InputDevicesDir = "/dev/input/" UinputFile = "/dev/uinput" )
Paths
const ( EV_SYN = 0x00 EV_KEY = 0x01 )
Event types
const ( UI_SET_EVBIT = 0x40045564 UI_SET_KEYBIT = 0x40045565 UI_DEV_SETUP = 0x405C5503 UI_DEV_CREATE = 0x00005501 UI_DEV_DESTROY = 0x00005502 )
ioctl commands for uinput
const ( KEY_BACKSPACE = 14 KEY_SPACE = 57 KEY_ENTER = 28 KEY_KPENTER = 96 KEY_LEFTCTRL = 29 KEY_RIGHTCTRL = 97 KEY_C = 46 KEY_V = 47 )
Key codes
const ( // DetectKeyboardIterations is ~57 seconds at 10ms per iteration DetectKeyboardIterations = 5700 // DetectKeyIterations is 60 seconds at 10ms per iteration DetectKeyIterations = 6000 // WaitForKeyboardIterations is 60 seconds at 100ms per iteration WaitForKeyboardIterations = 600 // DetectSleepMs is the sleep duration between detection attempts DetectSleepMs = 10 )
Timeouts and limits for keyboard detection (in iterations)
const ( // MaxDeviceNameLength is the maximum length of a device name MaxDeviceNameLength = 256 // KeyBitsSize is the size of the key capability bitmap (covers all keys up to 768) KeyBitsSize = 96 )
Device info buffer sizes
const ( // EventLoopTimeoutMs is the timeout for main event loop select EventLoopTimeoutMs = 100 // ShutdownTimeoutMs is the timeout waiting for goroutines to finish ShutdownTimeoutMs = 500 // PollingIntervalMs is the interval for polling-based event reading PollingIntervalMs = 5 // DeviceSettleMs is the delay after device hotplug before reading DeviceSettleMs = 100 // DeviceOpenRetryAttempts covers the short interval between inotify // reporting a new node and udev/logind applying the active user's ACL. DeviceOpenRetryAttempts = 10 DeviceOpenRetryDelayMs = 100 // SelectionConversionDelayMs is the initial delay before reading selection SelectionConversionInitDelayMs = 20 // ClipboardWriteDelayMs is the delay after writing to clipboard ClipboardWriteDelayMs = 50 )
Timing constants for event loop
const ( // MaxDelayMs is the maximum allowed delay value in config MaxDelayMs = 1000 // MaxLayoutSwitchDelayMs is the maximum allowed layout switch delay MaxLayoutSwitchDelayMs = 2000 )
Configuration limits
const ( // FNV1aOffset is the FNV-1a offset basis FNV1aOffset = 14695981039346656037 // FNV1aPrime is the FNV-1a prime FNV1aPrime = 1099511628211 )
FNV-1a hash constants
const (
BUS_USB = 0x03
)
Bus types
const (
// EVIOCGBIT(0, 4) - get event type bits (EV_KEY, EV_REL, etc.)
EVIOCGBIT_EV = 0x80044520
)
ioctl commands for evdev
const EVIOCGRAB = 0x40044590
EVIOCGRAB ioctl constant
const InputEventSize = int(unsafe.Sizeof(InputEvent{}))
const ( // MaxKeyBufSize limits the key buffer to prevent unbounded memory growth // This is approximately 4096 keypresses which is more than enough for any phrase MaxKeyBufSize = 4096 )
Buffer limits
const (
SYN_REPORT = 0x00
)
Synchronization event codes
Variables ¶
var ( ErrNoActiveSession = detect.ErrNoActiveSession ErrNoSystemd = detect.ErrNoSystemd ErrNoLayoutSwitchOption = detect.ErrNoLayoutSwitchOption )
var BufKillers = map[uint16]bool{ 15: true, 29: true, 56: true, 97: true, 100: true, 102: true, 103: true, 104: true, 105: true, 106: true, 107: true, 108: true, 109: true, 110: true, }
Buffer killers (keys that clear the buffer)
var KeyName = []string{}/* 249 elements not displayed */
Key names for debugging
var Letters = map[uint16]bool{ 2: true, 3: true, 4: true, 5: true, 6: true, 7: true, 8: true, 9: true, 10: true, 11: true, 12: true, 13: true, 14: true, 16: true, 17: true, 18: true, 19: true, 20: true, 21: true, 22: true, 23: true, 24: true, 25: true, 26: true, 27: true, 28: true, 30: true, 31: true, 32: true, 33: true, 34: true, 35: true, 36: true, 37: true, 38: true, 39: true, 40: true, 41: true, 43: true, 44: true, 45: true, 46: true, 47: true, 48: true, 49: true, 50: true, 51: true, 52: true, 53: true, 55: true, 57: true, 71: true, 72: true, 73: true, 74: true, 75: true, 76: true, 77: true, 78: true, 79: true, 80: true, 81: true, 82: true, 83: true, 96: true, 98: true, }
Keys to watch and replace (Letters)
var Shifts = map[uint16]bool{ 42: true, 54: true, }
Shift keys
Functions ¶
func ApplySessionEnv ¶
func ApplySessionEnv(env *SessionEnv) error
func CommandAsSessionUser ¶ added in v0.6.0
func FormatLayout ¶
func FormatLayout(spec LayoutSpec) string
FormatLayout returns layout name in config format.
func GetKeyName ¶
GetKeyName returns human-readable key name by scan code.
func HasKeyboardCapability ¶
HasKeyboardCapability reports whether device supports keyboard events.
func RunDefault ¶
func RunDefault(daemon bool)
RunDefault executes runtime and exits process with the resulting code.
func SetVersion ¶
func SetVersion(buildVersion string)
SetVersion configures runtime version for logging.
Types ¶
type Action ¶
type Action int
Action represents what conversion action to take
const ( ActionNone Action = iota ActionConvertWord ActionConvertAll // ActionDoubleShiftNoText: double-shift trigger fired but the buffer has // no text. Nothing to convert in the buffer, but the caller may act on // the trigger itself (Ctrl+DoubleShift converts the selection). ActionDoubleShiftNoText )
type BufferAction ¶
type BufferAction int
Buffer action type
const ( KeepBuffer BufferAction = iota ReplaceAll ReplaceWord )
type Clipboard ¶
type Clipboard struct {
// contains filtered or unexported fields
}
Clipboard handles reading and writing to the system clipboard
func NewClipboard ¶
func NewClipboard(sessionEnv *SessionEnv) (*Clipboard, error)
NewClipboard creates a new Clipboard instance
func (*Clipboard) BackendName ¶
BackendName returns the name of the clipboard backend
func (*Clipboard) HasPrimarySelection ¶
HasPrimarySelection returns true if PRIMARY selection is available
func (*Clipboard) ReadPrimarySelection ¶
ReadPrimarySelection reads the PRIMARY selection (currently selected text) Returns empty string if nothing is selected
type Converter ¶
type Converter struct {
ConvKey uint16 // key to trigger conversion (0 = double-shift)
LSKeys []uint16 // keys to switch layout
// contains filtered or unexported fields
}
Converter handles key buffering and conversion logic
func (*Converter) GetBufferDump ¶
GetBufferDump returns a string representation of the buffer for debugging
func (*Converter) Process ¶
Process checks if conversion should be triggered Returns the action to take
func (*Converter) SetDebugLogger ¶
func (c *Converter) SetDebugLogger(fn DebugLogger)
SetDebugLogger sets the debug logging function
type DebugLogger ¶
DebugLogger is a function type for debug logging
type DetectionOptions ¶
type DetectionOptions = detect.DetectionOptions
type DetectionResult ¶
type DetectionResult = detect.DetectionResult
func DetectLayoutSwitchKeys ¶
func DetectLayoutSwitchKeys(opts *DetectionOptions) (*DetectionResult, error)
type DeviceEvent ¶
DeviceEvent represents a device connection/disconnection event
type DeviceManager ¶
type DeviceManager struct {
// contains filtered or unexported fields
}
DeviceManager watches /dev/input/ for device changes using inotify
func NewDeviceManager ¶
func NewDeviceManager() (*DeviceManager, error)
NewDeviceManager creates a new DeviceManager instance
func (*DeviceManager) Close ¶
func (dm *DeviceManager) Close() error
Close closes the DeviceManager and releases resources
func (*DeviceManager) FD ¶
func (dm *DeviceManager) FD() int
FD returns the inotify file descriptor for use with epoll/select
func (*DeviceManager) ProcessEvents ¶
func (dm *DeviceManager) ProcessEvents() error
ProcessEvents reads inotify events and sends them to the events channel This should be called in a loop or goroutine
func (*DeviceManager) ScanExisting ¶
func (dm *DeviceManager) ScanExisting() error
ScanExisting scans existing event devices and sends them as connected events
func (*DeviceManager) Watch ¶
func (dm *DeviceManager) Watch() <-chan DeviceEvent
Watch starts watching for device changes in a goroutine Returns a channel that receives device events
type FileLogger ¶
type FileLogger struct {
// contains filtered or unexported fields
}
FileLogger handles file-based logging with rotation
func NewFileLogger ¶
func NewFileLogger() (*FileLogger, error)
NewFileLogger creates a new file logger with rotation support
type InputEvent ¶
Linux input event structure
type InputReader ¶
type InputReader struct {
// contains filtered or unexported fields
}
InputReader manages multiple input devices
func (*InputReader) AddDevice ¶
func (ir *InputReader) AddDevice(path string) (*Device, error)
AddDevice adds a new input device Returns the device if successfully added, nil if skipped/failed
func (*InputReader) AddToBlacklist ¶
func (ir *InputReader) AddToBlacklist(uid string)
AddToBlacklist adds a device UID to the blacklist
func (*InputReader) Count ¶
func (ir *InputReader) Count() int
Count returns the number of active devices
func (*InputReader) Flush ¶
func (ir *InputReader) Flush()
Flush discards all pending events from all devices
func (*InputReader) GetAllDevices ¶
func (ir *InputReader) GetAllDevices() []*Device
GetAllDevices returns all devices
func (*InputReader) GetAllFDs ¶
func (ir *InputReader) GetAllFDs() []int
GetAllFDs returns all file descriptors
func (*InputReader) GetDevice ¶
func (ir *InputReader) GetDevice(fd int) *Device
GetDevice returns a device by file descriptor
func (*InputReader) GetDeviceByPath ¶
func (ir *InputReader) GetDeviceByPath(path string) *Device
GetDeviceByPath returns a device by path
func (*InputReader) GrabAll ¶
func (ir *InputReader) GrabAll()
GrabAll grabs all input devices exclusively This prevents events from reaching other applications
func (*InputReader) HasEpoll ¶
func (ir *InputReader) HasEpoll() bool
HasEpoll returns true if epoll is available
func (*InputReader) IsBlacklisted ¶
func (ir *InputReader) IsBlacklisted(uid string) bool
IsBlacklisted checks if a UID is blacklisted
func (*InputReader) ReadEvent ¶
func (ir *InputReader) ReadEvent(fd int) (*InputEvent, bool)
ReadEvent reads an input event from a device Returns the event and true if successful, false if no event available
func (*InputReader) RemoveDevice ¶
func (ir *InputReader) RemoveDevice(path string) error
RemoveDevice removes a device by path
func (*InputReader) UngrabAll ¶
func (ir *InputReader) UngrabAll()
UngrabAll releases all grabbed input devices
func (*InputReader) WaitForEvents ¶
func (ir *InputReader) WaitForEvents(timeoutMs int) []int
WaitForEvents waits for input events using epoll Returns slice of file descriptors that have events ready timeoutMs: -1 for blocking, 0 for non-blocking, >0 for timeout in ms
type LayoutConverter ¶
type LayoutConverter struct {
Layout1 *LayoutInfo
Layout2 *LayoutInfo
ToLayout2 map[rune]rune // char from layout1 -> char in layout2
ToLayout1 map[rune]rune // char from layout2 -> char in layout1
}
LayoutConverter handles conversion between two keyboard layouts
func NewLayoutConverter ¶
func NewLayoutConverter(layout1, layout2 *LayoutInfo) *LayoutConverter
NewLayoutConverter creates a converter between two layouts
func (*LayoutConverter) Convert ¶
func (lc *LayoutConverter) Convert(text string, toLayout2 bool) string
Convert converts text from one layout to another direction: true = layout1->layout2, false = layout2->layout1
func (*LayoutConverter) DetectLayout ¶
func (lc *LayoutConverter) DetectLayout(text string) bool
DetectLayout attempts to detect which layout the text is in Returns true if text appears to be in layout1, false if in layout2
type LayoutInfo ¶
type LayoutInfo struct {
Name string // e.g., "us", "ru"
Variant string // e.g., "basic", "winkeys"
KeyMap map[string][]rune // keycode -> [normal, shift] runes
}
LayoutInfo holds information about a keyboard layout
func LoadLayout ¶
func LoadLayout(name, variant string) (*LayoutInfo, error)
LoadLayout loads a keyboard layout from XKB symbols file
type LayoutSpec ¶
type LayoutSpec = cfg.LayoutSpec
func GetCurrentLayouts ¶
func GetCurrentLayouts(env *SessionEnv) ([]LayoutSpec, error)
GetCurrentLayouts returns the currently configured keyboard layouts. env (may be nil) is the active graphical session: gsettings-based sources must run as the session user — root cannot reach the user's dconf, and on dbus-broker systems (Fedora) it silently gets schema defaults instead.
type Pattern ¶
type Pattern struct {
Code int // key code, or -1 for ANY_SHIFT
Value int // expected value (0=up, 1=down)
Condition bool // true = must match, false = must NOT match
}
Pattern represents a pattern element for matching
type SessionEnv ¶
type SessionEnv = detect.SessionEnv
func GetActiveSessionEnv ¶
func GetActiveSessionEnv() (*SessionEnv, error)
type Switcher ¶
type Switcher struct {
// contains filtered or unexported fields
}
Switcher handles the keyboard layout switching logic with multi-device support
func NewSwitcher ¶
NewSwitcher creates a new Switcher instance
type UinputSetup ¶
type UinputSetup struct {
ID struct {
Bustype uint16
Vendor uint16
Product uint16
Version uint16
}
Name [80]byte
FFEffectsMax uint32
}
uinput_setup structure
type VirtualKeyboard ¶
type VirtualKeyboard struct {
// contains filtered or unexported fields
}
VirtualKeyboard represents a virtual keyboard device via uinput
func NewVirtualKeyboard ¶
func NewVirtualKeyboard() (*VirtualKeyboard, error)
NewVirtualKeyboard creates a new virtual keyboard device
func (*VirtualKeyboard) Close ¶
func (vk *VirtualKeyboard) Close() error
Close destroys the virtual keyboard device
func (*VirtualKeyboard) EmitKey ¶
func (vk *VirtualKeyboard) EmitKey(code uint16, value int32) error
EmitKey sends a key event through the virtual keyboard
func (*VirtualKeyboard) KeyDown ¶
func (vk *VirtualKeyboard) KeyDown(code uint16) error
KeyDown simulates a key press (down only)
func (*VirtualKeyboard) KeyUp ¶
func (vk *VirtualKeyboard) KeyUp(code uint16) error
KeyUp simulates a key release (up only)
type WaitConfig ¶
type WaitConfig struct {
InitialInterval time.Duration
MaxInterval time.Duration
LogInterval time.Duration
}
WaitConfig holds configuration for waitForSession timing. Used for testing with shorter intervals.
func DefaultWaitConfig ¶
func DefaultWaitConfig() WaitConfig
DefaultWaitConfig returns the default production configuration.
type X11Selection ¶
type X11Selection struct {
// contains filtered or unexported fields
}
X11Selection provides access to X11 PRIMARY selection
func NewX11Selection ¶
func NewX11Selection() (*X11Selection, error)
NewX11Selection creates a new X11Selection instance
func (*X11Selection) ReadPrimary ¶
func (x *X11Selection) ReadPrimary() (string, error)
ReadPrimary reads the PRIMARY selection (currently selected text)