runtime

package
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 27, 2026 License: MIT Imports: 28 Imported by: 0

Documentation

Index

Constants

View Source
const (
	K_UP     = 0
	K_DOWN   = 1
	K_REPEAT = 2

	ANY_SHIFT = -1 // special placeholder for any shift key
)
View Source
const (
	LogDir        = "/tmp/gswitch"
	LogFileName   = "gswitch.log"
	LogMaxSize    = 5 * 1024 * 1024 // 5 MB
	LogMaxBackups = 3
)

Log file location and rotation settings

View Source
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

View Source
const (
	ConfigFile      = "/etc/gswitch/default.conf"
	InputDevicesDir = "/dev/input/"
	UinputFile      = "/dev/uinput"
)

Paths

View Source
const (
	EV_SYN = 0x00
	EV_KEY = 0x01
)

Event types

View Source
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

View Source
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

View Source
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)

View Source
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

View Source
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

View Source
const (
	// MaxDelayMs is the maximum allowed delay value in config
	MaxDelayMs = 1000
	// MaxLayoutSwitchDelayMs is the maximum allowed layout switch delay
	MaxLayoutSwitchDelayMs = 2000
)

Configuration limits

View Source
const (
	// FNV1aOffset is the FNV-1a offset basis
	FNV1aOffset = 14695981039346656037
	// FNV1aPrime is the FNV-1a prime
	FNV1aPrime = 1099511628211
)

FNV-1a hash constants

View Source
const (
	BUS_USB = 0x03
)

Bus types

View Source
const (
	// EVIOCGBIT(0, 4) - get event type bits (EV_KEY, EV_REL, etc.)
	EVIOCGBIT_EV = 0x80044520
)

ioctl commands for evdev

View Source
const EVIOCGRAB = 0x40044590

EVIOCGRAB ioctl constant

View Source
const InputEventSize = int(unsafe.Sizeof(InputEvent{}))
View Source
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

View Source
const (
	SYN_REPORT = 0x00
)

Synchronization event codes

Variables

View Source
var (
	ErrNoActiveSession      = detect.ErrNoActiveSession
	ErrNoSystemd            = detect.ErrNoSystemd
	ErrNoLayoutSwitchOption = detect.ErrNoLayoutSwitchOption
)
View Source
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)

View Source
var KeyName = []string{}/* 249 elements not displayed */

Key names for debugging

View Source
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)

View Source
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 CommandAsSessionUser(env *SessionEnv, name string, args ...string) (*exec.Cmd, error)

func FormatLayout

func FormatLayout(spec LayoutSpec) string

FormatLayout returns layout name in config format.

func GetKeyName

func GetKeyName(code uint16) string

GetKeyName returns human-readable key name by scan code.

func HasKeyboardCapability

func HasKeyboardCapability(fd int) bool

HasKeyboardCapability reports whether device supports keyboard events.

func Run

func Run(daemon bool, stderr io.Writer) int

Run executes daemon/debug mode lifecycle and returns process exit code.

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

func (cb *Clipboard) BackendName() string

BackendName returns the name of the clipboard backend

func (*Clipboard) Close

func (cb *Clipboard) Close()

Close releases resources

func (*Clipboard) HasPrimarySelection

func (cb *Clipboard) HasPrimarySelection() bool

HasPrimarySelection returns true if PRIMARY selection is available

func (*Clipboard) Read

func (cb *Clipboard) Read() (string, error)

Read reads text from the clipboard

func (*Clipboard) ReadPrimarySelection

func (cb *Clipboard) ReadPrimarySelection() (string, error)

ReadPrimarySelection reads the PRIMARY selection (currently selected text) Returns empty string if nothing is selected

func (*Clipboard) Write

func (cb *Clipboard) Write(text string) error

Write writes text to the clipboard

type Config

type Config = cfg.Config

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 NewConverter

func NewConverter() *Converter

NewConverter creates a new Converter

func (*Converter) BufferLen

func (c *Converter) BufferLen() int

BufferLen returns the buffer length

func (*Converter) ClearBuffer

func (c *Converter) ClearBuffer()

ClearBuffer clears the key buffer

func (*Converter) Convert

func (c *Converter) Convert(action Action) []KeyEvent

Convert generates the sequence of keys to emit for conversion

func (*Converter) GetBuffer

func (c *Converter) GetBuffer() []KeyEvent

GetBuffer returns a copy of the current buffer

func (*Converter) GetBufferDump

func (c *Converter) GetBufferDump() string

GetBufferDump returns a string representation of the buffer for debugging

func (*Converter) HasText

func (c *Converter) HasText() bool

HasText checks if buffer contains any non-shift keys

func (*Converter) Process

func (c *Converter) Process() Action

Process checks if conversion should be triggered Returns the action to take

func (*Converter) Push

func (c *Converter) Push(code uint16, value int32) bool

Push adds a key event to the buffer Returns true if buffer was modified

func (*Converter) SetDebugLogger

func (c *Converter) SetDebugLogger(fn DebugLogger)

SetDebugLogger sets the debug logging function

type DebugLogger

type DebugLogger func(format string, args ...any)

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 Device

type Device struct {
	FD   int
	File *os.File
	Path string
	UID  string
	Name string
}

Device represents an input device

type DeviceEvent

type DeviceEvent struct {
	Path      string
	Connected bool
}

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

func (*FileLogger) Close

func (l *FileLogger) Close()

Close closes the log file

func (*FileLogger) Path

func (l *FileLogger) Path() string

Path returns the current log file path

func (*FileLogger) Write

func (l *FileLogger) Write(level LogLevel, format string, args ...any)

Write writes a log message with the specified level

type InputEvent

type InputEvent struct {
	Time  syscall.Timeval
	Type  uint16
	Code  uint16
	Value int32
}

Linux input event structure

type InputReader

type InputReader struct {
	// contains filtered or unexported fields
}

InputReader manages multiple input devices

func NewInputReader

func NewInputReader() *InputReader

NewInputReader creates a new InputReader

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) Close

func (ir *InputReader) Close()

Close closes all devices and epoll

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 KeyEvent

type KeyEvent struct {
	Code  uint16
	Value int32
}

KeyEvent represents a key event in the buffer

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 LogLevel

type LogLevel int

LogLevel represents the severity of a log message

const (
	LogLevelDebug LogLevel = iota
	LogLevelInfo
	LogLevelError
)

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

func NewSwitcher(config *Config, debug bool) (*Switcher, error)

NewSwitcher creates a new Switcher instance

func (*Switcher) Close

func (s *Switcher) Close()

func (*Switcher) Run

func (s *Switcher) Run() error

Run starts the main event loop

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)

func (*VirtualKeyboard) PressKey

func (vk *VirtualKeyboard) PressKey(code uint16, delayMs int) error

PressKey simulates a key press (down + up) with a small delay between

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) Close

func (x *X11Selection) Close()

Close closes the X11 connection

func (*X11Selection) ReadPrimary

func (x *X11Selection) ReadPrimary() (string, error)

ReadPrimary reads the PRIMARY selection (currently selected text)

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL