diff --git a/src/BizHawk.Bizware.Input/evdev/EvDevKeyInput.cs b/src/BizHawk.Bizware.Input/evdev/EvDevKeyInput.cs new file mode 100644 index 0000000000..54c04dcebc --- /dev/null +++ b/src/BizHawk.Bizware.Input/evdev/EvDevKeyInput.cs @@ -0,0 +1,483 @@ +#nullable enable + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; + +using BizHawk.Client.Common; +using BizHawk.Common; +using BizHawk.Common.NumberExtensions; + +using static BizHawk.Common.EvDevImports; +using static BizHawk.Common.FildesImports; + +namespace BizHawk.Bizware.Input +{ + internal static class EvDevKeyInput + { + private struct EvDevKeyboard + { + public uint DriverVersion; + public ushort IdBus; + public ushort IdVendor; + public ushort IdProduct; + public ushort IdVersion; + public string Name; + public int Fd; + public string Path; + + public override string ToString() + { + var verMajor = DriverVersion >> 16; + var verMinor = DriverVersion >> 8 & 0xFF; + var varRev = DriverVersion & 0xFF; + return $"{Name} ({verMajor}.{verMinor}.{varRev} {IdBus:X4}/{IdVendor:X4}/{IdProduct:X4}/{IdVersion:X4})"; + } + } + + private static bool _isInit; + private static FileSystemWatcher? _fileSystemWatcher; + private static readonly Dictionary _keyboards = new(); + + private static readonly object _lockObj = new(); + + private static List DecodeBits(Span bits) + { + var result = new List(bits.Length * 8); + for (var i = 0; i < bits.Length; i++) + { + var b = bits[i]; + var bitPos = i * 8; + for (var j = 0; j < 8; j++) + { + if (b.Bit(j)) + { + result.Add(bitPos + j); + } + } + } + + return result; + } + + private static unsafe void MaybeAddKeyboard(string path) + { + if (_keyboards.ContainsKey(path)) + { + // already have this, ignore + return; + } + + var fd = open(path, OpenFlags.O_RDONLY | OpenFlags.O_NONBLOCK | OpenFlags.O_CLOEXEC); + if (fd == -1) + { + return; + } + + var version = 0u; + var id = stackalloc ushort[4]; + var str = stackalloc byte[256]; + new Span(id, 4).Clear(); + new Span(str, 256).Clear(); + + // if any of these fail, the device was either removed or garbage + if (ioctl(fd, new(EVIOCGVERSION), &version) == -1 || + ioctl(fd, new(EVIOCGID), id) == -1 || + ioctl(fd, new(EVIOCGNAME(256)), str) == -1) + { + _ = close(fd); + return; + } + + str[255] = 0; // not trusting this remains nul terminated + var name = Marshal.PtrToStringAnsi(new(str))!; + + const int eventBitBufferSize = (int)EvDevEventType.EV_MAX / 8 + 1; + var eventBits = stackalloc byte[eventBitBufferSize]; + new Span(eventBits, eventBitBufferSize).Clear(); + if (ioctl(fd, new(EVIOCGBIT(EvDevEventType.EV_SYN, (int)EvDevEventType.EV_MAX)), eventBits) == -1) + { + _ = close(fd); + return; + } + + var supportedEvents = DecodeBits(new(eventBits, eventBitBufferSize)); + if (!supportedEvents.Contains((int)EvDevEventType.EV_KEY)) + { + // we only care about keyboards + _ = close(fd); + return; + } + + const int keyBitBufferSize = (int)EvDevKeyCode.KEY_MAX / 8 + 1; + var keyBits = stackalloc byte[keyBitBufferSize]; + new Span(keyBits, keyBitBufferSize).Clear(); + if (ioctl(fd, new(EVIOCGBIT(EvDevEventType.EV_KEY, (int)EvDevKeyCode.KEY_MAX)), keyBits) == -1) + { + _ = close(fd); + return; + } + + var supportedKeys = DecodeBits(new(keyBits, keyBitBufferSize)); + if (supportedKeys.Count == 0) + { + // probably garbage + return; + } + + if (name.IndexOf("keyboard", StringComparison.InvariantCultureIgnoreCase) == -1) + { + // probably not be a keyboard + // TODO: do some better heuristics here (maybe check if supportedKeys has A-Z?) + return; + } + + var keyboard = new EvDevKeyboard + { + DriverVersion = version, + IdBus = id[0], + IdProduct = id[1], + IdVendor = id[2], + IdVersion = id[3], + Name = name, + Fd = fd, + Path = path, + }; + + Console.WriteLine($"Added keyboard {keyboard}"); + _keyboards.Add(path, keyboard); + } + + private static void MaybeRemoveKeyboard(string path) + { + if (_keyboards.TryGetValue(path, out var keyboard)) + { + _ = close(keyboard.Fd); + Console.WriteLine($"Removed keyboard {keyboard}"); + _keyboards.Remove(path); + } + } + + private static void OnWatcherEvent(object _, FileSystemEventArgs e) + { + lock (_lockObj) + { + if (!_isInit) + { + return; + } + + // ReSharper disable once SwitchStatementHandlesSomeKnownEnumValuesWithDefault + switch (e.ChangeType) + { + case WatcherChangeTypes.Created: + case WatcherChangeTypes.Changed: + MaybeAddKeyboard(e.FullPath); + break; + case WatcherChangeTypes.Deleted: + MaybeRemoveKeyboard(e.FullPath); + break; + default: + Console.WriteLine($"Unexpected watcher event {e.ChangeType}"); + break; + } + } + } + + public static void Initialize() + { + if (OSTailoredCode.CurrentOS != OSTailoredCode.DistinctOS.Linux) + { + // also supported in BSDs, I think OSTailoredCode.CurrentOS is Linux for BSDs? + throw new NotSupportedException("evdev is Linux only"); + } + + lock (_lockObj) + { + Deinitialize(); + + _fileSystemWatcher = new("/dev/input/", "event*") + { + NotifyFilter = NotifyFilters.FileName | NotifyFilters.Attributes, + }; + + _fileSystemWatcher.Created += OnWatcherEvent; + _fileSystemWatcher.Changed += OnWatcherEvent; + _fileSystemWatcher.Deleted += OnWatcherEvent; + _fileSystemWatcher.EnableRaisingEvents = true; + + var evFns = Directory.GetFiles("/dev/input/", "event*"); + foreach (var fn in evFns) + { + MaybeAddKeyboard(fn); + } + + _isInit = true; + } + } + + public static void Deinitialize() + { + lock (_lockObj) + { + _fileSystemWatcher?.Dispose(); + _fileSystemWatcher = null; + + foreach (var keyboard in _keyboards.Values) + { + _ = close(keyboard.Fd); + } + + _keyboards.Clear(); + _isInit = false; + } + } + + public static IEnumerable Update() + { + lock (_lockObj) + { + if (!_isInit) + { + return Enumerable.Empty(); + } + + var kbEvent = default(EvDevKeyboardEvent); + var kbEventSize = (IntPtr)Marshal.SizeOf(); + var kbsToClose = new List(); + Span keyChanges = stackalloc byte[(int)EvDevKeyCode.KEY_CNT]; + keyChanges.Clear(); + + foreach (var keyboard in _keyboards.Values) + { + while (true) + { + unsafe + { + var res = read(keyboard.Fd, &kbEvent, kbEventSize); + if (res == (IntPtr)(-1)) + { + // this is actually errno, despite the Win32 name + var errno = Marshal.GetLastWin32Error(); + + const int EAGAIN = 11; + const int ENODEV = 19; + + // EAGAIN means there's no more events left to read (generally expected) + if (errno == EAGAIN) + { + break; + } + + // ENODEV means the device is gone + if (errno == ENODEV) + { + kbsToClose.Add(keyboard.Path); + break; + } + + Debug.WriteLine($"Unexpected error reading keyboards: {errno}"); + break; + } + + if (res != kbEventSize) + { + Debug.WriteLine("Unexpected incomplete read"); + break; + } + } + + if (kbEvent.type != EvDevEventType.EV_KEY) + { + // don't care for non-EV_KEY events + continue; + } + + if (kbEvent.code > EvDevKeyCode.KEY_MAX) + { + Debug.WriteLine($"Unexpected event code {kbEvent.code}"); + continue; + } + + switch (kbEvent.value) + { + case EvDevKeyValue.KeyUp: + keyChanges[(int)kbEvent.code] = 1; + break; + case EvDevKeyValue.KeyDown: + case EvDevKeyValue.KeyRepeat: + keyChanges[(int)kbEvent.code] = 2; + break; + default: + Debug.WriteLine($"Unexpected event value {kbEvent.value}"); + break; + } + } + } + + foreach (var path in kbsToClose) + { + MaybeRemoveKeyboard(path); + } + + var keyEvents = new List(); + for (var i = 0; i < (int)EvDevKeyCode.KEY_CNT; i++) + { + if (keyChanges[i] != 0 && + KeyEnumMap.TryGetValue((EvDevKeyCode)i, out var key)) + { + keyEvents.Add(new(key, keyChanges[i] == 1)); + } + } + + return keyEvents; + } + } + + private static readonly Dictionary KeyEnumMap = new() + { + [EvDevKeyCode.KEY_ESC] = DistinctKey.Escape, + [EvDevKeyCode.KEY_1] = DistinctKey.D1, + [EvDevKeyCode.KEY_2] = DistinctKey.D2, + [EvDevKeyCode.KEY_3] = DistinctKey.D3, + [EvDevKeyCode.KEY_4] = DistinctKey.D4, + [EvDevKeyCode.KEY_5] = DistinctKey.D5, + [EvDevKeyCode.KEY_6] = DistinctKey.D6, + [EvDevKeyCode.KEY_7] = DistinctKey.D7, + [EvDevKeyCode.KEY_8] = DistinctKey.D8, + [EvDevKeyCode.KEY_9] = DistinctKey.D9, + [EvDevKeyCode.KEY_0] = DistinctKey.D0, + [EvDevKeyCode.KEY_MINUS] = DistinctKey.OemMinus, + [EvDevKeyCode.KEY_EQUAL] = DistinctKey.OemPlus, + [EvDevKeyCode.KEY_BACKSPACE] = DistinctKey.Back, + [EvDevKeyCode.KEY_TAB] = DistinctKey.Tab, + [EvDevKeyCode.KEY_Q] = DistinctKey.Q, + [EvDevKeyCode.KEY_W] = DistinctKey.W, + [EvDevKeyCode.KEY_E] = DistinctKey.E, + [EvDevKeyCode.KEY_R] = DistinctKey.R, + [EvDevKeyCode.KEY_T] = DistinctKey.T, + [EvDevKeyCode.KEY_Y] = DistinctKey.Y, + [EvDevKeyCode.KEY_U] = DistinctKey.U, + [EvDevKeyCode.KEY_I] = DistinctKey.I, + [EvDevKeyCode.KEY_O] = DistinctKey.O, + [EvDevKeyCode.KEY_P] = DistinctKey.P, + [EvDevKeyCode.KEY_LEFTBRACE] = DistinctKey.OemOpenBrackets, + [EvDevKeyCode.KEY_RIGHTBRACE] = DistinctKey.OemCloseBrackets, + [EvDevKeyCode.KEY_ENTER] = DistinctKey.Enter, + [EvDevKeyCode.KEY_LEFTCTRL] = DistinctKey.LeftCtrl, + [EvDevKeyCode.KEY_A] = DistinctKey.A, + [EvDevKeyCode.KEY_S] = DistinctKey.S, + [EvDevKeyCode.KEY_D] = DistinctKey.D, + [EvDevKeyCode.KEY_F] = DistinctKey.F, + [EvDevKeyCode.KEY_G] = DistinctKey.G, + [EvDevKeyCode.KEY_H] = DistinctKey.H, + [EvDevKeyCode.KEY_J] = DistinctKey.J, + [EvDevKeyCode.KEY_K] = DistinctKey.K, + [EvDevKeyCode.KEY_L] = DistinctKey.L, + [EvDevKeyCode.KEY_SEMICOLON] = DistinctKey.OemSemicolon, + [EvDevKeyCode.KEY_APOSTROPHE] = DistinctKey.OemQuotes, + [EvDevKeyCode.KEY_GRAVE] = DistinctKey.OemTilde, + [EvDevKeyCode.KEY_LEFTSHIFT] = DistinctKey.LeftShift, + [EvDevKeyCode.KEY_BACKSLASH] = DistinctKey.OemBackslash, + [EvDevKeyCode.KEY_Z] = DistinctKey.Z, + [EvDevKeyCode.KEY_X] = DistinctKey.X, + [EvDevKeyCode.KEY_C] = DistinctKey.C, + [EvDevKeyCode.KEY_V] = DistinctKey.V, + [EvDevKeyCode.KEY_B] = DistinctKey.B, + [EvDevKeyCode.KEY_N] = DistinctKey.N, + [EvDevKeyCode.KEY_M] = DistinctKey.M, + [EvDevKeyCode.KEY_COMMA] = DistinctKey.OemComma, + [EvDevKeyCode.KEY_DOT] = DistinctKey.OemPeriod, + [EvDevKeyCode.KEY_SLASH] = DistinctKey.OemQuestion, + [EvDevKeyCode.KEY_RIGHTSHIFT] = DistinctKey.RightShift, + [EvDevKeyCode.KEY_KPASTERISK] = DistinctKey.Multiply, + [EvDevKeyCode.KEY_LEFTALT] = DistinctKey.LeftAlt, + [EvDevKeyCode.KEY_SPACE] = DistinctKey.Space, + [EvDevKeyCode.KEY_CAPSLOCK] = DistinctKey.CapsLock, + [EvDevKeyCode.KEY_F1] = DistinctKey.F1, + [EvDevKeyCode.KEY_F2] = DistinctKey.F2, + [EvDevKeyCode.KEY_F3] = DistinctKey.F3, + [EvDevKeyCode.KEY_F4] = DistinctKey.F4, + [EvDevKeyCode.KEY_F5] = DistinctKey.F5, + [EvDevKeyCode.KEY_F6] = DistinctKey.F6, + [EvDevKeyCode.KEY_F7] = DistinctKey.F7, + [EvDevKeyCode.KEY_F8] = DistinctKey.F8, + [EvDevKeyCode.KEY_F9] = DistinctKey.F9, + [EvDevKeyCode.KEY_F10] = DistinctKey.F10, + [EvDevKeyCode.KEY_NUMLOCK] = DistinctKey.NumLock, + [EvDevKeyCode.KEY_SCROLLLOCK] = DistinctKey.Scroll, + [EvDevKeyCode.KEY_KP7] = DistinctKey.NumPad7, + [EvDevKeyCode.KEY_KP8] = DistinctKey.NumPad8, + [EvDevKeyCode.KEY_KP9] = DistinctKey.NumPad9, + [EvDevKeyCode.KEY_KPMINUS] = DistinctKey.Subtract, + [EvDevKeyCode.KEY_KP4] = DistinctKey.NumPad4, + [EvDevKeyCode.KEY_KP5] = DistinctKey.NumPad5, + [EvDevKeyCode.KEY_KP6] = DistinctKey.NumPad6, + [EvDevKeyCode.KEY_KPPLUS] = DistinctKey.Add, + [EvDevKeyCode.KEY_KP1] = DistinctKey.NumPad1, + [EvDevKeyCode.KEY_KP2] = DistinctKey.NumPad2, + [EvDevKeyCode.KEY_KP3] = DistinctKey.NumPad3, + [EvDevKeyCode.KEY_KPDOT] = DistinctKey.Decimal, + [EvDevKeyCode.KEY_102ND] = DistinctKey.Oem102, + [EvDevKeyCode.KEY_F11] = DistinctKey.F11, + [EvDevKeyCode.KEY_F12] = DistinctKey.F12, + [EvDevKeyCode.KEY_KPENTER] = DistinctKey.NumPadEnter, + [EvDevKeyCode.KEY_RIGHTCTRL] = DistinctKey.RightCtrl, + [EvDevKeyCode.KEY_KPSLASH] = DistinctKey.Divide, + [EvDevKeyCode.KEY_RIGHTALT] = DistinctKey.RightAlt, + [EvDevKeyCode.KEY_LINEFEED] = DistinctKey.LineFeed, + [EvDevKeyCode.KEY_HOME] = DistinctKey.Home, + [EvDevKeyCode.KEY_UP] = DistinctKey.Up, + [EvDevKeyCode.KEY_PAGEUP] = DistinctKey.PageUp, + [EvDevKeyCode.KEY_LEFT] = DistinctKey.Left, + [EvDevKeyCode.KEY_RIGHT] = DistinctKey.Right, + [EvDevKeyCode.KEY_END] = DistinctKey.End, + [EvDevKeyCode.KEY_DOWN] = DistinctKey.Down, + [EvDevKeyCode.KEY_PAGEDOWN] = DistinctKey.PageDown, + [EvDevKeyCode.KEY_INSERT] = DistinctKey.Insert, + [EvDevKeyCode.KEY_DELETE] = DistinctKey.Delete, + [EvDevKeyCode.KEY_MUTE] = DistinctKey.VolumeMute, + [EvDevKeyCode.KEY_VOLUMEDOWN] = DistinctKey.VolumeDown, + [EvDevKeyCode.KEY_VOLUMEUP] = DistinctKey.VolumeUp, + [EvDevKeyCode.KEY_LEFTMETA] = DistinctKey.LWin, + [EvDevKeyCode.KEY_RIGHTMETA] = DistinctKey.RWin, + [EvDevKeyCode.KEY_STOP] = DistinctKey.BrowserStop, + [EvDevKeyCode.KEY_HELP] = DistinctKey.Help, + [EvDevKeyCode.KEY_SLEEP] = DistinctKey.Sleep, + [EvDevKeyCode.KEY_PROG1] = DistinctKey.LaunchApplication1, + [EvDevKeyCode.KEY_PROG2] = DistinctKey.LaunchApplication2, + [EvDevKeyCode.KEY_MAIL] = DistinctKey.LaunchMail, + [EvDevKeyCode.KEY_BACK] = DistinctKey.BrowserBack, + [EvDevKeyCode.KEY_FORWARD] = DistinctKey.BrowserForward, + [EvDevKeyCode.KEY_NEXTSONG] = DistinctKey.MediaNextTrack, + [EvDevKeyCode.KEY_PLAYPAUSE] = DistinctKey.MediaPlayPause, + [EvDevKeyCode.KEY_PREVIOUSSONG] = DistinctKey.MediaPreviousTrack, + [EvDevKeyCode.KEY_STOPCD] = DistinctKey.MediaStop, + [EvDevKeyCode.KEY_HOMEPAGE] = DistinctKey.BrowserHome, + [EvDevKeyCode.KEY_REFRESH] = DistinctKey.BrowserRefresh, + [EvDevKeyCode.KEY_F13] = DistinctKey.F13, + [EvDevKeyCode.KEY_F14] = DistinctKey.F14, + [EvDevKeyCode.KEY_F15] = DistinctKey.F15, + [EvDevKeyCode.KEY_F16] = DistinctKey.F16, + [EvDevKeyCode.KEY_F17] = DistinctKey.F17, + [EvDevKeyCode.KEY_F18] = DistinctKey.F18, + [EvDevKeyCode.KEY_F19] = DistinctKey.F19, + [EvDevKeyCode.KEY_F20] = DistinctKey.F20, + [EvDevKeyCode.KEY_F21] = DistinctKey.F21, + [EvDevKeyCode.KEY_F22] = DistinctKey.F22, + [EvDevKeyCode.KEY_F23] = DistinctKey.F23, + [EvDevKeyCode.KEY_F24] = DistinctKey.F24, + [EvDevKeyCode.KEY_PLAY] = DistinctKey.Play, + [EvDevKeyCode.KEY_PRINT] = DistinctKey.Print, + [EvDevKeyCode.KEY_SEARCH] = DistinctKey.BrowserSearch, + [EvDevKeyCode.KEY_CANCEL] = DistinctKey.Cancel, + [EvDevKeyCode.KEY_MEDIA] = DistinctKey.SelectMedia, + [EvDevKeyCode.KEY_SELECT] = DistinctKey.Select, + [EvDevKeyCode.KEY_FAVORITES] = DistinctKey.BrowserFavorites, + [EvDevKeyCode.KEY_CLEAR] = DistinctKey.Clear, + }; + } +} diff --git a/src/BizHawk.Common/LSB/EvDevImports.cs b/src/BizHawk.Common/LSB/EvDevImports.cs new file mode 100644 index 0000000000..0ac4afd61c --- /dev/null +++ b/src/BizHawk.Common/LSB/EvDevImports.cs @@ -0,0 +1,639 @@ +#nullable enable + +using System; +using System.Runtime.InteropServices; + +// a lot of this is take from https://github.com/afshin-parsa/evdev-sharp + +namespace BizHawk.Common +{ + public static class EvDevImports + { + public enum EvDevEventType : ushort + { + EV_SYN = 0x00, + EV_KEY = 0x01, + EV_REL = 0x02, + EV_ABS = 0x03, + EV_MSC = 0x04, + EV_SW = 0x05, + EV_LED = 0x11, + EV_SND = 0x12, + EV_REP = 0x14, + EV_FF = 0x15, + EV_PWR = 0x16, + EV_FF_STATUS = 0x17, + EV_MAX = 0x1f, + EV_CNT = EV_MAX + 1, + } + + public enum EvDevKeyCode : ushort + { + KEY_RESERVED = 0, + KEY_ESC = 1, + KEY_1 = 2, + KEY_2 = 3, + KEY_3 = 4, + KEY_4 = 5, + KEY_5 = 6, + KEY_6 = 7, + KEY_7 = 8, + KEY_8 = 9, + KEY_9 = 10, + KEY_0 = 11, + KEY_MINUS = 12, + KEY_EQUAL = 13, + KEY_BACKSPACE = 14, + KEY_TAB = 15, + KEY_Q = 16, + KEY_W = 17, + KEY_E = 18, + KEY_R = 19, + KEY_T = 20, + KEY_Y = 21, + KEY_U = 22, + KEY_I = 23, + KEY_O = 24, + KEY_P = 25, + KEY_LEFTBRACE = 26, + KEY_RIGHTBRACE = 27, + KEY_ENTER = 28, + KEY_LEFTCTRL = 29, + KEY_A = 30, + KEY_S = 31, + KEY_D = 32, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_SEMICOLON = 39, + KEY_APOSTROPHE = 40, + KEY_GRAVE = 41, + KEY_LEFTSHIFT = 42, + KEY_BACKSLASH = 43, + KEY_Z = 44, + KEY_X = 45, + KEY_C = 46, + KEY_V = 47, + KEY_B = 48, + KEY_N = 49, + KEY_M = 50, + KEY_COMMA = 51, + KEY_DOT = 52, + KEY_SLASH = 53, + KEY_RIGHTSHIFT = 54, + KEY_KPASTERISK = 55, + KEY_LEFTALT = 56, + KEY_SPACE = 57, + KEY_CAPSLOCK = 58, + KEY_F1 = 59, + KEY_F2 = 60, + KEY_F3 = 61, + KEY_F4 = 62, + KEY_F5 = 63, + KEY_F6 = 64, + KEY_F7 = 65, + KEY_F8 = 66, + KEY_F9 = 67, + KEY_F10 = 68, + KEY_NUMLOCK = 69, + KEY_SCROLLLOCK = 70, + KEY_KP7 = 71, + KEY_KP8 = 72, + KEY_KP9 = 73, + KEY_KPMINUS = 74, + KEY_KP4 = 75, + KEY_KP5 = 76, + KEY_KP6 = 77, + KEY_KPPLUS = 78, + KEY_KP1 = 79, + KEY_KP2 = 80, + KEY_KP3 = 81, + KEY_KP0 = 82, + KEY_KPDOT = 83, + KEY_ZENKAKUHANKAKU = 85, + KEY_102ND = 86, + KEY_F11 = 87, + KEY_F12 = 88, + KEY_RO = 89, + KEY_KATAKANA = 90, + KEY_HIRAGANA = 91, + KEY_HENKAN = 92, + KEY_KATAKANAHIRAGANA = 93, + KEY_MUHENKAN = 94, + KEY_KPJPCOMMA = 95, + KEY_KPENTER = 96, + KEY_RIGHTCTRL = 97, + KEY_KPSLASH = 98, + KEY_SYSRQ = 99, + KEY_RIGHTALT = 100, + KEY_LINEFEED = 101, + KEY_HOME = 102, + KEY_UP = 103, + KEY_PAGEUP = 104, + KEY_LEFT = 105, + KEY_RIGHT = 106, + KEY_END = 107, + KEY_DOWN = 108, + KEY_PAGEDOWN = 109, + KEY_INSERT = 110, + KEY_DELETE = 111, + KEY_MACRO = 112, + KEY_MUTE = 113, + KEY_VOLUMEDOWN = 114, + KEY_VOLUMEUP = 115, + KEY_POWER = 116, + KEY_KPEQUAL = 117, + KEY_KPPLUSMINUS = 118, + KEY_PAUSE = 119, + KEY_SCALE = 120, + KEY_KPCOMMA = 121, + KEY_HANGEUL = 122, + KEY_HANGUEL = KEY_HANGEUL, + KEY_HANJA = 123, + KEY_YEN = 124, + KEY_LEFTMETA = 125, + KEY_RIGHTMETA = 126, + KEY_COMPOSE = 127, + KEY_STOP = 128, + KEY_AGAIN = 129, + KEY_PROPS = 130, + KEY_UNDO = 131, + KEY_FRONT = 132, + KEY_COPY = 133, + KEY_OPEN = 134, + KEY_PASTE = 135, + KEY_FIND = 136, + KEY_CUT = 137, + KEY_HELP = 138, + KEY_MENU = 139, + KEY_CALC = 140, + KEY_SETUP = 141, + KEY_SLEEP = 142, + KEY_WAKEUP = 143, + KEY_FILE = 144, + KEY_SENDFILE = 145, + KEY_DELETEFILE = 146, + KEY_XFER = 147, + KEY_PROG1 = 148, + KEY_PROG2 = 149, + KEY_WWW = 150, + KEY_MSDOS = 151, + KEY_COFFEE = 152, + KEY_SCREENLOCK = KEY_COFFEE, + KEY_ROTATE_DISPLAY = 153, + KEY_DIRECTION = KEY_ROTATE_DISPLAY, + KEY_CYCLEWINDOWS = 154, + KEY_MAIL = 155, + KEY_BOOKMARKS = 156, + KEY_COMPUTER = 157, + KEY_BACK = 158, + KEY_FORWARD = 159, + KEY_CLOSECD = 160, + KEY_EJECTCD = 161, + KEY_EJECTCLOSECD = 162, + KEY_NEXTSONG = 163, + KEY_PLAYPAUSE = 164, + KEY_PREVIOUSSONG = 165, + KEY_STOPCD = 166, + KEY_RECORD = 167, + KEY_REWIND = 168, + KEY_PHONE = 169, + KEY_ISO = 170, + KEY_CONFIG = 171, + KEY_HOMEPAGE = 172, + KEY_REFRESH = 173, + KEY_EXIT = 174, + KEY_MOVE = 175, + KEY_EDIT = 176, + KEY_SCROLLUP = 177, + KEY_SCROLLDOWN = 178, + KEY_KPLEFTPAREN = 179, + KEY_KPRIGHTPAREN = 180, + KEY_NEW = 181, + KEY_REDO = 182, + KEY_F13 = 183, + KEY_F14 = 184, + KEY_F15 = 185, + KEY_F16 = 186, + KEY_F17 = 187, + KEY_F18 = 188, + KEY_F19 = 189, + KEY_F20 = 190, + KEY_F21 = 191, + KEY_F22 = 192, + KEY_F23 = 193, + KEY_F24 = 194, + KEY_PLAYCD = 200, + KEY_PAUSECD = 201, + KEY_PROG3 = 202, + KEY_PROG4 = 203, + KEY_DASHBOARD = 204, + KEY_SUSPEND = 205, + KEY_CLOSE = 206, + KEY_PLAY = 207, + KEY_FASTFORWARD = 208, + KEY_BASSBOOST = 209, + KEY_PRINT = 210, + KEY_HP = 211, + KEY_CAMERA = 212, + KEY_SOUND = 213, + KEY_QUESTION = 214, + KEY_EMAIL = 215, + KEY_CHAT = 216, + KEY_SEARCH = 217, + KEY_CONNECT = 218, + KEY_FINANCE = 219, + KEY_SPORT = 220, + KEY_SHOP = 221, + KEY_ALTERASE = 222, + KEY_CANCEL = 223, + KEY_BRIGHTNESSDOWN = 224, + KEY_BRIGHTNESSUP = 225, + KEY_MEDIA = 226, + KEY_SWITCHVIDEOMODE = 227, + KEY_KBDILLUMTOGGLE = 228, + KEY_KBDILLUMDOWN = 229, + KEY_KBDILLUMUP = 230, + KEY_SEND = 231, + KEY_REPLY = 232, + KEY_FORWARDMAIL = 233, + KEY_SAVE = 234, + KEY_DOCUMENTS = 235, + KEY_BATTERY = 236, + KEY_BLUETOOTH = 237, + KEY_WLAN = 238, + KEY_UWB = 239, + KEY_UNKNOWN = 240, + KEY_VIDEO_NEXT = 241, + KEY_VIDEO_PREV = 242, + KEY_BRIGHTNESS_CYCLE = 243, + KEY_BRIGHTNESS_AUTO = 244, + KEY_BRIGHTNESS_ZERO = KEY_BRIGHTNESS_AUTO, + KEY_DISPLAY_OFF = 245, + KEY_WWAN = 246, + KEY_WIMAX = KEY_WWAN, + KEY_RFKILL = 247, + KEY_MICMUTE = 248, + BTN_MISC = 0x100, + BTN_0 = 0x100, + BTN_1 = 0x101, + BTN_2 = 0x102, + BTN_3 = 0x103, + BTN_4 = 0x104, + BTN_5 = 0x105, + BTN_6 = 0x106, + BTN_7 = 0x107, + BTN_8 = 0x108, + BTN_9 = 0x109, + BTN_MOUSE = 0x110, + BTN_LEFT = 0x110, + BTN_RIGHT = 0x111, + BTN_MIDDLE = 0x112, + BTN_SIDE = 0x113, + BTN_EXTRA = 0x114, + BTN_FORWARD = 0x115, + BTN_BACK = 0x116, + BTN_TASK = 0x117, + BTN_JOYSTICK = 0x120, + BTN_TRIGGER = 0x120, + BTN_THUMB = 0x121, + BTN_THUMB2 = 0x122, + BTN_TOP = 0x123, + BTN_TOP2 = 0x124, + BTN_PINKIE = 0x125, + BTN_BASE = 0x126, + BTN_BASE2 = 0x127, + BTN_BASE3 = 0x128, + BTN_BASE4 = 0x129, + BTN_BASE5 = 0x12a, + BTN_BASE6 = 0x12b, + BTN_DEAD = 0x12f, + BTN_GAMEPAD = 0x130, + BTN_SOUTH = 0x130, + BTN_A = BTN_SOUTH, + BTN_EAST = 0x131, + BTN_B = BTN_EAST, + BTN_C = 0x132, + BTN_NORTH = 0x133, + BTN_X = BTN_NORTH, + BTN_WEST = 0x134, + BTN_Y = BTN_WEST, + BTN_Z = 0x135, + BTN_TL = 0x136, + BTN_TR = 0x137, + BTN_TL2 = 0x138, + BTN_TR2 = 0x139, + BTN_SELECT = 0x13a, + BTN_START = 0x13b, + BTN_MODE = 0x13c, + BTN_THUMBL = 0x13d, + BTN_THUMBR = 0x13e, + BTN_DIGI = 0x140, + BTN_TOOL_PEN = 0x140, + BTN_TOOL_RUBBER = 0x141, + BTN_TOOL_BRUSH = 0x142, + BTN_TOOL_PENCIL = 0x143, + BTN_TOOL_AIRBRUSH = 0x144, + BTN_TOOL_FINGER = 0x145, + BTN_TOOL_MOUSE = 0x146, + BTN_TOOL_LENS = 0x147, + BTN_TOOL_QUINTTAP = 0x148, + BTN_STYLUS3 = 0x149, + BTN_TOUCH = 0x14a, + BTN_STYLUS = 0x14b, + BTN_STYLUS2 = 0x14c, + BTN_TOOL_DOUBLETAP = 0x14d, + BTN_TOOL_TRIPLETAP = 0x14e, + BTN_TOOL_QUADTAP = 0x14f, + BTN_WHEEL = 0x150, + BTN_GEAR_DOWN = 0x150, + BTN_GEAR_UP = 0x151, + KEY_OK = 0x160, + KEY_SELECT = 0x161, + KEY_GOTO = 0x162, + KEY_CLEAR = 0x163, + KEY_POWER2 = 0x164, + KEY_OPTION = 0x165, + KEY_INFO = 0x166, + KEY_TIME = 0x167, + KEY_VENDOR = 0x168, + KEY_ARCHIVE = 0x169, + KEY_PROGRAM = 0x16a, + KEY_CHANNEL = 0x16b, + KEY_FAVORITES = 0x16c, + KEY_EPG = 0x16d, + KEY_PVR = 0x16e, + KEY_MHP = 0x16f, + KEY_LANGUAGE = 0x170, + KEY_TITLE = 0x171, + KEY_SUBTITLE = 0x172, + KEY_ANGLE = 0x173, + KEY_ZOOM = 0x174, + KEY_MODE = 0x175, + KEY_KEYBOARD = 0x176, + KEY_SCREEN = 0x177, + KEY_PC = 0x178, + KEY_TV = 0x179, + KEY_TV2 = 0x17a, + KEY_VCR = 0x17b, + KEY_VCR2 = 0x17c, + KEY_SAT = 0x17d, + KEY_SAT2 = 0x17e, + KEY_CD = 0x17f, + KEY_TAPE = 0x180, + KEY_RADIO = 0x181, + KEY_TUNER = 0x182, + KEY_PLAYER = 0x183, + KEY_TEXT = 0x184, + KEY_DVD = 0x185, + KEY_AUX = 0x186, + KEY_MP3 = 0x187, + KEY_AUDIO = 0x188, + KEY_VIDEO = 0x189, + KEY_DIRECTORY = 0x18a, + KEY_LIST = 0x18b, + KEY_MEMO = 0x18c, + KEY_CALENDAR = 0x18d, + KEY_RED = 0x18e, + KEY_GREEN = 0x18f, + KEY_YELLOW = 0x190, + KEY_BLUE = 0x191, + KEY_CHANNELUP = 0x192, + KEY_CHANNELDOWN = 0x193, + KEY_FIRST = 0x194, + KEY_LAST = 0x195, + KEY_AB = 0x196, + KEY_NEXT = 0x197, + KEY_RESTART = 0x198, + KEY_SLOW = 0x199, + KEY_SHUFFLE = 0x19a, + KEY_BREAK = 0x19b, + KEY_PREVIOUS = 0x19c, + KEY_DIGITS = 0x19d, + KEY_TEEN = 0x19e, + KEY_TWEN = 0x19f, + KEY_VIDEOPHONE = 0x1a0, + KEY_GAMES = 0x1a1, + KEY_ZOOMIN = 0x1a2, + KEY_ZOOMOUT = 0x1a3, + KEY_ZOOMRESET = 0x1a4, + KEY_WORDPROCESSOR = 0x1a5, + KEY_EDITOR = 0x1a6, + KEY_SPREADSHEET = 0x1a7, + KEY_GRAPHICSEDITOR = 0x1a8, + KEY_PRESENTATION = 0x1a9, + KEY_DATABASE = 0x1aa, + KEY_NEWS = 0x1ab, + KEY_VOICEMAIL = 0x1ac, + KEY_ADDRESSBOOK = 0x1ad, + KEY_MESSENGER = 0x1ae, + KEY_DISPLAYTOGGLE = 0x1af, + KEY_BRIGHTNESS_TOGGLE = KEY_DISPLAYTOGGLE, + KEY_SPELLCHECK = 0x1b0, + KEY_LOGOFF = 0x1b1, + KEY_DOLLAR = 0x1b2, + KEY_EURO = 0x1b3, + KEY_FRAMEBACK = 0x1b4, + KEY_FRAMEFORWARD = 0x1b5, + KEY_CONTEXT_MENU = 0x1b6, + KEY_MEDIA_REPEAT = 0x1b7, + KEY_10CHANNELSUP = 0x1b8, + KEY_10CHANNELSDOWN = 0x1b9, + KEY_IMAGES = 0x1ba, + KEY_DEL_EOL = 0x1c0, + KEY_DEL_EOS = 0x1c1, + KEY_INS_LINE = 0x1c2, + KEY_DEL_LINE = 0x1c3, + KEY_FN = 0x1d0, + KEY_FN_ESC = 0x1d1, + KEY_FN_F1 = 0x1d2, + KEY_FN_F2 = 0x1d3, + KEY_FN_F3 = 0x1d4, + KEY_FN_F4 = 0x1d5, + KEY_FN_F5 = 0x1d6, + KEY_FN_F6 = 0x1d7, + KEY_FN_F7 = 0x1d8, + KEY_FN_F8 = 0x1d9, + KEY_FN_F9 = 0x1da, + KEY_FN_F10 = 0x1db, + KEY_FN_F11 = 0x1dc, + KEY_FN_F12 = 0x1dd, + KEY_FN_1 = 0x1de, + KEY_FN_2 = 0x1df, + KEY_FN_D = 0x1e0, + KEY_FN_E = 0x1e1, + KEY_FN_F = 0x1e2, + KEY_FN_S = 0x1e3, + KEY_FN_B = 0x1e4, + KEY_BRL_DOT1 = 0x1f1, + KEY_BRL_DOT2 = 0x1f2, + KEY_BRL_DOT3 = 0x1f3, + KEY_BRL_DOT4 = 0x1f4, + KEY_BRL_DOT5 = 0x1f5, + KEY_BRL_DOT6 = 0x1f6, + KEY_BRL_DOT7 = 0x1f7, + KEY_BRL_DOT8 = 0x1f8, + KEY_BRL_DOT9 = 0x1f9, + KEY_BRL_DOT10 = 0x1fa, + KEY_NUMERIC_0 = 0x200, + KEY_NUMERIC_1 = 0x201, + KEY_NUMERIC_2 = 0x202, + KEY_NUMERIC_3 = 0x203, + KEY_NUMERIC_4 = 0x204, + KEY_NUMERIC_5 = 0x205, + KEY_NUMERIC_6 = 0x206, + KEY_NUMERIC_7 = 0x207, + KEY_NUMERIC_8 = 0x208, + KEY_NUMERIC_9 = 0x209, + KEY_NUMERIC_STAR = 0x20a, + KEY_NUMERIC_POUND = 0x20b, + KEY_NUMERIC_A = 0x20c, + KEY_NUMERIC_B = 0x20d, + KEY_NUMERIC_C = 0x20e, + KEY_NUMERIC_D = 0x20f, + KEY_CAMERA_FOCUS = 0x210, + KEY_WPS_BUTTON = 0x211, + KEY_TOUCHPAD_TOGGLE = 0x212, + KEY_TOUCHPAD_ON = 0x213, + KEY_TOUCHPAD_OFF = 0x214, + KEY_CAMERA_ZOOMIN = 0x215, + KEY_CAMERA_ZOOMOUT = 0x216, + KEY_CAMERA_UP = 0x217, + KEY_CAMERA_DOWN = 0x218, + KEY_CAMERA_LEFT = 0x219, + KEY_CAMERA_RIGHT = 0x21a, + KEY_ATTENDANT_ON = 0x21b, + KEY_ATTENDANT_OFF = 0x21c, + KEY_ATTENDANT_TOGGLE = 0x21d, + KEY_LIGHTS_TOGGLE = 0x21e, + BTN_DPAD_UP = 0x220, + BTN_DPAD_DOWN = 0x221, + BTN_DPAD_LEFT = 0x222, + BTN_DPAD_RIGHT = 0x223, + KEY_ALS_TOGGLE = 0x230, + KEY_BUTTONCONFIG = 0x240, + KEY_TASKMANAGER = 0x241, + KEY_JOURNAL = 0x242, + KEY_CONTROLPANEL = 0x243, + KEY_APPSELECT = 0x244, + KEY_SCREENSAVER = 0x245, + KEY_VOICECOMMAND = 0x246, + KEY_ASSISTANT = 0x247, + KEY_BRIGHTNESS_MIN = 0x250, + KEY_BRIGHTNESS_MAX = 0x251, + KEY_KBDINPUTASSIST_PREV = 0x260, + KEY_KBDINPUTASSIST_NEXT = 0x261, + KEY_KBDINPUTASSIST_PREVGROUP = 0x262, + KEY_KBDINPUTASSIST_NEXTGROUP = 0x263, + KEY_KBDINPUTASSIST_ACCEPT = 0x264, + KEY_KBDINPUTASSIST_CANCEL = 0x265, + KEY_RIGHT_UP = 0x266, + KEY_RIGHT_DOWN = 0x267, + KEY_LEFT_UP = 0x268, + KEY_LEFT_DOWN = 0x269, + KEY_ROOT_MENU = 0x26a, + KEY_MEDIA_TOP_MENU = 0x26b, + KEY_NUMERIC_11 = 0x26c, + KEY_NUMERIC_12 = 0x26d, + KEY_AUDIO_DESC = 0x26e, + KEY_3D_MODE = 0x26f, + KEY_NEXT_FAVORITE = 0x270, + KEY_STOP_RECORD = 0x271, + KEY_PAUSE_RECORD = 0x272, + KEY_VOD = 0x273, + KEY_UNMUTE = 0x274, + KEY_FASTREVERSE = 0x275, + KEY_SLOWREVERSE = 0x276, + KEY_DATA = 0x277, + KEY_ONSCREEN_KEYBOARD = 0x278, + BTN_TRIGGER_HAPPY = 0x2c0, + BTN_TRIGGER_HAPPY1 = 0x2c0, + BTN_TRIGGER_HAPPY2 = 0x2c1, + BTN_TRIGGER_HAPPY3 = 0x2c2, + BTN_TRIGGER_HAPPY4 = 0x2c3, + BTN_TRIGGER_HAPPY5 = 0x2c4, + BTN_TRIGGER_HAPPY6 = 0x2c5, + BTN_TRIGGER_HAPPY7 = 0x2c6, + BTN_TRIGGER_HAPPY8 = 0x2c7, + BTN_TRIGGER_HAPPY9 = 0x2c8, + BTN_TRIGGER_HAPPY10 = 0x2c9, + BTN_TRIGGER_HAPPY11 = 0x2ca, + BTN_TRIGGER_HAPPY12 = 0x2cb, + BTN_TRIGGER_HAPPY13 = 0x2cc, + BTN_TRIGGER_HAPPY14 = 0x2cd, + BTN_TRIGGER_HAPPY15 = 0x2ce, + BTN_TRIGGER_HAPPY16 = 0x2cf, + BTN_TRIGGER_HAPPY17 = 0x2d0, + BTN_TRIGGER_HAPPY18 = 0x2d1, + BTN_TRIGGER_HAPPY19 = 0x2d2, + BTN_TRIGGER_HAPPY20 = 0x2d3, + BTN_TRIGGER_HAPPY21 = 0x2d4, + BTN_TRIGGER_HAPPY22 = 0x2d5, + BTN_TRIGGER_HAPPY23 = 0x2d6, + BTN_TRIGGER_HAPPY24 = 0x2d7, + BTN_TRIGGER_HAPPY25 = 0x2d8, + BTN_TRIGGER_HAPPY26 = 0x2d9, + BTN_TRIGGER_HAPPY27 = 0x2da, + BTN_TRIGGER_HAPPY28 = 0x2db, + BTN_TRIGGER_HAPPY29 = 0x2dc, + BTN_TRIGGER_HAPPY30 = 0x2dd, + BTN_TRIGGER_HAPPY31 = 0x2de, + BTN_TRIGGER_HAPPY32 = 0x2df, + BTN_TRIGGER_HAPPY33 = 0x2e0, + BTN_TRIGGER_HAPPY34 = 0x2e1, + BTN_TRIGGER_HAPPY35 = 0x2e2, + BTN_TRIGGER_HAPPY36 = 0x2e3, + BTN_TRIGGER_HAPPY37 = 0x2e4, + BTN_TRIGGER_HAPPY38 = 0x2e5, + BTN_TRIGGER_HAPPY39 = 0x2e6, + BTN_TRIGGER_HAPPY40 = 0x2e7, + KEY_MIN_INTERESTING = KEY_MUTE, + KEY_MAX = 0x2ff, + KEY_CNT = KEY_MAX + 1, + } + + public enum EvDevKeyValue : int + { + KeyUp = 0, + KeyDown = 1, + KeyRepeat = 2 + } + + [StructLayout(LayoutKind.Sequential)] + public struct EvDevKeyboardEvent + { + public IntPtr tv_sec; + public IntPtr tv_usec; + public EvDevEventType type; + public EvDevKeyCode code; + public EvDevKeyValue value; + } + + private const uint IOC_READ = 2; + private const uint IOC_EV_TYPE = 'E'; + private static uint EVIOCG(long nr, long size) + => (uint)((IOC_READ << 30) | (size << 16) | (IOC_EV_TYPE << 8) | nr); + + public static uint EVIOCGNAME(long len) => EVIOCG( 0x06, len); + public static uint EVIOCGPHYS(long len) => EVIOCG( 0x07, len); + public static uint EVIOCGUNIQ(long len) => EVIOCG( 0x08, len); + public static uint EVIOCGPROP(long len) => EVIOCG(0x09, len); + public static uint EVIOCGBIT(EvDevEventType ev, long len) => EVIOCG(0x20 + (long)ev, len); + public static uint EVIOCGABS(int abs) => EVIOCG(0x40 + abs, 24); + + public const uint EVIOCGVERSION = 0x80044501; + public const uint EVIOCGID = 0x80084502; + public const uint EVIOCGREP = 0x80084503; + public const uint EVIOCSREP = 0x40084503; + public const uint EVIOCGKEYCODE = 0x80084504; + public const uint EVIOCGKEYCODE_V2 = 0x80284504; + public const uint EVIOCSKEYCODE = 0x40084504; + public const uint EVIOCSKEYCODE_V2 = 0x40284504; + + [DllImport("libc")] + public static extern unsafe int ioctl(int fd, UIntPtr request, void* data); + } +} diff --git a/src/BizHawk.Common/POSIX/FildesImports.cs b/src/BizHawk.Common/POSIX/FildesImports.cs new file mode 100644 index 0000000000..457c805f76 --- /dev/null +++ b/src/BizHawk.Common/POSIX/FildesImports.cs @@ -0,0 +1,68 @@ +#nullable enable + +using System; +using System.Runtime.InteropServices; + +namespace BizHawk.Common +{ + public static class FildesImports + { + [Flags] + public enum OpenFlags : int + { + O_RDONLY = 0, + O_WRONLY = 1, + O_RDWR = 2, + // O_LARGEFILE = 0, + O_CREAT = 0x40, + O_EXCL = 0x80, + O_NOCTTY = 0x100, + O_TRUNC = 0x200, + O_APPEND = 0x400, + O_NONBLOCK = 0x800, + O_NDELAY = O_NONBLOCK, + O_DSYNC = 0x1000, + O_ASYNC = 0x2000, + O_DIRECT = 0x4000, + O_DIRECTORY = 0x10000, + O_NOFOLLOW = 0x20000, + O_NOATIME = 0x40000, + O_CLOEXEC = 0x80000, + O_SYNC = 0x100000 | O_DSYNC, + O_PATH = 0x200000, + O_TMPFILE = 0x400000 | O_DIRECTORY, + } + + [Flags] + public enum OpenMode : int + { + S_IXOTH = 0x1, + S_IWOTH = 0x2, + S_IROTH = 0x4, + S_IRWXO = S_IROTH | S_IWOTH | S_IXOTH, + S_IXGRP = 0x8, + S_IWGRP = 0x10, + S_IRGRP = 0x20, + S_IRWXG = S_IRGRP | S_IWGRP | S_IXGRP, + S_IXUSR = 0x40, + S_IWUSR = 0x80, + S_IRUSR = 0x100, + S_IRWXU = S_IRUSR | S_IWUSR | S_IXUSR, + S_ISVTX = 0x200, + S_ISGID = 0x400, + S_ISUID = 0x800, + } + + [DllImport("libc")] + public static extern int open(string pathname, OpenFlags flags); + + [DllImport("libc")] + public static extern int open(string pathname, OpenFlags flags, OpenMode mode); + + [DllImport("libc")] + public static extern int close(int fd); + + [DllImport("libc", SetLastError = true)] + public static extern unsafe IntPtr read(int fd, void* buf, IntPtr count); + } +}