From 63898671ec239976445d77d69d90dfa3497e24ff Mon Sep 17 00:00:00 2001 From: bendahl Date: Tue, 1 May 2018 11:59:19 +0200 Subject: [PATCH] Further improve code structure. - Keycode definitions were moved to a separate file - All utility functions were moved to uinput.go - uinputdefs.go contains all internal constants - Documentation has been updated accordingly --- keyboard.go | 39 ++++- keyboard_test.go | 2 +- keycodes.go | 250 ++++++++++++++++++++++++++++++++ mouse.go | 72 +++++++++ touchpad.go | 86 +++++++++++ uinput.go | 118 +++++++++++++++ uinputdefs.go | 303 ++++++++------------------------------ uinputwrapper.go | 369 ----------------------------------------------- 8 files changed, 620 insertions(+), 619 deletions(-) create mode 100644 keycodes.go delete mode 100644 uinputwrapper.go diff --git a/keyboard.go b/keyboard.go index 10fb46f..9c52eb8 100644 --- a/keyboard.go +++ b/keyboard.go @@ -13,12 +13,12 @@ type Keyboard interface { KeyPress(key int) error // KeyDown will send a keypress event to an existing keyboard device. - // The key can be any of the predefined keycodes from uinputdefs. + // The key can be any of the predefined keycodes from keycodes.go. // Note that the key will be "held down" until "KeyUp" is called. KeyDown(key int) error // KeyUp will send a keyrelease event to an existing keyboard device. - // The key can be any of the predefined keycodes from uinputdefs. + // The key can be any of the predefined keycodes from keycodes.go. KeyUp(key int) error io.Closer @@ -62,7 +62,7 @@ func (vk vKeyboard) KeyPress(key int) error { return nil } -// KeyDown will send the key code passed (see uinputdefs.go for available keycodes). Note that unless a key release +// KeyDown will send the key code passed (see keycodes.go for available keycodes). Note that unless a key release // event is sent to the device, the key will remain pressed and therefore input will continuously be generated. Therefore, // do not forget to call "KeyUp" afterwards. func (vk vKeyboard) KeyDown(key int) error { @@ -78,7 +78,7 @@ func (vk vKeyboard) KeyDown(key int) error { return nil } -// KeyUp will release the given key passed as a parameter (see uinputdefs.go for available keycodes). In most +// KeyUp will release the given key passed as a parameter (see keycodes.go for available keycodes). In most // cases it is recommended to call this function immediately after the "KeyDown" function in order to only issue a // single key press. func (vk vKeyboard) KeyUp(key int) error { @@ -99,3 +99,34 @@ func (vk vKeyboard) KeyUp(key int) error { func (vk vKeyboard) Close() error { return closeDevice(vk.deviceFile) } + +func createVKeyboardDevice(path string, name []byte) (fd *os.File, err error) { + deviceFile, err := createDeviceFile(path) + if err != nil { + return nil, fmt.Errorf("failed to create virtual keyboard device: %v", err) + } + + err = registerDevice(deviceFile, uintptr(evKey)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register virtual keyboard device: %v", err) + } + + // register key events + for i := 0; i < keyMax; i++ { + err = ioctl(deviceFile, uiSetKeyBit, uintptr(i)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register key number %d: %v", i, err) + } + } + + return createUsbDevice(deviceFile, + uinputUserDev{ + Name: toUinputName(name), + ID: inputID{ + Bustype: busUsb, + Vendor: 0x4711, + Product: 0x0815, + Version: 1}}) +} diff --git a/keyboard_test.go b/keyboard_test.go index 4438fd7..cbb2e6c 100644 --- a/keyboard_test.go +++ b/keyboard_test.go @@ -4,7 +4,7 @@ import "testing" // This test will confirm that basic key events are working. // Note that only Key1 is used here, as the purpose of this test is to ensure that the event handling for -// keyboard devices is working. All other keys, defined in uinputdefs should work as well if this test passes. +// keyboard devices is working. All other keys, defined in keycodes.go should work as well if this test passes. // Another thing to keep in mind is that there are certain key codes that might not be great candidates for // unit testing, as they may create unwanted side effects, like logging out the current user, etc... func TestBasicKeyboard(t *testing.T) { diff --git a/keycodes.go b/keycodes.go new file mode 100644 index 0000000..2cd3f2c --- /dev/null +++ b/keycodes.go @@ -0,0 +1,250 @@ +package uinput + +// the constants that are defined here relate 1:1 to the constants defined in input.h and represent actual +// key codes that can be triggered as key events +const ( + keyReserved = 0 + KeyEsc = 1 + Key1 = 2 + Key2 = 3 + Key3 = 4 + Key4 = 5 + Key5 = 6 + Key6 = 7 + Key7 = 8 + Key8 = 9 + Key9 = 10 + Key0 = 11 + KeyMinus = 12 + KeyEqual = 13 + KeyBackspace = 14 + KeyTab = 15 + KeyQ = 16 + KeyW = 17 + KeyE = 18 + KeyR = 19 + KeyT = 20 + KeyY = 21 + KeyU = 22 + KeyI = 23 + KeyO = 24 + KeyP = 25 + KeyLeftbrace = 26 + KeyRightbrace = 27 + KeyEnter = 28 + KeyLeftctrl = 29 + KeyA = 30 + KeyS = 31 + KeyD = 32 + KeyF = 33 + KeyG = 34 + KeyH = 35 + KeyJ = 36 + KeyK = 37 + KeyL = 38 + KeySemicolon = 39 + KeyApostrophe = 40 + KeyGrave = 41 + KeyLeftshift = 42 + KeyBackslash = 43 + KeyZ = 44 + KeyX = 45 + KeyC = 46 + KeyV = 47 + KeyB = 48 + KeyN = 49 + KeyM = 50 + KeyComma = 51 + KeyDot = 52 + KeySlash = 53 + KeyRightshift = 54 + KeyKpasterisk = 55 + KeyLeftalt = 56 + KeySpace = 57 + KeyCapslock = 58 + KeyF1 = 59 + KeyF2 = 60 + KeyF3 = 61 + KeyF4 = 62 + KeyF5 = 63 + KeyF6 = 64 + KeyF7 = 65 + KeyF8 = 66 + KeyF9 = 67 + KeyF10 = 68 + KeyNumlock = 69 + KeyScrolllock = 70 + KeyKp7 = 71 + KeyKp8 = 72 + KeyKp9 = 73 + KeyKpminus = 74 + KeyKp4 = 75 + KeyKp5 = 76 + KeyKp6 = 77 + KeyKpplus = 78 + KeyKp1 = 79 + KeyKp2 = 80 + KeyKp3 = 81 + KeyKp0 = 82 + KeyKpdot = 83 + KeyZenkakuhankaku = 85 + Key102Nd = 86 + KeyF11 = 87 + KeyF12 = 88 + KeyRo = 89 + KeyKatakana = 90 + KeyHiragana = 91 + KeyHenkan = 92 + KeyKatakanahiragana = 93 + KeyMuhenkan = 94 + KeyKpjpcomma = 95 + KeyKpenter = 96 + KeyRightctrl = 97 + KeyKpslash = 98 + KeySysrq = 99 + KeyRightalt = 100 + KeyLinefeed = 101 + KeyHome = 102 + KeyUp = 103 + KeyPageup = 104 + KeyLeft = 105 + KeyRight = 106 + KeyEnd = 107 + KeyDown = 108 + KeyPagedown = 109 + KeyInsert = 110 + KeyDelete = 111 + KeyMacro = 112 + KeyMute = 113 + KeyVolumedown = 114 + KeyVolumeup = 115 + KeyPower = 116 /*ScSystemPowerDown*/ + KeyKpequal = 117 + KeyKpplusminus = 118 + KeyPause = 119 + KeyScale = 120 /*AlCompizScale(Expose)*/ + KeyKpcomma = 121 + KeyHangeul = 122 + KeyHanja = 123 + KeyYen = 124 + KeyLeftmeta = 125 + KeyRightmeta = 126 + KeyCompose = 127 + KeyStop = 128 /*AcStop*/ + KeyAgain = 129 + KeyProps = 130 /*AcProperties*/ + KeyUndo = 131 /*AcUndo*/ + KeyFront = 132 + KeyCopy = 133 /*AcCopy*/ + KeyOpen = 134 /*AcOpen*/ + KeyPaste = 135 /*AcPaste*/ + KeyFind = 136 /*AcSearch*/ + KeyCut = 137 /*AcCut*/ + KeyHelp = 138 /*AlIntegratedHelpCenter*/ + KeyMenu = 139 /*Menu(ShowMenu)*/ + KeyCalc = 140 /*AlCalculator*/ + KeySetup = 141 + KeySleep = 142 /*ScSystemSleep*/ + KeyWakeup = 143 /*SystemWakeUp*/ + KeyFile = 144 /*AlLocalMachineBrowser*/ + KeySendfile = 145 + KeyDeletefile = 146 + KeyXfer = 147 + KeyProg1 = 148 + KeyProg2 = 149 + KeyWww = 150 /*AlInternetBrowser*/ + KeyMsdos = 151 + KeyCoffee = 152 /*AlTerminalLock/Screensaver*/ + KeyDirection = 153 + KeyCyclewindows = 154 + KeyMail = 155 + KeyBookmarks = 156 /*AcBookmarks*/ + KeyComputer = 157 + KeyBack = 158 /*AcBack*/ + KeyForward = 159 /*AcForward*/ + KeyClosecd = 160 + KeyEjectcd = 161 + KeyEjectclosecd = 162 + KeyNextsong = 163 + KeyPlaypause = 164 + KeyPrevioussong = 165 + KeyStopcd = 166 + KeyRecord = 167 + KeyRewind = 168 + KeyPhone = 169 /*MediaSelectTelephone*/ + KeyIso = 170 + KeyConfig = 171 /*AlConsumerControlConfiguration*/ + KeyHomepage = 172 /*AcHome*/ + KeyRefresh = 173 /*AcRefresh*/ + KeyExit = 174 /*AcExit*/ + KeyMove = 175 + KeyEdit = 176 + KeyScrollup = 177 + KeyScrolldown = 178 + KeyKpleftparen = 179 + KeyKprightparen = 180 + KeyNew = 181 /*AcNew*/ + KeyRedo = 182 /*AcRedo/Repeat*/ + KeyF13 = 183 + KeyF14 = 184 + KeyF15 = 185 + KeyF16 = 186 + KeyF17 = 187 + KeyF18 = 188 + KeyF19 = 189 + KeyF20 = 190 + KeyF21 = 191 + KeyF22 = 192 + KeyF23 = 193 + KeyF24 = 194 + KeyPlaycd = 200 + KeyPausecd = 201 + KeyProg3 = 202 + KeyProg4 = 203 + KeyDashboard = 204 /*AlDashboard*/ + KeySuspend = 205 + KeyClose = 206 /*AcClose*/ + KeyPlay = 207 + KeyFastforward = 208 + KeyBassboost = 209 + KeyPrint = 210 /*AcPrint*/ + KeyHp = 211 + KeyCamera = 212 + KeySound = 213 + KeyQuestion = 214 + KeyEmail = 215 + KeyChat = 216 + KeySearch = 217 + KeyConnect = 218 + KeyFinance = 219 /*AlCheckbook/Finance*/ + KeySport = 220 + KeyShop = 221 + KeyAlterase = 222 + KeyCancel = 223 /*AcCancel*/ + KeyBrightnessdown = 224 + KeyBrightnessup = 225 + KeyMedia = 226 + KeySwitchvideomode = 227 /*CycleBetweenAvailableVideo */ + KeyKbdillumtoggle = 228 + KeyKbdillumdown = 229 + KeyKbdillumup = 230 + KeySend = 231 /*AcSend*/ + KeyReply = 232 /*AcReply*/ + KeyForwardmail = 233 /*AcForwardMsg*/ + KeySave = 234 /*AcSave*/ + KeyDocuments = 235 + KeyBattery = 236 + KeyBluetooth = 237 + KeyWlan = 238 + KeyUwb = 239 + KeyUnknown = 240 + KeyVideoNext = 241 /*DriveNextVideoSource*/ + KeyVideoPrev = 242 /*DrivePreviousVideoSource*/ + KeyBrightnessCycle = 243 /*BrightnessUp,AfterMaxIsMin*/ + KeyBrightnessZero = 244 /*BrightnessOff,UseAmbient*/ + KeyDisplayOff = 245 /*DisplayDeviceToOffState*/ + KeyWimax = 246 + KeyRfkill = 247 /*KeyThatControlsAllRadios*/ + KeyMicmute = 248 /*Mute/UnmuteTheMicrophone*/ + keyMax = 248 // highest key currently defined +) diff --git a/mouse.go b/mouse.go index 04fa10c..0ede3f8 100644 --- a/mouse.go +++ b/mouse.go @@ -4,6 +4,7 @@ import ( "fmt" "io" "os" + "syscall" ) // A Mouse is a device that will trigger an absolute change event. @@ -179,3 +180,74 @@ func (vRel vMouse) RightRelease() error { func (vRel vMouse) Close() error { return closeDevice(vRel.deviceFile) } + +func createMouse(path string, name []byte) (fd *os.File, err error) { + deviceFile, err := createDeviceFile(path) + if err != nil { + return nil, fmt.Errorf("could not create relative axis input device: %v", err) + } + + err = registerDevice(deviceFile, uintptr(evKey)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register key device: %v", err) + } + // register button events (in order to enable left and right click) + err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnLeft)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register left click event: %v", err) + } + err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnRight)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register right click event: %v", err) + } + + err = registerDevice(deviceFile, uintptr(evRel)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register relative axis input device: %v", err) + } + + // register x and y axis events + err = ioctl(deviceFile, uiSetRelBit, uintptr(relX)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register relative x axis events: %v", err) + } + err = ioctl(deviceFile, uiSetRelBit, uintptr(relY)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register relative y axis events: %v", err) + } + + return createUsbDevice(deviceFile, + uinputUserDev{ + Name: toUinputName(name), + ID: inputID{ + Bustype: busUsb, + Vendor: 0x4711, + Product: 0x0816, + Version: 1}}) +} + +func sendRelEvent(deviceFile *os.File, eventCode uint16, pixel int32) error { + iev := inputEvent{ + Time: syscall.Timeval{Sec: 0, Usec: 0}, + Type: evRel, + Code: eventCode, + Value: pixel} + + buf, err := inputEventToBuffer(iev) + if err != nil { + return fmt.Errorf("writing abs event failed: %v", err) + } + + _, err = deviceFile.Write(buf) + if err != nil { + return fmt.Errorf("failed to write rel event to device file: %v", err) + } + + return syncEvents(deviceFile) +} diff --git a/touchpad.go b/touchpad.go index e46fced..c680b24 100644 --- a/touchpad.go +++ b/touchpad.go @@ -152,3 +152,89 @@ func (vTouch vTouchPad) RightRelease() error { func (vTouch vTouchPad) Close() error { return closeDevice(vTouch.deviceFile) } + +func createTouchPad(path string, name []byte, minX int32, maxX int32, minY int32, maxY int32) (fd *os.File, err error) { + deviceFile, err := createDeviceFile(path) + if err != nil { + return nil, fmt.Errorf("could not create absolute axis input device: %v", err) + } + + err = registerDevice(deviceFile, uintptr(evKey)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register key device: %v", err) + } + // register button events (in order to enable left and right click) + err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnLeft)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register left click event: %v", err) + } + err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnRight)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register right click event: %v", err) + } + + err = registerDevice(deviceFile, uintptr(evAbs)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register absolute axis input device: %v", err) + } + + // register x and y axis events + err = ioctl(deviceFile, uiSetAbsBit, uintptr(absX)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register absolute x axis events: %v", err) + } + err = ioctl(deviceFile, uiSetAbsBit, uintptr(absY)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to register absolute y axis events: %v", err) + } + + var absMin [absSize]int32 + absMin[absX] = minX + absMin[absY] = minY + + var absMax [absSize]int32 + absMax[absX] = maxX + absMax[absY] = maxY + + return createUsbDevice(deviceFile, + uinputUserDev{ + Name: toUinputName(name), + ID: inputID{ + Bustype: busUsb, + Vendor: 0x4711, + Product: 0x0817, + Version: 1}, + Absmin: absMin, + Absmax: absMax}) +} + +func sendAbsEvent(deviceFile *os.File, xPos int32, yPos int32) error { + var ev [2]inputEvent + ev[0].Type = evAbs + ev[0].Code = absX + ev[0].Value = xPos + + ev[1].Type = evAbs + ev[1].Code = absY + ev[1].Value = yPos + + for _, iev := range ev { + buf, err := inputEventToBuffer(iev) + if err != nil { + return fmt.Errorf("writing abs event failed: %v", err) + } + + _, err = deviceFile.Write(buf) + if err != nil { + return fmt.Errorf("failed to write abs event to device file: %v", err) + } + } + + return syncEvents(deviceFile) +} diff --git a/uinput.go b/uinput.go index 96b4397..1eb1171 100644 --- a/uinput.go +++ b/uinput.go @@ -77,8 +77,13 @@ device. Here are a few examples of how to use the virtual touch pad: package uinput import ( + "bytes" + "encoding/binary" + "errors" "fmt" "os" + "syscall" + "time" ) func validateDevicePath(path string) { @@ -96,3 +101,116 @@ func validateUinputName(name []byte) { panic(fmt.Sprintf("device name %s is too long (maximum of %d characters allowed)", name, uinputMaxNameSize)) } } + +func toUinputName(name []byte) (uinputName [uinputMaxNameSize]byte) { + var fixedSizeName [uinputMaxNameSize]byte + copy(fixedSizeName[:], name) + return fixedSizeName +} + +func createDeviceFile(path string) (fd *os.File, err error) { + deviceFile, err := os.OpenFile(path, syscall.O_WRONLY|syscall.O_NONBLOCK, 0660) + if err != nil { + return nil, errors.New("could not open device file") + } + return deviceFile, err +} + +func registerDevice(deviceFile *os.File, evType uintptr) error { + err := ioctl(deviceFile, uiSetEvBit, evType) + if err != nil { + err = releaseDevice(deviceFile) + if err != nil { + deviceFile.Close() + return fmt.Errorf("failed to close device: %v", err) + } + deviceFile.Close() + return fmt.Errorf("invalid file handle returned from ioctl: %v", err) + } + return nil +} + +func createUsbDevice(deviceFile *os.File, dev uinputUserDev) (fd *os.File, err error) { + buf := new(bytes.Buffer) + err = binary.Write(buf, binary.LittleEndian, dev) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to write user device buffer: %v", err) + } + _, err = deviceFile.Write(buf.Bytes()) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to write uidev struct to device file: %v", err) + } + + err = ioctl(deviceFile, uiDevCreate, uintptr(0)) + if err != nil { + deviceFile.Close() + return nil, fmt.Errorf("failed to create device: %v", err) + } + + time.Sleep(time.Millisecond * 200) + + return deviceFile, err +} + +func closeDevice(deviceFile *os.File) (err error) { + err = releaseDevice(deviceFile) + if err != nil { + return fmt.Errorf("failed to close device: %v", err) + } + return deviceFile.Close() +} + +func releaseDevice(deviceFile *os.File) (err error) { + return ioctl(deviceFile, uiDevDestroy, uintptr(0)) +} + +// Note that mice and touch pads do have buttons as well. Therefore, this function is used +// by all currently available devices and resides in the main source file. +func sendBtnEvent(deviceFile *os.File, key int, btnState int) (err error) { + buf, err := inputEventToBuffer(inputEvent{ + Time: syscall.Timeval{Sec: 0, Usec: 0}, + Type: evKey, + Code: uint16(key), + Value: int32(btnState)}) + if err != nil { + return fmt.Errorf("key event could not be set: %v", err) + } + _, err = deviceFile.Write(buf) + if err != nil { + return fmt.Errorf("writing btnEvent structure to the device file failed: %v", err) + } + return err +} + +func syncEvents(deviceFile *os.File) (err error) { + buf, err := inputEventToBuffer(inputEvent{ + Time: syscall.Timeval{Sec: 0, Usec: 0}, + Type: evSyn, + Code: 0, + Value: int32(synReport)}) + if err != nil { + return fmt.Errorf("writing sync event failed: %v", err) + } + _, err = deviceFile.Write(buf) + return err +} + +func inputEventToBuffer(iev inputEvent) (buffer []byte, err error) { + buf := new(bytes.Buffer) + err = binary.Write(buf, binary.LittleEndian, iev) + if err != nil { + return nil, fmt.Errorf("failed to write input event to buffer: %v", err) + } + return buf.Bytes(), nil +} + +// original function taken from: https://github.com/tianon/debian-golang-pty/blob/master/ioctl.go +func ioctl(deviceFile *os.File, cmd, ptr uintptr) error { + _, _, errorCode := syscall.Syscall(syscall.SYS_IOCTL, deviceFile.Fd(), cmd, ptr) + if errorCode != 0 { + return errorCode + } + return nil +} diff --git a/uinputdefs.go b/uinputdefs.go index a882adc..6a6e6d6 100644 --- a/uinputdefs.go +++ b/uinputdefs.go @@ -1,249 +1,62 @@ package uinput -// the constants were copied from input.h for convenience reasons +import "syscall" + +// types needed from uinput.h +const ( + uinputMaxNameSize = 80 + uiDevCreate = 0x5501 + uiDevDestroy = 0x5502 + uiSetEvBit = 0x40045564 + uiSetKeyBit = 0x40045565 + uiSetRelBit = 0x40045566 + uiSetAbsBit = 0x40045567 + busUsb = 0x03 +) + +// input event codes as specified in input-event-codes.h +const ( + evSyn = 0x00 + evKey = 0x01 + evRel = 0x02 + evAbs = 0x03 + relX = 0x0 + relY = 0x1 + absX = 0x0 + absY = 0x1 + synReport = 0 + evBtnLeft = 0x110 + evBtnRight = 0x111 +) + const ( - keyReserved = 0 - KeyEsc = 1 - Key1 = 2 - Key2 = 3 - Key3 = 4 - Key4 = 5 - Key5 = 6 - Key6 = 7 - Key7 = 8 - Key8 = 9 - Key9 = 10 - Key0 = 11 - KeyMinus = 12 - KeyEqual = 13 - KeyBackspace = 14 - KeyTab = 15 - KeyQ = 16 - KeyW = 17 - KeyE = 18 - KeyR = 19 - KeyT = 20 - KeyY = 21 - KeyU = 22 - KeyI = 23 - KeyO = 24 - KeyP = 25 - KeyLeftbrace = 26 - KeyRightbrace = 27 - KeyEnter = 28 - KeyLeftctrl = 29 - KeyA = 30 - KeyS = 31 - KeyD = 32 - KeyF = 33 - KeyG = 34 - KeyH = 35 - KeyJ = 36 - KeyK = 37 - KeyL = 38 - KeySemicolon = 39 - KeyApostrophe = 40 - KeyGrave = 41 - KeyLeftshift = 42 - KeyBackslash = 43 - KeyZ = 44 - KeyX = 45 - KeyC = 46 - KeyV = 47 - KeyB = 48 - KeyN = 49 - KeyM = 50 - KeyComma = 51 - KeyDot = 52 - KeySlash = 53 - KeyRightshift = 54 - KeyKpasterisk = 55 - KeyLeftalt = 56 - KeySpace = 57 - KeyCapslock = 58 - KeyF1 = 59 - KeyF2 = 60 - KeyF3 = 61 - KeyF4 = 62 - KeyF5 = 63 - KeyF6 = 64 - KeyF7 = 65 - KeyF8 = 66 - KeyF9 = 67 - KeyF10 = 68 - KeyNumlock = 69 - KeyScrolllock = 70 - KeyKp7 = 71 - KeyKp8 = 72 - KeyKp9 = 73 - KeyKpminus = 74 - KeyKp4 = 75 - KeyKp5 = 76 - KeyKp6 = 77 - KeyKpplus = 78 - KeyKp1 = 79 - KeyKp2 = 80 - KeyKp3 = 81 - KeyKp0 = 82 - KeyKpdot = 83 - KeyZenkakuhankaku = 85 - Key102Nd = 86 - KeyF11 = 87 - KeyF12 = 88 - KeyRo = 89 - KeyKatakana = 90 - KeyHiragana = 91 - KeyHenkan = 92 - KeyKatakanahiragana = 93 - KeyMuhenkan = 94 - KeyKpjpcomma = 95 - KeyKpenter = 96 - KeyRightctrl = 97 - KeyKpslash = 98 - KeySysrq = 99 - KeyRightalt = 100 - KeyLinefeed = 101 - KeyHome = 102 - KeyUp = 103 - KeyPageup = 104 - KeyLeft = 105 - KeyRight = 106 - KeyEnd = 107 - KeyDown = 108 - KeyPagedown = 109 - KeyInsert = 110 - KeyDelete = 111 - KeyMacro = 112 - KeyMute = 113 - KeyVolumedown = 114 - KeyVolumeup = 115 - KeyPower = 116 /*ScSystemPowerDown*/ - KeyKpequal = 117 - KeyKpplusminus = 118 - KeyPause = 119 - KeyScale = 120 /*AlCompizScale(Expose)*/ - KeyKpcomma = 121 - KeyHangeul = 122 - KeyHanja = 123 - KeyYen = 124 - KeyLeftmeta = 125 - KeyRightmeta = 126 - KeyCompose = 127 - KeyStop = 128 /*AcStop*/ - KeyAgain = 129 - KeyProps = 130 /*AcProperties*/ - KeyUndo = 131 /*AcUndo*/ - KeyFront = 132 - KeyCopy = 133 /*AcCopy*/ - KeyOpen = 134 /*AcOpen*/ - KeyPaste = 135 /*AcPaste*/ - KeyFind = 136 /*AcSearch*/ - KeyCut = 137 /*AcCut*/ - KeyHelp = 138 /*AlIntegratedHelpCenter*/ - KeyMenu = 139 /*Menu(ShowMenu)*/ - KeyCalc = 140 /*AlCalculator*/ - KeySetup = 141 - KeySleep = 142 /*ScSystemSleep*/ - KeyWakeup = 143 /*SystemWakeUp*/ - KeyFile = 144 /*AlLocalMachineBrowser*/ - KeySendfile = 145 - KeyDeletefile = 146 - KeyXfer = 147 - KeyProg1 = 148 - KeyProg2 = 149 - KeyWww = 150 /*AlInternetBrowser*/ - KeyMsdos = 151 - KeyCoffee = 152 /*AlTerminalLock/Screensaver*/ - KeyDirection = 153 - KeyCyclewindows = 154 - KeyMail = 155 - KeyBookmarks = 156 /*AcBookmarks*/ - KeyComputer = 157 - KeyBack = 158 /*AcBack*/ - KeyForward = 159 /*AcForward*/ - KeyClosecd = 160 - KeyEjectcd = 161 - KeyEjectclosecd = 162 - KeyNextsong = 163 - KeyPlaypause = 164 - KeyPrevioussong = 165 - KeyStopcd = 166 - KeyRecord = 167 - KeyRewind = 168 - KeyPhone = 169 /*MediaSelectTelephone*/ - KeyIso = 170 - KeyConfig = 171 /*AlConsumerControlConfiguration*/ - KeyHomepage = 172 /*AcHome*/ - KeyRefresh = 173 /*AcRefresh*/ - KeyExit = 174 /*AcExit*/ - KeyMove = 175 - KeyEdit = 176 - KeyScrollup = 177 - KeyScrolldown = 178 - KeyKpleftparen = 179 - KeyKprightparen = 180 - KeyNew = 181 /*AcNew*/ - KeyRedo = 182 /*AcRedo/Repeat*/ - KeyF13 = 183 - KeyF14 = 184 - KeyF15 = 185 - KeyF16 = 186 - KeyF17 = 187 - KeyF18 = 188 - KeyF19 = 189 - KeyF20 = 190 - KeyF21 = 191 - KeyF22 = 192 - KeyF23 = 193 - KeyF24 = 194 - KeyPlaycd = 200 - KeyPausecd = 201 - KeyProg3 = 202 - KeyProg4 = 203 - KeyDashboard = 204 /*AlDashboard*/ - KeySuspend = 205 - KeyClose = 206 /*AcClose*/ - KeyPlay = 207 - KeyFastforward = 208 - KeyBassboost = 209 - KeyPrint = 210 /*AcPrint*/ - KeyHp = 211 - KeyCamera = 212 - KeySound = 213 - KeyQuestion = 214 - KeyEmail = 215 - KeyChat = 216 - KeySearch = 217 - KeyConnect = 218 - KeyFinance = 219 /*AlCheckbook/Finance*/ - KeySport = 220 - KeyShop = 221 - KeyAlterase = 222 - KeyCancel = 223 /*AcCancel*/ - KeyBrightnessdown = 224 - KeyBrightnessup = 225 - KeyMedia = 226 - KeySwitchvideomode = 227 /*CycleBetweenAvailableVideo */ - KeyKbdillumtoggle = 228 - KeyKbdillumdown = 229 - KeyKbdillumup = 230 - KeySend = 231 /*AcSend*/ - KeyReply = 232 /*AcReply*/ - KeyForwardmail = 233 /*AcForwardMsg*/ - KeySave = 234 /*AcSave*/ - KeyDocuments = 235 - KeyBattery = 236 - KeyBluetooth = 237 - KeyWlan = 238 - KeyUwb = 239 - KeyUnknown = 240 - KeyVideoNext = 241 /*DriveNextVideoSource*/ - KeyVideoPrev = 242 /*DrivePreviousVideoSource*/ - KeyBrightnessCycle = 243 /*BrightnessUp,AfterMaxIsMin*/ - KeyBrightnessZero = 244 /*BrightnessOff,UseAmbient*/ - KeyDisplayOff = 245 /*DisplayDeviceToOffState*/ - KeyWimax = 246 - KeyRfkill = 247 /*KeyThatControlsAllRadios*/ - KeyMicmute = 248 /*Mute/UnmuteTheMicrophone*/ - keyMax = 248 // highest key currently defined + btnStateReleased = 0 + btnStatePressed = 1 + absSize = 64 ) + +type inputID struct { + Bustype uint16 + Vendor uint16 + Product uint16 + Version uint16 +} + +// translated to go from uinput.h +type uinputUserDev struct { + Name [uinputMaxNameSize]byte + ID inputID + EffectsMax uint32 + Absmax [absSize]int32 + Absmin [absSize]int32 + Absfuzz [absSize]int32 + Absflat [absSize]int32 +} + +// translated to go from input.h +type inputEvent struct { + Time syscall.Timeval + Type uint16 + Code uint16 + Value int32 +} diff --git a/uinputwrapper.go b/uinputwrapper.go deleted file mode 100644 index 3f16897..0000000 --- a/uinputwrapper.go +++ /dev/null @@ -1,369 +0,0 @@ -package uinput - -import ( - "bytes" - "encoding/binary" - "errors" - "fmt" - "os" - "syscall" - "time" -) - -// types needed from uinput.h -const ( - uinputMaxNameSize = 80 - uiDevCreate = 0x5501 - uiDevDestroy = 0x5502 - uiSetEvBit = 0x40045564 - uiSetKeyBit = 0x40045565 - uiSetRelBit = 0x40045566 - uiSetAbsBit = 0x40045567 - busUsb = 0x03 -) - -// input event codes as specified in input-event-codes.h -const ( - evSyn = 0x00 - evKey = 0x01 - evRel = 0x02 - evAbs = 0x03 - relX = 0x0 - relY = 0x1 - absX = 0x0 - absY = 0x1 - synReport = 0 - evBtnLeft = 0x110 - evBtnRight = 0x111 -) - -const ( - btnStateReleased = 0 - btnStatePressed = 1 - absSize = 64 -) - -type inputID struct { - Bustype uint16 - Vendor uint16 - Product uint16 - Version uint16 -} - -// translated to go from uinput.h -type uinputUserDev struct { - Name [uinputMaxNameSize]byte - ID inputID - EffectsMax uint32 - Absmax [absSize]int32 - Absmin [absSize]int32 - Absfuzz [absSize]int32 - Absflat [absSize]int32 -} - -// translated to go from input.h -type inputEvent struct { - Time syscall.Timeval - Type uint16 - Code uint16 - Value int32 -} - -func closeDevice(deviceFile *os.File) (err error) { - err = releaseDevice(deviceFile) - if err != nil { - return fmt.Errorf("failed to close device: %v", err) - } - return deviceFile.Close() -} - -func releaseDevice(deviceFile *os.File) (err error) { - return ioctl(deviceFile, uiDevDestroy, uintptr(0)) -} - -func createDeviceFile(path string) (fd *os.File, err error) { - deviceFile, err := os.OpenFile(path, syscall.O_WRONLY|syscall.O_NONBLOCK, 0660) - if err != nil { - return nil, errors.New("could not open device file") - } - return deviceFile, err -} - -func registerDevice(deviceFile *os.File, evType uintptr) error { - err := ioctl(deviceFile, uiSetEvBit, evType) - if err != nil { - err = releaseDevice(deviceFile) - if err != nil { - deviceFile.Close() - return fmt.Errorf("failed to close device: %v", err) - } - deviceFile.Close() - return fmt.Errorf("invalid file handle returned from ioctl: %v", err) - } - return nil -} - -func createVKeyboardDevice(path string, name []byte) (fd *os.File, err error) { - deviceFile, err := createDeviceFile(path) - if err != nil { - return nil, fmt.Errorf("failed to create virtual keyboard device: %v", err) - } - - err = registerDevice(deviceFile, uintptr(evKey)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register virtual keyboard device: %v", err) - } - - // register key events - for i := 0; i < keyMax; i++ { - err = ioctl(deviceFile, uiSetKeyBit, uintptr(i)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register key number %d: %v", i, err) - } - } - - return createUsbDevice(deviceFile, - uinputUserDev{ - Name: toUinputName(name), - ID: inputID{ - Bustype: busUsb, - Vendor: 0x4711, - Product: 0x0815, - Version: 1}}) -} - -func toUinputName(name []byte) (uinputName [uinputMaxNameSize]byte) { - var fixedSizeName [uinputMaxNameSize]byte - copy(fixedSizeName[:], name) - return fixedSizeName -} - -func createTouchPad(path string, name []byte, minX int32, maxX int32, minY int32, maxY int32) (fd *os.File, err error) { - deviceFile, err := createDeviceFile(path) - if err != nil { - return nil, fmt.Errorf("could not create absolute axis input device: %v", err) - } - - err = registerDevice(deviceFile, uintptr(evKey)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register key device: %v", err) - } - // register button events (in order to enable left and right click) - err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnLeft)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register left click event: %v", err) - } - err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnRight)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register right click event: %v", err) - } - - err = registerDevice(deviceFile, uintptr(evAbs)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register absolute axis input device: %v", err) - } - - // register x and y axis events - err = ioctl(deviceFile, uiSetAbsBit, uintptr(absX)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register absolute x axis events: %v", err) - } - err = ioctl(deviceFile, uiSetAbsBit, uintptr(absY)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register absolute y axis events: %v", err) - } - - var absMin [absSize]int32 - absMin[absX] = minX - absMin[absY] = minY - - var absMax [absSize]int32 - absMax[absX] = maxX - absMax[absY] = maxY - - return createUsbDevice(deviceFile, - uinputUserDev{ - Name: toUinputName(name), - ID: inputID{ - Bustype: busUsb, - Vendor: 0x4711, - Product: 0x0817, - Version: 1}, - Absmin: absMin, - Absmax: absMax}) -} - -func createMouse(path string, name []byte) (fd *os.File, err error) { - deviceFile, err := createDeviceFile(path) - if err != nil { - return nil, fmt.Errorf("could not create relative axis input device: %v", err) - } - - err = registerDevice(deviceFile, uintptr(evKey)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register key device: %v", err) - } - // register button events (in order to enable left and right click) - err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnLeft)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register left click event: %v", err) - } - err = ioctl(deviceFile, uiSetKeyBit, uintptr(evBtnRight)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register right click event: %v", err) - } - - err = registerDevice(deviceFile, uintptr(evRel)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register relative axis input device: %v", err) - } - - // register x and y axis events - err = ioctl(deviceFile, uiSetRelBit, uintptr(relX)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register relative x axis events: %v", err) - } - err = ioctl(deviceFile, uiSetRelBit, uintptr(relY)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to register relative y axis events: %v", err) - } - - return createUsbDevice(deviceFile, - uinputUserDev{ - Name: toUinputName(name), - ID: inputID{ - Bustype: busUsb, - Vendor: 0x4711, - Product: 0x0816, - Version: 1}}) -} - -func createUsbDevice(deviceFile *os.File, dev uinputUserDev) (fd *os.File, err error) { - buf := new(bytes.Buffer) - err = binary.Write(buf, binary.LittleEndian, dev) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to write user device buffer: %v", err) - } - _, err = deviceFile.Write(buf.Bytes()) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to write uidev struct to device file: %v", err) - } - - err = ioctl(deviceFile, uiDevCreate, uintptr(0)) - if err != nil { - deviceFile.Close() - return nil, fmt.Errorf("failed to create device: %v", err) - } - - time.Sleep(time.Millisecond * 200) - - return deviceFile, err -} - -func sendBtnEvent(deviceFile *os.File, key int, btnState int) (err error) { - buf, err := inputEventToBuffer(inputEvent{ - Time: syscall.Timeval{Sec: 0, Usec: 0}, - Type: evKey, - Code: uint16(key), - Value: int32(btnState)}) - if err != nil { - return fmt.Errorf("key event could not be set: %v", err) - } - _, err = deviceFile.Write(buf) - if err != nil { - return fmt.Errorf("writing btnEvent structure to the device file failed: %v", err) - } - return err -} - -func sendAbsEvent(deviceFile *os.File, xPos int32, yPos int32) error { - var ev [2]inputEvent - ev[0].Type = evAbs - ev[0].Code = absX - ev[0].Value = xPos - - ev[1].Type = evAbs - ev[1].Code = absY - ev[1].Value = yPos - - for _, iev := range ev { - buf, err := inputEventToBuffer(iev) - if err != nil { - return fmt.Errorf("writing abs event failed: %v", err) - } - - _, err = deviceFile.Write(buf) - if err != nil { - return fmt.Errorf("failed to write abs event to device file: %v", err) - } - } - - return syncEvents(deviceFile) -} - -func sendRelEvent(deviceFile *os.File, eventCode uint16, pixel int32) error { - iev := inputEvent{ - Time: syscall.Timeval{Sec: 0, Usec: 0}, - Type: evRel, - Code: eventCode, - Value: pixel} - - buf, err := inputEventToBuffer(iev) - if err != nil { - return fmt.Errorf("writing abs event failed: %v", err) - } - - _, err = deviceFile.Write(buf) - if err != nil { - return fmt.Errorf("failed to write rel event to device file: %v", err) - } - - return syncEvents(deviceFile) -} - -func syncEvents(deviceFile *os.File) (err error) { - buf, err := inputEventToBuffer(inputEvent{ - Time: syscall.Timeval{Sec: 0, Usec: 0}, - Type: evSyn, - Code: 0, - Value: int32(synReport)}) - if err != nil { - return fmt.Errorf("writing sync event failed: %v", err) - } - _, err = deviceFile.Write(buf) - return err -} - -func inputEventToBuffer(iev inputEvent) (buffer []byte, err error) { - buf := new(bytes.Buffer) - err = binary.Write(buf, binary.LittleEndian, iev) - if err != nil { - return nil, fmt.Errorf("failed to write input event to buffer: %v", err) - } - return buf.Bytes(), nil -} - -// original function taken from: https://github.com/tianon/debian-golang-pty/blob/master/ioctl.go -func ioctl(deviceFile *os.File, cmd, ptr uintptr) error { - _, _, errorCode := syscall.Syscall(syscall.SYS_IOCTL, deviceFile.Fd(), cmd, ptr) - if errorCode != 0 { - return errorCode - } - return nil -}