diff --git a/.gitignore b/.gitignore index 7905424..06bcfd6 100644 --- a/.gitignore +++ b/.gitignore @@ -182,8 +182,6 @@ packages/* !Libretro/hakchi/bin -Dependencies/* -Linux/* Libretro/* Docs/* Lua/* diff --git a/Core/BaseCartridge.cpp b/Core/BaseCartridge.cpp index 55b9d5b..d749d9d 100644 --- a/Core/BaseCartridge.cpp +++ b/Core/BaseCartridge.cpp @@ -201,7 +201,7 @@ void BaseCartridge::MapBanks(MemoryManager &mm, vector= handlers.size()) { diff --git a/Core/BaseCartridge.h b/Core/BaseCartridge.h index 8a459a7..17668e2 100644 --- a/Core/BaseCartridge.h +++ b/Core/BaseCartridge.h @@ -44,7 +44,7 @@ private: void DisplayCartInfo(); public: - ~BaseCartridge(); + virtual ~BaseCartridge(); static shared_ptr CreateCartridge(VirtualFile &romFile, VirtualFile &patchFile); diff --git a/Core/Console.cpp b/Core/Console.cpp index 091baff..762df7a 100644 --- a/Core/Console.cpp +++ b/Core/Console.cpp @@ -327,6 +327,7 @@ double Console::GetFrameDelay() } else { UpdateRegion(); switch(_region) { + default: case ConsoleRegion::Ntsc: frameDelay = _settings->GetVideoConfig().IntegerFpsMode ? 16.6666666666666666667 : 16.63926405550947; break; case ConsoleRegion::Pal: frameDelay = _settings->GetVideoConfig().IntegerFpsMode ? 20 : 19.99720882631146; break; } diff --git a/Core/ControlManager.h b/Core/ControlManager.h index c06bda6..67f5cc9 100644 --- a/Core/ControlManager.h +++ b/Core/ControlManager.h @@ -37,7 +37,7 @@ protected: public: ControlManager(shared_ptr console); - ~ControlManager(); + virtual ~ControlManager(); void UpdateControlDevices(); void UpdateInputState(); diff --git a/Core/Cpu.h b/Core/Cpu.h index 88ca6c1..b06a724 100644 --- a/Core/Cpu.h +++ b/Core/Cpu.h @@ -289,7 +289,7 @@ private: public: Cpu(Console *console); - ~Cpu(); + virtual ~Cpu(); void PowerOn(); @@ -316,8 +316,6 @@ private: uint32_t _readAddresses[10]; uint8_t _readValue[10]; - uint32_t _valueSize = 0; - void LogRead(uint32_t addr, uint8_t value); void LogWrite(uint32_t addr, uint8_t value); diff --git a/Core/DebugStats.cpp b/Core/DebugStats.cpp index 00592a1..018d6fc 100644 --- a/Core/DebugStats.cpp +++ b/Core/DebugStats.cpp @@ -43,10 +43,8 @@ void DebugStats::DisplayStats(double lastFrameTime) hud->DrawString(134, 10, "Video Stats", 0xFFFFFF, 0xFF000000, 1, startFrame); double totalDuration = 0; - if(_frameDurations) { - for(int i = 0; i < 60; i++) { - totalDuration += _frameDurations[i]; - } + for(int i = 0; i < 60; i++) { + totalDuration += _frameDurations[i]; } ss = std::stringstream(); diff --git a/Core/Debugger.cpp b/Core/Debugger.cpp index 738739b..332e7f6 100644 --- a/Core/Debugger.cpp +++ b/Core/Debugger.cpp @@ -251,7 +251,7 @@ void Debugger::SleepUntilResume() void Debugger::ProcessStepConditions(uint8_t opCode, uint32_t currentPc) { - if(_breakAddress == currentPc && (opCode == 0x60 || opCode == 0x40 || opCode == 0x6B || opCode == 0x44 || opCode == 0x54)) { + if(_breakAddress == (int32_t)currentPc && (opCode == 0x60 || opCode == 0x40 || opCode == 0x6B || opCode == 0x44 || opCode == 0x54)) { //RTS/RTL/RTI found, if we're on the expected return address, break immediately (for step over/step out) _cpuStepCount = 0; } @@ -277,6 +277,8 @@ void Debugger::ProcessInterrupt(uint32_t originalPc, uint32_t currentPc, bool fo void Debugger::ProcessEvent(EventType type) { switch(type) { + default: break; + case EventType::StartFrame: _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::EventViewerRefresh); _eventManager->ClearFrameEvents(); diff --git a/Core/DefaultVideoFilter.h b/Core/DefaultVideoFilter.h index 9de20ab..317d759 100644 --- a/Core/DefaultVideoFilter.h +++ b/Core/DefaultVideoFilter.h @@ -10,7 +10,6 @@ private: uint32_t _calculatedPalette[0x8000]; double _yiqToRgbMatrix[6]; VideoConfig _videoConfig; - bool _needToProcess = false; void InitConversionMatrix(double hueShift, double saturationShift); void InitLookupTable(); diff --git a/Core/Disassembler.cpp b/Core/Disassembler.cpp index cbdf951..28c680e 100644 --- a/Core/Disassembler.cpp +++ b/Core/Disassembler.cpp @@ -182,6 +182,7 @@ DisassemblyInfo Disassembler::GetDisassemblyInfo(AddressInfo &info) { DisassemblyInfo* disassemblyInfo = nullptr; switch(info.Type) { + default: break; case SnesMemoryType::PrgRom: disassemblyInfo = _prgCache[info.Address].get(); break; case SnesMemoryType::WorkRam: disassemblyInfo = _wramCache[info.Address].get(); break; case SnesMemoryType::SaveRam: disassemblyInfo = _sramCache[info.Address].get(); break; @@ -204,7 +205,7 @@ uint32_t Disassembler::GetLineIndex(uint32_t cpuAddress) { auto lock = _disassemblyLock.AcquireSafe(); uint32_t lastAddress = 0; - for(int i = 1; i < _disassembly.size(); i++) { + for(size_t i = 1; i < _disassembly.size(); i++) { if(_disassembly[i].CpuAddress < 0) { continue; } @@ -228,6 +229,7 @@ bool Disassembler::GetLineData(uint32_t lineIndex, CodeLineData &data) data.Flags = result.Flags; switch(result.Address.Type) { + default: break; case SnesMemoryType::PrgRom: data.Flags |= (uint8_t)LineFlags::PrgRom; break; case SnesMemoryType::WorkRam: data.Flags |= (uint8_t)LineFlags::WorkRam; break; case SnesMemoryType::SaveRam: data.Flags |= (uint8_t)LineFlags::SaveRam; break; @@ -297,7 +299,7 @@ int32_t Disassembler::SearchDisassembly(const char *searchString, int32_t startP } //Continue search from start/end of document - if(!searchBackwards && i == _disassembly.size() - 1) { + if(!searchBackwards && i == (int)(_disassembly.size() - 1)) { i = 0; } else if(searchBackwards && i == 0) { i = (int32_t)(_disassembly.size() - 1); diff --git a/Core/DmaController.cpp b/Core/DmaController.cpp index 9517218..01bceda 100644 --- a/Core/DmaController.cpp +++ b/Core/DmaController.cpp @@ -4,6 +4,12 @@ #include "MessageManager.h" #include "../Utilities/Serializer.h" +static constexpr uint8_t _transferByteCount[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; +static constexpr uint8_t _transferOffset[8][4] = { + { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 1 }, + { 0, 1, 2, 3 }, { 0, 1, 0, 1 }, { 0, 0, 0, 0 }, { 0, 0, 1, 1 } +}; + DmaController::DmaController(MemoryManager *memoryManager) { _memoryManager = memoryManager; @@ -53,7 +59,7 @@ void DmaController::RunSingleTransfer(DmaChannelConfig &channel) do { CopyDmaByte( (channel.SrcBank << 16) | channel.SrcAddress, - 0x2100 | channel.DestAddress + transferOffsets[i], + 0x2100 | (channel.DestAddress + transferOffsets[i]), channel.InvertDirection ); @@ -137,7 +143,7 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel) do { CopyDmaByte( (channel.HdmaBank << 16) | channel.TransferSize, - 0x2100 | channel.DestAddress + transferOffsets[i], + 0x2100 | (channel.DestAddress + transferOffsets[i]), channel.InvertDirection ); channel.TransferSize++; @@ -148,7 +154,7 @@ void DmaController::RunHdmaTransfer(DmaChannelConfig &channel) do { CopyDmaByte( (channel.SrcBank << 16) | channel.HdmaTableAddress, - 0x2100 | channel.DestAddress + transferOffsets[i], + 0x2100 | (channel.DestAddress + transferOffsets[i]), channel.InvertDirection ); channel.HdmaTableAddress++; diff --git a/Core/DmaController.h b/Core/DmaController.h index b1f862a..5a0faaf 100644 --- a/Core/DmaController.h +++ b/Core/DmaController.h @@ -29,15 +29,9 @@ struct DmaChannelConfig bool UnusedFlag; }; -class DmaController : public ISerializable +class DmaController final : public ISerializable { private: - static constexpr uint8_t _transferByteCount[8] = { 1, 2, 2, 4, 4, 4, 2, 4 }; - static constexpr uint8_t _transferOffset[8][4] = { - { 0, 0, 0, 0 }, { 0, 1, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 1, 1 }, - { 0, 1, 2, 3 }, { 0, 1, 0, 1 }, { 0, 0, 0, 0 }, { 0, 0, 1, 1 } - }; - bool _hdmaPending = false; uint8_t _hdmaChannels = 0; diff --git a/Core/EmuSettings.cpp b/Core/EmuSettings.cpp index f40246e..ecc471c 100644 --- a/Core/EmuSettings.cpp +++ b/Core/EmuSettings.cpp @@ -130,9 +130,9 @@ void EmuSettings::SetShortcutKeys(vector shortcuts) for(ShortcutKeyInfo &shortcut : shortcuts) { if(_emulatorKeys[0][(uint32_t)shortcut.Shortcut].GetKeys().empty()) { - SetShortcutKey(shortcut.Shortcut, shortcut.KeyCombination, 0); + SetShortcutKey(shortcut.Shortcut, shortcut.Keys, 0); } else { - SetShortcutKey(shortcut.Shortcut, shortcut.KeyCombination, 1); + SetShortcutKey(shortcut.Shortcut, shortcut.Keys, 1); } } } diff --git a/Core/EventManager.cpp b/Core/EventManager.cpp index cee4a36..8f13067 100644 --- a/Core/EventManager.cpp +++ b/Core/EventManager.cpp @@ -168,7 +168,6 @@ void EventManager::GetDisplayBuffer(uint32_t *buffer, EventViewerDisplayOptions bool overscanMode = _ppu->GetState().OverscanMode; //Skip the first 8 blank lines in the buffer when overscan mode is off uint16_t *ppuBuffer = _ppu->GetScreenBuffer() + (overscanMode ? 0 : (512 * 16)); - uint32_t pixelCount = 256*2*239*2; for(uint32_t y = 0, len = overscanMode ? 239*2 : 224*2; y < len; y++) { for(uint32_t x = 0; x < 512; x++) { buffer[(y + 2)*340*2 + x + 22*2] = DefaultVideoFilter::ToArgb(ppuBuffer[(y << 9) | x]); diff --git a/Core/ExpressionEvaluator.cpp b/Core/ExpressionEvaluator.cpp index 723c438..30d2602 100644 --- a/Core/ExpressionEvaluator.cpp +++ b/Core/ExpressionEvaluator.cpp @@ -56,7 +56,7 @@ EvalOperators ExpressionEvaluator::GetOperator(string token, bool unaryOperator) bool ExpressionEvaluator::CheckSpecialTokens(string expression, size_t &pos, string &output, ExpressionData &data) { string token; - size_t initialPos = pos; + //size_t initialPos = pos; size_t len = expression.size(); do { char c = std::tolower(expression[pos]); diff --git a/Core/InternalRegisters.h b/Core/InternalRegisters.h index b78c616..4a7dbfe 100644 --- a/Core/InternalRegisters.h +++ b/Core/InternalRegisters.h @@ -4,7 +4,7 @@ class Console; -class InternalRegisters : public ISerializable +class InternalRegisters final : public ISerializable { private: shared_ptr _console; diff --git a/Core/MemoryDumper.cpp b/Core/MemoryDumper.cpp index a5ee442..9caca8c 100644 --- a/Core/MemoryDumper.cpp +++ b/Core/MemoryDumper.cpp @@ -23,6 +23,7 @@ void MemoryDumper::SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t } switch(type) { + default: case SnesMemoryType::CpuMemory: break; @@ -39,6 +40,7 @@ void MemoryDumper::SetMemoryState(SnesMemoryType type, uint8_t *buffer, uint32_t uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type) { switch(type) { + default: return 0; case SnesMemoryType::CpuMemory: return 0x1000000; case SnesMemoryType::PrgRom: return _cartridge->DebugGetPrgRomSize(); case SnesMemoryType::WorkRam: return MemoryManager::WorkRamSize; @@ -48,12 +50,13 @@ uint32_t MemoryDumper::GetMemorySize(SnesMemoryType type) case SnesMemoryType::CGRam: return Ppu::CgRamSize; case SnesMemoryType::SpcRam: return Spc::SpcRamSize; } - return 0; } void MemoryDumper::GetMemoryState(SnesMemoryType type, uint8_t *buffer) { switch(type) { + default: break; + case SnesMemoryType::CpuMemory: for(int i = 0; i <= 0xFFFFFF; i+=0x1000) { _memoryManager->PeekBlock(i, buffer+i); @@ -84,6 +87,8 @@ void MemoryDumper::SetMemoryValue(SnesMemoryType memoryType, uint32_t address, u } switch(memoryType) { + default: break; + case SnesMemoryType::CpuMemory: _memoryManager->Write(address, value, MemoryOperationType::Write); break; case SnesMemoryType::PrgRom: _cartridge->DebugGetPrgRom()[address] = value; break; @@ -104,6 +109,8 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address } switch(memoryType) { + default: return 0; + case SnesMemoryType::CpuMemory: return _memoryManager->Peek(address); case SnesMemoryType::PrgRom: return _cartridge->DebugGetPrgRom()[address]; @@ -115,8 +122,6 @@ uint8_t MemoryDumper::GetMemoryValue(SnesMemoryType memoryType, uint32_t address case SnesMemoryType::CGRam: return _ppu->GetCgRam()[address]; case SnesMemoryType::SpcRam: return _spc->GetSpcRam()[address]; } - - return 0; } uint16_t MemoryDumper::GetMemoryValueWord(SnesMemoryType memoryType, uint32_t address) diff --git a/Core/MemoryManager.h b/Core/MemoryManager.h index e535a97..37a4d4c 100644 --- a/Core/MemoryManager.h +++ b/Core/MemoryManager.h @@ -39,7 +39,7 @@ private: public: void Initialize(shared_ptr console); - ~MemoryManager(); + virtual ~MemoryManager(); void Reset(); diff --git a/Core/Ppu.cpp b/Core/Ppu.cpp index 334fa6d..bb65052 100644 --- a/Core/Ppu.cpp +++ b/Core/Ppu.cpp @@ -15,6 +15,17 @@ #include "../Utilities/HexUtilities.h" #include "../Utilities/Serializer.h" +static constexpr uint8_t _oamSizes[8][2][2] = { + { { 1, 1 }, { 2, 2 } }, //8x8 + 16x16 + { { 1, 1 }, { 4, 4 } }, //8x8 + 32x32 + { { 1, 1 }, { 8, 8 } }, //8x8 + 64x64 + { { 2, 2 }, { 4, 4 } }, //16x16 + 32x32 + { { 2, 2 }, { 8, 8 } }, //16x16 + 64x64 + { { 4, 4 }, { 8, 8 } }, //32x32 + 64x64 + { { 2, 4 }, { 4, 8 } }, //16x32 + 32x64 + { { 2, 4 }, { 4, 4 } } //16x32 + 32x32 +}; + Ppu::Ppu(shared_ptr console) { _console = console; diff --git a/Core/Ppu.h b/Core/Ppu.h index 98ef299..1172185 100644 --- a/Core/Ppu.h +++ b/Core/Ppu.h @@ -32,17 +32,6 @@ private: constexpr static int SpriteLayerIndex = 4; constexpr static int ColorWindowIndex = 5; - constexpr static const uint8_t _oamSizes[8][2][2] = { - { { 1, 1 }, { 2, 2 } }, //8x8 + 16x16 - { { 1, 1 }, { 4, 4 } }, //8x8 + 32x32 - { { 1, 1 }, { 8, 8 } }, //8x8 + 64x64 - { { 2, 2 }, { 4, 4 } }, //16x16 + 32x32 - { { 2, 2 }, { 8, 8 } }, //16x16 + 64x64 - { { 4, 4 }, { 8, 8 } }, //32x32 + 64x64 - { { 2, 4 }, { 4, 8 } }, //16x32 + 32x64 - { { 2, 4 }, { 4, 4 } } //16x32 + 32x32 - }; - shared_ptr _console; shared_ptr _regs; @@ -223,7 +212,7 @@ private: public: Ppu(shared_ptr console); - ~Ppu(); + virtual ~Ppu(); void Reset(); diff --git a/Core/SettingTypes.h b/Core/SettingTypes.h index b090d35..7087fd6 100644 --- a/Core/SettingTypes.h +++ b/Core/SettingTypes.h @@ -1,5 +1,6 @@ #pragma once #include "stdafx.h" +#include enum class EmulationFlags { @@ -226,7 +227,7 @@ struct InputConfig uint32_t MouseSensitivity = 1; }; -enum class RamPowerOnState +enum class RamState { AllZeros = 0, AllOnes = 1, @@ -254,7 +255,7 @@ struct EmulationConfig uint32_t PpuExtraScanlinesBeforeNmi = 0; uint32_t PpuExtraScanlinesAfterNmi = 0; - RamPowerOnState RamPowerOnState = RamPowerOnState::AllZeros; + RamState RamPowerOnState = RamState::AllZeros; }; struct PreferencesConfig @@ -409,5 +410,5 @@ struct KeyCombination struct ShortcutKeyInfo { EmulatorShortcut Shortcut; - KeyCombination KeyCombination; + KeyCombination Keys; }; diff --git a/Core/ShortcutKeyHandler.cpp b/Core/ShortcutKeyHandler.cpp index cccd4e7..eab838c 100644 --- a/Core/ShortcutKeyHandler.cpp +++ b/Core/ShortcutKeyHandler.cpp @@ -7,6 +7,7 @@ #include "Console.h" #include "RewindManager.h" #include "NotificationManager.h" +#include "SaveStateManager.h" ShortcutKeyHandler::ShortcutKeyHandler(shared_ptr console) { @@ -110,7 +111,7 @@ void ShortcutKeyHandler::CheckMappedKeys() { shared_ptr settings = _console->GetSettings(); bool isNetplayClient = false; //TODO GameClient::Connected(); - bool isMovieActive = false; //TODO MovieManager::Playing() || MovieManager::Recording(); + //bool isMovieActive = false; //TODO MovieManager::Playing() || MovieManager::Recording(); bool isMovieRecording = false; //TODO MovieManager::Recording(); //Let the UI handle these shortcuts @@ -135,7 +136,6 @@ void ShortcutKeyHandler::CheckMappedKeys() } } - /* if(DetectKeyPress(EmulatorShortcut::MoveToNextStateSlot)) { _console->GetSaveStateManager()->MoveToNextSlot(); } @@ -152,7 +152,7 @@ void ShortcutKeyHandler::CheckMappedKeys() _console->GetSaveStateManager()->LoadState(); } - if(DetectKeyPress(EmulatorShortcut::ToggleCheats) && !isNetplayClient && !isMovieActive) { + /*if(DetectKeyPress(EmulatorShortcut::ToggleCheats) && !isNetplayClient && !isMovieActive) { _console->GetNotificationManager()->SendNotification(ConsoleNotificationType::ExecuteShortcut, (void*)EmulatorShortcut::ToggleCheats); }*/ diff --git a/Core/Spc.h b/Core/Spc.h index 98e5294..d7e77b5 100644 --- a/Core/Spc.h +++ b/Core/Spc.h @@ -238,7 +238,7 @@ private: public: Spc(shared_ptr console, vector &spcRomData); - ~Spc(); + virtual ~Spc(); void Run(); void Reset(); diff --git a/Core/dsp.cpp b/Core/dsp.cpp deleted file mode 100644 index 4aad859..0000000 --- a/Core/dsp.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "stdafx.h" -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "dsp.h" - -#include "SPC_DSP.h" - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -SPC_DSP* spc_dsp_new( void ) -{ - // be sure constants match - assert( spc_dsp_voice_count == (int) SPC_DSP::voice_count ); - assert( spc_dsp_register_count == (int) SPC_DSP::register_count ); - #if !SPC_NO_COPY_STATE_FUNCS - assert( spc_dsp_state_size == (int) SPC_DSP::state_size ); - #endif - - return new SPC_DSP; -} - -void spc_dsp_delete ( SPC_DSP* s ) { delete s; } -void spc_dsp_init ( SPC_DSP* s, void* ram_64k ) { s->init( ram_64k ); } -void spc_dsp_set_output ( SPC_DSP* s, spc_dsp_sample_t* p, int n ) { s->set_output( p, n ); } -int spc_dsp_sample_count( SPC_DSP const* s ) { return s->sample_count(); } -void spc_dsp_reset ( SPC_DSP* s ) { s->reset(); } -void spc_dsp_soft_reset ( SPC_DSP* s ) { s->soft_reset(); } -int spc_dsp_read ( SPC_DSP const* s, int addr ) { return s->read( addr ); } -void spc_dsp_write ( SPC_DSP* s, int addr, int data ) { s->write( addr, data ); } -void spc_dsp_run ( SPC_DSP* s, int clock_count ) { s->run( clock_count ); } -void spc_dsp_mute_voices ( SPC_DSP* s, int mask ) { s->mute_voices( mask ); } -void spc_dsp_disable_surround( SPC_DSP* s, int disable ) { s->disable_surround( disable ); } -void spc_dsp_load ( SPC_DSP* s, unsigned char const regs [spc_dsp_register_count] ) { s->load( regs ); } - -#if !SPC_NO_COPY_STATE_FUNCS -void spc_dsp_copy_state ( SPC_DSP* s, unsigned char** p, spc_dsp_copy_func_t f ) { s->copy_state( p, f ); } -int spc_dsp_check_kon ( SPC_DSP* s ) { return s->check_kon(); } -#endif diff --git a/Core/stdafx.h b/Core/stdafx.h index e1348d1..98e2a22 100644 --- a/Core/stdafx.h +++ b/Core/stdafx.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -50,9 +51,3 @@ using std::atomic_flag; using std::atomic; using std::thread; using std::deque; - -#ifdef _DEBUG -#pragma comment(lib, "C:\\Code\\Mesen-S\\bin\\x64\\Debug\\Utilities.lib") -#else -#pragma comment(lib, "C:\\Code\\Mesen-S\\bin\\x64\\Release\\Utilities.lib") -#endif \ No newline at end of file diff --git a/Dependencies/DirectXTK.Debug.Static.x64.lib b/Dependencies/DirectXTK.Debug.Static.x64.lib new file mode 100644 index 0000000..d747f07 Binary files /dev/null and b/Dependencies/DirectXTK.Debug.Static.x64.lib differ diff --git a/Dependencies/DirectXTK.Debug.Static.x86.lib b/Dependencies/DirectXTK.Debug.Static.x86.lib new file mode 100644 index 0000000..05b6741 Binary files /dev/null and b/Dependencies/DirectXTK.Debug.Static.x86.lib differ diff --git a/Dependencies/DirectXTK.Release.Static.x64.lib b/Dependencies/DirectXTK.Release.Static.x64.lib new file mode 100644 index 0000000..7fb146e Binary files /dev/null and b/Dependencies/DirectXTK.Release.Static.x64.lib differ diff --git a/Dependencies/DirectXTK.Release.Static.x86.lib b/Dependencies/DirectXTK.Release.Static.x86.lib new file mode 100644 index 0000000..76ee0cc Binary files /dev/null and b/Dependencies/DirectXTK.Release.Static.x86.lib differ diff --git a/InteropDLL/EmuApiWrapper.cpp b/InteropDLL/EmuApiWrapper.cpp index a0deb3c..6015dbb 100644 --- a/InteropDLL/EmuApiWrapper.cpp +++ b/InteropDLL/EmuApiWrapper.cpp @@ -11,6 +11,7 @@ #include "../Core/KeyManager.h" #include "../Core/ShortcutKeyHandler.h" #include "../Utilities/ArchiveReader.h" +#include "../Utilities/FolderUtilities.h" #include "InteropNotificationListeners.h" #ifdef _WIN32 @@ -172,12 +173,12 @@ extern "C" { _console->Stop(true); + _console->Release(); + _console.reset(); + _renderer.reset(); _soundManager.reset(); _keyManager.reset(); - - _console->Release(); - _console.reset(); } DllExport INotificationListener* __stdcall RegisterNotificationCallback(NotificationListenerCallback callback) diff --git a/Linux/LinuxGameController.cpp b/Linux/LinuxGameController.cpp new file mode 100644 index 0000000..f4d5148 --- /dev/null +++ b/Linux/LinuxGameController.cpp @@ -0,0 +1,231 @@ +#include "../Core/MessageManager.h" +#include "../Core/Console.h" +#include "../Core/EmuSettings.h" +#include "LinuxGameController.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +std::shared_ptr LinuxGameController::GetController(shared_ptr console, int deviceID, bool logInformation) +{ + std::string deviceName = "/dev/input/event" + std::to_string(deviceID); + struct stat buffer; + if(stat(deviceName.c_str(), &buffer) == 0) { + int fd = open(deviceName.c_str(), O_RDONLY | O_NONBLOCK); + if(fd < 0) { + if(logInformation) { + MessageManager::Log("[Input] " + deviceName + " error: " + std::to_string(errno) + " " + strerror(errno)); + } + return nullptr; + } + + libevdev* device = nullptr; + int rc = libevdev_new_from_fd(fd, &device); + if(rc < 0) { + if(logInformation) { + MessageManager::Log("[Input] " + deviceName + " error: " + std::to_string(errno) + " " + strerror(errno)); + } + close(fd); + return nullptr; + } + + if((libevdev_has_event_type(device, EV_KEY) && libevdev_has_event_code(device, EV_KEY, BTN_GAMEPAD)) || + (libevdev_has_event_type(device, EV_ABS) && libevdev_has_event_code(device, EV_ABS, ABS_X))) { + MessageManager::Log(std::string("[Input Connected] Name: ") + libevdev_get_name(device) + " Vendor: " + std::to_string(libevdev_get_id_vendor(device)) + " Product: " + std::to_string(libevdev_get_id_product(device))); + return std::shared_ptr(new LinuxGameController(console, deviceID, fd, device)); + } else { + MessageManager::Log(std::string("[Input] Device ignored (Not a gamepad) - Name: ") + libevdev_get_name(device) + " Vendor: " + std::to_string(libevdev_get_id_vendor(device)) + " Product: " + std::to_string(libevdev_get_id_product(device))); + close(fd); + } + } + return nullptr; +} + +LinuxGameController::LinuxGameController(shared_ptr console, int deviceID, int fileDescriptor, libevdev* device) +{ + _console = console; + _deviceID = deviceID; + _stopFlag = false; + _device = device; + _fd = fileDescriptor; + memset(_axisDefaultValue, 0, sizeof(_axisDefaultValue)); + + _eventThread = std::thread([=]() { + int rc; + bool calibrate = true; + + do { + fd_set readSet; + FD_ZERO(&readSet); + FD_SET(_fd, &readSet); + + //Timeout after 0.1 seconds (to allow thread to be terminated quickly) + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100000; + + rc = select((int)_fd+1, &readSet, nullptr, nullptr, &timeout); + if(rc) { + do { + struct input_event ev; + rc = libevdev_next_event(_device, LIBEVDEV_READ_FLAG_NORMAL, &ev); + if(rc == LIBEVDEV_READ_STATUS_SYNC) { + while (rc == LIBEVDEV_READ_STATUS_SYNC) { + rc = libevdev_next_event(_device, LIBEVDEV_READ_FLAG_SYNC, &ev); + } + } else if(rc == LIBEVDEV_READ_STATUS_SUCCESS) { + //print_event(&ev); + } + } while(rc == LIBEVDEV_READ_STATUS_SYNC || rc == LIBEVDEV_READ_STATUS_SUCCESS); + } + + if(rc != LIBEVDEV_READ_STATUS_SYNC && rc != LIBEVDEV_READ_STATUS_SUCCESS && rc != -EAGAIN && rc != EWOULDBLOCK) { + //Device was disconnected + MessageManager::Log("[Input Device] Disconnected"); + break; + } + + if(calibrate) { + std::this_thread::sleep_for(std::chrono::duration(100)); + Calibrate(); + calibrate = false; + } + } while(!_stopFlag); + + _disconnected = true; + }); +} + +LinuxGameController::~LinuxGameController() +{ + _stopFlag = true; + _eventThread.join(); + + libevdev_free(_device); + close(_fd); +} + +void LinuxGameController::Calibrate() +{ + int axes[14] = { ABS_X, ABS_Y, ABS_Z, ABS_RX, ABS_RY, ABS_RZ, ABS_HAT0X, ABS_HAT0Y, ABS_HAT1X, ABS_HAT1Y, ABS_HAT2X, ABS_HAT2Y, ABS_HAT3X, ABS_HAT3Y }; + for(int axis : axes) { + _axisDefaultValue[axis] = libevdev_get_event_value(_device, EV_ABS, axis); + //std::cout << "center values: " << std::to_string(_axisDefaultValue[axis]) << std::endl; + } +} + +bool LinuxGameController::CheckAxis(unsigned int code, bool forPositive) +{ + double deadZoneRatio = 1; //TODO _console->GetSettings()->GetControllerDeadzoneRatio(); + int deadZoneNegative = (_axisDefaultValue[code] - libevdev_get_abs_minimum(_device, code)) * 0.400 * deadZoneRatio; + int deadZonePositive = (libevdev_get_abs_maximum(_device, code) - _axisDefaultValue[code]) * 0.400 * deadZoneRatio; + + if(forPositive) { + return libevdev_get_event_value(_device, EV_ABS, code) - _axisDefaultValue[code] > deadZonePositive; + } else { + return libevdev_get_event_value(_device, EV_ABS, code) - _axisDefaultValue[code] < -deadZoneNegative; + } +} + +bool LinuxGameController::IsButtonPressed(int buttonNumber) +{ + switch(buttonNumber) { + case 0: return libevdev_get_event_value(_device, EV_KEY, BTN_A) == 1; + case 1: return libevdev_get_event_value(_device, EV_KEY, BTN_B) == 1; + case 2: return libevdev_get_event_value(_device, EV_KEY, BTN_C) == 1; + case 3: return libevdev_get_event_value(_device, EV_KEY, BTN_X) == 1; + case 4: return libevdev_get_event_value(_device, EV_KEY, BTN_Y) == 1; + case 5: return libevdev_get_event_value(_device, EV_KEY, BTN_Z) == 1; + case 6: return libevdev_get_event_value(_device, EV_KEY, BTN_TL) == 1; + case 7: return libevdev_get_event_value(_device, EV_KEY, BTN_TR) == 1; + case 8: return libevdev_get_event_value(_device, EV_KEY, BTN_TL2) == 1; + case 9: return libevdev_get_event_value(_device, EV_KEY, BTN_TR2) == 1; + case 10: return libevdev_get_event_value(_device, EV_KEY, BTN_SELECT) == 1; + case 11: return libevdev_get_event_value(_device, EV_KEY, BTN_START) == 1; + case 12: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMBL) == 1; + case 13: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMBR) == 1; + + case 14: return CheckAxis(ABS_X, true); + case 15: return CheckAxis(ABS_X, false); + case 16: return CheckAxis(ABS_Y, true); + case 17: return CheckAxis(ABS_Y, false); + case 18: return CheckAxis(ABS_Z, true); + case 19: return CheckAxis(ABS_Z, false); + case 20: return CheckAxis(ABS_RX, true); + case 21: return CheckAxis(ABS_RX, false); + case 22: return CheckAxis(ABS_RY, true); + case 23: return CheckAxis(ABS_RY, false); + case 24: return CheckAxis(ABS_RZ, true); + case 25: return CheckAxis(ABS_RZ, false); + + case 26: return CheckAxis(ABS_HAT0X, true); + case 27: return CheckAxis(ABS_HAT0X, false); + case 28: return CheckAxis(ABS_HAT0Y, true); + case 29: return CheckAxis(ABS_HAT0Y, false); + case 30: return CheckAxis(ABS_HAT1X, true); + case 31: return CheckAxis(ABS_HAT1X, false); + case 32: return CheckAxis(ABS_HAT1Y, true); + case 33: return CheckAxis(ABS_HAT1Y, false); + case 34: return CheckAxis(ABS_HAT2X, true); + case 35: return CheckAxis(ABS_HAT2X, false); + case 36: return CheckAxis(ABS_HAT2Y, true); + case 37: return CheckAxis(ABS_HAT2Y, false); + case 38: return CheckAxis(ABS_HAT3X, true); + case 39: return CheckAxis(ABS_HAT3X, false); + case 40: return CheckAxis(ABS_HAT3Y, true); + case 41: return CheckAxis(ABS_HAT3Y, false); + + case 42: return libevdev_get_event_value(_device, EV_KEY, BTN_TRIGGER) == 1; + case 43: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMB) == 1; + case 44: return libevdev_get_event_value(_device, EV_KEY, BTN_THUMB2) == 1; + case 45: return libevdev_get_event_value(_device, EV_KEY, BTN_TOP) == 1; + case 46: return libevdev_get_event_value(_device, EV_KEY, BTN_TOP2) == 1; + case 47: return libevdev_get_event_value(_device, EV_KEY, BTN_PINKIE) == 1; + case 48: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE) == 1; + case 49: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE2) == 1; + case 50: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE3) == 1; + case 51: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE4) == 1; + case 52: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE5) == 1; + case 53: return libevdev_get_event_value(_device, EV_KEY, BTN_BASE6) == 1; + case 54: return libevdev_get_event_value(_device, EV_KEY, BTN_DEAD) == 1; + } + + return false; +} + +bool LinuxGameController::IsDisconnected() +{ + return _disconnected; +} + +int LinuxGameController::GetDeviceID() +{ + return _deviceID; +} + +/* +static int print_event(struct input_event *ev) +{ + if (ev->type == EV_SYN) + printf("Event: time %ld.%06ld, ++++++++++++++++++++ %s +++++++++++++++\n", + ev->time.tv_sec, + ev->time.tv_usec, + libevdev_event_type_get_name(ev->type)); + else + printf("Event: time %ld.%06ld, type %d (%s), code %d (%s), value %d\n", + ev->time.tv_sec, + ev->time.tv_usec, + ev->type, + libevdev_event_type_get_name(ev->type), + ev->code, + libevdev_event_code_get_name(ev->type, ev->code), + ev->value); + return 0; +} +*/ diff --git a/Linux/LinuxGameController.h b/Linux/LinuxGameController.h new file mode 100644 index 0000000..7e2d321 --- /dev/null +++ b/Linux/LinuxGameController.h @@ -0,0 +1,32 @@ +#pragma once +#include +#include + +struct libevdev; +class Console; + +class LinuxGameController +{ +private: + int _fd = -1; + int _deviceID = -1; + libevdev *_device = nullptr; + bool _disconnected = false; + std::thread _eventThread; + std::atomic _stopFlag; + shared_ptr _console; + int _axisDefaultValue[0x100]; + + LinuxGameController(shared_ptr console, int deviceID, int fileDescriptor, libevdev *device); + bool CheckAxis(unsigned int code, bool forPositive); + void Calibrate(); + +public: + ~LinuxGameController(); + + static std::shared_ptr GetController(shared_ptr console, int deviceID, bool logInformation); + + bool IsDisconnected(); + int GetDeviceID(); + bool IsButtonPressed(int buttonNumber); +}; \ No newline at end of file diff --git a/Linux/LinuxKeyManager.cpp b/Linux/LinuxKeyManager.cpp new file mode 100644 index 0000000..2932143 --- /dev/null +++ b/Linux/LinuxKeyManager.cpp @@ -0,0 +1,443 @@ +#include +#include "LinuxKeyManager.h" +#include "LinuxGameController.h" +#include "../Utilities/FolderUtilities.h" +#include "../Core/ControlManager.h" +#include "../Core/Console.h" + +static vector _keyDefinitions = { + { "", 9, "Esc", "" }, + { "", 10, "1", "" }, + { "", 11, "2", "" }, + { "", 12, "3", "" }, + { "", 13, "4", "" }, + { "", 14, "5", "" }, + { "", 15, "6", "" }, + { "", 16, "7", "" }, + { "", 17, "8", "" }, + { "", 18, "9", "" }, + { "", 19, "0", "" }, + { "", 20, "-", "" }, + { "", 21, "=", "" }, + { "", 22, "Backspace", "" }, + { "", 23, "Tab", "" }, + { "", 24, "Q", "" }, + { "", 25, "W", "" }, + { "", 26, "E", "" }, + { "", 27, "R", "" }, + { "", 28, "T", "" }, + { "", 29, "Y", "" }, + { "", 30, "U", "" }, + { "", 31, "I", "" }, + { "", 32, "O", "" }, + { "", 33, "P", "" }, + { "", 34, "[", "" }, + { "", 35, "]", "" }, + { "", 36, "Enter", "" }, + { "", 37, "Ctrl", "" }, + { "", 38, "A", "" }, + { "", 39, "S", "" }, + { "", 40, "D", "" }, + { "", 41, "F", "" }, + { "", 42, "G", "" }, + { "", 43, "H", "" }, + { "", 44, "J", "" }, + { "", 45, "K", "" }, + { "", 46, "L", "" }, + { "", 47, ";", "" }, + { "", 48, "'", "" }, + { "", 49, "`", "" }, + { "", 50, "Shift", "" }, + { "", 51, "\\", "" }, + { "", 52, "Z", "" }, + { "", 53, "X", "" }, + { "", 54, "C", "" }, + { "", 55, "V", "" }, + { "", 56, "B", "" }, + { "", 57, "N", "" }, + { "", 58, "M", "" }, + { "", 59, ",", "" }, + { "", 60, ".", "" }, + { "", 61, "/", "" }, + { "", 62, "Right Shift", "" }, + { "", 63, "Numpad *", "" }, + { "", 64, "Alt", "" }, + { "", 65, "Spacebar", "" }, + { "", 66, "Caps Lock", "" }, + { "", 67, "F1", "" }, + { "", 68, "F2", "" }, + { "", 69, "F3", "" }, + { "", 70, "F4", "" }, + { "", 71, "F5", "" }, + { "", 72, "F6", "" }, + { "", 73, "F7", "" }, + { "", 74, "F8", "" }, + { "", 75, "F9", "" }, + { "", 76, "F10", "" }, + { "", 77, "Num Lock", "" }, + { "", 78, "Scroll Lock", "" }, + { "", 79, "Numpad 7", "" }, + { "", 80, "Numpad 8", "" }, + { "", 81, "Numpad 9", "" }, + { "", 82, "Numpad -", "" }, + { "", 83, "Numpad 4", "" }, + { "", 84, "Numpad 5", "" }, + { "", 85, "Numpad 6", "" }, + { "", 86, "Numpad +", "" }, + { "", 87, "Numpad 1", "" }, + { "", 88, "Numpad 2", "" }, + { "", 89, "Numpad 3", "" }, + { "", 90, "Numpad 0", "" }, + { "", 91, "Numpad .", "" }, + { "", 92, "ISO_Level3_Shift", "" }, + { "", 94, "Pipe", "" }, + { "", 95, "F11", "" }, + { "", 96, "F12", "" }, + { "", 98, "Katakana", "" }, + { "", 99, "Hiragana", "" }, + { "", 100, "Henkan_Mode", "" }, + { "", 101, "Hiragana_Katakana", "" }, + { "", 102, "Muhenkan", "" }, + { "", 104, "Numpad Enter", "" }, + { "", 105, "Control_R", "" }, + { "", 106, "Numpad /", "" }, + { "", 107, "Print", "" }, + { "", 108, "Right Alt", "" }, + { "", 109, "Linefeed", "" }, + { "", 110, "Home", "" }, + { "", 111, "Up Arrow", "" }, + { "", 112, "Page Up", "" }, + { "", 113, "Left Arrow", "" }, + { "", 114, "Right Arrow", "" }, + { "", 115, "End", "" }, + { "", 116, "Down Arrow", "" }, + { "", 117, "Page Down", "" }, + { "", 118, "Insert", "" }, + { "", 119, "Delete", "" }, + { "", 121, "XF86AudioMute", "" }, + { "", 122, "XF86AudioLowerVolume", "" }, + { "", 123, "XF86AudioRaiseVolume", "" }, + { "", 124, "XF86PowerOff", "" }, + { "", 125, "KP_Equal", "" }, + { "", 126, "PlusMinus", "" }, + { "", 127, "Pause", "" }, + { "", 128, "XF86LaunchA", "" }, + { "", 129, "KP_Decimal", "" }, + { "", 130, "Hangul", "" }, + { "", 131, "Hangul_Hanja", "" }, + { "", 133, "Super_L", "" }, + { "", 134, "Super_R", "" }, + { "", 135, "Menu", "" }, + { "", 136, "Cancel", "" }, + { "", 137, "Redo", "" }, + { "", 138, "SunProps", "" }, + { "", 139, "Undo", "" }, + { "", 140, "SunFront", "" }, + { "", 141, "XF86Copy", "" }, + { "", 142, "XF86Open", "" }, + { "", 143, "XF86Paste", "" }, + { "", 144, "Find", "" }, + { "", 145, "XF86Cut", "" }, + { "", 146, "Help", "" }, + { "", 147, "XF86MenuKB", "" }, + { "", 148, "XF86Calculator", "" }, + { "", 150, "XF86Sleep", "" }, + { "", 151, "XF86WakeUp", "" }, + { "", 152, "XF86Explorer", "" }, + { "", 153, "XF86Send", "" }, + { "", 155, "XF86Xfer", "" }, + { "", 156, "XF86Launch1", "" }, + { "", 157, "XF86Launch2", "" }, + { "", 158, "XF86WWW", "" }, + { "", 159, "XF86DOS", "" }, + { "", 160, "XF86ScreenSaver", "" }, + { "", 161, "XF86RotateWindows", "" }, + { "", 162, "XF86TaskPane", "" }, + { "", 163, "XF86Mail", "" }, + { "", 164, "XF86Favorites", "" }, + { "", 165, "XF86MyComputer", "" }, + { "", 166, "XF86Back", "" }, + { "", 167, "XF86Forward", "" }, + { "", 169, "XF86Eject", "" }, + { "", 170, "XF86Eject", "" }, + { "", 171, "XF86AudioNext", "" }, + { "", 172, "XF86AudioPlay", "" }, + { "", 173, "XF86AudioPrev", "" }, + { "", 174, "XF86AudioStop", "" }, + { "", 175, "XF86AudioRecord", "" }, + { "", 176, "XF86AudioRewind", "" }, + { "", 177, "XF86Phone", "" }, + { "", 179, "XF86Tools", "" }, + { "", 180, "XF86HomePage", "" }, + { "", 181, "XF86Reload", "" }, + { "", 182, "XF86Close", "" }, + { "", 185, "XF86ScrollUp", "" }, + { "", 186, "XF86ScrollDown", "" }, + { "", 187, "Paren Left", "" }, + { "", 188, "Paren Right", "" }, + { "", 189, "XF86New", "" }, + { "", 190, "Redo", "" }, + { "", 191, "XF86Tools", "" }, + { "", 192, "XF86Launch5", "" }, + { "", 193, "XF86Launch6", "" }, + { "", 194, "XF86Launch7", "" }, + { "", 195, "XF86Launch8", "" }, + { "", 196, "XF86Launch9", "" }, + { "", 198, "XF86AudioMicMute", "" }, + { "", 199, "XF86TouchpadToggle", "" }, + { "", 200, "XF86TouchpadOn", "" }, + { "", 201, "XF86TouchpadOff", "" }, + { "", 203, "Mode_switch", "" }, + { "", 204, "NoSymbol", "" }, + { "", 205, "NoSymbol", "" }, + { "", 206, "NoSymbol", "" }, + { "", 207, "NoSymbol", "" }, + { "", 208, "XF86AudioPlay", "" }, + { "", 209, "XF86AudioPause", "" }, + { "", 210, "XF86Launch3", "" }, + { "", 211, "XF86Launch4", "" }, + { "", 212, "XF86LaunchB", "" }, + { "", 213, "XF86Suspend", "" }, + { "", 214, "XF86Close", "" }, + { "", 215, "XF86AudioPlay", "" }, + { "", 216, "XF86AudioForward", "" }, + { "", 218, "Print", "" }, + { "", 220, "XF86WebCam", "" }, + { "", 223, "XF86Mail", "" }, + { "", 224, "XF86Messenger", "" }, + { "", 225, "XF86Search", "" }, + { "", 226, "XF86Go", "" }, + { "", 227, "XF86Finance", "" }, + { "", 228, "XF86Game", "" }, + { "", 229, "XF86Shop", "" }, + { "", 231, "Cancel", "" }, + { "", 232, "XF86MonBrightnessDown", "" }, + { "", 233, "XF86MonBrightnessUp", "" }, + { "", 234, "XF86AudioMedia", "" }, + { "", 235, "XF86Display", "" }, + { "", 236, "XF86KbdLightOnOff", "" }, + { "", 237, "XF86KbdBrightnessDown", "" }, + { "", 238, "XF86KbdBrightnessUp", "" }, + { "", 239, "XF86Send", "" }, + { "", 240, "XF86Reply", "" }, + { "", 241, "XF86MailForward", "" }, + { "", 242, "XF86Save", "" }, + { "", 243, "XF86Documents", "" }, + { "", 244, "XF86Battery", "" }, + { "", 245, "XF86Bluetooth", "" }, + { "", 246, "XF86WLAN", "" }, +}; + +LinuxKeyManager::LinuxKeyManager(shared_ptr console) +{ + _console = console; + + ResetKeyState(); + + vector buttonNames = { + "A", "B", "C", "X", "Y", "Z", "L1", "R1", "L2", "R2", "Select", "Start", "L3", "R3", + "X+", "X-", "Y+", "Y-", "Z+", "Z-", + "X2+", "X2-", "Y2+", "Y2-", "Z2+", "Z2-", + "Right", "Left", "Down", "Up", + "Right 2", "Left 2", "Down 2", "Up 2", + "Right 3", "Left 3", "Down 3", "Up 3", + "Right 4", "Left 4", "Down 4", "Up 4", + "Trigger", "Thumb", "Thumb2", "Top", "Top2", + "Pinkie", "Base", "Base2", "Base3", "Base4", + "Base5", "Base6", "Dead" + }; + + for(int i = 0; i < 20; i++) { + for(int j = 0; j < (int)buttonNames.size(); j++) { + _keyDefinitions.push_back({ "", (uint32_t)(0x10000 + i * 0x100 + j), "Pad" + std::to_string(i + 1) + " " + buttonNames[j], "" }); + } + } + + for(KeyDefinition &keyDef : _keyDefinitions) { + _keyNames[keyDef.keyCode] = keyDef.description; + _keyCodes[keyDef.description] = keyDef.keyCode; + } + + CheckForGamepads(true); + + _disableAllKeys = false; + _stopUpdateDeviceThread = false; + StartUpdateDeviceThread(); +} + +LinuxKeyManager::~LinuxKeyManager() +{ + _stopUpdateDeviceThread = true; + _stopSignal.Signal(); + _updateDeviceThread.join(); +} + +void LinuxKeyManager::RefreshState() +{ + //TODO: NOT IMPLEMENTED YET; + //Only needed to detect poll controller input +} + +bool LinuxKeyManager::IsKeyPressed(uint32_t key) +{ + if(_disableAllKeys) { + return false; + } + + if(key >= 0x10000) { + uint8_t gamepadPort = (key - 0x10000) / 0x100; + uint8_t gamepadButton = (key - 0x10000) % 0x100; + if(_controllers.size() > gamepadPort) { + return _controllers[gamepadPort]->IsButtonPressed(gamepadButton); + } + } else if(key < 0x200) { + return _keyState[key & 0xFF] != 0; + } + return false; +} + +bool LinuxKeyManager::IsMouseButtonPressed(MouseButton button) +{ + switch(button) { + case MouseButton::LeftButton: return _mouseState[0]; + case MouseButton::RightButton: return _mouseState[1]; + case MouseButton::MiddleButton: return _mouseState[2]; + } + + return false; +} + +vector LinuxKeyManager::GetPressedKeys() +{ + vector pressedKeys; + for(size_t i = 0; i < _controllers.size(); i++) { + for(int j = 0; j <= 54; j++) { + if(_controllers[i]->IsButtonPressed(j)) { + pressedKeys.push_back(0x10000 + i * 0x100 + j); + } + } + } + + for(int i = 0; i < 0x200; i++) { + if(_keyState[i]) { + pressedKeys.push_back(i); + } + } + return pressedKeys; +} + +string LinuxKeyManager::GetKeyName(uint32_t key) +{ + auto keyDef = _keyNames.find(key); + if(keyDef != _keyNames.end()) { + return keyDef->second; + } + return ""; +} + +uint32_t LinuxKeyManager::GetKeyCode(string keyName) +{ + auto keyDef = _keyCodes.find(keyName); + if(keyDef != _keyCodes.end()) { + return keyDef->second; + } + return 0; +} + +void LinuxKeyManager::UpdateDevices() +{ + //TODO: NOT IMPLEMENTED YET + //Only needed to detect newly plugged in devices +} + +void LinuxKeyManager::CheckForGamepads(bool logInformation) +{ + vector connectedIDs; + for(int i = _controllers.size() - 1; i >= 0; i--) { + if(!_controllers[i]->IsDisconnected()) { + connectedIDs.push_back(_controllers[i]->GetDeviceID()); + } + } + + vector files = FolderUtilities::GetFilesInFolder("/dev/input/", {}, false); + for(size_t i = 0; i < files.size(); i++) { + string filename = FolderUtilities::GetFilename(files[i], false); + if(filename.find("event", 0) == 0) { + int deviceId = 0; + try { + deviceId = std::stoi(filename.substr(5)); + } catch(std::exception e) { + continue; + } + + if(std::find(connectedIDs.begin(), connectedIDs.end(), deviceId) == connectedIDs.end()) { + std::shared_ptr controller = LinuxGameController::GetController(_console, deviceId, logInformation); + if(controller) { + _controllers.push_back(controller); + } + } + } + } +} + +void LinuxKeyManager::StartUpdateDeviceThread() +{ + _updateDeviceThread = std::thread([=]() { + while(!_stopUpdateDeviceThread) { + //Check for newly plugged in controllers every 5 secs + vector> controllersToAdd; + vector indexesToRemove; + for(int i = _controllers.size() - 1; i >= 0; i--) { + if(_controllers[i]->IsDisconnected()) { + indexesToRemove.push_back(i); + } + } + + CheckForGamepads(false); + + if(!indexesToRemove.empty() || !controllersToAdd.empty()) { + _console->Pause(); + for(int index : indexesToRemove) { + _controllers.erase(_controllers.begin()+index); + } + for(std::shared_ptr controller : controllersToAdd) { + _controllers.push_back(controller); + } + _console->Resume(); + } + + _stopSignal.Wait(5000); + } + }); +} + +void LinuxKeyManager::SetKeyState(uint16_t scanCode, bool state) +{ + if(scanCode > 0x1FF) { + _mouseState[scanCode & 0x03] = state; + } else { + scanCode &= 0xFF; + if(scanCode == 105) { + //Left + Right Ctrl + scanCode = 37; + } else if(scanCode == 62) { + //Left + Right Shift + scanCode = 50; + } else if(scanCode == 108) { + //Left + Right Alt + scanCode = 64; + } + _keyState[scanCode & 0xFF] = state; + } +} + +void LinuxKeyManager::ResetKeyState() +{ + memset(_mouseState, 0, sizeof(_mouseState)); + memset(_keyState, 0, sizeof(_keyState)); +} + +void LinuxKeyManager::SetDisabled(bool disabled) +{ + _disableAllKeys = disabled; +} diff --git a/Linux/LinuxKeyManager.h b/Linux/LinuxKeyManager.h new file mode 100644 index 0000000..975540a --- /dev/null +++ b/Linux/LinuxKeyManager.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include "../Core/IKeyManager.h" +#include "../Utilities/AutoResetEvent.h" + +class LinuxGameController; +class Console; + +struct KeyDefinition { + string name; + uint32_t keyCode; + string description; + string extDescription; +}; + +class LinuxKeyManager : public IKeyManager +{ +private: + shared_ptr _console; + std::vector> _controllers; + bool _keyState[0x200]; + bool _mouseState[0x03]; + std::unordered_map _keyNames; + std::unordered_map _keyCodes; + + std::thread _updateDeviceThread; + atomic _stopUpdateDeviceThread; + AutoResetEvent _stopSignal; + bool _disableAllKeys; + + void StartUpdateDeviceThread(); + void CheckForGamepads(bool logInformation); + +public: + LinuxKeyManager(shared_ptr console); + virtual ~LinuxKeyManager(); + + void RefreshState(); + bool IsKeyPressed(uint32_t key); + bool IsMouseButtonPressed(MouseButton button); + std::vector GetPressedKeys(); + string GetKeyName(uint32_t key); + uint32_t GetKeyCode(string keyName); + + void UpdateDevices(); + void SetKeyState(uint16_t scanCode, bool state); + void ResetKeyState(); + + void SetDisabled(bool disabled); +}; diff --git a/Linux/SdlRenderer.cpp b/Linux/SdlRenderer.cpp new file mode 100644 index 0000000..fa957aa --- /dev/null +++ b/Linux/SdlRenderer.cpp @@ -0,0 +1,219 @@ +#include "SdlRenderer.h" +#include "../Core/Console.h" +#include "../Core/Debugger.h" +#include "../Core/VideoRenderer.h" +#include "../Core/VideoDecoder.h" +#include "../Core/EmuSettings.h" +#include "../Core/MessageManager.h" + +SimpleLock SdlRenderer::_reinitLock; +SimpleLock SdlRenderer::_frameLock; + +SdlRenderer::SdlRenderer(shared_ptr console, void* windowHandle, bool registerAsMessageManager) : BaseRenderer(console, registerAsMessageManager), _windowHandle(windowHandle) +{ + _frameBuffer = nullptr; + SetScreenSize(512,480); +} + +SdlRenderer::~SdlRenderer() +{ + shared_ptr videoRenderer = _console->GetVideoRenderer(); + if(videoRenderer) { + videoRenderer->UnregisterRenderingDevice(this); + } + Cleanup(); +} + +void SdlRenderer::SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) +{ + //TODO: Implement exclusive fullscreen for Linux +} + +bool SdlRenderer::Init() +{ + auto log = [](const char* msg) { + MessageManager::Log(msg); + MessageManager::Log(SDL_GetError()); + }; + + if(SDL_InitSubSystem(SDL_INIT_VIDEO) != 0) { + log("[SDL] Failed to initialize video subsystem."); + return false; + }; + + _sdlWindow = SDL_CreateWindowFrom(_windowHandle); + if(!_sdlWindow) { + log("[SDL] Failed to create window from handle."); + return false; + } + + //Hack to make this work properly - otherwise SDL_CreateRenderer never returns + _sdlWindow->flags |= SDL_WINDOW_OPENGL; + + if(SDL_GL_LoadLibrary(NULL) != 0) { + log("[SDL] Failed to initialize OpenGL, attempting to continue with initialization."); + } + + uint32_t baseFlags = _vsyncEnabled ? SDL_RENDERER_PRESENTVSYNC : 0; + + _sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, baseFlags | SDL_RENDERER_ACCELERATED); + if(!_sdlRenderer) { + log("[SDL] Failed to create accelerated renderer."); + + MessageManager::Log("[SDL] Attempting to create software renderer..."); + _sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, baseFlags | SDL_RENDERER_SOFTWARE); + if(!_sdlRenderer) { + log("[SDL] Failed to create software renderer."); + return false; + } + } + + _sdlTexture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, _nesFrameWidth, _nesFrameHeight); + if(!_sdlTexture) { + log("[SDL] Failed to create texture."); + return false; + } + + _spriteFont.reset(new SpriteFont(_sdlRenderer, "Resources/Font.24.spritefont")); + _largeFont.reset(new SpriteFont(_sdlRenderer, "Resources/Font.64.spritefont")); + + SDL_SetWindowSize(_sdlWindow, _screenWidth, _screenHeight); + + _frameBuffer = new uint32_t[_nesFrameHeight*_nesFrameWidth]; + memset(_frameBuffer, 0, _nesFrameHeight*_nesFrameWidth*_bytesPerPixel); + + return true; +} + +void SdlRenderer::Cleanup() +{ + if(_sdlTexture) { + SDL_DestroyTexture(_sdlTexture); + _sdlTexture = nullptr; + } + if(_sdlRenderer) { + SDL_DestroyRenderer(_sdlRenderer); + _sdlRenderer = nullptr; + } + if(_frameBuffer) { + delete[] _frameBuffer; + _frameBuffer = nullptr; + } +} + +void SdlRenderer::Reset() +{ + Cleanup(); + if(Init()) { + _console->GetVideoRenderer()->RegisterRenderingDevice(this); + } else { + Cleanup(); + } +} + +void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height) +{ + ScreenSize screenSize = _console->GetVideoDecoder()->GetScreenSize(false); + + VideoConfig cfg = _console->GetSettings()->GetVideoConfig(); + if(_screenHeight != (uint32_t)screenSize.Height || _screenWidth != (uint32_t)screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _useBilinearInterpolation != cfg.UseBilinearInterpolation || _vsyncEnabled != cfg.VerticalSync) { + _reinitLock.Acquire(); + + _vsyncEnabled = cfg.VerticalSync; + _useBilinearInterpolation = cfg.UseBilinearInterpolation; + + _nesFrameHeight = height; + _nesFrameWidth = width; + _newFrameBufferSize = width*height; + + _screenHeight = screenSize.Height; + _screenWidth = screenSize.Width; + + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _useBilinearInterpolation ? "1" : "0"); + _screenBufferSize = _screenHeight*_screenWidth; + + Reset(); + _reinitLock.Release(); + } +} + +void SdlRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height) +{ + SetScreenSize(width, height); + _frameLock.Acquire(); + memcpy(_frameBuffer, frameBuffer, width*height*_bytesPerPixel); + _frameChanged = true; + _frameLock.Release(); +} + +void SdlRenderer::Render() +{ + if(!_sdlRenderer || !_sdlTexture) { + return; + } + + bool paused = _console->IsPaused() && _console->IsRunning(); + + if(_noUpdateCount > 10 || _frameChanged || paused || IsMessageShown()) { + auto lock = _reinitLock.AcquireSafe(); + + SDL_RenderClear(_sdlRenderer); + + uint8_t *textureBuffer; + int rowPitch; + SDL_LockTexture(_sdlTexture, nullptr, (void**)&textureBuffer, &rowPitch); + { + auto frameLock = _frameLock.AcquireSafe(); + uint32_t* ppuFrameBuffer = _frameBuffer; + for(uint32_t i = 0, iMax = _nesFrameHeight; i < iMax; i++) { + memcpy(textureBuffer, ppuFrameBuffer, _nesFrameWidth*_bytesPerPixel); + ppuFrameBuffer += _nesFrameWidth; + textureBuffer += rowPitch; + } + } + SDL_UnlockTexture(_sdlTexture); + + if(_frameChanged) { + _renderedFrameCount++; + _frameChanged = false; + } + + SDL_Rect source = {0, 0, (int)_nesFrameWidth, (int)_nesFrameHeight }; + SDL_Rect dest = {0, 0, (int)_screenWidth, (int)_screenHeight }; + SDL_RenderCopy(_sdlRenderer, _sdlTexture, &source, &dest); + + if(paused) { + DrawPauseScreen(); + } else if(_console->GetVideoDecoder()->IsRunning()) { + DrawCounters(); + } + + DrawToasts(); + + SDL_RenderPresent(_sdlRenderer); + } else { + _noUpdateCount++; + } +} + +void SdlRenderer::DrawPauseScreen() +{ + DrawString(L"I", 15, 15, 106, 90, 205, 168); + DrawString(L"I", 23, 15, 106, 90, 205, 168); +} + +void SdlRenderer::DrawString(std::wstring message, int x, int y, uint8_t r, uint8_t g, uint8_t b, uint8_t opacity) +{ + const wchar_t *text = message.c_str(); + _spriteFont->DrawString(_sdlRenderer, text, x, y, r, g, b); +} + +float SdlRenderer::MeasureString(std::wstring text) +{ + return _spriteFont->MeasureString(text.c_str()).x; +} + +bool SdlRenderer::ContainsCharacter(wchar_t character) +{ + return _spriteFont->ContainsCharacter(character); +} \ No newline at end of file diff --git a/Linux/SdlRenderer.h b/Linux/SdlRenderer.h new file mode 100644 index 0000000..23672b9 --- /dev/null +++ b/Linux/SdlRenderer.h @@ -0,0 +1,73 @@ +#pragma once +#include +#include "../Core/IRenderingDevice.h" +#include "../Utilities/SimpleLock.h" +#include "../Core/VideoRenderer.h" +#include "../Core/BaseRenderer.h" +#include "SpriteFont.h" + +struct SDL_Window +{ + const void *magic; + Uint32 id; + char *title; + SDL_Surface *icon; + int x, y; + int w, h; + int min_w, min_h; + int max_w, max_h; + Uint32 flags; +}; +typedef struct SDL_Window SDL_Window; + +class Console; + +class SdlRenderer : public IRenderingDevice, public BaseRenderer +{ +private: + void* _windowHandle; + SDL_Window* _sdlWindow = nullptr; + SDL_Renderer *_sdlRenderer = nullptr; + SDL_Texture *_sdlTexture = nullptr; + std::unique_ptr _spriteFont; + std::unique_ptr _largeFont; + + bool _useBilinearInterpolation = false; + + static SimpleLock _frameLock; + static SimpleLock _reinitLock; + uint32_t* _frameBuffer; + + const uint32_t _bytesPerPixel = 4; + uint32_t _screenBufferSize = 0; + + bool _frameChanged = true; + uint32_t _noUpdateCount = 0; + + uint32_t _nesFrameHeight = 0; + uint32_t _nesFrameWidth = 0; + uint32_t _newFrameBufferSize = 0; + + bool _vsyncEnabled = false; + + bool Init(); + void Cleanup(); + void SetScreenSize(uint32_t width, uint32_t height); + + void DrawPauseScreen(); + + float MeasureString(std::wstring text) override; + bool ContainsCharacter(wchar_t character) override; + +public: + SdlRenderer(shared_ptr console, void* windowHandle, bool registerAsMessageManager); + virtual ~SdlRenderer(); + + void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height) override; + void Render() override; + void Reset() override; + + void DrawString(std::wstring message, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255, uint8_t opacity = 255) override; + + void SetFullscreenMode(bool fullscreen, void* windowHandle, uint32_t monitorWidth, uint32_t monitorHeight) override; +}; \ No newline at end of file diff --git a/Linux/SdlSoundManager.cpp b/Linux/SdlSoundManager.cpp new file mode 100644 index 0000000..e9741e9 --- /dev/null +++ b/Linux/SdlSoundManager.cpp @@ -0,0 +1,195 @@ +#include "SdlSoundManager.h" +#include "../Core/EmuSettings.h" +#include "../Core/MessageManager.h" +#include "../Core/SoundMixer.h" +#include "../Core/Console.h" + +SdlSoundManager::SdlSoundManager(shared_ptr console) +{ + _console = console; + + if(InitializeAudio(44100, false)) { + _console->GetSoundMixer()->RegisterAudioDevice(this); + } +} + +SdlSoundManager::~SdlSoundManager() +{ + Release(); +} + +void SdlSoundManager::FillAudioBuffer(void *userData, uint8_t *stream, int len) +{ + SdlSoundManager* soundManager = (SdlSoundManager*)userData; + + soundManager->ReadFromBuffer(stream, len); +} + +void SdlSoundManager::Release() +{ + if(_audioDeviceID != 0) { + Stop(); + SDL_CloseAudioDevice(_audioDeviceID); + } + + if(_buffer) { + delete[] _buffer; + _buffer = nullptr; + _bufferSize = 0; + } +} + +bool SdlSoundManager::InitializeAudio(uint32_t sampleRate, bool isStereo) +{ + if(SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) { + MessageManager::Log("[Audio] Failed to initialize audio subsystem"); + return false; + } + + int isCapture = 0; + + _sampleRate = sampleRate; + _isStereo = isStereo; + _previousLatency = _console->GetSettings()->GetAudioConfig().AudioLatency; + + int bytesPerSample = 2 * (isStereo ? 2 : 1); + int32_t requestedByteLatency = (int32_t)((float)(sampleRate * _previousLatency) / 1000.0f * bytesPerSample); + _bufferSize = (int32_t)std::ceil((double)requestedByteLatency * 2 / 0x10000) * 0x10000; + _buffer = new uint8_t[_bufferSize]; + memset(_buffer, 0, _bufferSize); + + SDL_AudioSpec audioSpec; + SDL_memset(&audioSpec, 0, sizeof(audioSpec)); + audioSpec.freq = sampleRate; + audioSpec.format = AUDIO_S16SYS; //16-bit samples + audioSpec.channels = isStereo ? 2 : 1; + audioSpec.samples = 1024; + audioSpec.callback = &SdlSoundManager::FillAudioBuffer; + audioSpec.userdata = this; + + SDL_AudioSpec obtainedSpec; + + _audioDeviceID = SDL_OpenAudioDevice(_deviceName.empty() ? nullptr : _deviceName.c_str(), isCapture, &audioSpec, &obtainedSpec, 0); + if(_audioDeviceID == 0 && !_deviceName.empty()) { + MessageManager::Log("[Audio] Failed opening audio device '" + _deviceName + "', will retry with default device."); + _audioDeviceID = SDL_OpenAudioDevice(nullptr, isCapture, &audioSpec, &obtainedSpec, 0); + } + + _writePosition = 0; + _readPosition = 0; + + _needReset = false; + + return _audioDeviceID != 0; +} + +string SdlSoundManager::GetAvailableDevices() +{ + string deviceString; + for(string device : GetAvailableDeviceInfo()) { + deviceString += device + std::string("||"); + } + return deviceString; +} + +vector SdlSoundManager::GetAvailableDeviceInfo() +{ + vector deviceList; + int isCapture = 0; + int deviceCount = SDL_GetNumAudioDevices(isCapture); + + if(deviceCount == -1) { + //No devices found + } else { + for(int i = 0; i < deviceCount; i++) { + deviceList.push_back(SDL_GetAudioDeviceName(i, isCapture)); + } + } + + return deviceList; +} + +void SdlSoundManager::SetAudioDevice(string deviceName) +{ + if(deviceName.compare(_deviceName) != 0) { + _deviceName = deviceName; + _needReset = true; + } +} + +void SdlSoundManager::ReadFromBuffer(uint8_t* output, uint32_t len) +{ + if(_readPosition + len < _bufferSize) { + memcpy(output, _buffer+_readPosition, len); + _readPosition += len; + } else { + int remainingBytes = (_bufferSize - _readPosition); + memcpy(output, _buffer+_readPosition, remainingBytes); + memcpy(output+remainingBytes, _buffer, len - remainingBytes); + _readPosition = len - remainingBytes; + } + + if(_readPosition >= _writePosition && _readPosition - _writePosition < _bufferSize / 2) { + _bufferUnderrunEventCount++; + } +} + +void SdlSoundManager::WriteToBuffer(uint8_t* input, uint32_t len) +{ + if(_writePosition + len < _bufferSize) { + memcpy(_buffer+_writePosition, input, len); + _writePosition += len; + } else { + int remainingBytes = _bufferSize - _writePosition; + memcpy(_buffer+_writePosition, input, remainingBytes); + memcpy(_buffer, ((uint8_t*)input)+remainingBytes, len - remainingBytes); + _writePosition = len - remainingBytes; + } +} +void SdlSoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate, bool isStereo) +{ + uint32_t bytesPerSample = 2 * (isStereo ? 2 : 1); + uint32_t latency = _console->GetSettings()->GetAudioConfig().AudioLatency; + if(_sampleRate != sampleRate || _isStereo != isStereo || _needReset || _previousLatency != latency) { + Release(); + InitializeAudio(sampleRate, isStereo); + } + + WriteToBuffer((uint8_t*)soundBuffer, sampleCount * bytesPerSample); + + int32_t byteLatency = (int32_t)((float)(sampleRate * latency) / 1000.0f * bytesPerSample); + int32_t playWriteByteLatency = _writePosition - _readPosition; + if(playWriteByteLatency < 0) { + playWriteByteLatency = _bufferSize - _readPosition + _writePosition; + } + + if(playWriteByteLatency > byteLatency) { + //Start playing + SDL_PauseAudioDevice(_audioDeviceID, 0); + } +} + +void SdlSoundManager::Pause() +{ + SDL_PauseAudioDevice(_audioDeviceID, 1); +} + +void SdlSoundManager::Stop() +{ + Pause(); + + _readPosition = 0; + _writePosition = 0; + ResetStats(); +} + +void SdlSoundManager::ProcessEndOfFrame() +{ + ProcessLatency(_readPosition, _writePosition); + + uint32_t emulationSpeed = _console->GetSettings()->GetEmulationSpeed(); + if(_averageLatency > 0 && emulationSpeed <= 100 && emulationSpeed > 0 && std::abs(_averageLatency - _console->GetSettings()->GetAudioConfig().AudioLatency) > 50) { + //Latency is way off (over 50ms gap), stop audio & start again + Stop(); + } +} diff --git a/Linux/SdlSoundManager.h b/Linux/SdlSoundManager.h new file mode 100644 index 0000000..7a1ea93 --- /dev/null +++ b/Linux/SdlSoundManager.h @@ -0,0 +1,43 @@ +#pragma once +#include +#include "../Core/BaseSoundManager.h" + +class Console; + +class SdlSoundManager : public BaseSoundManager +{ +public: + SdlSoundManager(shared_ptr console); + ~SdlSoundManager(); + + void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize, uint32_t sampleRate, bool isStereo); + void Pause(); + void Stop(); + + void ProcessEndOfFrame(); + + string GetAvailableDevices(); + void SetAudioDevice(string deviceName); + +private: + vector GetAvailableDeviceInfo(); + bool InitializeAudio(uint32_t sampleRate, bool isStereo); + void Release(); + + static void FillAudioBuffer(void *userData, uint8_t *stream, int len); + + void ReadFromBuffer(uint8_t* output, uint32_t len); + void WriteToBuffer(uint8_t* output, uint32_t len); + +private: + shared_ptr _console; + SDL_AudioDeviceID _audioDeviceID; + string _deviceName; + bool _needReset = false; + + uint16_t _previousLatency = 0; + + uint8_t* _buffer = nullptr; + uint32_t _writePosition = 0; + uint32_t _readPosition = 0; +}; diff --git a/Linux/SpriteFont.cpp b/Linux/SpriteFont.cpp new file mode 100644 index 0000000..333069b --- /dev/null +++ b/Linux/SpriteFont.cpp @@ -0,0 +1,280 @@ +//-------------------------------------------------------------------------------------- +// This a heavily modified version of SpriteFont.cpp from DirectX Toolkit (MIT license) +// It strips down a lot of options not needed for Mesen and implements the minimum +// required to use .spritefont files in SDL. +//-------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------- +// File: SpriteFont.cpp +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include + +#include "SpriteFont.h" + +// Internal SpriteFont implementation class. +class SpriteFont::Impl +{ +public: + Impl(SDL_Renderer* renderer, BinaryReader* reader); + virtual ~Impl(); + + Glyph const* FindGlyph(wchar_t character) const; + + void SetDefaultCharacter(wchar_t character); + + template + void ForEachGlyph(wchar_t const* text, TAction action) const; + + + // Fields. + std::vector textureData; + SDL_Texture* texture; + std::vector glyphs; + Glyph const* defaultGlyph; + float lineSpacing; +}; + + +// Constants. +const XMFLOAT2 SpriteFont::Float2Zero(0, 0); + +static const char spriteFontMagic[] = "DXTKfont"; + + +// Comparison operators make our sorted glyph vector work with std::binary_search and lower_bound. +static inline bool operator< (wchar_t left, SpriteFont::Glyph const& right) +{ + return (uint32_t)left < right.Character; +} + +static inline bool operator< (SpriteFont::Glyph const& left, wchar_t right) +{ + return left.Character < (uint32_t)right; +} + +// Reads a SpriteFont from the binary format created by the MakeSpriteFont utility. +SpriteFont::Impl::Impl(SDL_Renderer* renderer, BinaryReader* reader) : + defaultGlyph(nullptr) +{ + // Validate the header. + for (char const* magic = spriteFontMagic; *magic; magic++) + { + if (reader->Read() != *magic) + { + throw std::runtime_error("Not a MakeSpriteFont output binary"); + } + } + + // Read the glyph data. + auto glyphCount = reader->Read(); + auto glyphData = reader->ReadArray(glyphCount); + + glyphs.assign(glyphData, glyphData + glyphCount); + + // Read font properties. + lineSpacing = reader->Read(); + + SetDefaultCharacter((wchar_t)reader->Read()); + + // Read the texture data. + auto textureWidth = reader->Read(); + auto textureHeight = reader->Read(); + reader->Read(); //DXGI_FORMAT, ignored - assume 32-bit RBGA + auto textureStride = reader->Read(); + auto textureRows = reader->Read(); + auto pixelData = reader->ReadArray(textureStride * textureRows); + + textureData.insert(textureData.end(), pixelData, pixelData+textureStride*textureHeight); + + SDL_Surface* surf = SDL_CreateRGBSurfaceFrom((void*)textureData.data(), textureWidth, textureHeight, 32, textureStride, 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000); + texture = SDL_CreateTextureFromSurface(renderer, surf); + SDL_FreeSurface(surf); +} + +SpriteFont::Impl::~Impl() +{ + SDL_DestroyTexture(texture); +} + +// Looks up the requested glyph, falling back to the default character if it is not in the font. +SpriteFont::Glyph const* SpriteFont::Impl::FindGlyph(wchar_t character) const +{ + auto glyph = std::lower_bound(glyphs.begin(), glyphs.end(), character); + + if (glyph != glyphs.end() && glyph->Character == (uint32_t)character) + { + return &*glyph; + } + + if (defaultGlyph) + { + return defaultGlyph; + } + + throw std::runtime_error("Character not in font"); +} + + +// Sets the missing-character fallback glyph. +void SpriteFont::Impl::SetDefaultCharacter(wchar_t character) +{ + defaultGlyph = nullptr; + + if (character) + { + defaultGlyph = FindGlyph(character); + } +} + + +// The core glyph layout algorithm, shared between DrawString and MeasureString. +template +void SpriteFont::Impl::ForEachGlyph(wchar_t const* text, TAction action) const +{ + float x = 0; + float y = 0; + + for (; *text; text++) + { + wchar_t character = *text; + + switch (character) + { + case '\r': + // Skip carriage returns. + continue; + + case '\n': + // New line. + x = 0; + y += lineSpacing; + break; + + default: + // Output this character. + auto glyph = FindGlyph(character); + + x += glyph->XOffset; + + if (x < 0) + x = 0; + + float advance = glyph->Subrect.right - glyph->Subrect.left + glyph->XAdvance; + + if(!std::iswspace(character) || (glyph->Subrect.right - glyph->Subrect.left) > 1 || (glyph->Subrect.bottom - glyph->Subrect.top ) > 1) { + action(glyph, x, y, advance); + } + + x += advance; + break; + } + } +} + + +// Construct from a binary file created by the MakeSpriteFont utility. +SpriteFont::SpriteFont(SDL_Renderer* renderer, string fileName) +{ + BinaryReader reader(fileName); + + pImpl = std::make_unique(renderer, &reader); +} + +// Move constructor. +SpriteFont::SpriteFont(SpriteFont&& moveFrom) + : pImpl(std::move(moveFrom.pImpl)) +{ +} + + +// Move assignment. +SpriteFont& SpriteFont::operator= (SpriteFont&& moveFrom) +{ + pImpl = std::move(moveFrom.pImpl); + return *this; +} + + +// Public destructor. +SpriteFont::~SpriteFont() +{ +} + +void SpriteFont::DrawString(SDL_Renderer *renderer, wchar_t const* text, int x, int y, uint8_t r, uint8_t g, uint8_t b) const +{ + SDL_SetTextureColorMod(pImpl->texture, r, g, b); + pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float offsetX, float offsetY, float advance) + { + int width = (int)(glyph->Subrect.right - glyph->Subrect.left); + int height = (int)(glyph->Subrect.bottom - glyph->Subrect.top); + + SDL_Rect source = {(int)glyph->Subrect.left, (int)glyph->Subrect.top, width, height}; + SDL_Rect dest = {x + (int)offsetX, y + (int)(offsetY + glyph->YOffset), width, height}; + SDL_RenderCopy(renderer, pImpl->texture, &source, &dest); + }); +} + +XMFLOAT2 SpriteFont::MeasureString(wchar_t const* text) const +{ + XMFLOAT2 result; + + pImpl->ForEachGlyph(text, [&](Glyph const* glyph, float x, float y, float advance) + { + float w = (float)(glyph->Subrect.right - glyph->Subrect.left); + float h = (float)(glyph->Subrect.bottom - glyph->Subrect.top) + glyph->YOffset; + + h = std::max(h, pImpl->lineSpacing); + + result.x = std::max(result.x, x + w); + result.y = std::max(result.y, y + h); + }); + + return result; +} + +// Spacing properties +float SpriteFont::GetLineSpacing() const +{ + return pImpl->lineSpacing; +} + +void SpriteFont::SetLineSpacing(float spacing) +{ + pImpl->lineSpacing = spacing; +} + +// Font properties +wchar_t SpriteFont::GetDefaultCharacter() const +{ + return pImpl->defaultGlyph ? (wchar_t)pImpl->defaultGlyph->Character : 0; +} + +void SpriteFont::SetDefaultCharacter(wchar_t character) +{ + pImpl->SetDefaultCharacter(character); +} + +bool SpriteFont::ContainsCharacter(wchar_t character) const +{ + return std::binary_search(pImpl->glyphs.begin(), pImpl->glyphs.end(), character); +} + +// Custom layout/rendering +SpriteFont::Glyph const* SpriteFont::FindGlyph(wchar_t character) const +{ + return pImpl->FindGlyph(character); +} diff --git a/Linux/SpriteFont.h b/Linux/SpriteFont.h new file mode 100644 index 0000000..7855980 --- /dev/null +++ b/Linux/SpriteFont.h @@ -0,0 +1,172 @@ +//-------------------------------------------------------------------------------------- +// This a heavily modified version of SpriteFont.h from DirectX Toolkit (MIT) +// It strips down a lot of options not needed for Mesen and implements the minimum +// required to use .spritefont files in SDL. +//-------------------------------------------------------------------------------------- + +//-------------------------------------------------------------------------------------- +// File: SpriteFont.h +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +// http://go.microsoft.com/fwlink/?LinkId=248929 +//-------------------------------------------------------------------------------------- + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +using std::string; + +struct RECT +{ + uint32_t left; + uint32_t top; + uint32_t right; + uint32_t bottom; +}; + +struct XMFLOAT2 +{ + float x = 0.0f; + float y = 0.0f; + + XMFLOAT2() {} + XMFLOAT2(float _x, float _y) : x(_x), y(_y) {} + explicit XMFLOAT2(const float *pArray) : x(pArray[0]), y(pArray[1]) {} + + XMFLOAT2& operator= (const XMFLOAT2& Float2) { x = Float2.x; y = Float2.y; return *this; } +}; + +class SpriteFont +{ +public: + struct Glyph; + + SpriteFont(SDL_Renderer* renderer, string fileName); + + SpriteFont(SpriteFont&& moveFrom); + SpriteFont& operator= (SpriteFont&& moveFrom); + + SpriteFont(SpriteFont const&) = delete; + SpriteFont& operator= (SpriteFont const&) = delete; + + virtual ~SpriteFont(); + + void DrawString(SDL_Renderer *renderer, wchar_t const* text, int x, int y, uint8_t r = 255, uint8_t g = 255, uint8_t b = 255) const; + + XMFLOAT2 MeasureString(wchar_t const* text) const; + + // Spacing properties + float GetLineSpacing() const; + void SetLineSpacing(float spacing); + + // Font properties + wchar_t GetDefaultCharacter() const; + void SetDefaultCharacter(wchar_t character); + + bool ContainsCharacter(wchar_t character) const; + + // Custom layout/rendering + Glyph const* FindGlyph(wchar_t character) const; + + // Describes a single character glyph. + struct Glyph + { + uint32_t Character; + RECT Subrect; + float XOffset; + float YOffset; + float XAdvance; + }; + + +private: + // Private implementation. + class Impl; + + std::unique_ptr pImpl; + + static const XMFLOAT2 Float2Zero; +}; + +class BinaryReader +{ +public: + BinaryReader(string fileName) : mPos(nullptr), mEnd(nullptr) + { + size_t dataSize; + + bool result = ReadEntireFile(fileName, mOwnedData, &dataSize); + if(!result) { + throw std::runtime_error( "BinaryReader" ); + } + + mPos = mOwnedData.get(); + mEnd = mOwnedData.get() + dataSize; + } + + // Reads a single value. + template T const& Read() + { + return *ReadArray(1); + } + + + // Reads an array of values. + template T const* ReadArray(size_t elementCount) + { + static_assert(std::is_pod::value, "Can only read plain-old-data types"); + + uint8_t const* newPos = mPos + sizeof(T) * elementCount; + + if (newPos < mPos) + throw std::overflow_error("ReadArray"); + + if (newPos > mEnd) + throw std::runtime_error("End of file"); + + auto result = reinterpret_cast(mPos); + + mPos = newPos; + + return result; + } + + // Lower level helper reads directly from the filesystem into memory. + static bool ReadEntireFile(string fileName, std::unique_ptr& data, size_t* dataSize) + { + std::ifstream file(fileName, std::ios::binary | std::ios::in); + file.seekg(0, std::ios::end); + size_t filesize = file.tellg(); + file.seekg(0, std::ios::beg); + + // Create enough space for the file data. + data.reset(new uint8_t[filesize]); + + // Read the data in. + file.read((char*)data.get(), filesize); + + *dataSize = filesize; + + return true; + } + +private: + // The data currently being read. + uint8_t const* mPos; + uint8_t const* mEnd; + + std::unique_ptr mOwnedData; +}; \ No newline at end of file diff --git a/Linux/libevdev/event-names.h b/Linux/libevdev/event-names.h new file mode 100644 index 0000000..78115d0 --- /dev/null +++ b/Linux/libevdev/event-names.h @@ -0,0 +1,1425 @@ +/* THIS FILE IS GENERATED, DO NOT EDIT */ + +#ifndef EVENT_NAMES_H +#define EVENT_NAMES_H + +static const char * const ev_map[EV_MAX + 1] = { + [EV_SYN] = "EV_SYN", + [EV_KEY] = "EV_KEY", + [EV_REL] = "EV_REL", + [EV_ABS] = "EV_ABS", + [EV_MSC] = "EV_MSC", + [EV_SW] = "EV_SW", + [EV_LED] = "EV_LED", + [EV_SND] = "EV_SND", + [EV_REP] = "EV_REP", + [EV_FF] = "EV_FF", + [EV_PWR] = "EV_PWR", + [EV_FF_STATUS] = "EV_FF_STATUS", + [EV_MAX] = "EV_MAX", +}; + +static const char * const rel_map[REL_MAX + 1] = { + [REL_X] = "REL_X", + [REL_Y] = "REL_Y", + [REL_Z] = "REL_Z", + [REL_RX] = "REL_RX", + [REL_RY] = "REL_RY", + [REL_RZ] = "REL_RZ", + [REL_HWHEEL] = "REL_HWHEEL", + [REL_DIAL] = "REL_DIAL", + [REL_WHEEL] = "REL_WHEEL", + [REL_MISC] = "REL_MISC", + [REL_MAX] = "REL_MAX", +}; + +static const char * const abs_map[ABS_MAX + 1] = { + [ABS_X] = "ABS_X", + [ABS_Y] = "ABS_Y", + [ABS_Z] = "ABS_Z", + [ABS_RX] = "ABS_RX", + [ABS_RY] = "ABS_RY", + [ABS_RZ] = "ABS_RZ", + [ABS_THROTTLE] = "ABS_THROTTLE", + [ABS_RUDDER] = "ABS_RUDDER", + [ABS_WHEEL] = "ABS_WHEEL", + [ABS_GAS] = "ABS_GAS", + [ABS_BRAKE] = "ABS_BRAKE", + [ABS_HAT0X] = "ABS_HAT0X", + [ABS_HAT0Y] = "ABS_HAT0Y", + [ABS_HAT1X] = "ABS_HAT1X", + [ABS_HAT1Y] = "ABS_HAT1Y", + [ABS_HAT2X] = "ABS_HAT2X", + [ABS_HAT2Y] = "ABS_HAT2Y", + [ABS_HAT3X] = "ABS_HAT3X", + [ABS_HAT3Y] = "ABS_HAT3Y", + [ABS_PRESSURE] = "ABS_PRESSURE", + [ABS_DISTANCE] = "ABS_DISTANCE", + [ABS_TILT_X] = "ABS_TILT_X", + [ABS_TILT_Y] = "ABS_TILT_Y", + [ABS_TOOL_WIDTH] = "ABS_TOOL_WIDTH", + [ABS_VOLUME] = "ABS_VOLUME", + [ABS_MISC] = "ABS_MISC", + [ABS_MT_SLOT] = "ABS_MT_SLOT", + [ABS_MT_TOUCH_MAJOR] = "ABS_MT_TOUCH_MAJOR", + [ABS_MT_TOUCH_MINOR] = "ABS_MT_TOUCH_MINOR", + [ABS_MT_WIDTH_MAJOR] = "ABS_MT_WIDTH_MAJOR", + [ABS_MT_WIDTH_MINOR] = "ABS_MT_WIDTH_MINOR", + [ABS_MT_ORIENTATION] = "ABS_MT_ORIENTATION", + [ABS_MT_POSITION_X] = "ABS_MT_POSITION_X", + [ABS_MT_POSITION_Y] = "ABS_MT_POSITION_Y", + [ABS_MT_TOOL_TYPE] = "ABS_MT_TOOL_TYPE", + [ABS_MT_BLOB_ID] = "ABS_MT_BLOB_ID", + [ABS_MT_TRACKING_ID] = "ABS_MT_TRACKING_ID", + [ABS_MT_PRESSURE] = "ABS_MT_PRESSURE", + [ABS_MT_DISTANCE] = "ABS_MT_DISTANCE", + [ABS_MT_TOOL_X] = "ABS_MT_TOOL_X", + [ABS_MT_TOOL_Y] = "ABS_MT_TOOL_Y", + [ABS_MAX] = "ABS_MAX", +}; + +static const char * const key_map[KEY_MAX + 1] = { + [KEY_RESERVED] = "KEY_RESERVED", + [KEY_ESC] = "KEY_ESC", + [KEY_1] = "KEY_1", + [KEY_2] = "KEY_2", + [KEY_3] = "KEY_3", + [KEY_4] = "KEY_4", + [KEY_5] = "KEY_5", + [KEY_6] = "KEY_6", + [KEY_7] = "KEY_7", + [KEY_8] = "KEY_8", + [KEY_9] = "KEY_9", + [KEY_0] = "KEY_0", + [KEY_MINUS] = "KEY_MINUS", + [KEY_EQUAL] = "KEY_EQUAL", + [KEY_BACKSPACE] = "KEY_BACKSPACE", + [KEY_TAB] = "KEY_TAB", + [KEY_Q] = "KEY_Q", + [KEY_W] = "KEY_W", + [KEY_E] = "KEY_E", + [KEY_R] = "KEY_R", + [KEY_T] = "KEY_T", + [KEY_Y] = "KEY_Y", + [KEY_U] = "KEY_U", + [KEY_I] = "KEY_I", + [KEY_O] = "KEY_O", + [KEY_P] = "KEY_P", + [KEY_LEFTBRACE] = "KEY_LEFTBRACE", + [KEY_RIGHTBRACE] = "KEY_RIGHTBRACE", + [KEY_ENTER] = "KEY_ENTER", + [KEY_LEFTCTRL] = "KEY_LEFTCTRL", + [KEY_A] = "KEY_A", + [KEY_S] = "KEY_S", + [KEY_D] = "KEY_D", + [KEY_F] = "KEY_F", + [KEY_G] = "KEY_G", + [KEY_H] = "KEY_H", + [KEY_J] = "KEY_J", + [KEY_K] = "KEY_K", + [KEY_L] = "KEY_L", + [KEY_SEMICOLON] = "KEY_SEMICOLON", + [KEY_APOSTROPHE] = "KEY_APOSTROPHE", + [KEY_GRAVE] = "KEY_GRAVE", + [KEY_LEFTSHIFT] = "KEY_LEFTSHIFT", + [KEY_BACKSLASH] = "KEY_BACKSLASH", + [KEY_Z] = "KEY_Z", + [KEY_X] = "KEY_X", + [KEY_C] = "KEY_C", + [KEY_V] = "KEY_V", + [KEY_B] = "KEY_B", + [KEY_N] = "KEY_N", + [KEY_M] = "KEY_M", + [KEY_COMMA] = "KEY_COMMA", + [KEY_DOT] = "KEY_DOT", + [KEY_SLASH] = "KEY_SLASH", + [KEY_RIGHTSHIFT] = "KEY_RIGHTSHIFT", + [KEY_KPASTERISK] = "KEY_KPASTERISK", + [KEY_LEFTALT] = "KEY_LEFTALT", + [KEY_SPACE] = "KEY_SPACE", + [KEY_CAPSLOCK] = "KEY_CAPSLOCK", + [KEY_F1] = "KEY_F1", + [KEY_F2] = "KEY_F2", + [KEY_F3] = "KEY_F3", + [KEY_F4] = "KEY_F4", + [KEY_F5] = "KEY_F5", + [KEY_F6] = "KEY_F6", + [KEY_F7] = "KEY_F7", + [KEY_F8] = "KEY_F8", + [KEY_F9] = "KEY_F9", + [KEY_F10] = "KEY_F10", + [KEY_NUMLOCK] = "KEY_NUMLOCK", + [KEY_SCROLLLOCK] = "KEY_SCROLLLOCK", + [KEY_KP7] = "KEY_KP7", + [KEY_KP8] = "KEY_KP8", + [KEY_KP9] = "KEY_KP9", + [KEY_KPMINUS] = "KEY_KPMINUS", + [KEY_KP4] = "KEY_KP4", + [KEY_KP5] = "KEY_KP5", + [KEY_KP6] = "KEY_KP6", + [KEY_KPPLUS] = "KEY_KPPLUS", + [KEY_KP1] = "KEY_KP1", + [KEY_KP2] = "KEY_KP2", + [KEY_KP3] = "KEY_KP3", + [KEY_KP0] = "KEY_KP0", + [KEY_KPDOT] = "KEY_KPDOT", + [KEY_ZENKAKUHANKAKU] = "KEY_ZENKAKUHANKAKU", + [KEY_102ND] = "KEY_102ND", + [KEY_F11] = "KEY_F11", + [KEY_F12] = "KEY_F12", + [KEY_RO] = "KEY_RO", + [KEY_KATAKANA] = "KEY_KATAKANA", + [KEY_HIRAGANA] = "KEY_HIRAGANA", + [KEY_HENKAN] = "KEY_HENKAN", + [KEY_KATAKANAHIRAGANA] = "KEY_KATAKANAHIRAGANA", + [KEY_MUHENKAN] = "KEY_MUHENKAN", + [KEY_KPJPCOMMA] = "KEY_KPJPCOMMA", + [KEY_KPENTER] = "KEY_KPENTER", + [KEY_RIGHTCTRL] = "KEY_RIGHTCTRL", + [KEY_KPSLASH] = "KEY_KPSLASH", + [KEY_SYSRQ] = "KEY_SYSRQ", + [KEY_RIGHTALT] = "KEY_RIGHTALT", + [KEY_LINEFEED] = "KEY_LINEFEED", + [KEY_HOME] = "KEY_HOME", + [KEY_UP] = "KEY_UP", + [KEY_PAGEUP] = "KEY_PAGEUP", + [KEY_LEFT] = "KEY_LEFT", + [KEY_RIGHT] = "KEY_RIGHT", + [KEY_END] = "KEY_END", + [KEY_DOWN] = "KEY_DOWN", + [KEY_PAGEDOWN] = "KEY_PAGEDOWN", + [KEY_INSERT] = "KEY_INSERT", + [KEY_DELETE] = "KEY_DELETE", + [KEY_MACRO] = "KEY_MACRO", + [KEY_MUTE] = "KEY_MUTE", + [KEY_VOLUMEDOWN] = "KEY_VOLUMEDOWN", + [KEY_VOLUMEUP] = "KEY_VOLUMEUP", + [KEY_POWER] = "KEY_POWER", + [KEY_KPEQUAL] = "KEY_KPEQUAL", + [KEY_KPPLUSMINUS] = "KEY_KPPLUSMINUS", + [KEY_PAUSE] = "KEY_PAUSE", + [KEY_SCALE] = "KEY_SCALE", + [KEY_KPCOMMA] = "KEY_KPCOMMA", + [KEY_HANGEUL] = "KEY_HANGEUL", + [KEY_HANJA] = "KEY_HANJA", + [KEY_YEN] = "KEY_YEN", + [KEY_LEFTMETA] = "KEY_LEFTMETA", + [KEY_RIGHTMETA] = "KEY_RIGHTMETA", + [KEY_COMPOSE] = "KEY_COMPOSE", + [KEY_STOP] = "KEY_STOP", + [KEY_AGAIN] = "KEY_AGAIN", + [KEY_PROPS] = "KEY_PROPS", + [KEY_UNDO] = "KEY_UNDO", + [KEY_FRONT] = "KEY_FRONT", + [KEY_COPY] = "KEY_COPY", + [KEY_OPEN] = "KEY_OPEN", + [KEY_PASTE] = "KEY_PASTE", + [KEY_FIND] = "KEY_FIND", + [KEY_CUT] = "KEY_CUT", + [KEY_HELP] = "KEY_HELP", + [KEY_MENU] = "KEY_MENU", + [KEY_CALC] = "KEY_CALC", + [KEY_SETUP] = "KEY_SETUP", + [KEY_SLEEP] = "KEY_SLEEP", + [KEY_WAKEUP] = "KEY_WAKEUP", + [KEY_FILE] = "KEY_FILE", + [KEY_SENDFILE] = "KEY_SENDFILE", + [KEY_DELETEFILE] = "KEY_DELETEFILE", + [KEY_XFER] = "KEY_XFER", + [KEY_PROG1] = "KEY_PROG1", + [KEY_PROG2] = "KEY_PROG2", + [KEY_WWW] = "KEY_WWW", + [KEY_MSDOS] = "KEY_MSDOS", + [KEY_COFFEE] = "KEY_COFFEE", + [KEY_ROTATE_DISPLAY] = "KEY_ROTATE_DISPLAY", + [KEY_CYCLEWINDOWS] = "KEY_CYCLEWINDOWS", + [KEY_MAIL] = "KEY_MAIL", + [KEY_BOOKMARKS] = "KEY_BOOKMARKS", + [KEY_COMPUTER] = "KEY_COMPUTER", + [KEY_BACK] = "KEY_BACK", + [KEY_FORWARD] = "KEY_FORWARD", + [KEY_CLOSECD] = "KEY_CLOSECD", + [KEY_EJECTCD] = "KEY_EJECTCD", + [KEY_EJECTCLOSECD] = "KEY_EJECTCLOSECD", + [KEY_NEXTSONG] = "KEY_NEXTSONG", + [KEY_PLAYPAUSE] = "KEY_PLAYPAUSE", + [KEY_PREVIOUSSONG] = "KEY_PREVIOUSSONG", + [KEY_STOPCD] = "KEY_STOPCD", + [KEY_RECORD] = "KEY_RECORD", + [KEY_REWIND] = "KEY_REWIND", + [KEY_PHONE] = "KEY_PHONE", + [KEY_ISO] = "KEY_ISO", + [KEY_CONFIG] = "KEY_CONFIG", + [KEY_HOMEPAGE] = "KEY_HOMEPAGE", + [KEY_REFRESH] = "KEY_REFRESH", + [KEY_EXIT] = "KEY_EXIT", + [KEY_MOVE] = "KEY_MOVE", + [KEY_EDIT] = "KEY_EDIT", + [KEY_SCROLLUP] = "KEY_SCROLLUP", + [KEY_SCROLLDOWN] = "KEY_SCROLLDOWN", + [KEY_KPLEFTPAREN] = "KEY_KPLEFTPAREN", + [KEY_KPRIGHTPAREN] = "KEY_KPRIGHTPAREN", + [KEY_NEW] = "KEY_NEW", + [KEY_REDO] = "KEY_REDO", + [KEY_F13] = "KEY_F13", + [KEY_F14] = "KEY_F14", + [KEY_F15] = "KEY_F15", + [KEY_F16] = "KEY_F16", + [KEY_F17] = "KEY_F17", + [KEY_F18] = "KEY_F18", + [KEY_F19] = "KEY_F19", + [KEY_F20] = "KEY_F20", + [KEY_F21] = "KEY_F21", + [KEY_F22] = "KEY_F22", + [KEY_F23] = "KEY_F23", + [KEY_F24] = "KEY_F24", + [KEY_PLAYCD] = "KEY_PLAYCD", + [KEY_PAUSECD] = "KEY_PAUSECD", + [KEY_PROG3] = "KEY_PROG3", + [KEY_PROG4] = "KEY_PROG4", + [KEY_DASHBOARD] = "KEY_DASHBOARD", + [KEY_SUSPEND] = "KEY_SUSPEND", + [KEY_CLOSE] = "KEY_CLOSE", + [KEY_PLAY] = "KEY_PLAY", + [KEY_FASTFORWARD] = "KEY_FASTFORWARD", + [KEY_BASSBOOST] = "KEY_BASSBOOST", + [KEY_PRINT] = "KEY_PRINT", + [KEY_HP] = "KEY_HP", + [KEY_CAMERA] = "KEY_CAMERA", + [KEY_SOUND] = "KEY_SOUND", + [KEY_QUESTION] = "KEY_QUESTION", + [KEY_EMAIL] = "KEY_EMAIL", + [KEY_CHAT] = "KEY_CHAT", + [KEY_SEARCH] = "KEY_SEARCH", + [KEY_CONNECT] = "KEY_CONNECT", + [KEY_FINANCE] = "KEY_FINANCE", + [KEY_SPORT] = "KEY_SPORT", + [KEY_SHOP] = "KEY_SHOP", + [KEY_ALTERASE] = "KEY_ALTERASE", + [KEY_CANCEL] = "KEY_CANCEL", + [KEY_BRIGHTNESSDOWN] = "KEY_BRIGHTNESSDOWN", + [KEY_BRIGHTNESSUP] = "KEY_BRIGHTNESSUP", + [KEY_MEDIA] = "KEY_MEDIA", + [KEY_SWITCHVIDEOMODE] = "KEY_SWITCHVIDEOMODE", + [KEY_KBDILLUMTOGGLE] = "KEY_KBDILLUMTOGGLE", + [KEY_KBDILLUMDOWN] = "KEY_KBDILLUMDOWN", + [KEY_KBDILLUMUP] = "KEY_KBDILLUMUP", + [KEY_SEND] = "KEY_SEND", + [KEY_REPLY] = "KEY_REPLY", + [KEY_FORWARDMAIL] = "KEY_FORWARDMAIL", + [KEY_SAVE] = "KEY_SAVE", + [KEY_DOCUMENTS] = "KEY_DOCUMENTS", + [KEY_BATTERY] = "KEY_BATTERY", + [KEY_BLUETOOTH] = "KEY_BLUETOOTH", + [KEY_WLAN] = "KEY_WLAN", + [KEY_UWB] = "KEY_UWB", + [KEY_UNKNOWN] = "KEY_UNKNOWN", + [KEY_VIDEO_NEXT] = "KEY_VIDEO_NEXT", + [KEY_VIDEO_PREV] = "KEY_VIDEO_PREV", + [KEY_BRIGHTNESS_CYCLE] = "KEY_BRIGHTNESS_CYCLE", + [KEY_BRIGHTNESS_AUTO] = "KEY_BRIGHTNESS_AUTO", + [KEY_DISPLAY_OFF] = "KEY_DISPLAY_OFF", + [KEY_WWAN] = "KEY_WWAN", + [KEY_RFKILL] = "KEY_RFKILL", + [KEY_MICMUTE] = "KEY_MICMUTE", + [KEY_OK] = "KEY_OK", + [KEY_SELECT] = "KEY_SELECT", + [KEY_GOTO] = "KEY_GOTO", + [KEY_CLEAR] = "KEY_CLEAR", + [KEY_POWER2] = "KEY_POWER2", + [KEY_OPTION] = "KEY_OPTION", + [KEY_INFO] = "KEY_INFO", + [KEY_TIME] = "KEY_TIME", + [KEY_VENDOR] = "KEY_VENDOR", + [KEY_ARCHIVE] = "KEY_ARCHIVE", + [KEY_PROGRAM] = "KEY_PROGRAM", + [KEY_CHANNEL] = "KEY_CHANNEL", + [KEY_FAVORITES] = "KEY_FAVORITES", + [KEY_EPG] = "KEY_EPG", + [KEY_PVR] = "KEY_PVR", + [KEY_MHP] = "KEY_MHP", + [KEY_LANGUAGE] = "KEY_LANGUAGE", + [KEY_TITLE] = "KEY_TITLE", + [KEY_SUBTITLE] = "KEY_SUBTITLE", + [KEY_ANGLE] = "KEY_ANGLE", + [KEY_ZOOM] = "KEY_ZOOM", + [KEY_MODE] = "KEY_MODE", + [KEY_KEYBOARD] = "KEY_KEYBOARD", + [KEY_SCREEN] = "KEY_SCREEN", + [KEY_PC] = "KEY_PC", + [KEY_TV] = "KEY_TV", + [KEY_TV2] = "KEY_TV2", + [KEY_VCR] = "KEY_VCR", + [KEY_VCR2] = "KEY_VCR2", + [KEY_SAT] = "KEY_SAT", + [KEY_SAT2] = "KEY_SAT2", + [KEY_CD] = "KEY_CD", + [KEY_TAPE] = "KEY_TAPE", + [KEY_RADIO] = "KEY_RADIO", + [KEY_TUNER] = "KEY_TUNER", + [KEY_PLAYER] = "KEY_PLAYER", + [KEY_TEXT] = "KEY_TEXT", + [KEY_DVD] = "KEY_DVD", + [KEY_AUX] = "KEY_AUX", + [KEY_MP3] = "KEY_MP3", + [KEY_AUDIO] = "KEY_AUDIO", + [KEY_VIDEO] = "KEY_VIDEO", + [KEY_DIRECTORY] = "KEY_DIRECTORY", + [KEY_LIST] = "KEY_LIST", + [KEY_MEMO] = "KEY_MEMO", + [KEY_CALENDAR] = "KEY_CALENDAR", + [KEY_RED] = "KEY_RED", + [KEY_GREEN] = "KEY_GREEN", + [KEY_YELLOW] = "KEY_YELLOW", + [KEY_BLUE] = "KEY_BLUE", + [KEY_CHANNELUP] = "KEY_CHANNELUP", + [KEY_CHANNELDOWN] = "KEY_CHANNELDOWN", + [KEY_FIRST] = "KEY_FIRST", + [KEY_LAST] = "KEY_LAST", + [KEY_AB] = "KEY_AB", + [KEY_NEXT] = "KEY_NEXT", + [KEY_RESTART] = "KEY_RESTART", + [KEY_SLOW] = "KEY_SLOW", + [KEY_SHUFFLE] = "KEY_SHUFFLE", + [KEY_BREAK] = "KEY_BREAK", + [KEY_PREVIOUS] = "KEY_PREVIOUS", + [KEY_DIGITS] = "KEY_DIGITS", + [KEY_TEEN] = "KEY_TEEN", + [KEY_TWEN] = "KEY_TWEN", + [KEY_VIDEOPHONE] = "KEY_VIDEOPHONE", + [KEY_GAMES] = "KEY_GAMES", + [KEY_ZOOMIN] = "KEY_ZOOMIN", + [KEY_ZOOMOUT] = "KEY_ZOOMOUT", + [KEY_ZOOMRESET] = "KEY_ZOOMRESET", + [KEY_WORDPROCESSOR] = "KEY_WORDPROCESSOR", + [KEY_EDITOR] = "KEY_EDITOR", + [KEY_SPREADSHEET] = "KEY_SPREADSHEET", + [KEY_GRAPHICSEDITOR] = "KEY_GRAPHICSEDITOR", + [KEY_PRESENTATION] = "KEY_PRESENTATION", + [KEY_DATABASE] = "KEY_DATABASE", + [KEY_NEWS] = "KEY_NEWS", + [KEY_VOICEMAIL] = "KEY_VOICEMAIL", + [KEY_ADDRESSBOOK] = "KEY_ADDRESSBOOK", + [KEY_MESSENGER] = "KEY_MESSENGER", + [KEY_DISPLAYTOGGLE] = "KEY_DISPLAYTOGGLE", + [KEY_SPELLCHECK] = "KEY_SPELLCHECK", + [KEY_LOGOFF] = "KEY_LOGOFF", + [KEY_DOLLAR] = "KEY_DOLLAR", + [KEY_EURO] = "KEY_EURO", + [KEY_FRAMEBACK] = "KEY_FRAMEBACK", + [KEY_FRAMEFORWARD] = "KEY_FRAMEFORWARD", + [KEY_CONTEXT_MENU] = "KEY_CONTEXT_MENU", + [KEY_MEDIA_REPEAT] = "KEY_MEDIA_REPEAT", + [KEY_10CHANNELSUP] = "KEY_10CHANNELSUP", + [KEY_10CHANNELSDOWN] = "KEY_10CHANNELSDOWN", + [KEY_IMAGES] = "KEY_IMAGES", + [KEY_DEL_EOL] = "KEY_DEL_EOL", + [KEY_DEL_EOS] = "KEY_DEL_EOS", + [KEY_INS_LINE] = "KEY_INS_LINE", + [KEY_DEL_LINE] = "KEY_DEL_LINE", + [KEY_FN] = "KEY_FN", + [KEY_FN_ESC] = "KEY_FN_ESC", + [KEY_FN_F1] = "KEY_FN_F1", + [KEY_FN_F2] = "KEY_FN_F2", + [KEY_FN_F3] = "KEY_FN_F3", + [KEY_FN_F4] = "KEY_FN_F4", + [KEY_FN_F5] = "KEY_FN_F5", + [KEY_FN_F6] = "KEY_FN_F6", + [KEY_FN_F7] = "KEY_FN_F7", + [KEY_FN_F8] = "KEY_FN_F8", + [KEY_FN_F9] = "KEY_FN_F9", + [KEY_FN_F10] = "KEY_FN_F10", + [KEY_FN_F11] = "KEY_FN_F11", + [KEY_FN_F12] = "KEY_FN_F12", + [KEY_FN_1] = "KEY_FN_1", + [KEY_FN_2] = "KEY_FN_2", + [KEY_FN_D] = "KEY_FN_D", + [KEY_FN_E] = "KEY_FN_E", + [KEY_FN_F] = "KEY_FN_F", + [KEY_FN_S] = "KEY_FN_S", + [KEY_FN_B] = "KEY_FN_B", + [KEY_BRL_DOT1] = "KEY_BRL_DOT1", + [KEY_BRL_DOT2] = "KEY_BRL_DOT2", + [KEY_BRL_DOT3] = "KEY_BRL_DOT3", + [KEY_BRL_DOT4] = "KEY_BRL_DOT4", + [KEY_BRL_DOT5] = "KEY_BRL_DOT5", + [KEY_BRL_DOT6] = "KEY_BRL_DOT6", + [KEY_BRL_DOT7] = "KEY_BRL_DOT7", + [KEY_BRL_DOT8] = "KEY_BRL_DOT8", + [KEY_BRL_DOT9] = "KEY_BRL_DOT9", + [KEY_BRL_DOT10] = "KEY_BRL_DOT10", + [KEY_NUMERIC_0] = "KEY_NUMERIC_0", + [KEY_NUMERIC_1] = "KEY_NUMERIC_1", + [KEY_NUMERIC_2] = "KEY_NUMERIC_2", + [KEY_NUMERIC_3] = "KEY_NUMERIC_3", + [KEY_NUMERIC_4] = "KEY_NUMERIC_4", + [KEY_NUMERIC_5] = "KEY_NUMERIC_5", + [KEY_NUMERIC_6] = "KEY_NUMERIC_6", + [KEY_NUMERIC_7] = "KEY_NUMERIC_7", + [KEY_NUMERIC_8] = "KEY_NUMERIC_8", + [KEY_NUMERIC_9] = "KEY_NUMERIC_9", + [KEY_NUMERIC_STAR] = "KEY_NUMERIC_STAR", + [KEY_NUMERIC_POUND] = "KEY_NUMERIC_POUND", + [KEY_NUMERIC_A] = "KEY_NUMERIC_A", + [KEY_NUMERIC_B] = "KEY_NUMERIC_B", + [KEY_NUMERIC_C] = "KEY_NUMERIC_C", + [KEY_NUMERIC_D] = "KEY_NUMERIC_D", + [KEY_CAMERA_FOCUS] = "KEY_CAMERA_FOCUS", + [KEY_WPS_BUTTON] = "KEY_WPS_BUTTON", + [KEY_TOUCHPAD_TOGGLE] = "KEY_TOUCHPAD_TOGGLE", + [KEY_TOUCHPAD_ON] = "KEY_TOUCHPAD_ON", + [KEY_TOUCHPAD_OFF] = "KEY_TOUCHPAD_OFF", + [KEY_CAMERA_ZOOMIN] = "KEY_CAMERA_ZOOMIN", + [KEY_CAMERA_ZOOMOUT] = "KEY_CAMERA_ZOOMOUT", + [KEY_CAMERA_UP] = "KEY_CAMERA_UP", + [KEY_CAMERA_DOWN] = "KEY_CAMERA_DOWN", + [KEY_CAMERA_LEFT] = "KEY_CAMERA_LEFT", + [KEY_CAMERA_RIGHT] = "KEY_CAMERA_RIGHT", + [KEY_ATTENDANT_ON] = "KEY_ATTENDANT_ON", + [KEY_ATTENDANT_OFF] = "KEY_ATTENDANT_OFF", + [KEY_ATTENDANT_TOGGLE] = "KEY_ATTENDANT_TOGGLE", + [KEY_LIGHTS_TOGGLE] = "KEY_LIGHTS_TOGGLE", + [KEY_ALS_TOGGLE] = "KEY_ALS_TOGGLE", + [KEY_BUTTONCONFIG] = "KEY_BUTTONCONFIG", + [KEY_TASKMANAGER] = "KEY_TASKMANAGER", + [KEY_JOURNAL] = "KEY_JOURNAL", + [KEY_CONTROLPANEL] = "KEY_CONTROLPANEL", + [KEY_APPSELECT] = "KEY_APPSELECT", + [KEY_SCREENSAVER] = "KEY_SCREENSAVER", + [KEY_VOICECOMMAND] = "KEY_VOICECOMMAND", + [KEY_BRIGHTNESS_MIN] = "KEY_BRIGHTNESS_MIN", + [KEY_BRIGHTNESS_MAX] = "KEY_BRIGHTNESS_MAX", + [KEY_KBDINPUTASSIST_PREV] = "KEY_KBDINPUTASSIST_PREV", + [KEY_KBDINPUTASSIST_NEXT] = "KEY_KBDINPUTASSIST_NEXT", + [KEY_KBDINPUTASSIST_PREVGROUP] = "KEY_KBDINPUTASSIST_PREVGROUP", + [KEY_KBDINPUTASSIST_NEXTGROUP] = "KEY_KBDINPUTASSIST_NEXTGROUP", + [KEY_KBDINPUTASSIST_ACCEPT] = "KEY_KBDINPUTASSIST_ACCEPT", + [KEY_KBDINPUTASSIST_CANCEL] = "KEY_KBDINPUTASSIST_CANCEL", + [KEY_MAX] = "KEY_MAX", + [BTN_DPAD_UP] = "BTN_DPAD_UP", + [BTN_DPAD_DOWN] = "BTN_DPAD_DOWN", + [BTN_DPAD_LEFT] = "BTN_DPAD_LEFT", + [BTN_DPAD_RIGHT] = "BTN_DPAD_RIGHT", + [BTN_TRIGGER_HAPPY1] = "BTN_TRIGGER_HAPPY1", + [BTN_TRIGGER_HAPPY2] = "BTN_TRIGGER_HAPPY2", + [BTN_TRIGGER_HAPPY3] = "BTN_TRIGGER_HAPPY3", + [BTN_TRIGGER_HAPPY4] = "BTN_TRIGGER_HAPPY4", + [BTN_TRIGGER_HAPPY5] = "BTN_TRIGGER_HAPPY5", + [BTN_TRIGGER_HAPPY6] = "BTN_TRIGGER_HAPPY6", + [BTN_TRIGGER_HAPPY7] = "BTN_TRIGGER_HAPPY7", + [BTN_TRIGGER_HAPPY8] = "BTN_TRIGGER_HAPPY8", + [BTN_TRIGGER_HAPPY9] = "BTN_TRIGGER_HAPPY9", + [BTN_TRIGGER_HAPPY10] = "BTN_TRIGGER_HAPPY10", + [BTN_TRIGGER_HAPPY11] = "BTN_TRIGGER_HAPPY11", + [BTN_TRIGGER_HAPPY12] = "BTN_TRIGGER_HAPPY12", + [BTN_TRIGGER_HAPPY13] = "BTN_TRIGGER_HAPPY13", + [BTN_TRIGGER_HAPPY14] = "BTN_TRIGGER_HAPPY14", + [BTN_TRIGGER_HAPPY15] = "BTN_TRIGGER_HAPPY15", + [BTN_TRIGGER_HAPPY16] = "BTN_TRIGGER_HAPPY16", + [BTN_TRIGGER_HAPPY17] = "BTN_TRIGGER_HAPPY17", + [BTN_TRIGGER_HAPPY18] = "BTN_TRIGGER_HAPPY18", + [BTN_TRIGGER_HAPPY19] = "BTN_TRIGGER_HAPPY19", + [BTN_TRIGGER_HAPPY20] = "BTN_TRIGGER_HAPPY20", + [BTN_TRIGGER_HAPPY21] = "BTN_TRIGGER_HAPPY21", + [BTN_TRIGGER_HAPPY22] = "BTN_TRIGGER_HAPPY22", + [BTN_TRIGGER_HAPPY23] = "BTN_TRIGGER_HAPPY23", + [BTN_TRIGGER_HAPPY24] = "BTN_TRIGGER_HAPPY24", + [BTN_TRIGGER_HAPPY25] = "BTN_TRIGGER_HAPPY25", + [BTN_TRIGGER_HAPPY26] = "BTN_TRIGGER_HAPPY26", + [BTN_TRIGGER_HAPPY27] = "BTN_TRIGGER_HAPPY27", + [BTN_TRIGGER_HAPPY28] = "BTN_TRIGGER_HAPPY28", + [BTN_TRIGGER_HAPPY29] = "BTN_TRIGGER_HAPPY29", + [BTN_TRIGGER_HAPPY30] = "BTN_TRIGGER_HAPPY30", + [BTN_TRIGGER_HAPPY31] = "BTN_TRIGGER_HAPPY31", + [BTN_TRIGGER_HAPPY32] = "BTN_TRIGGER_HAPPY32", + [BTN_TRIGGER_HAPPY33] = "BTN_TRIGGER_HAPPY33", + [BTN_TRIGGER_HAPPY34] = "BTN_TRIGGER_HAPPY34", + [BTN_TRIGGER_HAPPY35] = "BTN_TRIGGER_HAPPY35", + [BTN_TRIGGER_HAPPY36] = "BTN_TRIGGER_HAPPY36", + [BTN_TRIGGER_HAPPY37] = "BTN_TRIGGER_HAPPY37", + [BTN_TRIGGER_HAPPY38] = "BTN_TRIGGER_HAPPY38", + [BTN_TRIGGER_HAPPY39] = "BTN_TRIGGER_HAPPY39", + [BTN_TRIGGER_HAPPY40] = "BTN_TRIGGER_HAPPY40", + [BTN_0] = "BTN_0", + [BTN_1] = "BTN_1", + [BTN_2] = "BTN_2", + [BTN_3] = "BTN_3", + [BTN_4] = "BTN_4", + [BTN_5] = "BTN_5", + [BTN_6] = "BTN_6", + [BTN_7] = "BTN_7", + [BTN_8] = "BTN_8", + [BTN_9] = "BTN_9", + [BTN_LEFT] = "BTN_LEFT", + [BTN_RIGHT] = "BTN_RIGHT", + [BTN_MIDDLE] = "BTN_MIDDLE", + [BTN_SIDE] = "BTN_SIDE", + [BTN_EXTRA] = "BTN_EXTRA", + [BTN_FORWARD] = "BTN_FORWARD", + [BTN_BACK] = "BTN_BACK", + [BTN_TASK] = "BTN_TASK", + [BTN_TRIGGER] = "BTN_TRIGGER", + [BTN_THUMB] = "BTN_THUMB", + [BTN_THUMB2] = "BTN_THUMB2", + [BTN_TOP] = "BTN_TOP", + [BTN_TOP2] = "BTN_TOP2", + [BTN_PINKIE] = "BTN_PINKIE", + [BTN_BASE] = "BTN_BASE", + [BTN_BASE2] = "BTN_BASE2", + [BTN_BASE3] = "BTN_BASE3", + [BTN_BASE4] = "BTN_BASE4", + [BTN_BASE5] = "BTN_BASE5", + [BTN_BASE6] = "BTN_BASE6", + [BTN_DEAD] = "BTN_DEAD", + [BTN_SOUTH] = "BTN_SOUTH", + [BTN_EAST] = "BTN_EAST", + [BTN_C] = "BTN_C", + [BTN_NORTH] = "BTN_NORTH", + [BTN_WEST] = "BTN_WEST", + [BTN_Z] = "BTN_Z", + [BTN_TL] = "BTN_TL", + [BTN_TR] = "BTN_TR", + [BTN_TL2] = "BTN_TL2", + [BTN_TR2] = "BTN_TR2", + [BTN_SELECT] = "BTN_SELECT", + [BTN_START] = "BTN_START", + [BTN_MODE] = "BTN_MODE", + [BTN_THUMBL] = "BTN_THUMBL", + [BTN_THUMBR] = "BTN_THUMBR", + [BTN_TOOL_PEN] = "BTN_TOOL_PEN", + [BTN_TOOL_RUBBER] = "BTN_TOOL_RUBBER", + [BTN_TOOL_BRUSH] = "BTN_TOOL_BRUSH", + [BTN_TOOL_PENCIL] = "BTN_TOOL_PENCIL", + [BTN_TOOL_AIRBRUSH] = "BTN_TOOL_AIRBRUSH", + [BTN_TOOL_FINGER] = "BTN_TOOL_FINGER", + [BTN_TOOL_MOUSE] = "BTN_TOOL_MOUSE", + [BTN_TOOL_LENS] = "BTN_TOOL_LENS", + [BTN_TOOL_QUINTTAP] = "BTN_TOOL_QUINTTAP", + [BTN_TOUCH] = "BTN_TOUCH", + [BTN_STYLUS] = "BTN_STYLUS", + [BTN_STYLUS2] = "BTN_STYLUS2", + [BTN_TOOL_DOUBLETAP] = "BTN_TOOL_DOUBLETAP", + [BTN_TOOL_TRIPLETAP] = "BTN_TOOL_TRIPLETAP", + [BTN_TOOL_QUADTAP] = "BTN_TOOL_QUADTAP", + [BTN_GEAR_DOWN] = "BTN_GEAR_DOWN", + [BTN_GEAR_UP] = "BTN_GEAR_UP", +}; + +static const char * const led_map[LED_MAX + 1] = { + [LED_NUML] = "LED_NUML", + [LED_CAPSL] = "LED_CAPSL", + [LED_SCROLLL] = "LED_SCROLLL", + [LED_COMPOSE] = "LED_COMPOSE", + [LED_KANA] = "LED_KANA", + [LED_SLEEP] = "LED_SLEEP", + [LED_SUSPEND] = "LED_SUSPEND", + [LED_MUTE] = "LED_MUTE", + [LED_MISC] = "LED_MISC", + [LED_MAIL] = "LED_MAIL", + [LED_CHARGING] = "LED_CHARGING", + [LED_MAX] = "LED_MAX", +}; + +static const char * const snd_map[SND_MAX + 1] = { + [SND_CLICK] = "SND_CLICK", + [SND_BELL] = "SND_BELL", + [SND_TONE] = "SND_TONE", + [SND_MAX] = "SND_MAX", +}; + +static const char * const msc_map[MSC_MAX + 1] = { + [MSC_SERIAL] = "MSC_SERIAL", + [MSC_PULSELED] = "MSC_PULSELED", + [MSC_GESTURE] = "MSC_GESTURE", + [MSC_RAW] = "MSC_RAW", + [MSC_SCAN] = "MSC_SCAN", + [MSC_TIMESTAMP] = "MSC_TIMESTAMP", + [MSC_MAX] = "MSC_MAX", +}; + +static const char * const sw_map[SW_MAX + 1] = { + [SW_LID] = "SW_LID", + [SW_TABLET_MODE] = "SW_TABLET_MODE", + [SW_HEADPHONE_INSERT] = "SW_HEADPHONE_INSERT", + [SW_RFKILL_ALL] = "SW_RFKILL_ALL", + [SW_MICROPHONE_INSERT] = "SW_MICROPHONE_INSERT", + [SW_DOCK] = "SW_DOCK", + [SW_LINEOUT_INSERT] = "SW_LINEOUT_INSERT", + [SW_JACK_PHYSICAL_INSERT] = "SW_JACK_PHYSICAL_INSERT", + [SW_VIDEOOUT_INSERT] = "SW_VIDEOOUT_INSERT", + [SW_CAMERA_LENS_COVER] = "SW_CAMERA_LENS_COVER", + [SW_KEYPAD_SLIDE] = "SW_KEYPAD_SLIDE", + [SW_FRONT_PROXIMITY] = "SW_FRONT_PROXIMITY", + [SW_ROTATE_LOCK] = "SW_ROTATE_LOCK", + [SW_LINEIN_INSERT] = "SW_LINEIN_INSERT", + [SW_MUTE_DEVICE] = "SW_MUTE_DEVICE", + [SW_MAX] = "SW_MAX", +}; + +static const char * const ff_map[FF_MAX + 1] = { + [FF_STATUS_STOPPED] = "FF_STATUS_STOPPED", + [FF_STATUS_MAX] = "FF_STATUS_MAX", + [FF_AUTOCENTER] = "FF_AUTOCENTER", + [FF_GAIN] = "FF_GAIN", + [FF_RUMBLE] = "FF_RUMBLE", + [FF_PERIODIC] = "FF_PERIODIC", + [FF_CONSTANT] = "FF_CONSTANT", + [FF_SPRING] = "FF_SPRING", + [FF_FRICTION] = "FF_FRICTION", + [FF_DAMPER] = "FF_DAMPER", + [FF_INERTIA] = "FF_INERTIA", + [FF_RAMP] = "FF_RAMP", + [FF_SQUARE] = "FF_SQUARE", + [FF_TRIANGLE] = "FF_TRIANGLE", + [FF_SINE] = "FF_SINE", + [FF_SAW_UP] = "FF_SAW_UP", + [FF_SAW_DOWN] = "FF_SAW_DOWN", + [FF_CUSTOM] = "FF_CUSTOM", + [FF_MAX] = "FF_MAX", +}; + +static const char * const syn_map[SYN_MAX + 1] = { + [SYN_REPORT] = "SYN_REPORT", + [SYN_CONFIG] = "SYN_CONFIG", + [SYN_MT_REPORT] = "SYN_MT_REPORT", + [SYN_DROPPED] = "SYN_DROPPED", + [SYN_MAX] = "SYN_MAX", +}; + +static const char * const rep_map[REP_MAX + 1] = { + [REP_DELAY] = "REP_DELAY", + [REP_MAX] = "REP_MAX", +}; + +static const char * const input_prop_map[INPUT_PROP_MAX + 1] = { + [INPUT_PROP_POINTER] = "INPUT_PROP_POINTER", + [INPUT_PROP_DIRECT] = "INPUT_PROP_DIRECT", + [INPUT_PROP_BUTTONPAD] = "INPUT_PROP_BUTTONPAD", + [INPUT_PROP_SEMI_MT] = "INPUT_PROP_SEMI_MT", + [INPUT_PROP_TOPBUTTONPAD] = "INPUT_PROP_TOPBUTTONPAD", + [INPUT_PROP_POINTING_STICK] = "INPUT_PROP_POINTING_STICK", + [INPUT_PROP_ACCELEROMETER] = "INPUT_PROP_ACCELEROMETER", + [INPUT_PROP_MAX] = "INPUT_PROP_MAX", +}; + +static const char * const * const event_type_map[EV_MAX + 1] = { + [EV_REL] = rel_map, + [EV_ABS] = abs_map, + [EV_KEY] = key_map, + [EV_LED] = led_map, + [EV_SND] = snd_map, + [EV_MSC] = msc_map, + [EV_SW] = sw_map, + [EV_FF] = ff_map, + [EV_SYN] = syn_map, + [EV_REP] = rep_map, +}; + +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winitializer-overrides" +#else +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Woverride-init" +#endif +static const int ev_max[EV_MAX + 1] = { + [0 ... EV_MAX] = -1, + [EV_REL] = REL_MAX, + [EV_ABS] = ABS_MAX, + [EV_KEY] = KEY_MAX, + [EV_LED] = LED_MAX, + [EV_SND] = SND_MAX, + [EV_MSC] = MSC_MAX, + [EV_SW] = SW_MAX, + [EV_FF] = FF_MAX, + [EV_SYN] = SYN_MAX, + [EV_REP] = REP_MAX, +}; +#if __clang__ +#pragma clang diagnostic pop /* "-Winitializer-overrides" */ +#else +#pragma GCC diagnostic pop /* "-Woverride-init" */ +#endif + +struct name_entry { + const char *name; + unsigned int value; +}; + +static const struct name_entry ev_names[] = { + { .name = "EV_ABS", .value = EV_ABS }, + { .name = "EV_FF", .value = EV_FF }, + { .name = "EV_FF_STATUS", .value = EV_FF_STATUS }, + { .name = "EV_KEY", .value = EV_KEY }, + { .name = "EV_LED", .value = EV_LED }, + { .name = "EV_MAX", .value = EV_MAX }, + { .name = "EV_MSC", .value = EV_MSC }, + { .name = "EV_PWR", .value = EV_PWR }, + { .name = "EV_REL", .value = EV_REL }, + { .name = "EV_REP", .value = EV_REP }, + { .name = "EV_SND", .value = EV_SND }, + { .name = "EV_SW", .value = EV_SW }, + { .name = "EV_SYN", .value = EV_SYN }, +}; + +static const struct name_entry code_names[] = { + { .name = "ABS_BRAKE", .value = ABS_BRAKE }, + { .name = "ABS_DISTANCE", .value = ABS_DISTANCE }, + { .name = "ABS_GAS", .value = ABS_GAS }, + { .name = "ABS_HAT0X", .value = ABS_HAT0X }, + { .name = "ABS_HAT0Y", .value = ABS_HAT0Y }, + { .name = "ABS_HAT1X", .value = ABS_HAT1X }, + { .name = "ABS_HAT1Y", .value = ABS_HAT1Y }, + { .name = "ABS_HAT2X", .value = ABS_HAT2X }, + { .name = "ABS_HAT2Y", .value = ABS_HAT2Y }, + { .name = "ABS_HAT3X", .value = ABS_HAT3X }, + { .name = "ABS_HAT3Y", .value = ABS_HAT3Y }, + { .name = "ABS_MAX", .value = ABS_MAX }, + { .name = "ABS_MISC", .value = ABS_MISC }, + { .name = "ABS_MT_BLOB_ID", .value = ABS_MT_BLOB_ID }, + { .name = "ABS_MT_DISTANCE", .value = ABS_MT_DISTANCE }, + { .name = "ABS_MT_ORIENTATION", .value = ABS_MT_ORIENTATION }, + { .name = "ABS_MT_POSITION_X", .value = ABS_MT_POSITION_X }, + { .name = "ABS_MT_POSITION_Y", .value = ABS_MT_POSITION_Y }, + { .name = "ABS_MT_PRESSURE", .value = ABS_MT_PRESSURE }, + { .name = "ABS_MT_SLOT", .value = ABS_MT_SLOT }, + { .name = "ABS_MT_TOOL_TYPE", .value = ABS_MT_TOOL_TYPE }, + { .name = "ABS_MT_TOOL_X", .value = ABS_MT_TOOL_X }, + { .name = "ABS_MT_TOOL_Y", .value = ABS_MT_TOOL_Y }, + { .name = "ABS_MT_TOUCH_MAJOR", .value = ABS_MT_TOUCH_MAJOR }, + { .name = "ABS_MT_TOUCH_MINOR", .value = ABS_MT_TOUCH_MINOR }, + { .name = "ABS_MT_TRACKING_ID", .value = ABS_MT_TRACKING_ID }, + { .name = "ABS_MT_WIDTH_MAJOR", .value = ABS_MT_WIDTH_MAJOR }, + { .name = "ABS_MT_WIDTH_MINOR", .value = ABS_MT_WIDTH_MINOR }, + { .name = "ABS_PRESSURE", .value = ABS_PRESSURE }, + { .name = "ABS_RUDDER", .value = ABS_RUDDER }, + { .name = "ABS_RX", .value = ABS_RX }, + { .name = "ABS_RY", .value = ABS_RY }, + { .name = "ABS_RZ", .value = ABS_RZ }, + { .name = "ABS_THROTTLE", .value = ABS_THROTTLE }, + { .name = "ABS_TILT_X", .value = ABS_TILT_X }, + { .name = "ABS_TILT_Y", .value = ABS_TILT_Y }, + { .name = "ABS_TOOL_WIDTH", .value = ABS_TOOL_WIDTH }, + { .name = "ABS_VOLUME", .value = ABS_VOLUME }, + { .name = "ABS_WHEEL", .value = ABS_WHEEL }, + { .name = "ABS_X", .value = ABS_X }, + { .name = "ABS_Y", .value = ABS_Y }, + { .name = "ABS_Z", .value = ABS_Z }, + { .name = "BTN_0", .value = BTN_0 }, + { .name = "BTN_1", .value = BTN_1 }, + { .name = "BTN_2", .value = BTN_2 }, + { .name = "BTN_3", .value = BTN_3 }, + { .name = "BTN_4", .value = BTN_4 }, + { .name = "BTN_5", .value = BTN_5 }, + { .name = "BTN_6", .value = BTN_6 }, + { .name = "BTN_7", .value = BTN_7 }, + { .name = "BTN_8", .value = BTN_8 }, + { .name = "BTN_9", .value = BTN_9 }, + { .name = "BTN_A", .value = BTN_A }, + { .name = "BTN_B", .value = BTN_B }, + { .name = "BTN_BACK", .value = BTN_BACK }, + { .name = "BTN_BASE", .value = BTN_BASE }, + { .name = "BTN_BASE2", .value = BTN_BASE2 }, + { .name = "BTN_BASE3", .value = BTN_BASE3 }, + { .name = "BTN_BASE4", .value = BTN_BASE4 }, + { .name = "BTN_BASE5", .value = BTN_BASE5 }, + { .name = "BTN_BASE6", .value = BTN_BASE6 }, + { .name = "BTN_C", .value = BTN_C }, + { .name = "BTN_DEAD", .value = BTN_DEAD }, + { .name = "BTN_DPAD_DOWN", .value = BTN_DPAD_DOWN }, + { .name = "BTN_DPAD_LEFT", .value = BTN_DPAD_LEFT }, + { .name = "BTN_DPAD_RIGHT", .value = BTN_DPAD_RIGHT }, + { .name = "BTN_DPAD_UP", .value = BTN_DPAD_UP }, + { .name = "BTN_EAST", .value = BTN_EAST }, + { .name = "BTN_EXTRA", .value = BTN_EXTRA }, + { .name = "BTN_FORWARD", .value = BTN_FORWARD }, + { .name = "BTN_GEAR_DOWN", .value = BTN_GEAR_DOWN }, + { .name = "BTN_GEAR_UP", .value = BTN_GEAR_UP }, + { .name = "BTN_LEFT", .value = BTN_LEFT }, + { .name = "BTN_MIDDLE", .value = BTN_MIDDLE }, + { .name = "BTN_MODE", .value = BTN_MODE }, + { .name = "BTN_NORTH", .value = BTN_NORTH }, + { .name = "BTN_PINKIE", .value = BTN_PINKIE }, + { .name = "BTN_RIGHT", .value = BTN_RIGHT }, + { .name = "BTN_SELECT", .value = BTN_SELECT }, + { .name = "BTN_SIDE", .value = BTN_SIDE }, + { .name = "BTN_SOUTH", .value = BTN_SOUTH }, + { .name = "BTN_START", .value = BTN_START }, + { .name = "BTN_STYLUS", .value = BTN_STYLUS }, + { .name = "BTN_STYLUS2", .value = BTN_STYLUS2 }, + { .name = "BTN_TASK", .value = BTN_TASK }, + { .name = "BTN_THUMB", .value = BTN_THUMB }, + { .name = "BTN_THUMB2", .value = BTN_THUMB2 }, + { .name = "BTN_THUMBL", .value = BTN_THUMBL }, + { .name = "BTN_THUMBR", .value = BTN_THUMBR }, + { .name = "BTN_TL", .value = BTN_TL }, + { .name = "BTN_TL2", .value = BTN_TL2 }, + { .name = "BTN_TOOL_AIRBRUSH", .value = BTN_TOOL_AIRBRUSH }, + { .name = "BTN_TOOL_BRUSH", .value = BTN_TOOL_BRUSH }, + { .name = "BTN_TOOL_DOUBLETAP", .value = BTN_TOOL_DOUBLETAP }, + { .name = "BTN_TOOL_FINGER", .value = BTN_TOOL_FINGER }, + { .name = "BTN_TOOL_LENS", .value = BTN_TOOL_LENS }, + { .name = "BTN_TOOL_MOUSE", .value = BTN_TOOL_MOUSE }, + { .name = "BTN_TOOL_PEN", .value = BTN_TOOL_PEN }, + { .name = "BTN_TOOL_PENCIL", .value = BTN_TOOL_PENCIL }, + { .name = "BTN_TOOL_QUADTAP", .value = BTN_TOOL_QUADTAP }, + { .name = "BTN_TOOL_QUINTTAP", .value = BTN_TOOL_QUINTTAP }, + { .name = "BTN_TOOL_RUBBER", .value = BTN_TOOL_RUBBER }, + { .name = "BTN_TOOL_TRIPLETAP", .value = BTN_TOOL_TRIPLETAP }, + { .name = "BTN_TOP", .value = BTN_TOP }, + { .name = "BTN_TOP2", .value = BTN_TOP2 }, + { .name = "BTN_TOUCH", .value = BTN_TOUCH }, + { .name = "BTN_TR", .value = BTN_TR }, + { .name = "BTN_TR2", .value = BTN_TR2 }, + { .name = "BTN_TRIGGER", .value = BTN_TRIGGER }, + { .name = "BTN_TRIGGER_HAPPY1", .value = BTN_TRIGGER_HAPPY1 }, + { .name = "BTN_TRIGGER_HAPPY10", .value = BTN_TRIGGER_HAPPY10 }, + { .name = "BTN_TRIGGER_HAPPY11", .value = BTN_TRIGGER_HAPPY11 }, + { .name = "BTN_TRIGGER_HAPPY12", .value = BTN_TRIGGER_HAPPY12 }, + { .name = "BTN_TRIGGER_HAPPY13", .value = BTN_TRIGGER_HAPPY13 }, + { .name = "BTN_TRIGGER_HAPPY14", .value = BTN_TRIGGER_HAPPY14 }, + { .name = "BTN_TRIGGER_HAPPY15", .value = BTN_TRIGGER_HAPPY15 }, + { .name = "BTN_TRIGGER_HAPPY16", .value = BTN_TRIGGER_HAPPY16 }, + { .name = "BTN_TRIGGER_HAPPY17", .value = BTN_TRIGGER_HAPPY17 }, + { .name = "BTN_TRIGGER_HAPPY18", .value = BTN_TRIGGER_HAPPY18 }, + { .name = "BTN_TRIGGER_HAPPY19", .value = BTN_TRIGGER_HAPPY19 }, + { .name = "BTN_TRIGGER_HAPPY2", .value = BTN_TRIGGER_HAPPY2 }, + { .name = "BTN_TRIGGER_HAPPY20", .value = BTN_TRIGGER_HAPPY20 }, + { .name = "BTN_TRIGGER_HAPPY21", .value = BTN_TRIGGER_HAPPY21 }, + { .name = "BTN_TRIGGER_HAPPY22", .value = BTN_TRIGGER_HAPPY22 }, + { .name = "BTN_TRIGGER_HAPPY23", .value = BTN_TRIGGER_HAPPY23 }, + { .name = "BTN_TRIGGER_HAPPY24", .value = BTN_TRIGGER_HAPPY24 }, + { .name = "BTN_TRIGGER_HAPPY25", .value = BTN_TRIGGER_HAPPY25 }, + { .name = "BTN_TRIGGER_HAPPY26", .value = BTN_TRIGGER_HAPPY26 }, + { .name = "BTN_TRIGGER_HAPPY27", .value = BTN_TRIGGER_HAPPY27 }, + { .name = "BTN_TRIGGER_HAPPY28", .value = BTN_TRIGGER_HAPPY28 }, + { .name = "BTN_TRIGGER_HAPPY29", .value = BTN_TRIGGER_HAPPY29 }, + { .name = "BTN_TRIGGER_HAPPY3", .value = BTN_TRIGGER_HAPPY3 }, + { .name = "BTN_TRIGGER_HAPPY30", .value = BTN_TRIGGER_HAPPY30 }, + { .name = "BTN_TRIGGER_HAPPY31", .value = BTN_TRIGGER_HAPPY31 }, + { .name = "BTN_TRIGGER_HAPPY32", .value = BTN_TRIGGER_HAPPY32 }, + { .name = "BTN_TRIGGER_HAPPY33", .value = BTN_TRIGGER_HAPPY33 }, + { .name = "BTN_TRIGGER_HAPPY34", .value = BTN_TRIGGER_HAPPY34 }, + { .name = "BTN_TRIGGER_HAPPY35", .value = BTN_TRIGGER_HAPPY35 }, + { .name = "BTN_TRIGGER_HAPPY36", .value = BTN_TRIGGER_HAPPY36 }, + { .name = "BTN_TRIGGER_HAPPY37", .value = BTN_TRIGGER_HAPPY37 }, + { .name = "BTN_TRIGGER_HAPPY38", .value = BTN_TRIGGER_HAPPY38 }, + { .name = "BTN_TRIGGER_HAPPY39", .value = BTN_TRIGGER_HAPPY39 }, + { .name = "BTN_TRIGGER_HAPPY4", .value = BTN_TRIGGER_HAPPY4 }, + { .name = "BTN_TRIGGER_HAPPY40", .value = BTN_TRIGGER_HAPPY40 }, + { .name = "BTN_TRIGGER_HAPPY5", .value = BTN_TRIGGER_HAPPY5 }, + { .name = "BTN_TRIGGER_HAPPY6", .value = BTN_TRIGGER_HAPPY6 }, + { .name = "BTN_TRIGGER_HAPPY7", .value = BTN_TRIGGER_HAPPY7 }, + { .name = "BTN_TRIGGER_HAPPY8", .value = BTN_TRIGGER_HAPPY8 }, + { .name = "BTN_TRIGGER_HAPPY9", .value = BTN_TRIGGER_HAPPY9 }, + { .name = "BTN_WEST", .value = BTN_WEST }, + { .name = "BTN_X", .value = BTN_X }, + { .name = "BTN_Y", .value = BTN_Y }, + { .name = "BTN_Z", .value = BTN_Z }, + { .name = "FF_AUTOCENTER", .value = FF_AUTOCENTER }, + { .name = "FF_CONSTANT", .value = FF_CONSTANT }, + { .name = "FF_CUSTOM", .value = FF_CUSTOM }, + { .name = "FF_DAMPER", .value = FF_DAMPER }, + { .name = "FF_FRICTION", .value = FF_FRICTION }, + { .name = "FF_GAIN", .value = FF_GAIN }, + { .name = "FF_INERTIA", .value = FF_INERTIA }, + { .name = "FF_MAX", .value = FF_MAX }, + { .name = "FF_PERIODIC", .value = FF_PERIODIC }, + { .name = "FF_RAMP", .value = FF_RAMP }, + { .name = "FF_RUMBLE", .value = FF_RUMBLE }, + { .name = "FF_SAW_DOWN", .value = FF_SAW_DOWN }, + { .name = "FF_SAW_UP", .value = FF_SAW_UP }, + { .name = "FF_SINE", .value = FF_SINE }, + { .name = "FF_SPRING", .value = FF_SPRING }, + { .name = "FF_SQUARE", .value = FF_SQUARE }, + { .name = "FF_STATUS_MAX", .value = FF_STATUS_MAX }, + { .name = "FF_STATUS_STOPPED", .value = FF_STATUS_STOPPED }, + { .name = "FF_TRIANGLE", .value = FF_TRIANGLE }, + { .name = "KEY_0", .value = KEY_0 }, + { .name = "KEY_1", .value = KEY_1 }, + { .name = "KEY_102ND", .value = KEY_102ND }, + { .name = "KEY_10CHANNELSDOWN", .value = KEY_10CHANNELSDOWN }, + { .name = "KEY_10CHANNELSUP", .value = KEY_10CHANNELSUP }, + { .name = "KEY_2", .value = KEY_2 }, + { .name = "KEY_3", .value = KEY_3 }, + { .name = "KEY_4", .value = KEY_4 }, + { .name = "KEY_5", .value = KEY_5 }, + { .name = "KEY_6", .value = KEY_6 }, + { .name = "KEY_7", .value = KEY_7 }, + { .name = "KEY_8", .value = KEY_8 }, + { .name = "KEY_9", .value = KEY_9 }, + { .name = "KEY_A", .value = KEY_A }, + { .name = "KEY_AB", .value = KEY_AB }, + { .name = "KEY_ADDRESSBOOK", .value = KEY_ADDRESSBOOK }, + { .name = "KEY_AGAIN", .value = KEY_AGAIN }, + { .name = "KEY_ALS_TOGGLE", .value = KEY_ALS_TOGGLE }, + { .name = "KEY_ALTERASE", .value = KEY_ALTERASE }, + { .name = "KEY_ANGLE", .value = KEY_ANGLE }, + { .name = "KEY_APOSTROPHE", .value = KEY_APOSTROPHE }, + { .name = "KEY_APPSELECT", .value = KEY_APPSELECT }, + { .name = "KEY_ARCHIVE", .value = KEY_ARCHIVE }, + { .name = "KEY_ATTENDANT_OFF", .value = KEY_ATTENDANT_OFF }, + { .name = "KEY_ATTENDANT_ON", .value = KEY_ATTENDANT_ON }, + { .name = "KEY_ATTENDANT_TOGGLE", .value = KEY_ATTENDANT_TOGGLE }, + { .name = "KEY_AUDIO", .value = KEY_AUDIO }, + { .name = "KEY_AUX", .value = KEY_AUX }, + { .name = "KEY_B", .value = KEY_B }, + { .name = "KEY_BACK", .value = KEY_BACK }, + { .name = "KEY_BACKSLASH", .value = KEY_BACKSLASH }, + { .name = "KEY_BACKSPACE", .value = KEY_BACKSPACE }, + { .name = "KEY_BASSBOOST", .value = KEY_BASSBOOST }, + { .name = "KEY_BATTERY", .value = KEY_BATTERY }, + { .name = "KEY_BLUE", .value = KEY_BLUE }, + { .name = "KEY_BLUETOOTH", .value = KEY_BLUETOOTH }, + { .name = "KEY_BOOKMARKS", .value = KEY_BOOKMARKS }, + { .name = "KEY_BREAK", .value = KEY_BREAK }, + { .name = "KEY_BRIGHTNESSDOWN", .value = KEY_BRIGHTNESSDOWN }, + { .name = "KEY_BRIGHTNESSUP", .value = KEY_BRIGHTNESSUP }, + { .name = "KEY_BRIGHTNESS_AUTO", .value = KEY_BRIGHTNESS_AUTO }, + { .name = "KEY_BRIGHTNESS_CYCLE", .value = KEY_BRIGHTNESS_CYCLE }, + { .name = "KEY_BRIGHTNESS_MAX", .value = KEY_BRIGHTNESS_MAX }, + { .name = "KEY_BRIGHTNESS_MIN", .value = KEY_BRIGHTNESS_MIN }, + { .name = "KEY_BRL_DOT1", .value = KEY_BRL_DOT1 }, + { .name = "KEY_BRL_DOT10", .value = KEY_BRL_DOT10 }, + { .name = "KEY_BRL_DOT2", .value = KEY_BRL_DOT2 }, + { .name = "KEY_BRL_DOT3", .value = KEY_BRL_DOT3 }, + { .name = "KEY_BRL_DOT4", .value = KEY_BRL_DOT4 }, + { .name = "KEY_BRL_DOT5", .value = KEY_BRL_DOT5 }, + { .name = "KEY_BRL_DOT6", .value = KEY_BRL_DOT6 }, + { .name = "KEY_BRL_DOT7", .value = KEY_BRL_DOT7 }, + { .name = "KEY_BRL_DOT8", .value = KEY_BRL_DOT8 }, + { .name = "KEY_BRL_DOT9", .value = KEY_BRL_DOT9 }, + { .name = "KEY_BUTTONCONFIG", .value = KEY_BUTTONCONFIG }, + { .name = "KEY_C", .value = KEY_C }, + { .name = "KEY_CALC", .value = KEY_CALC }, + { .name = "KEY_CALENDAR", .value = KEY_CALENDAR }, + { .name = "KEY_CAMERA", .value = KEY_CAMERA }, + { .name = "KEY_CAMERA_DOWN", .value = KEY_CAMERA_DOWN }, + { .name = "KEY_CAMERA_FOCUS", .value = KEY_CAMERA_FOCUS }, + { .name = "KEY_CAMERA_LEFT", .value = KEY_CAMERA_LEFT }, + { .name = "KEY_CAMERA_RIGHT", .value = KEY_CAMERA_RIGHT }, + { .name = "KEY_CAMERA_UP", .value = KEY_CAMERA_UP }, + { .name = "KEY_CAMERA_ZOOMIN", .value = KEY_CAMERA_ZOOMIN }, + { .name = "KEY_CAMERA_ZOOMOUT", .value = KEY_CAMERA_ZOOMOUT }, + { .name = "KEY_CANCEL", .value = KEY_CANCEL }, + { .name = "KEY_CAPSLOCK", .value = KEY_CAPSLOCK }, + { .name = "KEY_CD", .value = KEY_CD }, + { .name = "KEY_CHANNEL", .value = KEY_CHANNEL }, + { .name = "KEY_CHANNELDOWN", .value = KEY_CHANNELDOWN }, + { .name = "KEY_CHANNELUP", .value = KEY_CHANNELUP }, + { .name = "KEY_CHAT", .value = KEY_CHAT }, + { .name = "KEY_CLEAR", .value = KEY_CLEAR }, + { .name = "KEY_CLOSE", .value = KEY_CLOSE }, + { .name = "KEY_CLOSECD", .value = KEY_CLOSECD }, + { .name = "KEY_COFFEE", .value = KEY_COFFEE }, + { .name = "KEY_COMMA", .value = KEY_COMMA }, + { .name = "KEY_COMPOSE", .value = KEY_COMPOSE }, + { .name = "KEY_COMPUTER", .value = KEY_COMPUTER }, + { .name = "KEY_CONFIG", .value = KEY_CONFIG }, + { .name = "KEY_CONNECT", .value = KEY_CONNECT }, + { .name = "KEY_CONTEXT_MENU", .value = KEY_CONTEXT_MENU }, + { .name = "KEY_CONTROLPANEL", .value = KEY_CONTROLPANEL }, + { .name = "KEY_COPY", .value = KEY_COPY }, + { .name = "KEY_CUT", .value = KEY_CUT }, + { .name = "KEY_CYCLEWINDOWS", .value = KEY_CYCLEWINDOWS }, + { .name = "KEY_D", .value = KEY_D }, + { .name = "KEY_DASHBOARD", .value = KEY_DASHBOARD }, + { .name = "KEY_DATABASE", .value = KEY_DATABASE }, + { .name = "KEY_DELETE", .value = KEY_DELETE }, + { .name = "KEY_DELETEFILE", .value = KEY_DELETEFILE }, + { .name = "KEY_DEL_EOL", .value = KEY_DEL_EOL }, + { .name = "KEY_DEL_EOS", .value = KEY_DEL_EOS }, + { .name = "KEY_DEL_LINE", .value = KEY_DEL_LINE }, + { .name = "KEY_DIGITS", .value = KEY_DIGITS }, + { .name = "KEY_DIRECTORY", .value = KEY_DIRECTORY }, + { .name = "KEY_DISPLAYTOGGLE", .value = KEY_DISPLAYTOGGLE }, + { .name = "KEY_DISPLAY_OFF", .value = KEY_DISPLAY_OFF }, + { .name = "KEY_DOCUMENTS", .value = KEY_DOCUMENTS }, + { .name = "KEY_DOLLAR", .value = KEY_DOLLAR }, + { .name = "KEY_DOT", .value = KEY_DOT }, + { .name = "KEY_DOWN", .value = KEY_DOWN }, + { .name = "KEY_DVD", .value = KEY_DVD }, + { .name = "KEY_E", .value = KEY_E }, + { .name = "KEY_EDIT", .value = KEY_EDIT }, + { .name = "KEY_EDITOR", .value = KEY_EDITOR }, + { .name = "KEY_EJECTCD", .value = KEY_EJECTCD }, + { .name = "KEY_EJECTCLOSECD", .value = KEY_EJECTCLOSECD }, + { .name = "KEY_EMAIL", .value = KEY_EMAIL }, + { .name = "KEY_END", .value = KEY_END }, + { .name = "KEY_ENTER", .value = KEY_ENTER }, + { .name = "KEY_EPG", .value = KEY_EPG }, + { .name = "KEY_EQUAL", .value = KEY_EQUAL }, + { .name = "KEY_ESC", .value = KEY_ESC }, + { .name = "KEY_EURO", .value = KEY_EURO }, + { .name = "KEY_EXIT", .value = KEY_EXIT }, + { .name = "KEY_F", .value = KEY_F }, + { .name = "KEY_F1", .value = KEY_F1 }, + { .name = "KEY_F10", .value = KEY_F10 }, + { .name = "KEY_F11", .value = KEY_F11 }, + { .name = "KEY_F12", .value = KEY_F12 }, + { .name = "KEY_F13", .value = KEY_F13 }, + { .name = "KEY_F14", .value = KEY_F14 }, + { .name = "KEY_F15", .value = KEY_F15 }, + { .name = "KEY_F16", .value = KEY_F16 }, + { .name = "KEY_F17", .value = KEY_F17 }, + { .name = "KEY_F18", .value = KEY_F18 }, + { .name = "KEY_F19", .value = KEY_F19 }, + { .name = "KEY_F2", .value = KEY_F2 }, + { .name = "KEY_F20", .value = KEY_F20 }, + { .name = "KEY_F21", .value = KEY_F21 }, + { .name = "KEY_F22", .value = KEY_F22 }, + { .name = "KEY_F23", .value = KEY_F23 }, + { .name = "KEY_F24", .value = KEY_F24 }, + { .name = "KEY_F3", .value = KEY_F3 }, + { .name = "KEY_F4", .value = KEY_F4 }, + { .name = "KEY_F5", .value = KEY_F5 }, + { .name = "KEY_F6", .value = KEY_F6 }, + { .name = "KEY_F7", .value = KEY_F7 }, + { .name = "KEY_F8", .value = KEY_F8 }, + { .name = "KEY_F9", .value = KEY_F9 }, + { .name = "KEY_FASTFORWARD", .value = KEY_FASTFORWARD }, + { .name = "KEY_FAVORITES", .value = KEY_FAVORITES }, + { .name = "KEY_FILE", .value = KEY_FILE }, + { .name = "KEY_FINANCE", .value = KEY_FINANCE }, + { .name = "KEY_FIND", .value = KEY_FIND }, + { .name = "KEY_FIRST", .value = KEY_FIRST }, + { .name = "KEY_FN", .value = KEY_FN }, + { .name = "KEY_FN_1", .value = KEY_FN_1 }, + { .name = "KEY_FN_2", .value = KEY_FN_2 }, + { .name = "KEY_FN_B", .value = KEY_FN_B }, + { .name = "KEY_FN_D", .value = KEY_FN_D }, + { .name = "KEY_FN_E", .value = KEY_FN_E }, + { .name = "KEY_FN_ESC", .value = KEY_FN_ESC }, + { .name = "KEY_FN_F", .value = KEY_FN_F }, + { .name = "KEY_FN_F1", .value = KEY_FN_F1 }, + { .name = "KEY_FN_F10", .value = KEY_FN_F10 }, + { .name = "KEY_FN_F11", .value = KEY_FN_F11 }, + { .name = "KEY_FN_F12", .value = KEY_FN_F12 }, + { .name = "KEY_FN_F2", .value = KEY_FN_F2 }, + { .name = "KEY_FN_F3", .value = KEY_FN_F3 }, + { .name = "KEY_FN_F4", .value = KEY_FN_F4 }, + { .name = "KEY_FN_F5", .value = KEY_FN_F5 }, + { .name = "KEY_FN_F6", .value = KEY_FN_F6 }, + { .name = "KEY_FN_F7", .value = KEY_FN_F7 }, + { .name = "KEY_FN_F8", .value = KEY_FN_F8 }, + { .name = "KEY_FN_F9", .value = KEY_FN_F9 }, + { .name = "KEY_FN_S", .value = KEY_FN_S }, + { .name = "KEY_FORWARD", .value = KEY_FORWARD }, + { .name = "KEY_FORWARDMAIL", .value = KEY_FORWARDMAIL }, + { .name = "KEY_FRAMEBACK", .value = KEY_FRAMEBACK }, + { .name = "KEY_FRAMEFORWARD", .value = KEY_FRAMEFORWARD }, + { .name = "KEY_FRONT", .value = KEY_FRONT }, + { .name = "KEY_G", .value = KEY_G }, + { .name = "KEY_GAMES", .value = KEY_GAMES }, + { .name = "KEY_GOTO", .value = KEY_GOTO }, + { .name = "KEY_GRAPHICSEDITOR", .value = KEY_GRAPHICSEDITOR }, + { .name = "KEY_GRAVE", .value = KEY_GRAVE }, + { .name = "KEY_GREEN", .value = KEY_GREEN }, + { .name = "KEY_H", .value = KEY_H }, + { .name = "KEY_HANGEUL", .value = KEY_HANGEUL }, + { .name = "KEY_HANJA", .value = KEY_HANJA }, + { .name = "KEY_HELP", .value = KEY_HELP }, + { .name = "KEY_HENKAN", .value = KEY_HENKAN }, + { .name = "KEY_HIRAGANA", .value = KEY_HIRAGANA }, + { .name = "KEY_HOME", .value = KEY_HOME }, + { .name = "KEY_HOMEPAGE", .value = KEY_HOMEPAGE }, + { .name = "KEY_HP", .value = KEY_HP }, + { .name = "KEY_I", .value = KEY_I }, + { .name = "KEY_IMAGES", .value = KEY_IMAGES }, + { .name = "KEY_INFO", .value = KEY_INFO }, + { .name = "KEY_INSERT", .value = KEY_INSERT }, + { .name = "KEY_INS_LINE", .value = KEY_INS_LINE }, + { .name = "KEY_ISO", .value = KEY_ISO }, + { .name = "KEY_J", .value = KEY_J }, + { .name = "KEY_JOURNAL", .value = KEY_JOURNAL }, + { .name = "KEY_K", .value = KEY_K }, + { .name = "KEY_KATAKANA", .value = KEY_KATAKANA }, + { .name = "KEY_KATAKANAHIRAGANA", .value = KEY_KATAKANAHIRAGANA }, + { .name = "KEY_KBDILLUMDOWN", .value = KEY_KBDILLUMDOWN }, + { .name = "KEY_KBDILLUMTOGGLE", .value = KEY_KBDILLUMTOGGLE }, + { .name = "KEY_KBDILLUMUP", .value = KEY_KBDILLUMUP }, + { .name = "KEY_KBDINPUTASSIST_ACCEPT", .value = KEY_KBDINPUTASSIST_ACCEPT }, + { .name = "KEY_KBDINPUTASSIST_CANCEL", .value = KEY_KBDINPUTASSIST_CANCEL }, + { .name = "KEY_KBDINPUTASSIST_NEXT", .value = KEY_KBDINPUTASSIST_NEXT }, + { .name = "KEY_KBDINPUTASSIST_NEXTGROUP", .value = KEY_KBDINPUTASSIST_NEXTGROUP }, + { .name = "KEY_KBDINPUTASSIST_PREV", .value = KEY_KBDINPUTASSIST_PREV }, + { .name = "KEY_KBDINPUTASSIST_PREVGROUP", .value = KEY_KBDINPUTASSIST_PREVGROUP }, + { .name = "KEY_KEYBOARD", .value = KEY_KEYBOARD }, + { .name = "KEY_KP0", .value = KEY_KP0 }, + { .name = "KEY_KP1", .value = KEY_KP1 }, + { .name = "KEY_KP2", .value = KEY_KP2 }, + { .name = "KEY_KP3", .value = KEY_KP3 }, + { .name = "KEY_KP4", .value = KEY_KP4 }, + { .name = "KEY_KP5", .value = KEY_KP5 }, + { .name = "KEY_KP6", .value = KEY_KP6 }, + { .name = "KEY_KP7", .value = KEY_KP7 }, + { .name = "KEY_KP8", .value = KEY_KP8 }, + { .name = "KEY_KP9", .value = KEY_KP9 }, + { .name = "KEY_KPASTERISK", .value = KEY_KPASTERISK }, + { .name = "KEY_KPCOMMA", .value = KEY_KPCOMMA }, + { .name = "KEY_KPDOT", .value = KEY_KPDOT }, + { .name = "KEY_KPENTER", .value = KEY_KPENTER }, + { .name = "KEY_KPEQUAL", .value = KEY_KPEQUAL }, + { .name = "KEY_KPJPCOMMA", .value = KEY_KPJPCOMMA }, + { .name = "KEY_KPLEFTPAREN", .value = KEY_KPLEFTPAREN }, + { .name = "KEY_KPMINUS", .value = KEY_KPMINUS }, + { .name = "KEY_KPPLUS", .value = KEY_KPPLUS }, + { .name = "KEY_KPPLUSMINUS", .value = KEY_KPPLUSMINUS }, + { .name = "KEY_KPRIGHTPAREN", .value = KEY_KPRIGHTPAREN }, + { .name = "KEY_KPSLASH", .value = KEY_KPSLASH }, + { .name = "KEY_L", .value = KEY_L }, + { .name = "KEY_LANGUAGE", .value = KEY_LANGUAGE }, + { .name = "KEY_LAST", .value = KEY_LAST }, + { .name = "KEY_LEFT", .value = KEY_LEFT }, + { .name = "KEY_LEFTALT", .value = KEY_LEFTALT }, + { .name = "KEY_LEFTBRACE", .value = KEY_LEFTBRACE }, + { .name = "KEY_LEFTCTRL", .value = KEY_LEFTCTRL }, + { .name = "KEY_LEFTMETA", .value = KEY_LEFTMETA }, + { .name = "KEY_LEFTSHIFT", .value = KEY_LEFTSHIFT }, + { .name = "KEY_LIGHTS_TOGGLE", .value = KEY_LIGHTS_TOGGLE }, + { .name = "KEY_LINEFEED", .value = KEY_LINEFEED }, + { .name = "KEY_LIST", .value = KEY_LIST }, + { .name = "KEY_LOGOFF", .value = KEY_LOGOFF }, + { .name = "KEY_M", .value = KEY_M }, + { .name = "KEY_MACRO", .value = KEY_MACRO }, + { .name = "KEY_MAIL", .value = KEY_MAIL }, + { .name = "KEY_MAX", .value = KEY_MAX }, + { .name = "KEY_MEDIA", .value = KEY_MEDIA }, + { .name = "KEY_MEDIA_REPEAT", .value = KEY_MEDIA_REPEAT }, + { .name = "KEY_MEMO", .value = KEY_MEMO }, + { .name = "KEY_MENU", .value = KEY_MENU }, + { .name = "KEY_MESSENGER", .value = KEY_MESSENGER }, + { .name = "KEY_MHP", .value = KEY_MHP }, + { .name = "KEY_MICMUTE", .value = KEY_MICMUTE }, + { .name = "KEY_MINUS", .value = KEY_MINUS }, + { .name = "KEY_MODE", .value = KEY_MODE }, + { .name = "KEY_MOVE", .value = KEY_MOVE }, + { .name = "KEY_MP3", .value = KEY_MP3 }, + { .name = "KEY_MSDOS", .value = KEY_MSDOS }, + { .name = "KEY_MUHENKAN", .value = KEY_MUHENKAN }, + { .name = "KEY_MUTE", .value = KEY_MUTE }, + { .name = "KEY_N", .value = KEY_N }, + { .name = "KEY_NEW", .value = KEY_NEW }, + { .name = "KEY_NEWS", .value = KEY_NEWS }, + { .name = "KEY_NEXT", .value = KEY_NEXT }, + { .name = "KEY_NEXTSONG", .value = KEY_NEXTSONG }, + { .name = "KEY_NUMERIC_0", .value = KEY_NUMERIC_0 }, + { .name = "KEY_NUMERIC_1", .value = KEY_NUMERIC_1 }, + { .name = "KEY_NUMERIC_2", .value = KEY_NUMERIC_2 }, + { .name = "KEY_NUMERIC_3", .value = KEY_NUMERIC_3 }, + { .name = "KEY_NUMERIC_4", .value = KEY_NUMERIC_4 }, + { .name = "KEY_NUMERIC_5", .value = KEY_NUMERIC_5 }, + { .name = "KEY_NUMERIC_6", .value = KEY_NUMERIC_6 }, + { .name = "KEY_NUMERIC_7", .value = KEY_NUMERIC_7 }, + { .name = "KEY_NUMERIC_8", .value = KEY_NUMERIC_8 }, + { .name = "KEY_NUMERIC_9", .value = KEY_NUMERIC_9 }, + { .name = "KEY_NUMERIC_A", .value = KEY_NUMERIC_A }, + { .name = "KEY_NUMERIC_B", .value = KEY_NUMERIC_B }, + { .name = "KEY_NUMERIC_C", .value = KEY_NUMERIC_C }, + { .name = "KEY_NUMERIC_D", .value = KEY_NUMERIC_D }, + { .name = "KEY_NUMERIC_POUND", .value = KEY_NUMERIC_POUND }, + { .name = "KEY_NUMERIC_STAR", .value = KEY_NUMERIC_STAR }, + { .name = "KEY_NUMLOCK", .value = KEY_NUMLOCK }, + { .name = "KEY_O", .value = KEY_O }, + { .name = "KEY_OK", .value = KEY_OK }, + { .name = "KEY_OPEN", .value = KEY_OPEN }, + { .name = "KEY_OPTION", .value = KEY_OPTION }, + { .name = "KEY_P", .value = KEY_P }, + { .name = "KEY_PAGEDOWN", .value = KEY_PAGEDOWN }, + { .name = "KEY_PAGEUP", .value = KEY_PAGEUP }, + { .name = "KEY_PASTE", .value = KEY_PASTE }, + { .name = "KEY_PAUSE", .value = KEY_PAUSE }, + { .name = "KEY_PAUSECD", .value = KEY_PAUSECD }, + { .name = "KEY_PC", .value = KEY_PC }, + { .name = "KEY_PHONE", .value = KEY_PHONE }, + { .name = "KEY_PLAY", .value = KEY_PLAY }, + { .name = "KEY_PLAYCD", .value = KEY_PLAYCD }, + { .name = "KEY_PLAYER", .value = KEY_PLAYER }, + { .name = "KEY_PLAYPAUSE", .value = KEY_PLAYPAUSE }, + { .name = "KEY_POWER", .value = KEY_POWER }, + { .name = "KEY_POWER2", .value = KEY_POWER2 }, + { .name = "KEY_PRESENTATION", .value = KEY_PRESENTATION }, + { .name = "KEY_PREVIOUS", .value = KEY_PREVIOUS }, + { .name = "KEY_PREVIOUSSONG", .value = KEY_PREVIOUSSONG }, + { .name = "KEY_PRINT", .value = KEY_PRINT }, + { .name = "KEY_PROG1", .value = KEY_PROG1 }, + { .name = "KEY_PROG2", .value = KEY_PROG2 }, + { .name = "KEY_PROG3", .value = KEY_PROG3 }, + { .name = "KEY_PROG4", .value = KEY_PROG4 }, + { .name = "KEY_PROGRAM", .value = KEY_PROGRAM }, + { .name = "KEY_PROPS", .value = KEY_PROPS }, + { .name = "KEY_PVR", .value = KEY_PVR }, + { .name = "KEY_Q", .value = KEY_Q }, + { .name = "KEY_QUESTION", .value = KEY_QUESTION }, + { .name = "KEY_R", .value = KEY_R }, + { .name = "KEY_RADIO", .value = KEY_RADIO }, + { .name = "KEY_RECORD", .value = KEY_RECORD }, + { .name = "KEY_RED", .value = KEY_RED }, + { .name = "KEY_REDO", .value = KEY_REDO }, + { .name = "KEY_REFRESH", .value = KEY_REFRESH }, + { .name = "KEY_REPLY", .value = KEY_REPLY }, + { .name = "KEY_RESERVED", .value = KEY_RESERVED }, + { .name = "KEY_RESTART", .value = KEY_RESTART }, + { .name = "KEY_REWIND", .value = KEY_REWIND }, + { .name = "KEY_RFKILL", .value = KEY_RFKILL }, + { .name = "KEY_RIGHT", .value = KEY_RIGHT }, + { .name = "KEY_RIGHTALT", .value = KEY_RIGHTALT }, + { .name = "KEY_RIGHTBRACE", .value = KEY_RIGHTBRACE }, + { .name = "KEY_RIGHTCTRL", .value = KEY_RIGHTCTRL }, + { .name = "KEY_RIGHTMETA", .value = KEY_RIGHTMETA }, + { .name = "KEY_RIGHTSHIFT", .value = KEY_RIGHTSHIFT }, + { .name = "KEY_RO", .value = KEY_RO }, + { .name = "KEY_ROTATE_DISPLAY", .value = KEY_ROTATE_DISPLAY }, + { .name = "KEY_S", .value = KEY_S }, + { .name = "KEY_SAT", .value = KEY_SAT }, + { .name = "KEY_SAT2", .value = KEY_SAT2 }, + { .name = "KEY_SAVE", .value = KEY_SAVE }, + { .name = "KEY_SCALE", .value = KEY_SCALE }, + { .name = "KEY_SCREEN", .value = KEY_SCREEN }, + { .name = "KEY_SCREENSAVER", .value = KEY_SCREENSAVER }, + { .name = "KEY_SCROLLDOWN", .value = KEY_SCROLLDOWN }, + { .name = "KEY_SCROLLLOCK", .value = KEY_SCROLLLOCK }, + { .name = "KEY_SCROLLUP", .value = KEY_SCROLLUP }, + { .name = "KEY_SEARCH", .value = KEY_SEARCH }, + { .name = "KEY_SELECT", .value = KEY_SELECT }, + { .name = "KEY_SEMICOLON", .value = KEY_SEMICOLON }, + { .name = "KEY_SEND", .value = KEY_SEND }, + { .name = "KEY_SENDFILE", .value = KEY_SENDFILE }, + { .name = "KEY_SETUP", .value = KEY_SETUP }, + { .name = "KEY_SHOP", .value = KEY_SHOP }, + { .name = "KEY_SHUFFLE", .value = KEY_SHUFFLE }, + { .name = "KEY_SLASH", .value = KEY_SLASH }, + { .name = "KEY_SLEEP", .value = KEY_SLEEP }, + { .name = "KEY_SLOW", .value = KEY_SLOW }, + { .name = "KEY_SOUND", .value = KEY_SOUND }, + { .name = "KEY_SPACE", .value = KEY_SPACE }, + { .name = "KEY_SPELLCHECK", .value = KEY_SPELLCHECK }, + { .name = "KEY_SPORT", .value = KEY_SPORT }, + { .name = "KEY_SPREADSHEET", .value = KEY_SPREADSHEET }, + { .name = "KEY_STOP", .value = KEY_STOP }, + { .name = "KEY_STOPCD", .value = KEY_STOPCD }, + { .name = "KEY_SUBTITLE", .value = KEY_SUBTITLE }, + { .name = "KEY_SUSPEND", .value = KEY_SUSPEND }, + { .name = "KEY_SWITCHVIDEOMODE", .value = KEY_SWITCHVIDEOMODE }, + { .name = "KEY_SYSRQ", .value = KEY_SYSRQ }, + { .name = "KEY_T", .value = KEY_T }, + { .name = "KEY_TAB", .value = KEY_TAB }, + { .name = "KEY_TAPE", .value = KEY_TAPE }, + { .name = "KEY_TASKMANAGER", .value = KEY_TASKMANAGER }, + { .name = "KEY_TEEN", .value = KEY_TEEN }, + { .name = "KEY_TEXT", .value = KEY_TEXT }, + { .name = "KEY_TIME", .value = KEY_TIME }, + { .name = "KEY_TITLE", .value = KEY_TITLE }, + { .name = "KEY_TOUCHPAD_OFF", .value = KEY_TOUCHPAD_OFF }, + { .name = "KEY_TOUCHPAD_ON", .value = KEY_TOUCHPAD_ON }, + { .name = "KEY_TOUCHPAD_TOGGLE", .value = KEY_TOUCHPAD_TOGGLE }, + { .name = "KEY_TUNER", .value = KEY_TUNER }, + { .name = "KEY_TV", .value = KEY_TV }, + { .name = "KEY_TV2", .value = KEY_TV2 }, + { .name = "KEY_TWEN", .value = KEY_TWEN }, + { .name = "KEY_U", .value = KEY_U }, + { .name = "KEY_UNDO", .value = KEY_UNDO }, + { .name = "KEY_UNKNOWN", .value = KEY_UNKNOWN }, + { .name = "KEY_UP", .value = KEY_UP }, + { .name = "KEY_UWB", .value = KEY_UWB }, + { .name = "KEY_V", .value = KEY_V }, + { .name = "KEY_VCR", .value = KEY_VCR }, + { .name = "KEY_VCR2", .value = KEY_VCR2 }, + { .name = "KEY_VENDOR", .value = KEY_VENDOR }, + { .name = "KEY_VIDEO", .value = KEY_VIDEO }, + { .name = "KEY_VIDEOPHONE", .value = KEY_VIDEOPHONE }, + { .name = "KEY_VIDEO_NEXT", .value = KEY_VIDEO_NEXT }, + { .name = "KEY_VIDEO_PREV", .value = KEY_VIDEO_PREV }, + { .name = "KEY_VOICECOMMAND", .value = KEY_VOICECOMMAND }, + { .name = "KEY_VOICEMAIL", .value = KEY_VOICEMAIL }, + { .name = "KEY_VOLUMEDOWN", .value = KEY_VOLUMEDOWN }, + { .name = "KEY_VOLUMEUP", .value = KEY_VOLUMEUP }, + { .name = "KEY_W", .value = KEY_W }, + { .name = "KEY_WAKEUP", .value = KEY_WAKEUP }, + { .name = "KEY_WLAN", .value = KEY_WLAN }, + { .name = "KEY_WORDPROCESSOR", .value = KEY_WORDPROCESSOR }, + { .name = "KEY_WPS_BUTTON", .value = KEY_WPS_BUTTON }, + { .name = "KEY_WWAN", .value = KEY_WWAN }, + { .name = "KEY_WWW", .value = KEY_WWW }, + { .name = "KEY_X", .value = KEY_X }, + { .name = "KEY_XFER", .value = KEY_XFER }, + { .name = "KEY_Y", .value = KEY_Y }, + { .name = "KEY_YELLOW", .value = KEY_YELLOW }, + { .name = "KEY_YEN", .value = KEY_YEN }, + { .name = "KEY_Z", .value = KEY_Z }, + { .name = "KEY_ZENKAKUHANKAKU", .value = KEY_ZENKAKUHANKAKU }, + { .name = "KEY_ZOOM", .value = KEY_ZOOM }, + { .name = "KEY_ZOOMIN", .value = KEY_ZOOMIN }, + { .name = "KEY_ZOOMOUT", .value = KEY_ZOOMOUT }, + { .name = "KEY_ZOOMRESET", .value = KEY_ZOOMRESET }, + { .name = "LED_CAPSL", .value = LED_CAPSL }, + { .name = "LED_CHARGING", .value = LED_CHARGING }, + { .name = "LED_COMPOSE", .value = LED_COMPOSE }, + { .name = "LED_KANA", .value = LED_KANA }, + { .name = "LED_MAIL", .value = LED_MAIL }, + { .name = "LED_MAX", .value = LED_MAX }, + { .name = "LED_MISC", .value = LED_MISC }, + { .name = "LED_MUTE", .value = LED_MUTE }, + { .name = "LED_NUML", .value = LED_NUML }, + { .name = "LED_SCROLLL", .value = LED_SCROLLL }, + { .name = "LED_SLEEP", .value = LED_SLEEP }, + { .name = "LED_SUSPEND", .value = LED_SUSPEND }, + { .name = "MSC_GESTURE", .value = MSC_GESTURE }, + { .name = "MSC_MAX", .value = MSC_MAX }, + { .name = "MSC_PULSELED", .value = MSC_PULSELED }, + { .name = "MSC_RAW", .value = MSC_RAW }, + { .name = "MSC_SCAN", .value = MSC_SCAN }, + { .name = "MSC_SERIAL", .value = MSC_SERIAL }, + { .name = "MSC_TIMESTAMP", .value = MSC_TIMESTAMP }, + { .name = "REL_DIAL", .value = REL_DIAL }, + { .name = "REL_HWHEEL", .value = REL_HWHEEL }, + { .name = "REL_MAX", .value = REL_MAX }, + { .name = "REL_MISC", .value = REL_MISC }, + { .name = "REL_RX", .value = REL_RX }, + { .name = "REL_RY", .value = REL_RY }, + { .name = "REL_RZ", .value = REL_RZ }, + { .name = "REL_WHEEL", .value = REL_WHEEL }, + { .name = "REL_X", .value = REL_X }, + { .name = "REL_Y", .value = REL_Y }, + { .name = "REL_Z", .value = REL_Z }, + { .name = "REP_DELAY", .value = REP_DELAY }, + { .name = "REP_MAX", .value = REP_MAX }, + { .name = "SND_BELL", .value = SND_BELL }, + { .name = "SND_CLICK", .value = SND_CLICK }, + { .name = "SND_MAX", .value = SND_MAX }, + { .name = "SND_TONE", .value = SND_TONE }, + { .name = "SW_CAMERA_LENS_COVER", .value = SW_CAMERA_LENS_COVER }, + { .name = "SW_DOCK", .value = SW_DOCK }, + { .name = "SW_FRONT_PROXIMITY", .value = SW_FRONT_PROXIMITY }, + { .name = "SW_HEADPHONE_INSERT", .value = SW_HEADPHONE_INSERT }, + { .name = "SW_JACK_PHYSICAL_INSERT", .value = SW_JACK_PHYSICAL_INSERT }, + { .name = "SW_KEYPAD_SLIDE", .value = SW_KEYPAD_SLIDE }, + { .name = "SW_LID", .value = SW_LID }, + { .name = "SW_LINEIN_INSERT", .value = SW_LINEIN_INSERT }, + { .name = "SW_LINEOUT_INSERT", .value = SW_LINEOUT_INSERT }, + { .name = "SW_MAX", .value = SW_MAX }, + { .name = "SW_MICROPHONE_INSERT", .value = SW_MICROPHONE_INSERT }, + { .name = "SW_MUTE_DEVICE", .value = SW_MUTE_DEVICE }, + { .name = "SW_RFKILL_ALL", .value = SW_RFKILL_ALL }, + { .name = "SW_ROTATE_LOCK", .value = SW_ROTATE_LOCK }, + { .name = "SW_TABLET_MODE", .value = SW_TABLET_MODE }, + { .name = "SW_VIDEOOUT_INSERT", .value = SW_VIDEOOUT_INSERT }, + { .name = "SYN_CONFIG", .value = SYN_CONFIG }, + { .name = "SYN_DROPPED", .value = SYN_DROPPED }, + { .name = "SYN_MAX", .value = SYN_MAX }, + { .name = "SYN_MT_REPORT", .value = SYN_MT_REPORT }, + { .name = "SYN_REPORT", .value = SYN_REPORT }, +}; + +static const struct name_entry prop_names[] = { + { .name = "INPUT_PROP_ACCELEROMETER", .value = INPUT_PROP_ACCELEROMETER }, + { .name = "INPUT_PROP_BUTTONPAD", .value = INPUT_PROP_BUTTONPAD }, + { .name = "INPUT_PROP_DIRECT", .value = INPUT_PROP_DIRECT }, + { .name = "INPUT_PROP_MAX", .value = INPUT_PROP_MAX }, + { .name = "INPUT_PROP_POINTER", .value = INPUT_PROP_POINTER }, + { .name = "INPUT_PROP_POINTING_STICK", .value = INPUT_PROP_POINTING_STICK }, + { .name = "INPUT_PROP_SEMI_MT", .value = INPUT_PROP_SEMI_MT }, + { .name = "INPUT_PROP_TOPBUTTONPAD", .value = INPUT_PROP_TOPBUTTONPAD }, +}; + +#endif /* EVENT_NAMES_H */ diff --git a/Linux/libevdev/libevdev-int.h b/Linux/libevdev/libevdev-int.h new file mode 100644 index 0000000..8b0e1b2 --- /dev/null +++ b/Linux/libevdev/libevdev-int.h @@ -0,0 +1,341 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef LIBEVDEV_INT_H +#define LIBEVDEV_INT_H + +#include +#include +#include +#include +#include "libevdev.h" +#include "libevdev-util.h" + +#define MAX_NAME 256 +#define ABS_MT_MIN ABS_MT_SLOT +#define ABS_MT_MAX ABS_MT_TOOL_Y +#define ABS_MT_CNT (ABS_MT_MAX - ABS_MT_MIN + 1) +#define LIBEVDEV_EXPORT __attribute__((visibility("default"))) +#define ALIAS(_to) __attribute__((alias(#_to))) + +/** + * Sync state machine: + * default state: SYNC_NONE + * + * SYNC_NONE → SYN_DROPPED or forced sync → SYNC_NEEDED + * SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC) → SYNC_IN_PROGRESS + * SYNC_NEEDED → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) → SYNC_NONE + * SYNC_IN_PROGRESS → libevdev_next_event(LIBEVDEV_READ_FLAG_SYNC_NONE) → SYNC_NONE + * SYNC_IN_PROGRESS → no sync events left → SYNC_NONE + * + */ +enum SyncState { + SYNC_NONE, + SYNC_NEEDED, + SYNC_IN_PROGRESS, +}; + +struct mt_sync_state { + int code; + int val[]; +}; + +/** + * Internal only: log data used to send messages to the respective log + * handler. We re-use the same struct for a global and inside + * struct libevdev. + * For the global, device_handler is NULL, for per-device instance + * global_handler is NULL. + */ +struct logdata { + enum libevdev_log_priority priority; /** minimum logging priority */ + libevdev_log_func_t global_handler; /** global handler function */ + libevdev_device_log_func_t device_handler; /** per-device handler function */ + void *userdata; /** user-defined data pointer */ +}; + +struct libevdev { + int fd; + bool initialized; + char *name; + char *phys; + char *uniq; + struct input_id ids; + int driver_version; + unsigned long bits[NLONGS(EV_CNT)]; + unsigned long props[NLONGS(INPUT_PROP_CNT)]; + unsigned long key_bits[NLONGS(KEY_CNT)]; + unsigned long rel_bits[NLONGS(REL_CNT)]; + unsigned long abs_bits[NLONGS(ABS_CNT)]; + unsigned long led_bits[NLONGS(LED_CNT)]; + unsigned long msc_bits[NLONGS(MSC_CNT)]; + unsigned long sw_bits[NLONGS(SW_CNT)]; + unsigned long rep_bits[NLONGS(REP_CNT)]; /* convenience, always 1 */ + unsigned long ff_bits[NLONGS(FF_CNT)]; + unsigned long snd_bits[NLONGS(SND_CNT)]; + unsigned long key_values[NLONGS(KEY_CNT)]; + unsigned long led_values[NLONGS(LED_CNT)]; + unsigned long sw_values[NLONGS(SW_CNT)]; + struct input_absinfo abs_info[ABS_CNT]; + int *mt_slot_vals; /* [num_slots * ABS_MT_CNT] */ + int num_slots; /**< valid slots in mt_slot_vals */ + int current_slot; + int rep_values[REP_CNT]; + + enum SyncState sync_state; + enum libevdev_grab_mode grabbed; + + struct input_event *queue; + size_t queue_size; /**< size of queue in elements */ + size_t queue_next; /**< next event index */ + size_t queue_nsync; /**< number of sync events */ + + struct timeval last_event_time; + + struct { + struct mt_sync_state *mt_state; + size_t mt_state_sz; /* in bytes */ + unsigned long *slot_update; + size_t slot_update_sz; /* in bytes */ + unsigned long *tracking_id_changes; + size_t tracking_id_changes_sz; /* in bytes */ + } mt_sync; + + struct logdata log; +}; + +#define log_msg_cond(dev, priority, ...) \ + do { \ + if (_libevdev_log_priority(dev) >= priority) \ + _libevdev_log_msg(dev, priority, __FILE__, __LINE__, __func__, __VA_ARGS__); \ + } while(0) + +#define log_error(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_ERROR, __VA_ARGS__) +#define log_info(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_INFO, __VA_ARGS__) +#define log_dbg(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_DEBUG, __VA_ARGS__) +#define log_bug(dev, ...) log_msg_cond(dev, LIBEVDEV_LOG_ERROR, "BUG: "__VA_ARGS__) + +extern void +_libevdev_log_msg(const struct libevdev *dev, + enum libevdev_log_priority priority, + const char *file, int line, const char *func, + const char *format, ...) LIBEVDEV_ATTRIBUTE_PRINTF(6, 7); +extern enum libevdev_log_priority +_libevdev_log_priority(const struct libevdev *dev); + +/** + * @return a pointer to the next element in the queue, or NULL if the queue + * is full. + */ +static inline struct input_event* +queue_push(struct libevdev *dev) +{ + if (dev->queue_next >= dev->queue_size) + return NULL; + + return &dev->queue[dev->queue_next++]; +} + +/** + * Set ev to the last element in the queue, removing it from the queue. + * + * @return 0 on success, 1 if the queue is empty. + */ +static inline int +queue_pop(struct libevdev *dev, struct input_event *ev) +{ + if (dev->queue_next == 0) + return 1; + + *ev = dev->queue[--dev->queue_next]; + + return 0; +} + +static inline int +queue_peek(struct libevdev *dev, size_t idx, struct input_event *ev) +{ + if (dev->queue_next == 0 || idx > dev->queue_next) + return 1; + *ev = dev->queue[idx]; + return 0; +} + +/** + * Shift the first n elements into ev and return the number of elements + * shifted. + * ev must be large enough to store n elements. + * + * @param ev The buffer to copy into, or NULL + * @return The number of elements in ev. + */ +static inline int +queue_shift_multiple(struct libevdev *dev, size_t n, struct input_event *ev) +{ + size_t remaining; + + if (dev->queue_next == 0) + return 0; + + remaining = dev->queue_next; + n = min(n, remaining); + remaining -= n; + + if (ev) + memcpy(ev, dev->queue, n * sizeof(*ev)); + + memmove(dev->queue, &dev->queue[n], remaining * sizeof(*dev->queue)); + + dev->queue_next = remaining; + return n; +} + +/** + * Set ev to the first element in the queue, shifting everything else + * forward by one. + * + * @return 0 on success, 1 if the queue is empty. + */ +static inline int +queue_shift(struct libevdev *dev, struct input_event *ev) +{ + return queue_shift_multiple(dev, 1, ev) == 1 ? 0 : 1; +} + +static inline int +queue_alloc(struct libevdev *dev, size_t size) +{ + if (size == 0) + return -ENOMEM; + + dev->queue = calloc(size, sizeof(struct input_event)); + if (!dev->queue) + return -ENOMEM; + + dev->queue_size = size; + dev->queue_next = 0; + return 0; +} + +static inline void +queue_free(struct libevdev *dev) +{ + free(dev->queue); + dev->queue_size = 0; + dev->queue_next = 0; +} + +static inline size_t +queue_num_elements(struct libevdev *dev) +{ + return dev->queue_next; +} + +static inline size_t +queue_size(struct libevdev *dev) +{ + return dev->queue_size; +} + +static inline size_t +queue_num_free_elements(struct libevdev *dev) +{ + if (dev->queue_size == 0) + return 0; + + return dev->queue_size - dev->queue_next; +} + +static inline struct input_event * +queue_next_element(struct libevdev *dev) +{ + if (dev->queue_next == dev->queue_size) + return NULL; + + return &dev->queue[dev->queue_next]; +} + +static inline int +queue_set_num_elements(struct libevdev *dev, size_t nelem) +{ + if (nelem > dev->queue_size) + return 1; + + dev->queue_next = nelem; + + return 0; +} + +#define max_mask(uc, lc) \ + case EV_##uc: \ + *mask = dev->lc##_bits; \ + max = libevdev_event_type_get_max(type); \ + break; + +static inline int +type_to_mask_const(const struct libevdev *dev, unsigned int type, const unsigned long **mask) +{ + int max; + + switch(type) { + max_mask(ABS, abs); + max_mask(REL, rel); + max_mask(KEY, key); + max_mask(LED, led); + max_mask(MSC, msc); + max_mask(SW, sw); + max_mask(FF, ff); + max_mask(REP, rep); + max_mask(SND, snd); + default: + max = -1; + break; + } + + return max; +} + +static inline int +type_to_mask(struct libevdev *dev, unsigned int type, unsigned long **mask) +{ + int max; + + switch(type) { + max_mask(ABS, abs); + max_mask(REL, rel); + max_mask(KEY, key); + max_mask(LED, led); + max_mask(MSC, msc); + max_mask(SW, sw); + max_mask(FF, ff); + max_mask(REP, rep); + max_mask(SND, snd); + default: + max = -1; + break; + } + + return max; +} + +#undef max_mask +#endif diff --git a/Linux/libevdev/libevdev-util.h b/Linux/libevdev/libevdev-util.h new file mode 100644 index 0000000..dad7591 --- /dev/null +++ b/Linux/libevdev/libevdev-util.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef _UTIL_H_ +#define _UTIL_H_ + +#include +#include + +#define LONG_BITS (sizeof(long) * 8) +#define NLONGS(x) (((x) + LONG_BITS - 1) / LONG_BITS) +#define ARRAY_LENGTH(a) (sizeof(a) / (sizeof((a)[0]))) +#define unlikely(x) (__builtin_expect(!!(x),0)) + +#undef min +#undef max +#define min(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _b : _a; \ + }) +#define max(a,b) \ + ({ __typeof__ (a) _a = (a); \ + __typeof__ (b) _b = (b); \ + _a > _b ? _a : _b; \ + }) + +static inline bool +startswith(const char *str, size_t len, const char *prefix, size_t plen) +{ + return len >= plen && !strncmp(str, prefix, plen); +} + +static inline int +bit_is_set(const unsigned long *array, int bit) +{ + return !!(array[bit / LONG_BITS] & (1LL << (bit % LONG_BITS))); +} + +static inline void +set_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] |= (1LL << (bit % LONG_BITS)); +} + +static inline void +clear_bit(unsigned long *array, int bit) +{ + array[bit / LONG_BITS] &= ~(1LL << (bit % LONG_BITS)); +} + +static inline void +set_bit_state(unsigned long *array, int bit, int state) +{ + if (state) + set_bit(array, bit); + else + clear_bit(array, bit); +} + +#endif diff --git a/Linux/libevdev/libevdev.c b/Linux/libevdev/libevdev.c new file mode 100644 index 0000000..9589a64 --- /dev/null +++ b/Linux/libevdev/libevdev.c @@ -0,0 +1,1722 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "libevdev.h" +#include "libevdev-int.h" +#include "libevdev-util.h" +#include "event-names.h" + +#define MAXEVENTS 64 + +enum event_filter_status { + EVENT_FILTER_NONE, /**< Event untouched by filters */ + EVENT_FILTER_MODIFIED, /**< Event was modified */ + EVENT_FILTER_DISCARD, /**< Discard current event */ +}; + +static int sync_mt_state(struct libevdev *dev, int create_events); + +static inline int* +slot_value(const struct libevdev *dev, int slot, int axis) +{ + if (unlikely(slot > dev->num_slots)) { + log_bug(dev, "Slot %d exceeds number of slots (%d)\n", slot, dev->num_slots); + slot = 0; + } + if (unlikely(axis < ABS_MT_MIN || axis > ABS_MT_MAX)) { + log_bug(dev, "MT axis %d is outside the valid range [%d,%d]\n", + axis, ABS_MT_MIN, ABS_MT_MAX); + axis = ABS_MT_MIN; + } + return &dev->mt_slot_vals[slot * ABS_MT_CNT + axis - ABS_MT_MIN]; +} + +static int +init_event_queue(struct libevdev *dev) +{ + const int MIN_QUEUE_SIZE = 256; + int nevents = 1; /* terminating SYN_REPORT */ + int nslots; + unsigned int type, code; + + /* count the number of axes, keys, etc. to get a better idea at how + many events per EV_SYN we could possibly get. That's the max we + may get during SYN_DROPPED too. Use double that, just so we have + room for events while syncing a device. + */ + for (type = EV_KEY; type < EV_MAX; type++) { + int max = libevdev_event_type_get_max(type); + for (code = 0; max > 0 && code < (unsigned int) max; code++) { + if (libevdev_has_event_code(dev, type, code)) + nevents++; + } + } + + nslots = libevdev_get_num_slots(dev); + if (nslots > 1) { + int num_mt_axes = 0; + + for (code = ABS_MT_SLOT; code <= ABS_MAX; code++) { + if (libevdev_has_event_code(dev, EV_ABS, code)) + num_mt_axes++; + } + + /* We already counted the first slot in the initial count */ + nevents += num_mt_axes * (nslots - 1); + } + + return queue_alloc(dev, max(MIN_QUEUE_SIZE, nevents * 2)); +} + +static void +libevdev_dflt_log_func(enum libevdev_log_priority priority, + void *data, + const char *file, int line, const char *func, + const char *format, va_list args) +{ + const char *prefix; + switch(priority) { + case LIBEVDEV_LOG_ERROR: prefix = "libevdev error"; break; + case LIBEVDEV_LOG_INFO: prefix = "libevdev info"; break; + case LIBEVDEV_LOG_DEBUG: + prefix = "libevdev debug"; + break; + default: + prefix = "libevdev INVALID LOG PRIORITY"; + break; + } + /* default logging format: + libevev error in libevdev_some_func: blah blah + libevev info in libevdev_some_func: blah blah + libevev debug in file.c:123:libevdev_some_func: blah blah + */ + + fprintf(stderr, "%s in ", prefix); + if (priority == LIBEVDEV_LOG_DEBUG) + fprintf(stderr, "%s:%d:", file, line); + fprintf(stderr, "%s: ", func); + vfprintf(stderr, format, args); +} + +static void +fix_invalid_absinfo(const struct libevdev *dev, + int axis, + struct input_absinfo* abs_info) +{ + /* + * The reported absinfo for ABS_MT_TRACKING_ID is sometimes + * uninitialized for certain mtk-soc, due to init code mangling + * in the vendor kernel. + */ + if (axis == ABS_MT_TRACKING_ID && + abs_info->maximum == abs_info->minimum) { + abs_info->minimum = -1; + abs_info->maximum = 0xFFFF; + log_bug(dev, + "Device \"%s\" has invalid ABS_MT_TRACKING_ID range", + dev->name); + } +} + +/* + * Global logging settings. + */ +static struct logdata log_data = { + .priority = LIBEVDEV_LOG_INFO, + .global_handler = libevdev_dflt_log_func, + .userdata = NULL, +}; + +void +_libevdev_log_msg(const struct libevdev *dev, + enum libevdev_log_priority priority, + const char *file, int line, const char *func, + const char *format, ...) +{ + va_list args; + + if (dev && dev->log.device_handler) { + /** + * if both global handler and device handler are set + * we've set up the handlers wrong. And that means we'll + * likely get the printf args wrong and cause all sorts of + * mayhem. Seppuku is called for. + */ + if (unlikely(dev->log.global_handler)) + abort(); + + if (priority > dev->log.priority) + return; + } else if (!log_data.global_handler || priority > log_data.priority) + return; + else if (unlikely(log_data.device_handler)) + abort(); /* Seppuku, see above */ + + va_start(args, format); + if (dev && dev->log.device_handler) + dev->log.device_handler(dev, priority, dev->log.userdata, file, line, func, format, args); + else + log_data.global_handler(priority, log_data.userdata, file, line, func, format, args); + va_end(args); +} + +static void +libevdev_reset(struct libevdev *dev) +{ + enum libevdev_log_priority pri = dev->log.priority; + libevdev_device_log_func_t handler = dev->log.device_handler; + + free(dev->name); + free(dev->phys); + free(dev->uniq); + free(dev->mt_slot_vals); + free(dev->mt_sync.mt_state); + free(dev->mt_sync.tracking_id_changes); + free(dev->mt_sync.slot_update); + memset(dev, 0, sizeof(*dev)); + dev->fd = -1; + dev->initialized = false; + dev->num_slots = -1; + dev->current_slot = -1; + dev->grabbed = LIBEVDEV_UNGRAB; + dev->sync_state = SYNC_NONE; + dev->log.priority = pri; + dev->log.device_handler = handler; + libevdev_enable_event_type(dev, EV_SYN); +} + +LIBEVDEV_EXPORT struct libevdev* +libevdev_new(void) +{ + struct libevdev *dev; + + dev = calloc(1, sizeof(*dev)); + if (!dev) + return NULL; + + libevdev_reset(dev); + + return dev; +} + +LIBEVDEV_EXPORT int +libevdev_new_from_fd(int fd, struct libevdev **dev) +{ + struct libevdev *d; + int rc; + + d = libevdev_new(); + if (!d) + return -ENOMEM; + + rc = libevdev_set_fd(d, fd); + if (rc < 0) + libevdev_free(d); + else + *dev = d; + return rc; +} + +LIBEVDEV_EXPORT void +libevdev_free(struct libevdev *dev) +{ + if (!dev) + return; + + queue_free(dev); + libevdev_reset(dev); + free(dev); +} + +LIBEVDEV_EXPORT void +libevdev_set_log_function(libevdev_log_func_t logfunc, void *data) +{ + log_data.global_handler = logfunc; + log_data.userdata = data; +} + +LIBEVDEV_EXPORT void +libevdev_set_log_priority(enum libevdev_log_priority priority) +{ + if (priority > LIBEVDEV_LOG_DEBUG) + priority = LIBEVDEV_LOG_DEBUG; + log_data.priority = priority; +} + +LIBEVDEV_EXPORT enum libevdev_log_priority +libevdev_get_log_priority(void) +{ + return log_data.priority; +} + +LIBEVDEV_EXPORT void +libevdev_set_device_log_function(struct libevdev *dev, + libevdev_device_log_func_t logfunc, + enum libevdev_log_priority priority, + void *data) +{ + if (!dev) { + log_bug(NULL, "device must not be NULL\n"); + return; + } + + dev->log.priority = priority; + dev->log.device_handler = logfunc; + dev->log.userdata = data; +} + +enum libevdev_log_priority +_libevdev_log_priority(const struct libevdev *dev) +{ + if (dev && dev->log.device_handler) + return dev->log.priority; + else + return libevdev_get_log_priority(); +} + +LIBEVDEV_EXPORT int +libevdev_change_fd(struct libevdev *dev, int fd) +{ + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -1; + } + dev->fd = fd; + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_set_fd(struct libevdev* dev, int fd) +{ + int rc; + int i; + char buf[256]; + + if (dev->initialized) { + log_bug(dev, "device already initialized.\n"); + return -EBADF; + } else if (fd < 0) + return -EBADF; + + libevdev_reset(dev); + + rc = ioctl(fd, EVIOCGBIT(0, sizeof(dev->bits)), dev->bits); + if (rc < 0) + goto out; + + memset(buf, 0, sizeof(buf)); + rc = ioctl(fd, EVIOCGNAME(sizeof(buf) - 1), buf); + if (rc < 0) + goto out; + + free(dev->name); + dev->name = strdup(buf); + if (!dev->name) { + errno = ENOMEM; + goto out; + } + + free(dev->phys); + dev->phys = NULL; + memset(buf, 0, sizeof(buf)); + rc = ioctl(fd, EVIOCGPHYS(sizeof(buf) - 1), buf); + if (rc < 0) { + /* uinput has no phys */ + if (errno != ENOENT) + goto out; + } else { + dev->phys = strdup(buf); + if (!dev->phys) { + errno = ENOMEM; + goto out; + } + } + + free(dev->uniq); + dev->uniq = NULL; + memset(buf, 0, sizeof(buf)); + rc = ioctl(fd, EVIOCGUNIQ(sizeof(buf) - 1), buf); + if (rc < 0) { + if (errno != ENOENT) + goto out; + } else { + dev->uniq = strdup(buf); + if (!dev->uniq) { + errno = ENOMEM; + goto out; + } + } + + rc = ioctl(fd, EVIOCGID, &dev->ids); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGVERSION, &dev->driver_version); + if (rc < 0) + goto out; + + /* Built on a kernel with props, running against a kernel without property + support. This should not be a fatal case, we'll be missing properties but other + than that everything is as expected. + */ + rc = ioctl(fd, EVIOCGPROP(sizeof(dev->props)), dev->props); + if (rc < 0 && errno != EINVAL) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_REL, sizeof(dev->rel_bits)), dev->rel_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(dev->abs_bits)), dev->abs_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_LED, sizeof(dev->led_bits)), dev->led_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(dev->key_bits)), dev->key_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_SW, sizeof(dev->sw_bits)), dev->sw_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_MSC, sizeof(dev->msc_bits)), dev->msc_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_FF, sizeof(dev->ff_bits)), dev->ff_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGBIT(EV_SND, sizeof(dev->snd_bits)), dev->snd_bits); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGKEY(sizeof(dev->key_values)), dev->key_values); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGLED(sizeof(dev->led_values)), dev->led_values); + if (rc < 0) + goto out; + + rc = ioctl(fd, EVIOCGSW(sizeof(dev->sw_values)), dev->sw_values); + if (rc < 0) + goto out; + + /* rep is a special case, always set it to 1 for both values if EV_REP is set */ + if (bit_is_set(dev->bits, EV_REP)) { + for (i = 0; i < REP_CNT; i++) + set_bit(dev->rep_bits, i); + rc = ioctl(fd, EVIOCGREP, dev->rep_values); + if (rc < 0) + goto out; + } + + for (i = ABS_X; i <= ABS_MAX; i++) { + if (bit_is_set(dev->abs_bits, i)) { + struct input_absinfo abs_info; + rc = ioctl(fd, EVIOCGABS(i), &abs_info); + if (rc < 0) + goto out; + + fix_invalid_absinfo(dev, i, &abs_info); + + dev->abs_info[i] = abs_info; + } + } + + dev->fd = fd; + + /* devices with ABS_MT_SLOT - 1 aren't MT devices, + see the documentation for multitouch-related + functions for more details */ + if (!libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT - 1) && + libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) { + const struct input_absinfo *abs_info; + + abs_info = libevdev_get_abs_info(dev, ABS_MT_SLOT); + + dev->num_slots = abs_info->maximum + 1; + dev->mt_slot_vals = calloc(dev->num_slots * ABS_MT_CNT, sizeof(int)); + if (!dev->mt_slot_vals) { + rc = -ENOMEM; + goto out; + } + dev->current_slot = abs_info->value; + + dev->mt_sync.mt_state_sz = sizeof(*dev->mt_sync.mt_state) + + (dev->num_slots) * sizeof(int); + dev->mt_sync.mt_state = calloc(1, dev->mt_sync.mt_state_sz); + + dev->mt_sync.tracking_id_changes_sz = NLONGS(dev->num_slots) * sizeof(long); + dev->mt_sync.tracking_id_changes = malloc(dev->mt_sync.tracking_id_changes_sz); + + dev->mt_sync.slot_update_sz = NLONGS(dev->num_slots * ABS_MT_CNT) * sizeof(long); + dev->mt_sync.slot_update = malloc(dev->mt_sync.slot_update_sz); + + if (!dev->mt_sync.tracking_id_changes || + !dev->mt_sync.slot_update || + !dev->mt_sync.mt_state) { + rc = -ENOMEM; + goto out; + } + + sync_mt_state(dev, 0); + } + + rc = init_event_queue(dev); + if (rc < 0) { + dev->fd = -1; + return -rc; + } + + /* not copying key state because we won't know when we'll start to + * use this fd and key's are likely to change state by then. + * Same with the valuators, really, but they may not change. + */ + + dev->initialized = true; +out: + if (rc) + libevdev_reset(dev); + return rc ? -errno : 0; +} + +LIBEVDEV_EXPORT int +libevdev_get_fd(const struct libevdev* dev) +{ + return dev->fd; +} + +static inline void +init_event(struct libevdev *dev, struct input_event *ev, int type, int code, int value) +{ + ev->time = dev->last_event_time; + ev->type = type; + ev->code = code; + ev->value = value; +} + +static int +sync_key_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long keystate[NLONGS(KEY_CNT)] = {0}; + + rc = ioctl(dev->fd, EVIOCGKEY(sizeof(keystate)), keystate); + if (rc < 0) + goto out; + + for (i = 0; i < KEY_CNT; i++) { + int old, new; + old = bit_is_set(dev->key_values, i); + new = bit_is_set(keystate, i); + if (old ^ new) { + struct input_event *ev = queue_push(dev); + init_event(dev, ev, EV_KEY, i, new ? 1 : 0); + } + } + + memcpy(dev->key_values, keystate, rc); + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_sw_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long swstate[NLONGS(SW_CNT)] = {0}; + + rc = ioctl(dev->fd, EVIOCGSW(sizeof(swstate)), swstate); + if (rc < 0) + goto out; + + for (i = 0; i < SW_CNT; i++) { + int old, new; + old = bit_is_set(dev->sw_values, i); + new = bit_is_set(swstate, i); + if (old ^ new) { + struct input_event *ev = queue_push(dev); + init_event(dev, ev, EV_SW, i, new ? 1 : 0); + } + } + + memcpy(dev->sw_values, swstate, rc); + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_led_state(struct libevdev *dev) +{ + int rc; + int i; + unsigned long ledstate[NLONGS(LED_CNT)] = {0}; + + rc = ioctl(dev->fd, EVIOCGLED(sizeof(ledstate)), ledstate); + if (rc < 0) + goto out; + + for (i = 0; i < LED_CNT; i++) { + int old, new; + old = bit_is_set(dev->led_values, i); + new = bit_is_set(ledstate, i); + if (old ^ new) { + struct input_event *ev = queue_push(dev); + init_event(dev, ev, EV_LED, i, new ? 1 : 0); + } + } + + memcpy(dev->led_values, ledstate, rc); + + rc = 0; +out: + return rc ? -errno : 0; +} +static int +sync_abs_state(struct libevdev *dev) +{ + int rc; + int i; + + for (i = ABS_X; i < ABS_CNT; i++) { + struct input_absinfo abs_info; + + if (i >= ABS_MT_MIN && i <= ABS_MT_MAX) + continue; + + if (!bit_is_set(dev->abs_bits, i)) + continue; + + rc = ioctl(dev->fd, EVIOCGABS(i), &abs_info); + if (rc < 0) + goto out; + + if (dev->abs_info[i].value != abs_info.value) { + struct input_event *ev = queue_push(dev); + + init_event(dev, ev, EV_ABS, i, abs_info.value); + dev->abs_info[i].value = abs_info.value; + } + } + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +sync_mt_state(struct libevdev *dev, int create_events) +{ + struct input_event *ev; + struct input_absinfo abs_info; + int rc; + int axis, slot; + int ioctl_success = 0; + int last_reported_slot = 0; + struct mt_sync_state *mt_state = dev->mt_sync.mt_state; + unsigned long *slot_update = dev->mt_sync.slot_update; + unsigned long *tracking_id_changes = dev->mt_sync.tracking_id_changes; + int need_tracking_id_changes = 0; + + memset(dev->mt_sync.slot_update, 0, dev->mt_sync.slot_update_sz); + memset(dev->mt_sync.tracking_id_changes, 0, + dev->mt_sync.tracking_id_changes_sz); + +#define AXISBIT(_slot, _axis) (_slot * ABS_MT_CNT + _axis - ABS_MT_MIN) + + for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) { + if (axis == ABS_MT_SLOT) + continue; + + if (!libevdev_has_event_code(dev, EV_ABS, axis)) + continue; + + mt_state->code = axis; + rc = ioctl(dev->fd, EVIOCGMTSLOTS(dev->mt_sync.mt_state_sz), mt_state); + if (rc < 0) { + /* if the first ioctl fails with -EINVAL, chances are the kernel + doesn't support the ioctl. Simply continue */ + if (errno == -EINVAL && !ioctl_success) { + rc = 0; + } else /* if the second, ... ioctl fails, really fail */ + goto out; + } else { + if (ioctl_success == 0) + ioctl_success = 1; + + for (slot = 0; slot < dev->num_slots; slot++) { + + if (*slot_value(dev, slot, axis) == mt_state->val[slot]) + continue; + + if (axis == ABS_MT_TRACKING_ID && + *slot_value(dev, slot, axis) != -1 && + mt_state->val[slot] != -1) { + set_bit(tracking_id_changes, slot); + need_tracking_id_changes = 1; + } + + *slot_value(dev, slot, axis) = mt_state->val[slot]; + + set_bit(slot_update, AXISBIT(slot, axis)); + /* note that this slot has updates */ + set_bit(slot_update, AXISBIT(slot, ABS_MT_SLOT)); + } + + } + } + + if (!create_events) { + rc = 0; + goto out; + } + + if (need_tracking_id_changes) { + for (slot = 0; slot < dev->num_slots; slot++) { + if (!bit_is_set(tracking_id_changes, slot)) + continue; + + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot); + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_TRACKING_ID, -1); + + last_reported_slot = slot; + } + + ev = queue_push(dev); + init_event(dev, ev, EV_SYN, SYN_REPORT, 0); + } + + for (slot = 0; slot < dev->num_slots; slot++) { + if (!bit_is_set(slot_update, AXISBIT(slot, ABS_MT_SLOT))) + continue; + + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, slot); + last_reported_slot = slot; + + for (axis = ABS_MT_MIN; axis <= ABS_MT_MAX; axis++) { + if (axis == ABS_MT_SLOT || + !libevdev_has_event_code(dev, EV_ABS, axis)) + continue; + + if (bit_is_set(slot_update, AXISBIT(slot, axis))) { + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, axis, *slot_value(dev, slot, axis)); + } + } + } + + /* add one last slot event to make sure the client is on the same + slot as the kernel */ + + rc = ioctl(dev->fd, EVIOCGABS(ABS_MT_SLOT), &abs_info); + if (rc < 0) + goto out; + + dev->current_slot = abs_info.value; + + if (dev->current_slot != last_reported_slot) { + ev = queue_push(dev); + init_event(dev, ev, EV_ABS, ABS_MT_SLOT, dev->current_slot); + } + +#undef AXISBIT + + rc = 0; +out: + return rc ? -errno : 0; +} + +static int +read_more_events(struct libevdev *dev) +{ + int free_elem; + int len; + struct input_event *next; + + free_elem = queue_num_free_elements(dev); + if (free_elem <= 0) + return 0; + + next = queue_next_element(dev); + len = read(dev->fd, next, free_elem * sizeof(struct input_event)); + if (len < 0) { + return -errno; + } else if (len > 0 && len % sizeof(struct input_event) != 0) + return -EINVAL; + else if (len > 0) { + int nev = len/sizeof(struct input_event); + queue_set_num_elements(dev, queue_num_elements(dev) + nev); + } + + return 0; +} + +static inline void +drain_events(struct libevdev *dev) +{ + int rc; + size_t nelem; + int iterations = 0; + const int max_iterations = 8; /* EVDEV_BUF_PACKETS in + kernel/drivers/input/evedev.c */ + + queue_shift_multiple(dev, queue_num_elements(dev), NULL); + + do { + rc = read_more_events(dev); + if (rc == -EAGAIN) + return; + + if (rc < 0) { + log_error(dev, "Failed to drain events before sync.\n"); + return; + } + + nelem = queue_num_elements(dev); + queue_shift_multiple(dev, nelem, NULL); + } while (iterations++ < max_iterations && nelem >= queue_size(dev)); + + /* Our buffer should be roughly the same or bigger than the kernel + buffer in most cases, so we usually don't expect to recurse. If + we do, make sure we stop after max_iterations and proceed with + what we have. This could happen if events queue up faster than + we can drain them. + */ + if (iterations >= max_iterations) + log_info(dev, "Unable to drain events, buffer size mismatch.\n"); +} + +static int +sync_state(struct libevdev *dev) +{ + int rc = 0; + struct input_event *ev; + + /* see section "Discarding events before synchronizing" in + * libevdev/libevdev.h */ + drain_events(dev); + + if (libevdev_has_event_type(dev, EV_KEY)) + rc = sync_key_state(dev); + if (libevdev_has_event_type(dev, EV_LED)) + rc = sync_led_state(dev); + if (libevdev_has_event_type(dev, EV_SW)) + rc = sync_sw_state(dev); + if (rc == 0 && libevdev_has_event_type(dev, EV_ABS)) + rc = sync_abs_state(dev); + if (rc == 0 && dev->num_slots > -1 && + libevdev_has_event_code(dev, EV_ABS, ABS_MT_SLOT)) + rc = sync_mt_state(dev, 1); + + dev->queue_nsync = queue_num_elements(dev); + + if (dev->queue_nsync > 0) { + ev = queue_push(dev); + init_event(dev, ev, EV_SYN, SYN_REPORT, 0); + dev->queue_nsync++; + } + + return rc; +} + +static int +update_key_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_KEY)) + return 1; + + if (e->code > KEY_MAX) + return 1; + + set_bit_state(dev->key_values, e->code, e->value != 0); + + return 0; +} + +static int +update_mt_state(struct libevdev *dev, const struct input_event *e) +{ + if (e->code == ABS_MT_SLOT && dev->num_slots > -1) { + int i; + dev->current_slot = e->value; + /* sync abs_info with the current slot values */ + for (i = ABS_MT_SLOT + 1; i <= ABS_MT_MAX; i++) { + if (libevdev_has_event_code(dev, EV_ABS, i)) + dev->abs_info[i].value = *slot_value(dev, dev->current_slot, i); + } + + return 0; + } else if (dev->current_slot == -1) + return 1; + + *slot_value(dev, dev->current_slot, e->code) = e->value; + + return 0; +} + +static int +update_abs_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_ABS)) + return 1; + + if (e->code > ABS_MAX) + return 1; + + if (e->code >= ABS_MT_MIN && e->code <= ABS_MT_MAX) + update_mt_state(dev, e); + + dev->abs_info[e->code].value = e->value; + + return 0; +} + +static int +update_led_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_LED)) + return 1; + + if (e->code > LED_MAX) + return 1; + + set_bit_state(dev->led_values, e->code, e->value != 0); + + return 0; +} + +static int +update_sw_state(struct libevdev *dev, const struct input_event *e) +{ + if (!libevdev_has_event_type(dev, EV_SW)) + return 1; + + if (e->code > SW_MAX) + return 1; + + set_bit_state(dev->sw_values, e->code, e->value != 0); + + return 0; +} + +static int +update_state(struct libevdev *dev, const struct input_event *e) +{ + int rc = 0; + + switch(e->type) { + case EV_SYN: + case EV_REL: + break; + case EV_KEY: + rc = update_key_state(dev, e); + break; + case EV_ABS: + rc = update_abs_state(dev, e); + break; + case EV_LED: + rc = update_led_state(dev, e); + break; + case EV_SW: + rc = update_sw_state(dev, e); + break; + } + + dev->last_event_time = e->time; + + return rc; +} + +/** + * Sanitize/modify events where needed. + */ +static inline enum event_filter_status +sanitize_event(const struct libevdev *dev, + struct input_event *ev, + enum SyncState sync_state) +{ + if (!libevdev_has_event_code(dev, ev->type, ev->code)) + return EVENT_FILTER_DISCARD; + + if (unlikely(dev->num_slots > -1 && + libevdev_event_is_code(ev, EV_ABS, ABS_MT_SLOT) && + (ev->value < 0 || ev->value >= dev->num_slots))) { + log_bug(dev, "Device \"%s\" received an invalid slot index %d." + "Capping to announced max slot number %d.\n", + dev->name, ev->value, dev->num_slots - 1); + ev->value = dev->num_slots - 1; + return EVENT_FILTER_MODIFIED; + + /* Drop any invalid tracking IDs, they are only supposed to go from + N to -1 or from -1 to N. Never from -1 to -1, or N to M. Very + unlikely to ever happen from a real device. + */ + } else if (unlikely(sync_state == SYNC_NONE && + dev->num_slots > -1 && + libevdev_event_is_code(ev, EV_ABS, ABS_MT_TRACKING_ID) && + ((ev->value == -1 && + *slot_value(dev, dev->current_slot, ABS_MT_TRACKING_ID) == -1) || + (ev->value != -1 && + *slot_value(dev, dev->current_slot, ABS_MT_TRACKING_ID) != -1)))) { + log_bug(dev, "Device \"%s\" received a double tracking ID %d in slot %d.\n", + dev->name, ev->value, dev->current_slot); + return EVENT_FILTER_DISCARD; + } + + return EVENT_FILTER_NONE; +} + +LIBEVDEV_EXPORT int +libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev) +{ + int rc = LIBEVDEV_READ_STATUS_SUCCESS; + enum event_filter_status filter_status; + const unsigned int valid_flags = LIBEVDEV_READ_FLAG_NORMAL | + LIBEVDEV_READ_FLAG_SYNC | + LIBEVDEV_READ_FLAG_FORCE_SYNC | + LIBEVDEV_READ_FLAG_BLOCKING; + + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if ((flags & valid_flags) == 0) { + log_bug(dev, "invalid flags %#x.\n", flags); + return -EINVAL; + } + + if (flags & LIBEVDEV_READ_FLAG_SYNC) { + if (dev->sync_state == SYNC_NEEDED) { + rc = sync_state(dev); + if (rc != 0) + return rc; + dev->sync_state = SYNC_IN_PROGRESS; + } + + if (dev->queue_nsync == 0) { + dev->sync_state = SYNC_NONE; + return -EAGAIN; + } + + } else if (dev->sync_state != SYNC_NONE) { + struct input_event e; + + /* call update_state for all events here, otherwise the library has the wrong view + of the device too */ + while (queue_shift(dev, &e) == 0) { + dev->queue_nsync--; + if (sanitize_event(dev, &e, dev->sync_state) != EVENT_FILTER_DISCARD) + update_state(dev, &e); + } + + dev->sync_state = SYNC_NONE; + } + + /* Always read in some more events. Best case this smoothes over a potential SYN_DROPPED, + worst case we don't read fast enough and end up with SYN_DROPPED anyway. + + Except if the fd is in blocking mode and we still have events from the last read, don't + read in any more. + */ + do { + if (!(flags & LIBEVDEV_READ_FLAG_BLOCKING) || + queue_num_elements(dev) == 0) { + rc = read_more_events(dev); + if (rc < 0 && rc != -EAGAIN) + goto out; + } + + if (flags & LIBEVDEV_READ_FLAG_FORCE_SYNC) { + dev->sync_state = SYNC_NEEDED; + rc = LIBEVDEV_READ_STATUS_SYNC; + goto out; + } + + if (queue_shift(dev, ev) != 0) + return -EAGAIN; + + filter_status = sanitize_event(dev, ev, dev->sync_state); + if (filter_status != EVENT_FILTER_DISCARD) + update_state(dev, ev); + + /* if we disabled a code, get the next event instead */ + } while(filter_status == EVENT_FILTER_DISCARD || + !libevdev_has_event_code(dev, ev->type, ev->code)); + + rc = LIBEVDEV_READ_STATUS_SUCCESS; + if (ev->type == EV_SYN && ev->code == SYN_DROPPED) { + dev->sync_state = SYNC_NEEDED; + rc = LIBEVDEV_READ_STATUS_SYNC; + } + + if (flags & LIBEVDEV_READ_FLAG_SYNC && dev->queue_nsync > 0) { + dev->queue_nsync--; + rc = LIBEVDEV_READ_STATUS_SYNC; + if (dev->queue_nsync == 0) { + struct input_event next; + dev->sync_state = SYNC_NONE; + + if (queue_peek(dev, 0, &next) == 0 && + next.type == EV_SYN && next.code == SYN_DROPPED) + log_info(dev, "SYN_DROPPED received after finished " + "sync - you're not keeping up\n"); + } + } + +out: + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_has_event_pending(struct libevdev *dev) +{ + struct pollfd fds = { dev->fd, POLLIN, 0 }; + int rc; + + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (queue_num_elements(dev) != 0) + return 1; + + rc = poll(&fds, 1, 0); + return (rc >= 0) ? rc : -errno; +} + +LIBEVDEV_EXPORT const char * +libevdev_get_name(const struct libevdev *dev) +{ + return dev->name ? dev->name : ""; +} + +LIBEVDEV_EXPORT const char * +libevdev_get_phys(const struct libevdev *dev) +{ + return dev->phys; +} + +LIBEVDEV_EXPORT const char * +libevdev_get_uniq(const struct libevdev *dev) +{ + return dev->uniq; +} + +#define STRING_SETTER(field) \ +LIBEVDEV_EXPORT void libevdev_set_##field(struct libevdev *dev, const char *field) \ +{ \ + if (field == NULL) \ + return; \ + free(dev->field); \ + dev->field = strdup(field); \ +} + +STRING_SETTER(name) +STRING_SETTER(phys) +STRING_SETTER(uniq) + +#define PRODUCT_GETTER(name) \ +LIBEVDEV_EXPORT int libevdev_get_id_##name(const struct libevdev *dev) \ +{ \ + return dev->ids.name; \ +} + +PRODUCT_GETTER(product) +PRODUCT_GETTER(vendor) +PRODUCT_GETTER(bustype) +PRODUCT_GETTER(version) + +#define PRODUCT_SETTER(field) \ +LIBEVDEV_EXPORT void libevdev_set_id_##field(struct libevdev *dev, int field) \ +{ \ + dev->ids.field = field;\ +} + +PRODUCT_SETTER(product) +PRODUCT_SETTER(vendor) +PRODUCT_SETTER(bustype) +PRODUCT_SETTER(version) + +LIBEVDEV_EXPORT int +libevdev_get_driver_version(const struct libevdev *dev) +{ + return dev->driver_version; +} + +LIBEVDEV_EXPORT int +libevdev_has_property(const struct libevdev *dev, unsigned int prop) +{ + return (prop <= INPUT_PROP_MAX) && bit_is_set(dev->props, prop); +} + +LIBEVDEV_EXPORT int +libevdev_enable_property(struct libevdev *dev, unsigned int prop) +{ + if (prop > INPUT_PROP_MAX) + return -1; + + set_bit(dev->props, prop); + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_has_event_type(const struct libevdev *dev, unsigned int type) +{ + return type == EV_SYN ||(type <= EV_MAX && bit_is_set(dev->bits, type)); +} + +LIBEVDEV_EXPORT int +libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + const unsigned long *mask = NULL; + int max; + + if (!libevdev_has_event_type(dev, type)) + return 0; + + if (type == EV_SYN) + return 1; + + max = type_to_mask_const(dev, type, &mask); + + if (max == -1 || code > (unsigned int)max) + return 0; + + return bit_is_set(mask, code); +} + +LIBEVDEV_EXPORT int +libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code) +{ + int value = 0; + + if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code)) + return 0; + + switch (type) { + case EV_ABS: value = dev->abs_info[code].value; break; + case EV_KEY: value = bit_is_set(dev->key_values, code); break; + case EV_LED: value = bit_is_set(dev->led_values, code); break; + case EV_SW: value = bit_is_set(dev->sw_values, code); break; + case EV_REP: + switch(code) { + case REP_DELAY: + libevdev_get_repeat(dev, &value, NULL); + break; + case REP_PERIOD: + libevdev_get_repeat(dev, NULL, &value); + break; + default: + value = 0; + break; + } + break; + default: + value = 0; + break; + } + + return value; +} + +LIBEVDEV_EXPORT int +libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int code, int value) +{ + int rc = 0; + struct input_event e; + + if (!libevdev_has_event_type(dev, type) || !libevdev_has_event_code(dev, type, code)) + return -1; + + e.type = type; + e.code = code; + e.value = value; + + if (sanitize_event(dev, &e, SYNC_NONE) != EVENT_FILTER_NONE) + return -1; + + switch(type) { + case EV_ABS: rc = update_abs_state(dev, &e); break; + case EV_KEY: rc = update_key_state(dev, &e); break; + case EV_LED: rc = update_led_state(dev, &e); break; + case EV_SW: rc = update_sw_state(dev, &e); break; + default: + rc = -1; + break; + } + + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, type) && + libevdev_has_event_code(dev, type, code)) { + *value = libevdev_get_event_value(dev, type, code); + return 1; + } else + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code)) + return 0; + + if (dev->num_slots < 0 || slot >= (unsigned int)dev->num_slots) + return 0; + + if (code > ABS_MT_MAX || code < ABS_MT_MIN) + return 0; + + return *slot_value(dev, slot, code); +} + +LIBEVDEV_EXPORT int +libevdev_set_slot_value(struct libevdev *dev, unsigned int slot, unsigned int code, int value) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || !libevdev_has_event_code(dev, EV_ABS, code)) + return -1; + + if (dev->num_slots == -1 || slot >= (unsigned int)dev->num_slots) + return -1; + + if (code > ABS_MT_MAX || code < ABS_MT_MIN) + return -1; + + if (code == ABS_MT_SLOT) { + if (value < 0 || value >= libevdev_get_num_slots(dev)) + return -1; + dev->current_slot = value; + } + + *slot_value(dev, slot, code) = value; + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value) +{ + if (libevdev_has_event_type(dev, EV_ABS) && + libevdev_has_event_code(dev, EV_ABS, code) && + dev->num_slots >= 0 && + slot < (unsigned int)dev->num_slots) { + *value = libevdev_get_slot_value(dev, slot, code); + return 1; + } else + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_get_num_slots(const struct libevdev *dev) +{ + return dev->num_slots; +} + +LIBEVDEV_EXPORT int +libevdev_get_current_slot(const struct libevdev *dev) +{ + return dev->current_slot; +} + +LIBEVDEV_EXPORT const struct input_absinfo* +libevdev_get_abs_info(const struct libevdev *dev, unsigned int code) +{ + if (!libevdev_has_event_type(dev, EV_ABS) || + !libevdev_has_event_code(dev, EV_ABS, code)) + return NULL; + + return &dev->abs_info[code]; +} + +#define ABS_GETTER(name) \ +LIBEVDEV_EXPORT int libevdev_get_abs_##name(const struct libevdev *dev, unsigned int code) \ +{ \ + const struct input_absinfo *absinfo = libevdev_get_abs_info(dev, code); \ + return absinfo ? absinfo->name : 0; \ +} + +ABS_GETTER(maximum) +ABS_GETTER(minimum) +ABS_GETTER(fuzz) +ABS_GETTER(flat) +ABS_GETTER(resolution) + +#define ABS_SETTER(field) \ +LIBEVDEV_EXPORT void libevdev_set_abs_##field(struct libevdev *dev, unsigned int code, int val) \ +{ \ + if (!libevdev_has_event_code(dev, EV_ABS, code)) \ + return; \ + dev->abs_info[code].field = val; \ +} + +ABS_SETTER(maximum) +ABS_SETTER(minimum) +ABS_SETTER(fuzz) +ABS_SETTER(flat) +ABS_SETTER(resolution) + +LIBEVDEV_EXPORT void +libevdev_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) +{ + if (!libevdev_has_event_code(dev, EV_ABS, code)) + return; + + dev->abs_info[code] = *abs; +} + +LIBEVDEV_EXPORT int +libevdev_enable_event_type(struct libevdev *dev, unsigned int type) +{ + int max; + + if (type > EV_MAX) + return -1; + + if (libevdev_has_event_type(dev, type)) + return 0; + + max = libevdev_event_type_get_max(type); + if (max == -1) + return -1; + + set_bit(dev->bits, type); + + if (type == EV_REP) { + int delay = 0, period = 0; + libevdev_enable_event_code(dev, EV_REP, REP_DELAY, &delay); + libevdev_enable_event_code(dev, EV_REP, REP_PERIOD, &period); + } + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_disable_event_type(struct libevdev *dev, unsigned int type) +{ + int max; + + if (type > EV_MAX || type == EV_SYN) + return -1; + + max = libevdev_event_type_get_max(type); + if (max == -1) + return -1; + + clear_bit(dev->bits, type); + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_enable_event_code(struct libevdev *dev, unsigned int type, + unsigned int code, const void *data) +{ + unsigned int max; + unsigned long *mask = NULL; + + if (libevdev_enable_event_type(dev, type)) + return -1; + + switch(type) { + case EV_SYN: + return 0; + case EV_ABS: + case EV_REP: + if (data == NULL) + return -1; + break; + default: + if (data != NULL) + return -1; + break; + } + + max = type_to_mask(dev, type, &mask); + + if (code > max || (int)max == -1) + return -1; + + set_bit(mask, code); + + if (type == EV_ABS) { + const struct input_absinfo *abs = data; + dev->abs_info[code] = *abs; + } else if (type == EV_REP) { + const int *value = data; + dev->rep_values[code] = *value; + } + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code) +{ + unsigned int max; + unsigned long *mask = NULL; + + if (type > EV_MAX || type == EV_SYN) + return -1; + + max = type_to_mask(dev, type, &mask); + + if (code > max || (int)max == -1) + return -1; + + clear_bit(mask, code); + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs) +{ + int rc; + + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (code > ABS_MAX) + return -EINVAL; + + rc = ioctl(dev->fd, EVIOCSABS(code), abs); + if (rc < 0) + rc = -errno; + else + rc = libevdev_enable_event_code(dev, EV_ABS, code, abs); + + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_grab(struct libevdev *dev, enum libevdev_grab_mode grab) +{ + int rc = 0; + + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + if (grab != LIBEVDEV_GRAB && grab != LIBEVDEV_UNGRAB) { + log_bug(dev, "invalid grab parameter %#x\n", grab); + return -EINVAL; + } + + if (grab == dev->grabbed) + return 0; + + if (grab == LIBEVDEV_GRAB) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)1); + else if (grab == LIBEVDEV_UNGRAB) + rc = ioctl(dev->fd, EVIOCGRAB, (void *)0); + + if (rc == 0) + dev->grabbed = grab; + + return rc < 0 ? -errno : 0; +} + +LIBEVDEV_EXPORT int +libevdev_event_is_type(const struct input_event *ev, unsigned int type) +{ + return type < EV_CNT && ev->type == type; +} + +LIBEVDEV_EXPORT int +libevdev_event_is_code(const struct input_event *ev, unsigned int type, unsigned int code) +{ + int max; + + if (!libevdev_event_is_type(ev, type)) + return 0; + + max = libevdev_event_type_get_max(type); + return (max > -1 && code <= (unsigned int)max && ev->code == code); +} + +LIBEVDEV_EXPORT const char* +libevdev_event_type_get_name(unsigned int type) +{ + if (type > EV_MAX) + return NULL; + return ev_map[type]; +} + +LIBEVDEV_EXPORT const char* +libevdev_event_code_get_name(unsigned int type, unsigned int code) +{ + int max = libevdev_event_type_get_max(type); + + if (max == -1 || code > (unsigned int)max) + return NULL; + + return event_type_map[type][code]; +} + +LIBEVDEV_EXPORT const char* +libevdev_property_get_name(unsigned int prop) +{ + if (prop > INPUT_PROP_MAX) + return NULL; + + return input_prop_map[prop]; +} + +LIBEVDEV_EXPORT int +libevdev_event_type_get_max(unsigned int type) +{ + if (type > EV_MAX) + return -1; + + return ev_max[type]; +} + +LIBEVDEV_EXPORT int +libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period) +{ + if (!libevdev_has_event_type(dev, EV_REP)) + return -1; + + if (delay != NULL) + *delay = dev->rep_values[REP_DELAY]; + if (period != NULL) + *period = dev->rep_values[REP_PERIOD]; + + return 0; +} + +LIBEVDEV_EXPORT int +libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum libevdev_led_value value) +{ + return libevdev_kernel_set_led_values(dev, code, value, -1); +} + +LIBEVDEV_EXPORT int +libevdev_kernel_set_led_values(struct libevdev *dev, ...) +{ + struct input_event ev[LED_MAX + 1]; + enum libevdev_led_value val; + va_list args; + int code; + int rc = 0; + size_t nleds = 0; + + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + memset(ev, 0, sizeof(ev)); + + va_start(args, dev); + code = va_arg(args, unsigned int); + while (code != -1) { + if (code > LED_MAX) { + rc = -EINVAL; + break; + } + val = va_arg(args, enum libevdev_led_value); + if (val != LIBEVDEV_LED_ON && val != LIBEVDEV_LED_OFF) { + rc = -EINVAL; + break; + } + + if (libevdev_has_event_code(dev, EV_LED, code)) { + struct input_event *e = ev; + + while (e->type > 0 && e->code != code) + e++; + + if (e->type == 0) + nleds++; + e->type = EV_LED; + e->code = code; + e->value = (val == LIBEVDEV_LED_ON); + } + code = va_arg(args, unsigned int); + } + va_end(args); + + if (rc == 0 && nleds > 0) { + ev[nleds].type = EV_SYN; + ev[nleds++].code = SYN_REPORT; + + rc = write(libevdev_get_fd(dev), ev, nleds * sizeof(ev[0])); + if (rc > 0) { + nleds--; /* last is EV_SYN */ + while (nleds--) + update_led_state(dev, &ev[nleds]); + } + rc = (rc != -1) ? 0 : -errno; + } + + return rc; +} + +LIBEVDEV_EXPORT int +libevdev_set_clock_id(struct libevdev *dev, int clockid) +{ + if (!dev->initialized) { + log_bug(dev, "device not initialized. call libevdev_set_fd() first\n"); + return -EBADF; + } else if (dev->fd < 0) + return -EBADF; + + return ioctl(dev->fd, EVIOCSCLOCKID, &clockid) ? -errno : 0; +} diff --git a/Linux/libevdev/libevdev.h b/Linux/libevdev/libevdev.h new file mode 100644 index 0000000..3edc3ae --- /dev/null +++ b/Linux/libevdev/libevdev.h @@ -0,0 +1,2177 @@ +/* + * Copyright © 2013 Red Hat, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that copyright + * notice and this permission notice appear in supporting documentation, and + * that the name of the copyright holders not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. The copyright holders make no representations + * about the suitability of this software for any purpose. It is provided "as + * is" without express or implied warranty. + * + * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO + * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, + * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER + * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE + * OF THIS SOFTWARE. + */ + +#ifndef LIBEVDEV_H +#define LIBEVDEV_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#define LIBEVDEV_ATTRIBUTE_PRINTF(_format, _args) __attribute__ ((format (printf, _format, _args))) + +/** + * @mainpage + * + * **libevdev** is a library for handling evdev kernel devices. It abstracts + * the \ref ioctls through type-safe interfaces and provides functions to change + * the appearance of the device. + * + * Development of libevdev is discussed on + * [input-tools@lists.freedesktop.org](http://lists.freedesktop.org/mailman/listinfo/input-tools) + * Please submit patches, questions or general comments there. + * + * Handling events and SYN_DROPPED + * =============================== + * + * libevdev provides an interface for handling events, including most notably + * SYN_DROPPED events. SYN_DROPPED events are sent by the kernel when the + * process does not read events fast enough and the kernel is forced to drop + * some events. This causes the device to get out of sync with the process' + * view of it. libevdev handles this by telling the caller that a SYN_DROPPED + * has been received and that the state of the device is different to what is + * to be expected. It then provides the delta between the previous state and + * the actual state of the device as a set of events. See + * libevdev_next_event() and @ref syn_dropped for more information on how + * SYN_DROPPED is handled. + * + * Signal safety + * ============= + * + * libevdev is signal-safe for the majority of its operations, i.e. many of + * its functions are safe to be called from within a signal handler. + * Check the API documentation to make sure, unless explicitly stated a call + * is not signal safe. + * + * Device handling + * =============== + * + * A libevdev context is valid for a given file descriptor and its + * duration. Closing the file descriptor will not destroy the libevdev device + * but libevdev will not be able to read further events. + * + * libevdev does not attempt duplicate detection. Initializing two libevdev + * devices for the same fd is valid and behaves the same as for two different + * devices. + * + * libevdev does not handle the file descriptors directly, it merely uses + * them. The caller is responsible for opening the file descriptors, setting + * them to O_NONBLOCK and handling permissions. A caller should drain any + * events pending on the file descriptor before passing it to libevdev. + * + * Where does libevdev sit? + * ======================== + * + * libevdev is essentially a `read(2)` on steroids for `/dev/input/eventX` + * devices. It sits below the process that handles input events, in between + * the kernel and that process. In the simplest case, e.g. an evtest-like tool + * the stack would look like this: + * + * kernel → libevdev → evtest + * + * For X.Org input modules, the stack would look like this: + * + * kernel → libevdev → xf86-input-evdev → X server → X client + * + * For Weston/Wayland, the stack would look like this: + * + * kernel → libevdev → Weston → Wayland client + * + * libevdev does **not** have knowledge of X clients or Wayland clients, it is + * too low in the stack. + * + * Example + * ======= + * Below is a simple example that shows how libevdev could be used. This example + * opens a device, checks for relative axes and a left mouse button and if it + * finds them monitors the device to print the event. + * + * @code + * struct libevdev *dev = NULL; + * int fd; + * int rc = 1; + * + * fd = open("/dev/input/event0", O_RDONLY|O_NONBLOCK); + * rc = libevdev_new_from_fd(fd, &dev); + * if (rc < 0) { + * fprintf(stderr, "Failed to init libevdev (%s)\n", strerror(-rc)); + * exit(1); + * } + * printf("Input device name: \"%s\"\n", libevdev_get_name(dev)); + * printf("Input device ID: bus %#x vendor %#x product %#x\n", + * libevdev_get_id_bustype(dev), + * libevdev_get_id_vendor(dev), + * libevdev_get_id_product(dev)); + * if (!libevdev_has_event_type(dev, EV_REL) || + * !libevdev_has_event_code(dev, EV_KEY, BTN_LEFT)) { + * printf("This device does not look like a mouse\n"); + * exit(1); + * } + * + * do { + * struct input_event ev; + * rc = libevdev_next_event(dev, LIBEVDEV_READ_FLAG_NORMAL, &ev); + * if (rc == 0) + * printf("Event: %s %s %d\n", + * libevdev_get_event_type_name(ev.type), + * libevdev_get_event_code_name(ev.type, ev.code), + * ev.value); + * } while (rc == 1 || rc == 0 || rc == -EAGAIN); + * @endcode + * + * A more complete example is available with the libevdev-events tool here: + * http://cgit.freedesktop.org/libevdev/tree/tools/libevdev-events.c + * + * Backwards compatibility with older kernel + * ========================================= + * libevdev attempts to build and run correctly on a number of kernel versions. + * If features required are not available, libevdev attempts to work around them + * in the most reasonable way. For more details see \ref backwardscompatibility. + * + * License information + * =================== + * libevdev is licensed under the + * [X11 license](http://cgit.freedesktop.org/libevdev/tree/COPYING). + * + * Reporting bugs + * ============== + * Please report bugs in the freedesktop.org bugzilla under the libevdev product: + * https://bugs.freedesktop.org/enter_bug.cgi?product=libevdev + */ + +/** + * @page syn_dropped SYN_DROPPED handling + * + * This page describes how libevdev handles SYN_DROPPED events. + * + * Receiving SYN_DROPPED events + * ============================ + * + * The kernel sends evdev events separated by an event of type EV_SYN and + * code SYN_REPORT. Such an event marks the end of a frame of hardware + * events. The number of events between SYN_REPORT events is arbitrary and + * depends on the hardware. An example event sequence may look like this: + * @code + * EV_ABS ABS_X 9 + * EV_ABS ABS_Y 8 + * EV_SYN SYN_REPORT 0 + * ------------------------ + * EV_ABS ABS_X 10 + * EV_ABS ABS_Y 10 + * EV_KEY BTN_TOUCH 1 + * EV_SYN SYN_REPORT 0 + * ------------------------ + * EV_ABS ABS_X 11 + * EV_SYN SYN_REPORT 0 + * @endcode + * + * Events are handed to the client buffer as they appear, the kernel adjusts + * the buffer size to handle at least one full event. In the normal case, + * the client reads the event and the kernel can place the next event in the + * buffer. If the client is not fast enough, the kernel places an event of + * type EV_SYN and code SYN_DROPPED into the buffer, effectively notifying + * the client that some events were lost. The above example event sequence + * may look like this (note the missing/repeated events): + * @code + * EV_ABS ABS_X 9 + * EV_ABS ABS_Y 8 + * EV_SYN SYN_REPORT 0 + * ------------------------ + * EV_ABS ABS_X 10 + * EV_ABS ABS_Y 10 + * EV_SYN SYN_DROPPED 0 + * EV_ABS ABS_Y 15 + * EV_SYN SYN_REPORT 0 + * ------------------------ + * EV_ABS ABS_X 11 + * EV_KEY BTN_TOUCH 0 + * EV_SYN SYN_REPORT 0 + * @endcode + * + * A SYN_DROPPED event may be recieved at any time in the event sequence. + * When a SYN_DROPPED event is received, the client must: + * * discard all events since the last SYN_REPORT + * * discard all events until including the next SYN_REPORT + * These event are part of incomplete event frames. + * + * Synchronizing the state of the device + * ===================================== + * + * The handling of the device after a SYN_DROPPED depends on the available + * event codes. For all event codes of type EV_REL, no handling is + * necessary, there is no state attached. For all event codes of type + * EV_KEY, EV_SW, EV_LED and EV_SND, the matching @ref ioctls retrieve the + * current state. The caller must then compare the last-known state to the + * retrieved state and handle the deltas accordingly. + * libevdev simplifies this approach: if the state of the device has + * changed, libevdev generates an event for each code with the new value and + * passes it to the caller during libevdev_next_event() if + * @ref LIBEVDEV_READ_FLAG_SYNC is set. + * + * For events of type EV_ABS and an event code less than ABS_MT_SLOT, the + * handling of state changes is as described above. For events between + * ABS_MT_SLOT and ABS_MAX, the event handling differs. + * Slots are the vehicles to transport information for multiple simultaneous + * touchpoints on a device. Slots are re-used once a touchpoint has ended. + * The kernel sends an ABS_MT_SLOT event whenever the current slot + * changes; any event in the above axis range applies only to the currently + * active slot. + * Thus, an event sequence from a slot-capable device may look like this: + * @code + * EV_ABS ABS_MT_POSITION_Y 10 + * EV_ABS ABS_MT_SLOT 1 + * EV_ABS ABS_MT_POSITION_X 100 + * EV_ABS ABS_MT_POSITION_Y 80 + * EV_SYN SYN_REPORT 0 + * @endcode + * Note the lack of ABS_MT_SLOT: the first ABS_MT_POSITION_Y applies to + * a slot opened previously, and is the only axis that changed for that + * slot. The touchpoint in slot 1 now has position 100/80. + * The kernel does not provide events if a value does not change, and does + * not send ABS_MT_SLOT events if the slot does not change, or none of the + * values within a slot changes. A client must thus keep the state for each + * slot. + * + * If a SYN_DROPPED is received, the client must sync all slots + * individually and update its internal state. libevdev simplifies this by + * generating multiple events: + * * for each slot on the device, libevdev generates an + * ABS_MT_SLOT event with the value set to the slot number + * * for each event code between ABS_MT_SLOT + 1 and ABS_MAX that changed + * state for this slot, libevdev generates an event for the new state + * * libevdev sends a final ABS_MT_SLOT event for the current slot as + * seen by the kernel + * * libevdev terminates this sequence with an EV_SYN SYN_REPORT event + * + * An example event sequence for such a sync may look like this: + * @code + * EV_ABS ABS_MT_SLOT 0 + * EV_ABS ABS_MT_POSITION_Y 10 + * EV_ABS ABS_MT_SLOT 1 + * EV_ABS ABS_MT_POSITION_X 100 + * EV_ABS ABS_MT_POSITION_Y 80 + * EV_ABS ABS_MT_SLOT 2 + * EV_ABS ABS_MT_POSITION_Y 8 + * EV_ABS ABS_MT_PRESSURE 12 + * EV_ABS ABS_MT_SLOT 1 + * EV_SYN SYN_REPORT 0 + * @endcode + * Note the terminating ABS_MT_SLOT event, this indicates that the kernel + * currently has slot 1 active. + * + * Synchronizing ABS_MT_TRACKING_ID + * ================================ + * + * The event code ABS_MT_TRACKING_ID is used to denote the start and end of + * a touch point within a slot. An ABS_MT_TRACKING_ID of zero or greater + * denotes the start of a touchpoint, an ABS_MT_TRACKING_ID of -1 denotes + * the end of a touchpoint within this slot. During SYN_DROPPED, a touch + * point may have ended and re-started within a slot - a client must check + * the ABS_MT_TRACKING_ID. libevdev simplifies this by emulating extra + * events if the ABS_MT_TRACKING_ID has changed: + * * if the ABS_MT_TRACKING_ID was valid and is -1, libevdev enqueues an + * ABS_MT_TRACKING_ID event with value -1. + * * if the ABS_MT_TRACKING_ID was -1 and is now a valid ID, libevdev + * enqueues an ABS_MT_TRACKING_ID event with the current value. + * * if the ABS_MT_TRACKING_ID was a valid ID and is now a different valid + * ID, libevev enqueues an ABS_MT_TRACKING_ID event with value -1 and + * another ABS_MT_TRACKING_ID event with the new value. + * + * An example event sequence for such a sync may look like this: + * @code + * EV_ABS ABS_MT_SLOT 0 + * EV_ABS ABS_MT_TRACKING_ID -1 + * EV_ABS ABS_MT_SLOT 2 + * EV_ABS ABS_MT_TRACKING_ID -1 + * EV_SYN SYN_REPORT 0 + * ------------------------ + * EV_ABS ABS_MT_SLOT 1 + * EV_ABS ABS_MT_POSITION_X 100 + * EV_ABS ABS_MT_POSITION_Y 80 + * EV_ABS ABS_MT_SLOT 2 + * EV_ABS ABS_MT_TRACKING_ID 45 + * EV_ABS ABS_MT_POSITION_Y 8 + * EV_ABS ABS_MT_PRESSURE 12 + * EV_ABS ABS_MT_SLOT 1 + * EV_SYN SYN_REPORT 0 + * @endcode + * Note how the touchpoint in slot 0 was terminated, the touchpoint in slot + * 2 was terminated and then started with a new ABS_MT_TRACKING_ID. The touchpoint + * in slot 1 maintained the same ABS_MT_TRACKING_ID and only updated the + * coordinates. Slot 1 is the currently active slot. + * + * In the case of a SYN_DROPPED event, a touch point may be invisible to a + * client if it started after SYN_DROPPED and finished before the client + * handles events again. The below example shows an example event sequence + * and what libevdev sees in the case of a SYN_DROPPED event: + * @code + * + * kernel | userspace + * | + * EV_ABS ABS_MT_SLOT 0 | EV_ABS ABS_MT_SLOT 0 + * EV_ABS ABS_MT_TRACKING_ID -1 | EV_ABS ABS_MT_TRACKING_ID -1 + * EV_SYN SYN_REPORT 0 | EV_SYN SYN_REPORT 0 + * ------------------------ | ------------------------ + * EV_ABS ABS_MT_TRACKING_ID 30 | + * EV_ABS ABS_MT_POSITION_X 100 | + * EV_ABS ABS_MT_POSITION_Y 80 | + * EV_SYN SYN_REPORT 0 | SYN_DROPPED + * ------------------------ | + * EV_ABS ABS_MT_TRACKING_ID -1 | + * EV_SYN SYN_REPORT 0 | + * ------------------------ | ------------------------ + * EV_ABS ABS_MT_SLOT 1 | EV_ABS ABS_MT_SLOT 1 + * EV_ABS ABS_MT_POSITION_X 90 | EV_ABS ABS_MT_POSITION_X 90 + * EV_ABS ABS_MT_POSITION_Y 10 | EV_ABS ABS_MT_POSITION_Y 10 + * EV_SYN SYN_REPORT 0 | EV_SYN SYN_REPORT 0 + * @endcode + * If such an event sequence occurs, libevdev will send all updated axes + * during the sync process. Axis events may thus be generated for devices + * without a currently valid ABS_MT_TRACKING_ID. Specifically for the above + * example, the client would receive the following event sequence: + * @code + * EV_ABS ABS_MT_SLOT 0 ← LIBEVDEV_READ_FLAG_NORMAL + * EV_ABS ABS_MT_TRACKING_ID -1 + * EV_SYN SYN_REPORT 0 + * ------------------------ + * EV_SYN SYN_DROPPED 0 → LIBEVDEV_READ_STATUS_SYNC + * ------------------------ + * EV_ABS ABS_MT_POSITION_X 100 ← LIBEVDEV_READ_FLAG_SYNC + * EV_ABS ABS_MT_POSITION_Y 80 + * EV_SYN SYN_REPORT 0 + * ----------------------------- → -EGAIN + * EV_ABS ABS_MT_SLOT 1 ← LIBEVDEV_READ_FLAG_NORMAL + * EV_ABS ABS_MT_POSITION_X 90 + * EV_ABS ABS_MT_POSITION_Y 10 + * EV_SYN SYN_REPORT 0 + * ------------------- + * @endcode + * The axis events do not reflect the position of a current touch point, a + * client must take care not to generate a new touch point based on those + * updates. + * + * Discarding events before synchronizing + * ===================================== + * + * The kernel implements the client buffer as a ring buffer. SYN_DROPPED + * events are handled when the buffer is full and a new event is received + * from a device. All existing events are discarded, a SYN_DROPPED is added + * to the buffer followed by the actual device event. Further events will be + * appended to the buffer until it is either read by the client, or filled + * again, at which point the sequence repeats. + * + * When the client reads the buffer, the buffer will thus always consist of + * exactly one SYN_DROPPED event followed by an unspecified number of real + * events. The data the ioctls return is the current state of the device, + * i.e. the state after all these events have been processed. For example, + * assume the buffer contains the following sequence: + * + * @code + * EV_SYN SYN_DROPPED + * EV_ABS ABS_X 1 + * EV_SYN SYN_REPORT 0 + * EV_ABS ABS_X 2 + * EV_SYN SYN_REPORT 0 + * EV_ABS ABS_X 3 + * EV_SYN SYN_REPORT 0 + * EV_ABS ABS_X 4 + * EV_SYN SYN_REPORT 0 + * EV_ABS ABS_X 5 + * EV_SYN SYN_REPORT 0 + * EV_ABS ABS_X 6 + * EV_SYN SYN_REPORT 0 + * @endcode + * An ioctl at any time in this sequence will return a value of 6 for ABS_X. + * + * libevdev discards all events after a SYN_DROPPED to ensure the events + * during @ref LIBEVDEV_READ_FLAG_SYNC represent the last known state of the + * device. This loses some granularity of the events especially as the time + * between the SYN_DROPPED and the sync process increases. It does however + * avoid spurious cursor movements. In the above example, the event sequence + * by libevdev is: + * @code + * EV_SYN SYN_DROPPED + * EV_ABS ABS_X 6 + * EV_SYN SYN_REPORT 0 + * @endcode + */ + +/** + * @page backwardscompatibility Compatibility and Behavior across kernel versions + * + * This page describes libevdev's behavior when the build-time kernel and the + * run-time kernel differ in their feature set. + * + * With the exception of event names, libevdev defines features that may be + * missing on older kernels and building on such kernels will not disable + * features. Running libevdev on a kernel that is missing some feature will + * change libevdev's behavior. In most cases, the new behavior should be + * obvious, but it is spelled out below in detail. + * + * Minimum requirements + * ==================== + * libevdev requires a 2.6.36 kernel as minimum. Specifically, it requires + * kernel-support for ABS_MT_SLOT. + * + * Event and input property names + * ============================== + * Event names and input property names are defined at build-time by the + * linux/input.h shipped with libevdev. + * The list of event names is compiled at build-time, any events not defined + * at build time will not resolve. Specifically, + * libevdev_event_code_get_name() for an undefined type or code will + * always return NULL. Likewise, libevdev_property_get_name() will return NULL + * for properties undefined at build-time. + * + * Input properties + * ================ + * If the kernel does not support input properties, specifically the + * EVIOCGPROPS ioctl, libevdev does not expose input properties to the caller. + * Specifically, libevdev_has_property() will always return 0 unless the + * property has been manually set with libevdev_enable_property(). + * + * This also applies to the libevdev-uinput code. If uinput does not honor + * UI_SET_PROPBIT, libevdev will continue without setting the properties on + * the device. + * + * MT slot behavior + * ================= + * If the kernel does not support the EVIOCGMTSLOTS ioctl, libevdev + * assumes all values in all slots are 0 and continues without an error. + * + * SYN_DROPPED behavior + * ==================== + * A kernel without SYN_DROPPED won't send such an event. libevdev_next_event() + * will never require the switch to sync mode. + */ + +/** + * @page ioctls evdev ioctls + * + * This page lists the status of the evdev-specific ioctls in libevdev. + * + *
+ *
EVIOCGVERSION:
+ *
supported, see libevdev_get_driver_version()
+ *
EVIOCGID:
+ *
supported, see libevdev_get_id_product(), libevdev_get_id_vendor(), + * libevdev_get_id_bustype(), libevdev_get_id_version()
+ *
EVIOCGREP:
+ *
supported, see libevdev_get_event_value())
+ *
EVIOCSREP:
+ *
supported, see libevdev_enable_event_code()
+ *
EVIOCGKEYCODE:
+ *
currently not supported
+ *
EVIOCGKEYCODE:
+ *
currently not supported
+ *
EVIOCSKEYCODE:
+ *
currently not supported
+ *
EVIOCSKEYCODE:
+ *
currently not supported
+ *
EVIOCGNAME:
+ *
supported, see libevdev_get_name()
+ *
EVIOCGPHYS:
+ *
supported, see libevdev_get_phys()
+ *
EVIOCGUNIQ:
+ *
supported, see libevdev_get_uniq()
+ *
EVIOCGPROP:
+ *
supported, see libevdev_has_property()
+ *
EVIOCGMTSLOTS:
+ *
supported, see libevdev_get_num_slots(), libevdev_get_slot_value()
+ *
EVIOCGKEY:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGLED:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGSND:
+ *
currently not supported
+ *
EVIOCGSW:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGBIT:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value()
+ *
EVIOCGABS:
+ *
supported, see libevdev_has_event_code(), libevdev_get_event_value(), + * libevdev_get_abs_info()
+ *
EVIOCSABS:
+ *
supported, see libevdev_kernel_set_abs_info()
+ *
EVIOCSFF:
+ *
currently not supported
+ *
EVIOCRMFF:
+ *
currently not supported
+ *
EVIOCGEFFECTS:
+ *
currently not supported
+ *
EVIOCGRAB:
+ *
supported, see libevdev_grab()
+ *
EVIOCSCLOCKID:
+ *
supported, see libevdev_set_clock_id()
+ *
EVIOCREVOKE:
+ *
currently not supported, see + * http://lists.freedesktop.org/archives/input-tools/2014-January/000688.html
+ *
+ * + */ + +/** + * @page kernel_header Kernel header + * + * libevdev provides its own copy of the Linux kernel header file and + * compiles against the definitions define here. Event type and event code + * names, etc. are taken from the file below: + * @include linux/input.h + */ + +/** + * @page static_linking Statically linking libevdev + * + * Statically linking libevdev.a is not recommended. Symbol visibility is + * difficult to control in a static library, so extra care must be taken to + * only use symbols that are explicitly exported. libevdev's API stability + * guarantee only applies to those symbols. + * + * If you do link libevdev statically, note that in addition to the exported + * symbols, libevdev reserves the _libevdev_* namespace. Do not use + * or create symbols with that prefix, they are subject to change at any + * time. + */ + +/** + * @page testing libevdev-internal test suite + * + * libevdev's internal test suite uses the + * [Check unit testing framework](http://check.sourceforge.net/). Tests are + * divided into test suites and test cases. Most tests create a uinput device, + * so you'll need to run as root, and your kernel must have + * CONFIG_INPUT_UINPUT enabled. + * + * To run a specific suite only: + * + * export CK_RUN_SUITE="suite name" + * + * To run a specific test case only: + * + * export CK_RUN_TEST="test case name" + * + * To get a list of all suites or tests: + * + * git grep "suite_create" + * git grep "tcase_create" + * + * By default, Check forks, making debugging harder. The test suite tries to detect + * if it is running inside gdb and disable forking. If that doesn't work for + * some reason, run gdb as below to avoid forking. + * + * sudo CK_FORK=no CK_RUN_TEST="test case name" gdb ./test/test-libevdev + * + * A special target `make gcov-report.txt` exists that runs gcov and leaves a + * `libevdev.c.gcov` file. Check that for test coverage. + * + * `make check` is hooked up to run the test and gcov (again, needs root). + * + * The test suite creates a lot of devices, very quickly. Add the following + * xorg.conf.d snippet to avoid the devices being added as X devices (at the + * time of writing, mutter can't handle these devices and exits after getting + * a BadDevice error). + * + * $ cat /etc/X11/xorg.conf.d/99-ignore-libevdev-devices.conf + * Section "InputClass" + * Identifier "Ignore libevdev test devices" + * MatchProduct "libevdev test device" + * Option "Ignore" "on" + * EndSection + * + */ + +/** + * @defgroup init Initialization and setup + * + * Initialization, initial setup and file descriptor handling. + * These functions are the main entry points for users of libevdev, usually a + * caller will use this series of calls: + * + * @code + * struct libevdev *dev; + * int err; + * + * dev = libevdev_new(); + * if (!dev) + * return ENOMEM; + * + * err = libevdev_set_fd(dev, fd); + * if (err < 0) { + * printf("Failed (errno %d): %s\n", -err, strerror(-err)); + * + * libevdev_free(dev); + * @endcode + * + * libevdev_set_fd() is the central call and initializes the internal structs + * for the device at the given fd. libevdev functions will fail if called + * before libevdev_set_fd() unless documented otherwise. + */ + +/** + * @defgroup logging Library logging facilities + * + * libevdev provides two methods of logging library-internal messages. The + * old method is to provide a global log handler in + * libevdev_set_log_function(). The new method is to provide a per-context + * log handler in libevdev_set_device_log_function(). Developers are encouraged + * to use the per-context logging facilities over the global log handler as + * it provides access to the libevdev instance that caused a message, and is + * more flexible when libevdev is used from within a shared library. + * + * If a caller sets both the global log handler and a per-context log + * handler, each device with a per-context log handler will only invoke that + * log handler. + * + * @note To set a context-specific log handler, a context is needed. + * Thus developers are discouraged from using libevdev_new_from_fd() as + * important messages from the device initialization process may get lost. + * + * @note A context-specific handler cannot be used for libevdev's uinput + * devices. @ref uinput must use the global log handler. + */ + +/** + * @defgroup bits Querying device capabilities + * + * Abstraction functions to handle device capabilities, specificially + * device properties such as the name of the device and the bits + * representing the events suppported by this device. + * + * The logical state returned may lag behind the physical state of the device. + * libevdev queries the device state on libevdev_set_fd() and then relies on + * the caller to parse events through libevdev_next_event(). If a caller does not + * use libevdev_next_event(), libevdev will not update the internal state of the + * device and thus returns outdated values. + */ + +/** + * @defgroup mt Multi-touch related functions + * Functions for querying multi-touch-related capabilities. MT devices + * following the kernel protocol B (using ABS_MT_SLOT) provide multiple touch + * points through so-called slots on the same axis. The slots are enumerated, + * a client reading from the device will first get an ABS_MT_SLOT event, then + * the values of axes changed in this slot. Multiple slots may be provided in + * before an EV_SYN event. + * + * As with @ref bits, the logical state of the device as seen by the library + * depends on the caller using libevdev_next_event(). + * + * The Linux kernel requires all axes on a device to have a semantic + * meaning, matching the axis names in linux/input.h. Some devices merely + * export a number of axes beyond the available axis list. For those + * devices, the multitouch information is invalid. Specfically, if a device + * provides the ABS_MT_SLOT axis AND also the (ABS_MT_SLOT - 1) axis, the + * device is not treated as multitouch device. No slot information is + * available and the ABS_MT axis range for these devices is treated as all + * other EV_ABS axes. + * + * Note that because of limitations in the kernel API, such fake multitouch + * devices can not be reliably synched after a SYN_DROPPED event. libevdev + * ignores all ABS_MT axis values during the sync process and instead + * relies on the device to send the current axis value with the first event + * after SYN_DROPPED. + */ + +/** + * @defgroup kernel Modifying the appearance or capabilities of the device + * + * Modifying the set of events reported by this device. By default, the + * libevdev device mirrors the kernel device, enabling only those bits + * exported by the kernel. This set of functions enable or disable bits as + * seen from the caller. + * + * Enabling an event type or code does not affect event reporting - a + * software-enabled event will not be generated by the physical hardware. + * Disabling an event will prevent libevdev from routing such events to the + * caller. Enabling and disabling event types and codes is at the library + * level and thus only affects the caller. + * + * If an event type or code is enabled at kernel-level, future users of this + * device will see this event enabled. Currently there is no option of + * disabling an event type or code at kernel-level. + */ + +/** + * @defgroup misc Miscellaneous helper functions + * + * Functions for printing or querying event ranges. The list of names is + * compiled into libevdev and is independent of the run-time kernel. + * Likewise, the max for each event type is compiled in and does not check + * the kernel at run-time. + */ + +/** + * @defgroup events Event handling + * + * Functions to handle events and fetch the current state of the event. + * libevdev updates its internal state as the event is processed and forwarded + * to the caller. Thus, the libevdev state of the device should always be identical + * to the caller's state. It may however lag behind the actual state of the device. + */ + +/** + * @ingroup init + * + * Opaque struct representing an evdev device. + */ +struct libevdev; + +/** + * @ingroup events + */ +enum libevdev_read_flag { + LIBEVDEV_READ_FLAG_SYNC = 1, /**< Process data in sync mode */ + LIBEVDEV_READ_FLAG_NORMAL = 2, /**< Process data in normal mode */ + LIBEVDEV_READ_FLAG_FORCE_SYNC = 4, /**< Pretend the next event is a SYN_DROPPED and + require the caller to sync */ + LIBEVDEV_READ_FLAG_BLOCKING = 8 /**< The fd is not in O_NONBLOCK and a read may block */ +}; + +/** + * @ingroup init + * + * Initialize a new libevdev device. This function only allocates the + * required memory and initializes the struct to sane default values. + * To actually hook up the device to a kernel device, use + * libevdev_set_fd(). + * + * Memory allocated through libevdev_new() must be released by the + * caller with libevdev_free(). + * + * @see libevdev_set_fd + * @see libevdev_free + */ +struct libevdev* libevdev_new(void); + +/** + * @ingroup init + * + * Initialize a new libevdev device from the given fd. + * + * This is a shortcut for + * + * @code + * int err; + * struct libevdev *dev = libevdev_new(); + * err = libevdev_set_fd(dev, fd); + * @endcode + * + * @param fd A file descriptor to the device in O_RDWR or O_RDONLY mode. + * @param[out] dev The newly initialized evdev device. + * + * @return On success, 0 is returned and dev is set to the newly + * allocated struct. On failure, a negative errno is returned and the value + * of dev is undefined. + * + * @see libevdev_free + */ +int libevdev_new_from_fd(int fd, struct libevdev **dev); + +/** + * @ingroup init + * + * Clean up and free the libevdev struct. After completion, the struct + * libevdev is invalid and must not be used. + * + * Note that calling libevdev_free() does not close the file descriptor + * currently asssociated with this instance. + * + * @param dev The evdev device + * + * @note This function may be called before libevdev_set_fd(). + */ +void libevdev_free(struct libevdev *dev); + +/** + * @ingroup logging + */ +enum libevdev_log_priority { + LIBEVDEV_LOG_ERROR = 10, /**< critical errors and application bugs */ + LIBEVDEV_LOG_INFO = 20, /**< informational messages */ + LIBEVDEV_LOG_DEBUG = 30 /**< debug information */ +}; + +/** + * @ingroup logging + * + * Logging function called by library-internal logging. + * This function is expected to treat its input like printf would. + * + * @param priority Log priority of this message + * @param data User-supplied data pointer (see libevdev_set_log_function()) + * @param file libevdev source code file generating this message + * @param line libevdev source code line generating this message + * @param func libevdev source code function generating this message + * @param format printf-style format string + * @param args List of arguments + * + * @see libevdev_set_log_function + */ +typedef void (*libevdev_log_func_t)(enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args) + LIBEVDEV_ATTRIBUTE_PRINTF(6, 0); + +/** + * @ingroup logging + * + * Set a printf-style logging handler for library-internal logging. The default + * logging function is to stdout. + * + * @note The global log handler is only called if no context-specific log + * handler has been set with libevdev_set_device_log_function(). + * + * @param logfunc The logging function for this device. If NULL, the current + * logging function is unset and no logging is performed. + * @param data User-specific data passed to the log handler. + * + * @note This function may be called before libevdev_set_fd(). + * + * @deprecated Use per-context logging instead, see + * libevdev_set_device_log_function(). + */ +void libevdev_set_log_function(libevdev_log_func_t logfunc, void *data); + +/** + * @ingroup logging + * + * Define the minimum level to be printed to the log handler. + * Messages higher than this level are printed, others are discarded. This + * is a global setting and applies to any future logging messages. + * + * @param priority Minimum priority to be printed to the log. + * + * @deprecated Use per-context logging instead, see + * libevdev_set_device_log_function(). + */ +void libevdev_set_log_priority(enum libevdev_log_priority priority); + +/** + * @ingroup logging + * + * Return the current log priority level. Messages higher than this level + * are printed, others are discarded. This is a global setting. + * + * @return the current log level + * + * @deprecated Use per-context logging instead, see + * libevdev_set_device_log_function(). + */ +enum libevdev_log_priority libevdev_get_log_priority(void); + +/** + * @ingroup logging + * + * Logging function called by library-internal logging for a specific + * libevdev context. This function is expected to treat its input like + * printf would. + * + * @param dev The evdev device + * @param priority Log priority of this message + * @param data User-supplied data pointer (see libevdev_set_log_function()) + * @param file libevdev source code file generating this message + * @param line libevdev source code line generating this message + * @param func libevdev source code function generating this message + * @param format printf-style format string + * @param args List of arguments + * + * @see libevdev_set_log_function + * @since 1.3 + */ +typedef void (*libevdev_device_log_func_t)(const struct libevdev *dev, + enum libevdev_log_priority priority, + void *data, + const char *file, int line, + const char *func, + const char *format, va_list args) + LIBEVDEV_ATTRIBUTE_PRINTF(7, 0); + +/** + * @ingroup logging + * + * Set a printf-style logging handler for library-internal logging for this + * device context. The default logging function is NULL, i.e. the global log + * handler is invoked. If a context-specific log handler is set, the global + * log handler is not invoked for this device. + * + * @note This log function applies for this device context only, even if + * another context exists for the same fd. + * + * @param dev The evdev device + * @param logfunc The logging function for this device. If NULL, the current + * logging function is unset and logging falls back to the global log + * handler, if any. + * @param priority Minimum priority to be printed to the log. + * @param data User-specific data passed to the log handler. + * + * @note This function may be called before libevdev_set_fd(). + * @since 1.3 + */ +void libevdev_set_device_log_function(struct libevdev *dev, + libevdev_device_log_func_t logfunc, + enum libevdev_log_priority priority, + void *data); + +/** + * @ingroup init + */ +enum libevdev_grab_mode { + LIBEVDEV_GRAB = 3, /**< Grab the device if not currently grabbed */ + LIBEVDEV_UNGRAB = 4 /**< Ungrab the device if currently grabbed */ +}; + +/** + * @ingroup init + * + * Grab or ungrab the device through a kernel EVIOCGRAB. This prevents other + * clients (including kernel-internal ones such as rfkill) from receiving + * events from this device. + * + * This is generally a bad idea. Don't do this. + * + * Grabbing an already grabbed device, or ungrabbing an ungrabbed device is + * a noop and always succeeds. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param grab If true, grab the device. Otherwise ungrab the device. + * + * @return 0 if the device was successfull grabbed or ungrabbed, or a + * negative errno in case of failure. + */ +int libevdev_grab(struct libevdev *dev, enum libevdev_grab_mode grab); + +/** + * @ingroup init + * + * Set the fd for this struct and initialize internal data. + * The fd must be in O_RDONLY or O_RDWR mode. + * + * This function may only be called once per device. If the device changed and + * you need to re-read a device, use libevdev_free() and libevdev_new(). If + * you need to change the fd after closing and re-opening the same device, use + * libevdev_change_fd(). + * + * A caller should ensure that any events currently pending on the fd are + * drained before the file descriptor is passed to libevdev for + * initialization. Due to how the kernel's ioctl handling works, the initial + * device state will reflect the current device state *after* applying all + * events currently pending on the fd. Thus, if the fd is not drained, the + * state visible to the caller will be inconsistent with the events + * immediately available on the device. This does not affect state-less + * events like EV_REL. + * + * Unless otherwise specified, libevdev function behavior is undefined until + * a successfull call to libevdev_set_fd(). + * + * @param dev The evdev device + * @param fd The file descriptor for the device + * + * @return 0 on success, or a negative errno on failure + * + * @see libevdev_change_fd + * @see libevdev_new + * @see libevdev_free + */ +int libevdev_set_fd(struct libevdev* dev, int fd); + +/** + * @ingroup init + * + * Change the fd for this device, without re-reading the actual device. If the fd + * changes after initializing the device, for example after a VT-switch in the + * X.org X server, this function updates the internal fd to the newly opened. + * No check is made that new fd points to the same device. If the device has + * changed, libevdev's behavior is undefined. + * + * libevdev does not sync itself after changing the fd and keeps the current + * device state. Use libevdev_next_event with the + * @ref LIBEVDEV_READ_FLAG_FORCE_SYNC flag to force a re-sync. + * + * The example code below illustrates how to force a re-sync of the + * library-internal state. Note that this code doesn't handle the events in + * the caller, it merely forces an update of the internal library state. + * @code + * struct input_event ev; + * libevdev_change_fd(dev, new_fd); + * libevdev_next_event(dev, LIBEVDEV_READ_FLAG_FORCE_SYNC, &ev); + * while (libevdev_next_event(dev, LIBEVDEV_READ_FLAG_SYNC, &ev) == LIBEVDEV_READ_STATUS_SYNC) + * ; // noop + * @endcode + * + * The fd may be open in O_RDONLY or O_RDWR. + * + * It is an error to call this function before calling libevdev_set_fd(). + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param fd The new fd + * + * @return 0 on success, or -1 on failure. + * + * @see libevdev_set_fd + */ +int libevdev_change_fd(struct libevdev* dev, int fd); + +/** + * @ingroup init + * + * @param dev The evdev device + * + * @return The previously set fd, or -1 if none had been set previously. + * @note This function may be called before libevdev_set_fd(). + */ +int libevdev_get_fd(const struct libevdev* dev); + +/** + * @ingroup events + */ +enum libevdev_read_status { + /** + * libevdev_next_event() has finished without an error + * and an event is available for processing. + * + * @see libevdev_next_event + */ + LIBEVDEV_READ_STATUS_SUCCESS = 0, + /** + * Depending on the libevdev_next_event() read flag: + * * libevdev received a SYN_DROPPED from the device, and the caller should + * now resync the device, or, + * * an event has been read in sync mode. + * + * @see libevdev_next_event + */ + LIBEVDEV_READ_STATUS_SYNC = 1 +}; + +/** + * @ingroup events + * + * Get the next event from the device. This function operates in two different + * modes: normal mode or sync mode. + * + * In normal mode (when flags has @ref LIBEVDEV_READ_FLAG_NORMAL set), this + * function returns @ref LIBEVDEV_READ_STATUS_SUCCESS and returns the event + * in the argument @p ev. If no events are available at this + * time, it returns -EAGAIN and ev is undefined. + * + * If the current event is an EV_SYN SYN_DROPPED event, this function returns + * @ref LIBEVDEV_READ_STATUS_SYNC and ev is set to the EV_SYN event. + * The caller should now call this function with the + * @ref LIBEVDEV_READ_FLAG_SYNC flag set, to get the set of events that make up the + * device state delta. This function returns @ref LIBEVDEV_READ_STATUS_SYNC for + * each event part of that delta, until it returns -EAGAIN once all events + * have been synced. For more details on what libevdev does to sync after a + * SYN_DROPPED event, see @ref syn_dropped. + * + * If a device needs to be synced by the caller but the caller does not call + * with the @ref LIBEVDEV_READ_FLAG_SYNC flag set, all events from the diff are + * dropped after libevdev updates its internal state and event processing + * continues as normal. Note that the current slot and the state of touch + * points may have updated during the SYN_DROPPED event, it is strongly + * recommended that a caller ignoring all sync events calls + * libevdev_get_current_slot() and checks the ABS_MT_TRACKING_ID values for + * all slots. + * + * If a device has changed state without events being enqueued in libevdev, + * e.g. after changing the file descriptor, use the @ref + * LIBEVDEV_READ_FLAG_FORCE_SYNC flag. This triggers an internal sync of the + * device and libevdev_next_event() returns @ref LIBEVDEV_READ_STATUS_SYNC. + * Any state changes are available as events as described above. If + * @ref LIBEVDEV_READ_FLAG_FORCE_SYNC is set, the value of ev is undefined. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param flags Set of flags to determine behaviour. If @ref LIBEVDEV_READ_FLAG_NORMAL + * is set, the next event is read in normal mode. If @ref LIBEVDEV_READ_FLAG_SYNC is + * set, the next event is read in sync mode. + * @param ev On success, set to the current event. + * @return On failure, a negative errno is returned. + * @retval LIBEVDEV_READ_STATUS_SUCCESS One or more events were read of the + * device and ev points to the next event in the queue + * @retval -EAGAIN No events are currently available on the device + * @retval LIBEVDEV_READ_STATUS_SYNC A SYN_DROPPED event was received, or a + * synced event was returned and ev points to the SYN_DROPPED event + * + * @note This function is signal-safe. + */ +int libevdev_next_event(struct libevdev *dev, unsigned int flags, struct input_event *ev); + +/** + * @ingroup events + * + * Check if there are events waiting for us. This function does not read an + * event off the fd and may not access the fd at all. If there are events + * queued internally this function will return non-zero. If the internal + * queue is empty, this function will poll the file descriptor for data. + * + * This is a convenience function for simple processes, most complex programs + * are expected to use select(2) or poll(2) on the file descriptor. The kernel + * guarantees that if data is available, it is a multiple of sizeof(struct + * input_event), and thus calling libevdev_next_event() when select(2) or + * poll(2) return is safe. You do not need libevdev_has_event_pending() if + * you're using select(2) or poll(2). + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @return On failure, a negative errno is returned. + * @retval 0 No event is currently available + * @retval 1 One or more events are available on the fd + * + * @note This function is signal-safe. + */ +int libevdev_has_event_pending(struct libevdev *dev); + +/** + * @ingroup bits + * + * Retrieve the device's name, either as set by the caller or as read from + * the kernel. The string returned is valid until libevdev_free() or until + * libevdev_set_name(), whichever comes earlier. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device name as read off the kernel device. The name is never + * NULL but it may be the empty string. + * + * @note This function is signal-safe. + */ +const char* libevdev_get_name(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the device's name as returned by libevdev_get_name(). This + * function destroys the string previously returned by libevdev_get_name(), + * a caller must take care that no references are kept. + * + * @param dev The evdev device + * @param name The new, non-NULL, name to assign to this device. + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_name(struct libevdev *dev, const char *name); + +/** + * @ingroup bits + * + * Retrieve the device's physical location, either as set by the caller or + * as read from the kernel. The string returned is valid until + * libevdev_free() or until libevdev_set_phys(), whichever comes earlier. + * + * Virtual devices such as uinput devices have no phys location. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The physical location of this device, or NULL if there is none + * + * @note This function is signal safe. + */ +const char * libevdev_get_phys(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the device's physical location as returned by libevdev_get_phys(). + * This function destroys the string previously returned by + * libevdev_get_phys(), a caller must take care that no references are kept. + * + * @param dev The evdev device + * @param phys The new phys to assign to this device. + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_phys(struct libevdev *dev, const char *phys); + +/** + * @ingroup bits + * + * Retrieve the device's unique identifier, either as set by the caller or + * as read from the kernel. The string returned is valid until + * libevdev_free() or until libevdev_set_uniq(), whichever comes earlier. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The unique identifier for this device, or NULL if there is none + * + * @note This function is signal safe. + */ +const char * libevdev_get_uniq(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the device's unique identifier as returned by libevdev_get_uniq(). + * This function destroys the string previously returned by + * libevdev_get_uniq(), a caller must take care that no references are kept. + * + * @param dev The evdev device + * @param uniq The new uniq to assign to this device. + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_uniq(struct libevdev *dev, const char *uniq); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's product ID + * + * @note This function is signal-safe. + */ +int libevdev_get_id_product(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param product_id The product ID to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_product(struct libevdev *dev, int product_id); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's vendor ID + * + * @note This function is signal-safe. + */ +int libevdev_get_id_vendor(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param vendor_id The vendor ID to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_vendor(struct libevdev *dev, int vendor_id); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's bus type + * + * @note This function is signal-safe. + */ +int libevdev_get_id_bustype(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param bustype The bustype to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_bustype(struct libevdev *dev, int bustype); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The device's firmware version + * + * @note This function is signal-safe. + */ +int libevdev_get_id_version(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param version The version to assign to this device + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +void libevdev_set_id_version(struct libevdev *dev, int version); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The driver version for this device + * + * @note This function is signal-safe. + */ +int libevdev_get_driver_version(const struct libevdev *dev); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param prop The input property to query for, one of INPUT_PROP_... + * + * @return 1 if the device provides this input property, or 0 otherwise. + * + * @note This function is signal-safe + */ +int libevdev_has_property(const struct libevdev *dev, unsigned int prop); + +/** + * @ingroup kernel + * + * @param dev The evdev device + * @param prop The input property to enable, one of INPUT_PROP_... + * + * @return 0 on success or -1 on failure + * + * @note This function may be called before libevdev_set_fd(). A call to + * libevdev_set_fd() will overwrite any previously set value. + */ +int libevdev_enable_property(struct libevdev *dev, unsigned int prop); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to query for, one of EV_SYN, EV_REL, etc. + * + * @return 1 if the device supports this event type, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_type(const struct libevdev *dev, unsigned int type); + +/** + * @ingroup bits + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to query for, one of ABS_X, REL_X, etc. + * + * @return 1 if the device supports this event type and code, or 0 otherwise. + * + * @note This function is signal-safe. + */ +int libevdev_has_event_code(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @ingroup bits + * + * Get the minimum axis value for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis minimum for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_minimum(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Get the maximum axis value for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis maximum for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_maximum(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Get the axis fuzz for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis fuzz for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_fuzz(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Get the axis flat for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis flat for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_flat(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Get the axis resolution for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return axis resolution for the given axis or 0 if the axis is invalid + * + * @note This function is signal-safe. + */ +int libevdev_get_abs_resolution(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Get the axis info for the given axis, as advertised by the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to query for, one of ABS_X, ABS_Y, etc. + * + * @return The input_absinfo for the given code, or NULL if the device does + * not support this event code. + * + * @note This function is signal-safe. + */ +const struct input_absinfo* libevdev_get_abs_info(const struct libevdev *dev, unsigned int code); + +/** + * @ingroup bits + * + * Behaviour of this function is undefined if the device does not provide + * the event. + * + * If the device supports ABS_MT_SLOT, the value returned for any ABS_MT_* + * event code is the value of the currently active slot. You should use + * libevdev_get_slot_value() instead. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to query for, one of ABS_X, REL_X, etc. + * + * @return The current value of the event. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_get_slot_value() instead + * + * @see libevdev_get_slot_value + */ +int libevdev_get_event_value(const struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @ingroup kernel + * + * Set the value for a given event type and code. This only makes sense for + * some event types, e.g. setting the value for EV_REL is pointless. + * + * This is a local modification only affecting only this representation of + * this device. A future call to libevdev_get_event_value() will return this + * value, unless the value was overwritten by an event. + * + * If the device supports ABS_MT_SLOT, the value set for any ABS_MT_* + * event code is the value of the currently active slot. You should use + * libevdev_set_slot_value() instead. + * + * If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is + * ABS_MT_SLOT, the value must be a positive number less then the number of + * slots on the device. Otherwise, libevdev_set_event_value() returns -1. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to set the value for, one of ABS_X, LED_NUML, etc. + * @param value The new value to set + * + * @return 0 on success, or -1 on failure. + * @retval -1 the device does not have the event type or code enabled, or the code is outside the + * allowed limits for the given type, or the type cannot be set, or the + * value is not permitted for the given code. + * + * @see libevdev_set_slot_value + * @see libevdev_get_event_value + */ +int libevdev_set_event_value(struct libevdev *dev, unsigned int type, unsigned int code, int value); + +/** + * @ingroup bits + * + * Fetch the current value of the event type. This is a shortcut for + * + * @code + * if (libevdev_has_event_type(dev, t) && libevdev_has_event_code(dev, t, c)) + * val = libevdev_get_event_value(dev, t, c); + * @endcode + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to query for, one of ABS_X, REL_X, etc. + * @param[out] value The current value of this axis returned. + * + * @return If the device supports this event type and code, the return value is + * non-zero and value is set to the current value of this axis. Otherwise, + * 0 is returned and value is unmodified. + * + * @note This function is signal-safe. + * @note The value for ABS_MT_ events is undefined, use + * libevdev_fetch_slot_value() instead + * + * @see libevdev_fetch_slot_value + */ +int libevdev_fetch_event_value(const struct libevdev *dev, unsigned int type, unsigned int code, int *value); + +/** + * @ingroup mt + * + * Return the current value of the code for the given slot. + * + * The return value is undefined for a slot exceeding the available slots on + * the device, for a code that is not in the permitted ABS_MT range or for a + * device that does not have slots. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param slot The numerical slot number, must be smaller than the total number + * of slots on this device + * @param code The event code to query for, one of ABS_MT_POSITION_X, etc. + * + * @note This function is signal-safe. + * @note The value for events other than ABS_MT_ is undefined, use + * libevdev_fetch_value() instead + * + * @see libevdev_get_event_value + */ +int libevdev_get_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code); + +/** + * @ingroup kernel + * + * Set the value for a given code for the given slot. + * + * This is a local modification only affecting only this representation of + * this device. A future call to libevdev_get_slot_value() will return this + * value, unless the value was overwritten by an event. + * + * This function does not set event values for axes outside the ABS_MT range, + * use libevdev_set_event_value() instead. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param slot The numerical slot number, must be smaller than the total number + * of slots on this device + * @param code The event code to set the value for, one of ABS_MT_POSITION_X, etc. + * @param value The new value to set + * + * @return 0 on success, or -1 on failure. + * @retval -1 the device does not have the event code enabled, or the code is + * outside the allowed limits for multitouch events, or the slot number is outside + * the limits for this device, or the device does not support multitouch events. + * + * @see libevdev_set_event_value + * @see libevdev_get_slot_value + */ +int libevdev_set_slot_value(struct libevdev *dev, unsigned int slot, unsigned int code, int value); + +/** + * @ingroup mt + * + * Fetch the current value of the code for the given slot. This is a shortcut for + * + * @code + * if (libevdev_has_event_type(dev, EV_ABS) && + * libevdev_has_event_code(dev, EV_ABS, c) && + * slot < device->number_of_slots) + * val = libevdev_get_slot_value(dev, slot, c); + * @endcode + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param slot The numerical slot number, must be smaller than the total number + * of slots on this * device + * @param[out] value The current value of this axis returned. + * + * @param code The event code to query for, one of ABS_MT_POSITION_X, etc. + * @return If the device supports this event code, the return value is + * non-zero and value is set to the current value of this axis. Otherwise, or + * if the event code is not an ABS_MT_* event code, 0 is returned and value + * is unmodified. + * + * @note This function is signal-safe. + */ +int libevdev_fetch_slot_value(const struct libevdev *dev, unsigned int slot, unsigned int code, int *value); + +/** + * @ingroup mt + * + * Get the number of slots supported by this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return The number of slots supported, or -1 if the device does not provide + * any slots + * + * @note A device may provide ABS_MT_SLOT but a total number of 0 slots. Hence + * the return value of -1 for "device does not provide slots at all" + */ +int libevdev_get_num_slots(const struct libevdev *dev); + +/** + * @ingroup mt + * + * Get the currently active slot. This may differ from the value + * an ioctl may return at this time as events may have been read off the fd + * since changing the slot value but those events are still in the buffer + * waiting to be processed. The returned value is the value a caller would + * see if it were to process events manually one-by-one. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * + * @return the currently active slot (logically) + * + * @note This function is signal-safe. + */ +int libevdev_get_current_slot(const struct libevdev *dev); + +/** + * @ingroup kernel + * + * Change the minimum for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param min The new minimum for this axis + */ +void libevdev_set_abs_minimum(struct libevdev *dev, unsigned int code, int min); + +/** + * @ingroup kernel + * + * Change the maximum for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param max The new maxium for this axis + */ +void libevdev_set_abs_maximum(struct libevdev *dev, unsigned int code, int max); + +/** + * @ingroup kernel + * + * Change the fuzz for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param fuzz The new fuzz for this axis + */ +void libevdev_set_abs_fuzz(struct libevdev *dev, unsigned int code, int fuzz); + +/** + * @ingroup kernel + * + * Change the flat for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param flat The new flat for this axis + */ +void libevdev_set_abs_flat(struct libevdev *dev, unsigned int code, int flat); + +/** + * @ingroup kernel + * + * Change the resolution for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param resolution The new axis resolution + */ +void libevdev_set_abs_resolution(struct libevdev *dev, unsigned int code, int resolution); + +/** + * @ingroup kernel + * + * Change the abs info for the given EV_ABS event code, if the code exists. + * This function has no effect if libevdev_has_event_code() returns false for + * this code. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code One of ABS_X, ABS_Y, ... + * @param abs The new absolute axis data (min, max, fuzz, flat, resolution) + */ +void libevdev_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs); + +/** + * @ingroup kernel + * + * Forcibly enable an event type on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_type(). + * + * This is a local modification only affecting only this representation of + * this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + */ +int libevdev_enable_event_type(struct libevdev *dev, unsigned int type); + +/** + * @ingroup kernel + * + * Forcibly disable an event type on this device, even if the underlying + * device provides it. This effectively mutes the respective set of + * events. libevdev will filter any events matching this type and none will + * reach the caller. libevdev_has_event_type() will return false for this + * type. + * + * In most cases, a caller likely only wants to disable a single code, not + * the whole type. Use libevdev_disable_event_code() for that. + * + * Disabling EV_SYN will not work. Don't shoot yourself in the foot. + * It hurts. + * + * This is a local modification only affecting only this representation of + * this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to disable (EV_ABS, EV_KEY, ...) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_type + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_type(struct libevdev *dev, unsigned int type); + +/** + * @ingroup kernel + * + * Forcibly enable an event code on this device, even if the underlying + * device does not support it. While this cannot make the device actually + * report such events, it will now return true for libevdev_has_event_code(). + * + * The last argument depends on the type and code: + * - If type is EV_ABS, data must be a pointer to a struct input_absinfo + * containing the data for this axis. + * - If type is EV_REP, data must be a pointer to a int containing the data + * for this axis + * - For all other types, the argument must be NULL. + * + * This function calls libevdev_enable_event_type() if necessary. + * + * This is a local modification only affecting only this representation of + * this device. + * + * If this function is called with a type of EV_ABS and EV_REP on a device + * that already has the given event code enabled, the values in data + * overwrite the previous values. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to enable (EV_ABS, EV_KEY, ...) + * @param code The event code to enable (ABS_X, REL_X, etc.) + * @param data If type is EV_ABS, data points to a struct input_absinfo. If type is EV_REP, data + * points to an integer. Otherwise, data must be NULL. + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_enable_event_type + */ +int libevdev_enable_event_code(struct libevdev *dev, unsigned int type, unsigned int code, const void *data); + +/** + * @ingroup kernel + * + * Forcibly disable an event code on this device, even if the underlying + * device provides it. This effectively mutes the respective set of + * events. libevdev will filter any events matching this type and code and + * none will reach the caller. libevdev_has_event_code() will return false for + * this code. + * + * Disabling all event codes for a given type will not disable the event + * type. Use libevdev_disable_event_type() for that. + * + * This is a local modification only affecting only this representation of + * this device. + * + * Disabling codes of type EV_SYN will not work. Don't shoot yourself in the + * foot. It hurts. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param type The event type to disable (EV_ABS, EV_KEY, ...) + * @param code The event code to disable (ABS_X, REL_X, etc.) + * + * @return 0 on success or -1 otherwise + * + * @see libevdev_has_event_code + * @see libevdev_disable_event_type + */ +int libevdev_disable_event_code(struct libevdev *dev, unsigned int type, unsigned int code); + +/** + * @ingroup kernel + * + * Set the device's EV_ABS axis to the value defined in the abs + * parameter. This will be written to the kernel. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_ABS event code to modify, one of ABS_X, ABS_Y, etc. + * @param abs Axis info to set the kernel axis to + * + * @return 0 on success, or a negative errno on failure + * + * @see libevdev_enable_event_code + */ +int libevdev_kernel_set_abs_info(struct libevdev *dev, unsigned int code, const struct input_absinfo *abs); + +/** + * @ingroup kernel + */ +enum libevdev_led_value { + LIBEVDEV_LED_ON = 3, /**< Turn the LED on */ + LIBEVDEV_LED_OFF = 4 /**< Turn the LED off */ +}; + +/** + * @ingroup kernel + * + * Turn an LED on or off. Convenience function, if you need to modify multiple + * LEDs simultaneously, use libevdev_kernel_set_led_values() instead. + * + * @note enabling an LED requires write permissions on the device's file descriptor. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param code The EV_LED event code to modify, one of LED_NUML, LED_CAPSL, ... + * @param value Specifies whether to turn the LED on or off + * @return 0 on success, or a negative errno on failure + */ +int libevdev_kernel_set_led_value(struct libevdev *dev, unsigned int code, enum libevdev_led_value value); + +/** + * @ingroup kernel + * + * Turn multiple LEDs on or off simultaneously. This function expects a pair + * of LED codes and values to set them to, terminated by a -1. For example, to + * switch the NumLock LED on but the CapsLock LED off, use: + * + * @code + * libevdev_kernel_set_led_values(dev, LED_NUML, LIBEVDEV_LED_ON, + * LED_CAPSL, LIBEVDEV_LED_OFF, + * -1); + * @endcode + * + * If any LED code or value is invalid, this function returns -EINVAL and no + * LEDs are modified. + * + * @note enabling an LED requires write permissions on the device's file descriptor. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param ... A pair of LED_* event codes and libevdev_led_value_t, followed by + * -1 to terminate the list. + * @return 0 on success, or a negative errno on failure + */ +int libevdev_kernel_set_led_values(struct libevdev *dev, ...); + +/** + * @ingroup kernel + * + * Set the clock ID to be used for timestamps. Further events from this device + * will report an event time based on the given clock. + * + * This is a modification only affecting this representation of + * this device. + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param clockid The clock to use for future events. Permitted values + * are CLOCK_MONOTONIC and CLOCK_REALTIME (the default). + * @return 0 on success, or a negative errno on failure + */ +int libevdev_set_clock_id(struct libevdev *dev, int clockid); + +/** + * @ingroup misc + * + * Helper function to check if an event is of a specific type. This is + * virtually the same as: + * + * ev->type == type + * + * with the exception that some sanity checks are performed to ensure type + * is valid. + * + * @note The ranges for types are compiled into libevdev. If the kernel + * changes the max value, libevdev will not automatically pick these up. + * + * @param ev The input event to check + * @param type Input event type to compare the event against (EV_REL, EV_ABS, + * etc.) + * + * @return 1 if the event type matches the given type, 0 otherwise (or if + * type is invalid) + */ +int libevdev_event_is_type(const struct input_event *ev, unsigned int type); + +/** + * @ingroup misc + * + * Helper function to check if an event is of a specific type and code. This + * is virtually the same as: + * + * ev->type == type && ev->code == code + * + * with the exception that some sanity checks are performed to ensure type and + * code are valid. + * + * @note The ranges for types and codes are compiled into libevdev. If the kernel + * changes the max value, libevdev will not automatically pick these up. + * + * @param ev The input event to check + * @param type Input event type to compare the event against (EV_REL, EV_ABS, + * etc.) + * @param code Input event code to compare the event against (ABS_X, REL_X, + * etc.) + * + * @return 1 if the event type matches the given type and code, 0 otherwise + * (or if type/code are invalid) + */ +int libevdev_event_is_code(const struct input_event *ev, unsigned int type, unsigned int code); + +/** + * @ingroup misc + * + * @param type The event type to return the name for. + * + * @return The name of the given event type (e.g. EV_ABS) or NULL for an + * invalid type + * + * @note The list of names is compiled into libevdev. If the kernel adds new + * defines for new event types, libevdev will not automatically pick these up. + */ +const char * libevdev_event_type_get_name(unsigned int type); +/** + * @ingroup misc + * + * @param type The event type for the code to query (EV_SYN, EV_REL, etc.) + * @param code The event code to return the name for (e.g. ABS_X) + * + * @return The name of the given event code (e.g. ABS_X) or NULL for an + * invalid type or code + * + * @note The list of names is compiled into libevdev. If the kernel adds new + * defines for new event codes, libevdev will not automatically pick these up. + */ +const char * libevdev_event_code_get_name(unsigned int type, unsigned int code); + +/** + * @ingroup misc + * + * @param prop The input prop to return the name for (e.g. INPUT_PROP_BUTTONPAD) + * + * @return The name of the given input prop (e.g. INPUT_PROP_BUTTONPAD) or NULL for an + * invalid property + * + * @note The list of names is compiled into libevdev. If the kernel adds new + * defines for new properties libevdev will not automatically pick these up. + * @note On older kernels input properties may not be defined and + * libevdev_property_get_name() will always return NULL + */ +const char* libevdev_property_get_name(unsigned int prop); + +/** + * @ingroup misc + * + * @param type The event type to return the maximum for (EV_ABS, EV_REL, etc.). No max is defined for + * EV_SYN. + * + * @return The max value defined for the given event type, e.g. ABS_MAX for a type of EV_ABS, or -1 + * for an invalid type. + * + * @note The max value is compiled into libevdev. If the kernel changes the + * max value, libevdev will not automatically pick these up. + */ +int libevdev_event_type_get_max(unsigned int type); + +/** + * @ingroup misc + * + * Look up an event-type by its name. Event-types start with "EV_" followed by + * the name (eg., "EV_ABS"). The "EV_" prefix must be included in the name. It + * returns the constant assigned to the event-type or -1 if not found. + * + * @param name A non-NULL string describing an input-event type ("EV_KEY", + * "EV_ABS", ...), zero-terminated. + * + * @return The given type constant for the passed name or -1 if not found. + * + * @note EV_MAX is also recognized. + */ +int libevdev_event_type_from_name(const char *name); + +/** + * @ingroup misc + * + * Look up an event-type by its name. Event-types start with "EV_" followed by + * the name (eg., "EV_ABS"). The "EV_" prefix must be included in the name. It + * returns the constant assigned to the event-type or -1 if not found. + * + * @param name A non-NULL string describing an input-event type ("EV_KEY", + * "EV_ABS", ...). + * @param len The length of the passed string excluding any terminating 0 + * character. + * + * @return The given type constant for the passed name or -1 if not found. + * + * @note EV_MAX is also recognized. + */ +int libevdev_event_type_from_name_n(const char *name, size_t len); + +/** + * @ingroup misc + * + * Look up an event code by its type and name. Event codes start with a fixed + * prefix followed by their name (eg., "ABS_X"). The prefix must be included in + * the name. It returns the constant assigned to the event code or -1 if not + * found. + * + * You have to pass the event type where to look for the name. For instance, to + * resolve "ABS_X" you need to pass EV_ABS as type and "ABS_X" as string. + * Supported event codes are codes starting with SYN_, KEY_, BTN_, REL_, ABS_, + * MSC_, SND_, SW_, LED_, REP_, FF_. + * + * @param type The event type (EV_* constant) where to look for the name. + * @param name A non-NULL string describing an input-event code ("KEY_A", + * "ABS_X", "BTN_Y", ...), zero-terminated. + * + * @return The given code constant for the passed name or -1 if not found. + */ +int libevdev_event_code_from_name(unsigned int type, const char *name); + +/** + * @ingroup misc + * + * Look up an event code by its type and name. Event codes start with a fixed + * prefix followed by their name (eg., "ABS_X"). The prefix must be included in + * the name. It returns the constant assigned to the event code or -1 if not + * found. + * + * You have to pass the event type where to look for the name. For instance, to + * resolve "ABS_X" you need to pass EV_ABS as type and "ABS_X" as string. + * Supported event codes are codes starting with SYN_, KEY_, BTN_, REL_, ABS_, + * MSC_, SND_, SW_, LED_, REP_, FF_. + * + * @param type The event type (EV_* constant) where to look for the name. + * @param name A non-NULL string describing an input-event code ("KEY_A", + * "ABS_X", "BTN_Y", ...). + * @param len The length of the string in @p name excluding any terminating 0 + * character. + * + * @return The given code constant for the name or -1 if not found. + */ +int libevdev_event_code_from_name_n(unsigned int type, const char *name, + size_t len); + +/** + * @ingroup misc + * + * Look up an input property by its name. Properties start with the fixed + * prefix "INPUT_PROP_" followed by their name (eg., "INPUT_PROP_POINTER"). + * The prefix must be included in the name. It returns the constant assigned + * to the property or -1 if not found. + * + * @param name A non-NULL string describing an input property + * + * @return The given code constant for the name or -1 if not found. + */ +int libevdev_property_from_name(const char *name); + +/** + * @ingroup misc + * + * Look up an input property by its name. Properties start with the fixed + * prefix "INPUT_PROP_" followed by their name (eg., "INPUT_PROP_POINTER"). + * The prefix must be included in the name. It returns the constant assigned + * to the property or -1 if not found. + * + * @param name A non-NULL string describing an input property + * @param len The length of the string in @p name excluding any terminating 0 + * character. + * + * @return The given code constant for the name or -1 if not found. + */ +int libevdev_property_from_name_n(const char *name, size_t len); + +/** + * @ingroup bits + * + * Get the repeat delay and repeat period values for this device. This + * function is a convenience function only, EV_REP is supported by + * libevdev_get_event_value(). + * + * @param dev The evdev device, already initialized with libevdev_set_fd() + * @param delay If not null, set to the repeat delay value + * @param period If not null, set to the repeat period value + * + * @return 0 on success, -1 if this device does not have repeat settings. + * + * @note This function is signal-safe + * + * @see libevdev_get_event_value + */ +int libevdev_get_repeat(const struct libevdev *dev, int *delay, int *period); + +/********* DEPRECATED SECTION *********/ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define LIBEVDEV_DEPRECATED __attribute__ ((deprecated)) +#else +#define LIBEVDEV_DEPRECATED +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* LIBEVDEV_H */ diff --git a/UI/Config/EmulationConfig.cs b/UI/Config/EmulationConfig.cs index 1938dc8..35751a5 100644 --- a/UI/Config/EmulationConfig.cs +++ b/UI/Config/EmulationConfig.cs @@ -22,7 +22,7 @@ namespace Mesen.GUI.Config [MinMax(0, 1000)] public UInt32 PpuExtraScanlinesBeforeNmi = 0; [MinMax(0, 1000)] public UInt32 PpuExtraScanlinesAfterNmi = 0; - public RamPowerOnState RamPowerOnState; + public RamState RamPowerOnState = RamState.AllZeros; public void ApplyConfig() { @@ -37,7 +37,7 @@ namespace Mesen.GUI.Config Pal = 2 } - public enum RamPowerOnState + public enum RamState { AllZeros = 0, AllOnes = 1, diff --git a/UI/Forms/frmMain.cs b/UI/Forms/frmMain.cs index 5d0a99b..38f8f97 100644 --- a/UI/Forms/frmMain.cs +++ b/UI/Forms/frmMain.cs @@ -88,6 +88,11 @@ namespace Mesen.GUI.Forms { base.OnFormClosing(e); + DebugApi.ResumeExecution(); + DebugWindowManager.CloseAll(); + + EmuApi.Stop(); + if(_notifListener != null) { _notifListener.Dispose(); _notifListener = null; @@ -97,10 +102,6 @@ namespace Mesen.GUI.Forms ConfigManager.Config.WindowSize = this.WindowState == FormWindowState.Normal ? this.Size : this.RestoreBounds.Size; ConfigManager.ApplyChanges(); - DebugApi.ResumeExecution(); - DebugWindowManager.CloseAll(); - - EmuApi.Stop(); EmuApi.Release(); } diff --git a/UI/UI.csproj b/UI/UI.csproj index ead1339..64ca5aa 100644 --- a/UI/UI.csproj +++ b/UI/UI.csproj @@ -8,7 +8,7 @@ WinExe Properties Mesen.GUI - MesenS + Mesen-S v4.5 512 publish\ diff --git a/Utilities/stdafx.h b/Utilities/stdafx.h index 28964d0..a3de647 100644 --- a/Utilities/stdafx.h +++ b/Utilities/stdafx.h @@ -7,6 +7,7 @@ #include #include #include +#include #include "UTF8Util.h" diff --git a/build.sh b/build.sh index ef22dcc..e7fd56a 100644 --- a/build.sh +++ b/build.sh @@ -6,12 +6,7 @@ if [ "$1" = libretro ]; then MESENPLATFORM=x64 make clean LTO=true MESENPLATFORM=x64 make libretro -j 16 - - MESENPLATFORM=x86 make clean - LTO=true MESENPLATFORM=x86 make libretro -j 16 else MESENPLATFORM=x64 BUILDTARGET=core ./buildPGO.sh - MESENPLATFORM=x86 BUILDTARGET=core ./buildPGO.sh - cp ./InteropDLL/obj.x64/libMesenCore.x64.dll ./bin/Any\ CPU/PGO\ Profile/Dependencies - cp ./InteropDLL/obj.x86/libMesenCore.x86.dll ./bin/Any\ CPU/PGO\ Profile/Dependencies + cp ./InteropDLL/obj.x64/libMesenSCore.x64.dll ./bin/Any\ CPU/PGO\ Profile/Dependencies fi diff --git a/makefile b/makefile index 28ce787..71cf623 100644 --- a/makefile +++ b/makefile @@ -62,8 +62,8 @@ ifeq ($(PGO),optimize) endif OBJFOLDER=obj.$(MESENPLATFORM) -SHAREDLIB=libMesenCore.$(MESENPLATFORM).dll -LIBRETROLIB=mesen_libretro.$(MESENPLATFORM).so +SHAREDLIB=libMesenSCore.$(MESENPLATFORM).dll +LIBRETROLIB=mesens_libretro.$(MESENPLATFORM).so RELEASEFOLDER=bin/$(MESENPLATFORM)/Release COREOBJ=$(patsubst Core/%.cpp,Core/$(OBJFOLDER)/%.o,$(wildcard Core/*.cpp)) @@ -71,6 +71,7 @@ UTILOBJ=$(patsubst Utilities/%.cpp,Utilities/$(OBJFOLDER)/%.o,$(wildcard Utiliti LINUXOBJ=$(patsubst Linux/%.cpp,Linux/$(OBJFOLDER)/%.o,$(wildcard Linux/*.cpp)) SEVENZIPOBJ=$(patsubst SevenZip/%.c,SevenZip/$(OBJFOLDER)/%.o,$(wildcard SevenZip/*.c)) LUAOBJ=$(patsubst Lua/%.c,Lua/$(OBJFOLDER)/%.o,$(wildcard Lua/*.c)) +DLLOBJ=$(patsubst InteropDLL/%.cpp,InteropDLL/$(OBJFOLDER)/%.o,$(wildcard InteropDLL/*.cpp)) ifeq ($(SYSTEM_LIBEVDEV), true) LIBEVDEVLIB=$(shell pkg-config --libs libevdev) @@ -90,10 +91,10 @@ ui: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB) rm -fr $(RELEASEFOLDER)/Dependencies/* cd UpdateHelper && msbuild /property:Configuration="Release" /property:Platform="AnyCPU" cp "bin/Any CPU/Release/MesenUpdater.exe" $(RELEASEFOLDER)/Dependencies/ - cp -r GUI.NET/Dependencies/* $(RELEASEFOLDER)/Dependencies/ + cp -r UI/Dependencies/* $(RELEASEFOLDER)/Dependencies/ cp InteropDLL/$(OBJFOLDER)/$(SHAREDLIB) $(RELEASEFOLDER)/Dependencies/$(SHAREDLIB) cd $(RELEASEFOLDER)/Dependencies && zip -r ../Dependencies.zip * - cd GUI.NET && msbuild /property:Configuration="Release" /property:Platform="$(MESENPLATFORM)" /property:PreBuildEvent="" '/property:DefineConstants="HIDETESTMENU;DISABLEAUTOUPDATE"' /property:CodeAnalysisRuleSet="" + cd UI && msbuild /property:Configuration="Release" /property:Platform="$(MESENPLATFORM)" /property:PreBuildEvent="" '/property:DefineConstants="HIDETESTMENU;DISABLEAUTOUPDATE"' /property:CodeAnalysisRuleSet="" libretro: Libretro/$(OBJFOLDER)/$(LIBRETROLIB) mkdir -p bin @@ -104,9 +105,6 @@ core: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB) runtests: cd TestHelper/$(OBJFOLDER) && ./testhelper -rungametests: - cd TestHelper/$(OBJFOLDER) && ./testhelper ~/Mesen/TestGames - testhelper: InteropDLL/$(OBJFOLDER)/$(SHAREDLIB) mkdir -p TestHelper/$(OBJFOLDER) $(CPPC) $(GCCOPTIONS) -Wl,-z,defs -o testhelper TestHelper/*.cpp InteropDLL/ConsoleWrapper.cpp $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(COREOBJ) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB) @@ -135,10 +133,12 @@ Linux/$(OBJFOLDER)/%.o: Linux/%.cpp mkdir -p Linux/$(OBJFOLDER) && cd Linux/$(OBJFOLDER) && $(CPPC) $(GCCOPTIONS) -c $(patsubst Linux/%, ../%, $<) $(SDL2INC) $(LIBEVDEVINC) Linux/$(OBJFOLDER)/%.o: Linux/libevdev/%.c mkdir -p Linux/$(OBJFOLDER) && cd Linux/$(OBJFOLDER) && $(CC) $(CCOPTIONS) -c $(patsubst Linux/%, ../%, $<) - -InteropDLL/$(OBJFOLDER)/$(SHAREDLIB): $(SEVENZIPOBJ) $(LUAOBJ) $(UTILOBJ) $(COREOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) InteropDLL/ConsoleWrapper.cpp InteropDLL/DebugWrapper.cpp +InteropDLL/$(OBJFOLDER)/%.o: InteropDLL/%.cpp + mkdir -p InteropDLL/$(OBJFOLDER) && cd InteropDLL/$(OBJFOLDER) && $(CPPC) $(GCCOPTIONS) -c $(patsubst InteropDLL/%, ../%, $<) + +InteropDLL/$(OBJFOLDER)/$(SHAREDLIB): $(SEVENZIPOBJ) $(LUAOBJ) $(UTILOBJ) $(COREOBJ) $(LIBEVDEVOBJ) $(LINUXOBJ) $(DLLOBJ) mkdir -p InteropDLL/$(OBJFOLDER) - $(CPPC) $(GCCOPTIONS) -Wl,-z,defs -shared -o $(SHAREDLIB) InteropDLL/*.cpp $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(COREOBJ) $(SDL2INC) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB) + $(CPPC) $(GCCOPTIONS) -Wl,-z,defs -shared -o $(SHAREDLIB) $(DLLOBJ) $(SEVENZIPOBJ) $(LUAOBJ) $(LINUXOBJ) $(LIBEVDEVOBJ) $(UTILOBJ) $(COREOBJ) $(SDL2INC) -pthread $(FSLIB) $(SDL2LIB) $(LIBEVDEVLIB) cp $(SHAREDLIB) bin/pgohelperlib.so mv $(SHAREDLIB) InteropDLL/$(OBJFOLDER) @@ -155,10 +155,10 @@ official: ./build.sh debug: - MONO_LOG_LEVEL=debug mono $(RELEASEFOLDER)/Mesen.exe + MONO_LOG_LEVEL=debug mono $(RELEASEFOLDER)/Mesen-S.exe run: - mono $(RELEASEFOLDER)/Mesen.exe + mono $(RELEASEFOLDER)/Mesen-S.exe clean: rm -rf Lua/$(OBJFOLDER)