diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index 726dc2d97c..1b965f3cbc 100644 --- a/cmake/SelectPcsx2Plugins.cmake +++ b/cmake/SelectPcsx2Plugins.cmake @@ -6,7 +6,7 @@ set(msg_dep_pcsx2 "check these libraries -> wxWidgets (>=2.8.10), gtk2 (>= set(msg_dep_cdvdiso "check these libraries -> bzip2 (>=1.0.5), gtk2 (>=2.16)") set(msg_dep_zerogs "check these libraries -> glew (>=1.6), opengl, X11, nvidia-cg-toolkit (>=2.1)") set(msg_dep_gsdx "check these libraries -> opengl, egl, X11") -set(msg_dep_onepad "check these libraries -> sdl (>=1.2)") +set(msg_dep_onepad "check these libraries -> sdl (>=1.2), X11") set(msg_dep_spu2x "check these libraries -> soundtouch (>=1.5), alsa, portaudio (>=1.9), sdl (>=1.2) pcsx2 common libs") set(msg_dep_zerospu2 "check these libraries -> soundtouch (>=1.5), alsa") if(GLSL_API) @@ -205,12 +205,22 @@ if(GTKn_FOUND AND EXTRA_PLUGINS) endif() #--------------------------------------- +#--------------------------------------- +# LilyPad +# requires: -X11 +#--------------------------------------- +if(GTKn_FOUND AND X11_FOUND) + set(LilyPad TRUE) +endif() +#--------------------------------------- + #--------------------------------------- # onepad #--------------------------------------- # requires: -SDL +# -X11 #--------------------------------------- -if(SDLn_FOUND) +if(SDLn_FOUND AND X11_FOUND) set(onepad TRUE) elseif(NOT EXISTS "${CMAKE_SOURCE_DIR}/plugins/onepad") set(onepad FALSE) @@ -286,7 +296,6 @@ set(cdvdGigaherz FALSE) set(CDVDisoEFP FALSE) set(CDVDolio FALSE) set(CDVDpeops FALSE) -set(LilyPad FALSE) set(PeopsSPU2 FALSE) set(SSSPSXPAD FALSE) set(xpad FALSE) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 5b21a52245..4cfe70b136 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -6,123 +6,99 @@ if(NOT TOP_CMAKE_WAS_SOURCED) endif() -# make cdvdGigaherz #if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/cdvdGigaherz" AND cdvdGigaherz) # add_subdirectory(cdvdGigaherz) -#endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/cdvdGigaherz" AND cdvdGigaherz) +#endif() -# make CDVDiso if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDiso" AND CDVDiso) add_subdirectory(CDVDiso/src) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDiso" AND CDVDiso) +endif() -# make CDVDisoEFP # if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDisoEFP" AND CDVDisoEFP) # add_subdirectory(CDVDisoEFP) -# endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDisoEFP" AND CDVDisoEFP) +# endif() -# make CDVDlinuz if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDlinuz" AND CDVDlinuz) add_subdirectory(CDVDlinuz/Src) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDlinuz" AND CDVDlinuz) +endif() -# make CDVDnull if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDnull" AND CDVDnull) add_subdirectory(CDVDnull) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDnull" AND CDVDnull) +endif() -# make CDVDolio # if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDolio" AND CDVDolio) # add_subdirectory(CDVDolio) -# endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDolio" AND CDVDolio) +# endif() -# make CDVDpeops #if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDpeops" AND CDVDpeops) # add_subdirectory(CDVDpeops) -#endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/CDVDpeops" AND CDVDpeops) +#endif() -# make dev9null if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/dev9null" AND dev9null) add_subdirectory(dev9null) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/dev9null" AND dev9null) +endif() -# make FWnull if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/FWnull" AND FWnull) add_subdirectory(FWnull) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/FWnull" AND FWnull) +endif() -# make GSdx if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSdx" AND GSdx) add_subdirectory(GSdx) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSdx" AND GSdx) +endif() -# make GSnull if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSnull" AND GSnull) add_subdirectory(GSnull) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/GSnull" AND GSnull) +endif() -# make LilyPad -#if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/LilyPad" AND LilyPad) -# add_subdirectory(LilyPad) -#endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/LilyPad" AND LilyPad) +if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/LilyPad" AND LilyPad) + add_subdirectory(LilyPad) +endif() -# make onepad if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/onepad" AND onepad) add_subdirectory(onepad) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/onepad" AND onepad) +endif() -# make PadNull if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/PadNull" AND PadNull) add_subdirectory(PadNull) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/PadNull" AND PadNull) +endif() -# make PeopsSPU2 # if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/PeopsSPU2" AND PeopsSPU2) # add_subdirectory(PeopsSPU2) -# endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/PeopsSPU2" AND PeopsSPU2) +# endif() -# make SPU2null if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/SPU2null" AND SPU2null) add_subdirectory(SPU2null) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/SPU2null" AND SPU2null) +endif() -# make spu2-x if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/spu2-x" AND spu2-x) add_subdirectory(spu2-x/src) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/spu2-x" AND spu2-x) +endif() -# make SSSPSXPAD #if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/SSSPSXPAD" AND SSSPSXPAD) # add_subdirectory(SSSPSXPAD) -#endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/SSSPSXPAD" AND SSSPSXPAD) +#endif() -# make USBnull if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/USBnull" AND USBnull) add_subdirectory(USBnull) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/USBnull" AND USBnull) +endif() -# make xpad #if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/xpad" AND xpad) # add_subdirectory(xpad) -#endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/xpad" AND xpad) +#endif() -# make zerogs #if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zerogs" AND zerogs) # add_subdirectory(zerogs) -#endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zerogs" AND zerogs) +#endif() -# make zzogl-pg if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zzogl-pg" AND zzogl) add_subdirectory(zzogl-pg/opengl) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zzogl-pg" AND zzogl) +endif() -# make zeropad if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zeropad" AND zeropad) add_subdirectory(zeropad) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zeropad" AND zeropad) +endif() -# make zerospu2 if(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zerospu2" AND zerospu2) add_subdirectory(zerospu2) -endif(EXISTS "${CMAKE_SOURCE_DIR}/plugins/zerospu2" AND zerospu2) +endif() diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt new file mode 100644 index 0000000000..799ada8207 --- /dev/null +++ b/plugins/LilyPad/CMakeLists.txt @@ -0,0 +1,75 @@ +# Check that people use the good file +if(NOT TOP_CMAKE_WAS_SOURCED) + message(FATAL_ERROR " + You did not 'cmake' the good CMakeLists.txt file. Use the one in the top dir. + It is advice to delete all wrongly generated cmake stuff => CMakeFiles & CMakeCache.txt") +endif() + + +# plugin name +set(Output LilyPad-0.11.0) + +set(OptimizationFlags + -O2 + ) + +if(CMAKE_BUILD_TYPE STREQUAL Debug) + set(lilypadFinalFlags "-DPCSX2_DEBUG") + +elseif(CMAKE_BUILD_TYPE STREQUAL Devel) + set(lilypadFinalFlags ${OptimizationFlags}) + +elseif(CMAKE_BUILD_TYPE STREQUAL Release) + set(lilypadFinalFlags ${OptimizationFlags}) + +endif() + +# lilypad sources +set(lilypadSources + DeviceEnumerator.cpp + InputManager.cpp + KeyboardQueue.cpp + LilyPad.cpp + Linux/Config.cpp + Linux/ConfigHelper.cpp + Linux/JoyEvdev.cpp + Linux/KeyboardMouse.cpp + Linux/KeyboardQueue.cpp + ) + +# lilypad headers +set(lilypadHeaders + ) + +# lilypad Linux sources +set(lilypadLinuxSources + ) + +# lilypad Linux headers +set(lilypadLinuxHeaders + ) + +if (SDL2_API) + set(lilypadFinalLibs + ${SDL2_LIBRARIES} + ) +else() + set(lilypadFinalLibs + ${SDL_LIBRARY} + ) +endif() + +set(lilypadFinalLibs + #${lilypadFinalLibs} + #${GTK2_LIBRARIES} + #${X11_LIBRARIES} +) + +set(lilypadFinalSources + ${lilypadSources} + ${lilypadHeaders} + ${lilypadLinuxSources} + ${lilypadLinuxHeaders} +) + +add_pcsx2_plugin(${Output} "${lilypadFinalSources}" "${lilypadFinalLibs}" "${lilypadFinalFlags}") diff --git a/plugins/LilyPad/Config.h b/plugins/LilyPad/Config.h index c610dce81c..9e274906d1 100644 --- a/plugins/LilyPad/Config.h +++ b/plugins/LilyPad/Config.h @@ -84,8 +84,6 @@ extern GeneralConfig config; void UnloadConfigs(); -void AddIgnore(LPARAM k); - void SetVolume(int volume); int LoadSettings(int force = 0, wchar_t *file = 0); diff --git a/plugins/LilyPad/DeviceEnumerator.cpp b/plugins/LilyPad/DeviceEnumerator.cpp index ca5b4076df..a848095d3f 100644 --- a/plugins/LilyPad/DeviceEnumerator.cpp +++ b/plugins/LilyPad/DeviceEnumerator.cpp @@ -27,18 +27,28 @@ #include "HidDevice.h" #include "DualShock3.h" +#ifdef __linux__ +#include "Linux/KeyboardMouse.h" +#include "Linux/JoyEvdev.h" +#endif + void EnumDevices(int hideDXXinput) { // Needed for enumeration of some device types. dm->ReleaseInput(); InputDeviceManager *oldDm = dm; dm = new InputDeviceManager(); +#ifdef _MSC_VER EnumHookDevices(); EnumWindowsMessagingDevices(); EnumRawInputDevices(); EnumDualShock3s(); EnumXInputDevices(); EnumDirectInputDevices(hideDXXinput); +#else + EnumLnx(); + EnumJoystickEvdev(); +#endif dm->CopyBindings(oldDm->numDevices, oldDm->devices); diff --git a/plugins/LilyPad/Global.h b/plugins/LilyPad/Global.h index 7c6162d13b..8201d34daf 100644 --- a/plugins/LilyPad/Global.h +++ b/plugins/LilyPad/Global.h @@ -20,6 +20,76 @@ // dll size by over 100k while avoiding any dependencies on updated CRT dlls. #pragma once +#ifdef __linux__ +// Seriously why there is no standard +#include "stdint.h" +typedef uint32_t DWORD; +typedef uint16_t USHORT; +typedef int64_t __int64; + +#define MAX_PATH (256) // random value + +#include + +#define VK_SHIFT XK_Shift_L +#define VK_LSHIFT XK_Shift_L +#define VK_RSHIFT XK_Shift_R +#define VK_LMENU XK_Menu +#define VK_RMENU XK_Menu +#define VK_MENU XK_Menu +#define VK_CONTROL XK_Control_L +#define VK_TAB XK_Tab +#define VK_ESCAPE XK_Escape +#define VK_F4 XK_F4 + +#include +#include + +template +void wsprintfW(Array& buf, const wchar_t *format, ...) { + va_list a; + va_start(a, format); + + vswprintf(buf, sizeof(buf)/sizeof(buf[0]), format, a); + + va_end(a); +} + +template +void wsprintf(Array& buf, const wchar_t *format, ...) { + va_list a; + va_start(a, format); + + vswprintf(buf, sizeof(buf)/sizeof(buf[0]), format, a); + + va_end(a); +} + +static inline int wcsicmp(const wchar_t* w1, const wchar_t* w2) { + // I didn't find a way to put ignore case ... + return wcscmp(w1, w2); +} + +#include +static inline unsigned int timeGetTime() { + struct timeval now; + gettimeofday(&now, NULL); + uint64_t ms = (now.tv_usec/1000) + (now.tv_sec * 1000); + return (ms & 0xFFFFFFFF); // MS code is u32 ... +} + +#include "Utilities/Dependencies.h" +#include "Utilities/StringHelpers.h" +#include "Utilities/Path.h" + +#include + +extern Display *GSdsp; +extern Window GSwin; + +#endif + + #define DIRECTINPUT_VERSION 0x0800 #ifdef NO_CRT @@ -40,9 +110,10 @@ inline void * realloc(void *mem, size_t size); #ifdef _MSC_VER #define EXPORT_C_(type) extern "C" __declspec(dllexport) type CALLBACK #else -#define EXPORT_C_(type) extern "C" type +#define EXPORT_C_(type) extern "C" __attribute__((externally_visible,visibility("default"))) type CALLBACK #endif +#ifdef _MSC_VER // Actually works with 0x0400, but need 0x500 to get XBUTTON defines, // 0x501 to get raw input structures, and 0x0600 to get WM_MOUSEHWHEEL. #define WINVER 0x0600 @@ -61,17 +132,28 @@ inline void * realloc(void *mem, size_t size); #include #endif +#else + +#include +#include + +#endif + #include #include #include +#ifdef _MSC_VER #include // Only needed for DBT_DEVNODES_CHANGED #include +#endif #include "PS2Edefs.h" +#ifdef _MSC_VER extern HINSTANCE hInst; +#endif // Needed for config screen void GetNameAndVersionString(wchar_t *out); @@ -106,6 +188,7 @@ EXPORT_C_(void) PADconfigure(); EXPORT_C_(s32) PADfreeze(int mode, freezeData *data); EXPORT_C_(s32) PADsetSlot(u8 port, u8 slot); EXPORT_C_(s32) PADqueryMtap(u8 port); +EXPORT_C_(void) PADsetSettingsDir(const char *dir); #ifdef NO_CRT diff --git a/plugins/LilyPad/InputManager.cpp b/plugins/LilyPad/InputManager.cpp index 44d1ea0b41..ccf9ed4636 100644 --- a/plugins/LilyPad/InputManager.cpp +++ b/plugins/LilyPad/InputManager.cpp @@ -54,7 +54,9 @@ Device::Device(DeviceAPI api, DeviceType d, const wchar_t *displayName, const wc attached = 1; enabled = 0; +#ifdef _MSC_VER hWndProc = 0; +#endif virtualControls = 0; numVirtualControls = 0; @@ -221,7 +223,11 @@ void Device::CalcVirtualState() { double East = sin(angle); double South = -cos(angle); // Normalize so greatest direction is 1. +#ifdef __linux__ + double mul = FULLY_DOWN / std::max(fabs(South), fabs(East)); +#else double mul = FULLY_DOWN / max(fabs(South), fabs(East)); +#endif iEast = (int) floor(East * mul + 0.5); iSouth = (int) floor(South * mul + 0.5); } diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index 78ae4d4f1e..f78ddf99ab 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -130,6 +130,9 @@ enum DeviceAPI { // to ignore individual buttons. Wrapper itself takes care // of ignoring bound keys. Otherwise, works normally. IGNORE_KEYBOARD = 7, + // XXX + LNX_KEYBOARD = 16, + LNX_JOY = 17, }; enum DeviceType { @@ -196,12 +199,18 @@ struct InitInfo { // 1 when binding. int binding; +#ifdef _MSC_VER HWND hWndTop; // For config screen, need to eat button's message handling. //HWND hWndButton; WndProcEater* hWndProc; +#else + // Linux equivalent to HWND + Display *GSdsp; + Window GSwin; +#endif }; @@ -216,9 +225,11 @@ public: // Based on input modes. char enabled; +#ifdef _MSC_VER // Not all devices need to subclass the windproc, but most do so might as well // put it here... --air WndProcEater* hWndProc; +#endif union { // Allows for one loop to compare all 3 in order. diff --git a/plugins/LilyPad/KeyboardQueue.cpp b/plugins/LilyPad/KeyboardQueue.cpp index e6391d03f7..846b649d68 100644 --- a/plugins/LilyPad/KeyboardQueue.cpp +++ b/plugins/LilyPad/KeyboardQueue.cpp @@ -21,8 +21,12 @@ // What MS calls a single process Mutex. Faster, supposedly. // More importantly, can be abbreviated, amusingly, as cSection. +#ifdef _MSC_VER static CRITICAL_SECTION cSection; static u8 csInitialized = 0; +#else +static std::mutex cSection; +#endif #define EVENT_QUEUE_LEN 16 // Actually points one beyond the last queued event. @@ -31,11 +35,15 @@ static u8 nextQueuedEvent = 0; static keyEvent queuedEvents[EVENT_QUEUE_LEN]; void QueueKeyEvent(int key, int event) { +#ifdef _MSC_VER if (!csInitialized) { csInitialized = 1; InitializeCriticalSection(&cSection); } EnterCriticalSection(&cSection); +#else + std::lock_guard lock(cSection); +#endif // Don't queue events if escape is on top of queue. This is just for safety // purposes when a game is killing the emulator for whatever reason. @@ -57,23 +65,33 @@ void QueueKeyEvent(int key, int event) { nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; } } +#ifdef _MSC_VER LeaveCriticalSection(&cSection); +#endif } int GetQueuedKeyEvent(keyEvent *event) { if (lastQueuedEvent == nextQueuedEvent) return 0; +#ifdef _MSC_VER EnterCriticalSection(&cSection); +#else + std::lock_guard lock(cSection); +#endif *event = queuedEvents[nextQueuedEvent]; nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; +#ifdef _MSC_VER LeaveCriticalSection(&cSection); +#endif return 1; } void ClearKeyQueue() { lastQueuedEvent = nextQueuedEvent; +#ifdef _MSC_VER if (csInitialized) { DeleteCriticalSection(&cSection); csInitialized = 0; } +#endif } diff --git a/plugins/LilyPad/KeyboardQueue.h b/plugins/LilyPad/KeyboardQueue.h index 2512ca5375..f79ef32658 100644 --- a/plugins/LilyPad/KeyboardQueue.h +++ b/plugins/LilyPad/KeyboardQueue.h @@ -24,3 +24,9 @@ int GetQueuedKeyEvent(keyEvent *event); // Cleans up as well as clears queue. void ClearKeyQueue(); + +#ifdef __linux__ +void R_QueueKeyEvent(const keyEvent& event); +int R_GetQueuedKeyEvent(keyEvent *event); +void R_ClearKeyQueue(); +#endif diff --git a/plugins/LilyPad/LilyPad.cpp b/plugins/LilyPad/LilyPad.cpp index 58725ccb41..6fc998ef4d 100644 --- a/plugins/LilyPad/LilyPad.cpp +++ b/plugins/LilyPad/LilyPad.cpp @@ -26,7 +26,9 @@ #define PADdefs #include "DeviceEnumerator.h" +#ifdef _MSC_VER #include "WndProcEater.h" +#endif #include "KeyboardQueue.h" #include "svnrev.h" #include "DualShock3.h" @@ -39,6 +41,10 @@ // LilyPad version. #define VERSION ((0<<8) | 11 | (0<<24)) +#ifdef __linux__ +Display *GSdsp; +Window GSwin; +#else HINSTANCE hInst; HWND hWnd; HWND hWndTop; @@ -49,10 +55,15 @@ WndProcEater hWndTopProc; // ButtonProc is used mostly by the Config panel for eating the procedures of the // button with keyboard focus. WndProcEater hWndButtonProc; +#endif // Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking // havoc on each other... +#ifdef __linux__ +static std::mutex updateLock; +#else CRITICAL_SECTION updateLock; +#endif // Used to toggle mouse listening. u8 miceEnabled; @@ -61,8 +72,10 @@ u8 miceEnabled; int openCount = 0; int activeWindow = 0; +#ifdef _MSC_VER int windowThreadId = 0; int updateQueued = 0; +#endif int bufSize = 0; unsigned char outBuf[50]; @@ -74,6 +87,7 @@ unsigned char inBuf[50]; #define MODE_ANALOG 0x73 #define MODE_DS2_NATIVE 0x79 +#ifdef _MSC_VER int IsWindowMaximized (HWND hWnd) { RECT rect; if (GetWindowRect(hWnd, &rect)) { @@ -92,8 +106,10 @@ int IsWindowMaximized (HWND hWnd) { } return 0; } +#endif void DEBUG_TEXT_OUT(const char *text) { +#ifdef _MSC_VER if (config.debug) { HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { @@ -102,9 +118,11 @@ void DEBUG_TEXT_OUT(const char *text) { CloseHandle(hFile);; } } +#endif } void DEBUG_NEW_SET() { +#ifdef _MSC_VER if (config.debug && bufSize>1) { HANDLE hFile = CreateFileA("logs\\padLog.txt", FILE_APPEND_DATA, FILE_SHARE_READ, 0, OPEN_ALWAYS, 0, 0); if (hFile != INVALID_HANDLE_VALUE) { @@ -132,6 +150,7 @@ void DEBUG_NEW_SET() { } } bufSize = 0; +#endif } inline void DEBUG_IN(unsigned char c) { @@ -289,6 +308,7 @@ void UpdateEnabledDevices(int updateList = 0) { } } +#ifdef _MSC_VER BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, void* lpvReserved) { hInst = hInstance; if (fdwReason == DLL_PROCESS_ATTACH) { @@ -306,6 +326,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD fdwReason, void* lpvReserved) { } return 1; } +#endif void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { if (!delta) return; @@ -400,7 +421,11 @@ void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { void CapSum(ButtonSum *sum) { int i; for (i=0; i<3; i++) { +#ifdef __linux__ + int div = std::max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); +#else int div = max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); +#endif if (div > 255) { sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; @@ -423,6 +448,7 @@ char padReadKeyUpdated[4] = {0, 0, 0, 0}; #define LOCK_BUTTONS 4 #define LOCK_BOTH 1 +#ifdef _MSC_VER struct EnterScopedSection { CRITICAL_SECTION& m_cs; @@ -435,6 +461,7 @@ struct EnterScopedSection LeaveCriticalSection( &m_cs ); } }; +#endif void Update(unsigned int port, unsigned int slot) { char *stateUpdated; @@ -452,12 +479,17 @@ void Update(unsigned int port, unsigned int slot) { } // Lock prior to timecheck code to avoid pesky race conditions. +#ifdef __linux__ + std::lock_guard lock(updateLock); +#else EnterScopedSection padlock( updateLock ); +#endif static unsigned int LastCheck = 0; unsigned int t = timeGetTime(); if (t - LastCheck < 15 || !openCount) return; +#ifdef _MSC_VER if (windowThreadId != GetCurrentThreadId()) { if (stateUpdated[0] < 0) { if (!updateQueued) { @@ -470,6 +502,7 @@ void Update(unsigned int port, unsigned int slot) { } return; } +#endif LastCheck = t; @@ -481,10 +514,15 @@ void Update(unsigned int port, unsigned int slot) { for (i=0; i<8; i++) { s[i&1][i>>1] = pads[i&1][i>>1].lockedSum; } +#ifdef __linux__ + InitInfo info = { + 0, 0, GSdsp, GSwin + }; +#else InitInfo info = { 0, 0, hWndTop, &hWndGSProc }; - +#endif dm->Update(&info); static int turbo = 0; turbo++; @@ -669,6 +707,7 @@ u32 CALLBACK PS2EgetLibVersion2(u32 type) { return 0; } +#ifdef _MSC_VER // Used in about and config screens. void GetNameAndVersionString(wchar_t *out) { #ifdef NO_CRT @@ -679,6 +718,7 @@ void GetNameAndVersionString(wchar_t *out) { wsprintfW(out, L"LilyPad %i.%i.%i (%lld)", (VERSION>>8)&0xFF, VERSION&0xFF, (VERSION>>24)&0xFF, SVN_REV); #endif } +#endif char* CALLBACK PSEgetLibName() { #ifdef NO_CRT @@ -760,7 +800,9 @@ struct QueryInfo { u8 response[42]; } query = {0,0,0,0, 0,0xFF, 0xF3}; +#ifdef _MSC_VER int saveStateIndex = 0; +#endif s32 CALLBACK PADinit(u32 flags) { // Note: Won't load settings if already loaded. @@ -773,7 +815,7 @@ s32 CALLBACK PADinit(u32 flags) { return PADinit(2); } - #ifdef PCSX2_DEBUG + #if defined(PCSX2_DEBUG) && defined(_MSC_VER) int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; _CrtSetDbgFlag( tmpFlag ); @@ -790,6 +832,9 @@ s32 CALLBACK PADinit(u32 flags) { query.lastByte = 1; query.numBytes = 0; ClearKeyQueue(); +#ifdef __linux__ + R_ClearKeyQueue(); +#endif // Just in case, when resuming emulation. ReleaseModifierKeys(); @@ -836,6 +881,7 @@ static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; +#ifdef _MSC_VER // Implements a couple of the hacks that affect whatever top-level window // the GS viewport belongs to (title, screensaver) ExtraWndProcResult TitleHackWndProc(HWND hWndTop, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT *output) { @@ -943,6 +989,7 @@ DWORD WINAPI MaximizeWindowThreadProc(void *lpParameter) { keybd_event(VK_LMENU, MapVirtualKey(VK_LMENU, MAPVK_VK_TO_VSC), KEYEVENTF_KEYUP, 0); return 0; } +#endif void CALLBACK PADconfigure() { if (openCount) { @@ -951,7 +998,7 @@ void CALLBACK PADconfigure() { Configure(); } - +#ifdef _MSC_VER DWORD WINAPI RenameWindowThreadProc(void *lpParameter) { wchar_t newTitle[200]; if (hWndTop) { @@ -973,12 +1020,14 @@ void SaveStateChanged() { if (hThread) CloseHandle(hThread); } } +#endif s32 CALLBACK PADopen(void *pDsp) { if (openCount++) return 0; DEBUG_TEXT_OUT("LilyPad opened\n\n"); miceEnabled = !config.mouseUnfocus; +#ifdef _MSC_VER if (!hWnd) { if (IsWindow((HWND)pDsp)) { hWnd = (HWND) pDsp; @@ -1036,6 +1085,7 @@ s32 CALLBACK PADopen(void *pDsp) { } restoreFullScreen = 0; } +#endif for (int port=0; port<2; port++) { for (int slot=0; slot<4; slot++) { memset(&pads[port][slot].sum, 0, sizeof(pads[port][slot].sum)); @@ -1044,6 +1094,7 @@ s32 CALLBACK PADopen(void *pDsp) { } } +#ifdef _MSC_VER // I'd really rather use this line, but GetActiveWindow() does not have complete specs. // It *seems* to return null when no window from this thread has focus, but the // Microsoft specs seem to imply it returns the window from this thread that would have focus, @@ -1052,6 +1103,11 @@ s32 CALLBACK PADopen(void *pDsp) { // activeWindow = GetActiveWindow() == hWnd; // activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); +#else + // Not used so far + GSdsp = *(Display**)pDsp; + GSwin = (Window)*(((uptr*)pDsp)+1); +#endif activeWindow = 1; UpdateEnabledDevices(); return 0; @@ -1060,12 +1116,16 @@ s32 CALLBACK PADopen(void *pDsp) { void CALLBACK PADclose() { if (openCount && !--openCount) { DEBUG_TEXT_OUT("LilyPad closed\n\n"); +#ifdef _MSC_VER updateQueued = 0; hWndGSProc.Release(); hWndTopProc.Release(); dm->ReleaseInput(); hWnd = 0; hWndTop = 0; +#else + R_ClearKeyQueue(); +#endif ClearKeyQueue(); } } @@ -1363,6 +1423,7 @@ u32 CALLBACK PADquery() { return 3; } +#ifdef _MSC_VER INT_PTR CALLBACK AboutDialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { if (uMsg == WM_INITDIALOG) { wchar_t idString[100]; @@ -1375,10 +1436,13 @@ INT_PTR CALLBACK AboutDialogProc(HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM } return 0; } +#endif void CALLBACK PADabout() { +#ifdef _MSC_VER DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUT), 0, AboutDialogProc); +#endif } s32 CALLBACK PADtest() { @@ -1395,11 +1459,12 @@ keyEvent* CALLBACK PADkeyEvent() { eventCount = 0; Update(2, 0); - static char shiftDown = 0; - static char altDown = 0; static keyEvent ev; if (!GetQueuedKeyEvent(&ev)) return 0; +#ifdef _MSC_VER + static char shiftDown = 0; + static char altDown = 0; if (miceEnabled && (ev.key == VK_ESCAPE || (int)ev.key == -2) && ev.evt == KEYPRESS) { // Disable mouse/KB hooks on escape (before going into paused mode). // This is a hack, since PADclose (which is called on pause) should enevtually also deactivate the @@ -1453,6 +1518,7 @@ keyEvent* CALLBACK PADkeyEvent() { ev.key = VK_MENU; altDown = (ev.evt == KEYPRESS); } +#endif return &ev; } diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp new file mode 100644 index 0000000000..eaf9a5e4f2 --- /dev/null +++ b/plugins/LilyPad/Linux/Config.cpp @@ -0,0 +1,514 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Global.h" + +#include "InputManager.h" +#include "Config.h" +#include "DeviceEnumerator.h" +#include "Linux/ConfigHelper.h" + +GeneralConfig config; +u8 ps2e = 0; + +#if 0 +remove 0x10F0 to compute the cmd value + +#define ID_SENSITIVITY 0x1007 +#define ID_LOCK_BUTTONS 0x10FC +#define ID_LOCK 0x10FD +#define ID_LOCK_DIRECTION 0x10FE +#define ID_MOUSE 0x10FF +#define ID_SELECT 0x1100 +#define ID_L3 0x1101 +#define ID_R3 0x1102 +#define ID_START 0x1103 +#define ID_DPAD_UP 0x1104 +#define ID_DPAD_RIGHT 0x1105 +#define ID_DPAD_DOWN 0x1106 +#define ID_DPAD_LEFT 0x1107 +#define ID_L2 0x1108 +#define ID_R2 0x1109 +#define ID_L1 0x110A +#define ID_R1 0x110B +#define ID_TRIANGLE 0x110C +#define ID_CIRCLE 0x110D +#define ID_CROSS 0x110E +#define ID_SQUARE 0x110F +#define ID_LSTICK_UP 0x1110 +#define ID_LSTICK_RIGHT 0x1111 +#define ID_LSTICK_DOWN 0x1112 +#define ID_LSTICK_LEFT 0x1113 +#define ID_RSTICK_UP 0x1114 +#define ID_RSTICK_RIGHT 0x1115 +#define ID_RSTICK_DOWN 0x1116 +#define ID_RSTICK_LEFT 0x1117 +#define ID_ANALOG 0x1118 +#define ID_DELETE 0x11FF +#define ID_DEBUG 0x1200 +#define ID_IGNORE 0x1201 +#define ID_CLEAR 0x1202 +#define ID_REFRESH 0x1202 +#define ID_SAVE 0x1204 +#define ID_LOAD 0x1205 +#define ID_BIG_MOTOR 0x120A +#define ID_SMALL_MOTOR 0x120B +#define ID_TEST 0x1300 +#define ID_CONTROLS 0x1301 +#define ID_FF 0x1304 + +#endif + +struct GeneralSettingsBool { + const wchar_t *name; + unsigned int ControlId; + u8 defaultValue; +}; + +// XXX: I try to remove only gui stuff +void DeleteBinding(int port, int slot, Device *dev, Binding *b) { + fprintf(stderr, "delete binding %d:%d\n", port, slot); + Binding *bindings = dev->pads[port][slot].bindings; + int i = b - bindings; + memmove(bindings+i, bindings+i+1, sizeof(Binding) * (dev->pads[port][slot].numBindings - i - 1)); + dev->pads[port][slot].numBindings--; +} + +void DeleteBinding(int port, int slot, Device *dev, ForceFeedbackBinding *b) { + ForceFeedbackBinding *bindings = dev->pads[port][slot].ffBindings; + int i = b - bindings; + memmove(bindings+i, bindings+i+1, sizeof(Binding) * (dev->pads[port][slot].numFFBindings - i - 1)); + dev->pads[port][slot].numFFBindings--; +} + +int BindCommand(Device *dev, unsigned int uid, unsigned int port, unsigned int slot, int command, int sensitivity, int turbo, int deadZone) { + // Checks needed because I use this directly when loading bindings. + if (port > 1 || slot>3) { + return -1; + } + if (!sensitivity) sensitivity = BASE_SENSITIVITY; + if ((uid>>16) & (PSHBTN|TGLBTN)) { + deadZone = 0; + } + else if (!deadZone) { + if ((uid>>16) & PRESSURE_BTN) { + deadZone = 1; + } + else { + deadZone = DEFAULT_DEADZONE; + } + } + // Relative axes can have negative sensitivity. + else if (((uid>>16) & 0xFF) == RELAXIS) { + sensitivity = abs(sensitivity); + } + VirtualControl *c = dev->GetVirtualControl(uid); + if (!c) return -1; + // Add before deleting. Means I won't scroll up one line when scrolled down to bottom. + int controlIndex = c - dev->virtualControls; + int index = 0; + PadBindings *p = dev->pads[port]+slot; + p->bindings = (Binding*) realloc(p->bindings, (p->numBindings+1) * sizeof(Binding)); + for (index = p->numBindings; index > 0; index--) { + if (p->bindings[index-1].controlIndex < controlIndex) break; + p->bindings[index] = p->bindings[index-1]; + } + Binding *b = p->bindings+index; + p->numBindings++; + b->command = command; + b->controlIndex = controlIndex; + b->turbo = turbo; + b->sensitivity = sensitivity; + b->deadZone = deadZone; + // Where it appears in listview. + //int count = ListBoundCommand(port, slot, dev, b); + + int newBindingIndex = index; + index = 0; + while (index < p->numBindings) { + if (index == newBindingIndex) { + index ++; + continue; + } + b = p->bindings + index; + int nuke = 0; + if (config.multipleBinding) { + if (b->controlIndex == controlIndex && b->command == command) + nuke = 1; + } + else { + int uid2 = dev->virtualControls[b->controlIndex].uid; + if (b->controlIndex == controlIndex || (!((uid2^uid) & 0xFFFFFF) && ((uid|uid2) & (UID_POV | UID_AXIS)))) + nuke = 1; + } + if (!nuke) { + index++; + continue; + } + if (index < newBindingIndex) { + newBindingIndex--; + //count --; + } + DeleteBinding(port, slot, dev, b); + } + if (!config.multipleBinding) { + for (int port2=0; port2<2; port2++) { + for (int slot2=0; slot2<4; slot2++) { + if (port2==port && slot2 == slot) continue; + PadBindings *p = dev->pads[port2]+slot2; + for (int i=0; i < p->numBindings; i++) { + Binding *b = p->bindings+i; + int uid2 = dev->virtualControls[b->controlIndex].uid; + if (b->controlIndex == controlIndex || (!((uid2^uid) & 0xFFFFFF) && ((uid|uid2) & (UID_POV | UID_AXIS)))) { + DeleteBinding(port2, slot2, dev, b); + i--; + } + } + } + } + } + + //return count; + return 0; +} + +// Ties together config data structure, config files, and general config +// dialog. +const GeneralSettingsBool BoolOptionsInfo[] = { + {L"Force Cursor Hide", 0 /*IDC_FORCE_HIDE*/, 0}, + {L"Mouse Unfocus", 0 /*IDC_MOUSE_UNFOCUS*/, 1}, + {L"Background", 0 /*IDC_BACKGROUND*/, 1}, + {L"Multiple Bindings", 0 /*IDC_MULTIPLE_BINDING*/, 0}, + + {L"DirectInput Game Devices", 0 /*IDC_G_DI*/, 1}, + {L"XInput", 0 /*IDC_G_XI*/, 1}, + {L"DualShock 3", 0 /*IDC_G_DS3*/, 0}, + + {L"Multitap 1", 0 /*IDC_MULTITAP1*/, 0}, + {L"Multitap 2", 0 /*IDC_MULTITAP2*/, 0}, + + {L"Escape Fullscreen Hack", 0 /*IDC_ESCAPE_FULLSCREEN_HACK*/, 1}, + {L"Disable Screen Saver", 0 /*IDC_DISABLE_SCREENSAVER*/, 1}, + {L"Logging", 0 /*IDC_DEBUG_FILE*/, 0}, + + {L"Save State in Title", 0 /*IDC_SAVE_STATE_TITLE*/, 0}, //No longer required, PCSX2 now handles it - avih 2011-05-17 + {L"GH2", 0 /*IDC_GH2_HACK*/, 0}, + {L"Turbo Key Hack", 0 /*IDC_TURBO_KEY_HACK*/, 0}, + + {L"Vista Volume", 0 /*IDC_VISTA_VOLUME*/, 1}, +}; + +void CALLBACK PADsetSettingsDir( const char *dir ) +{ + CfgHelper::SetSettingsDir(dir); +} + +int SaveSettings(wchar_t *file=0) { + CfgHelper cfg; + + for (int i=0; inumDevices; i++) { + wchar_t id[50]; + wchar_t temp[50], temp2[1000]; + wsprintfW(id, L"Device %i", i); + Device *dev = dm->devices[i]; + wchar_t *name = dev->displayName; + while (name[0] == '[') { + wchar_t *name2 = wcschr(name, ']'); + if (!name2) break; + name = name2+1; + while (iswspace(name[0])) name++; + } + + cfg.WriteStr(id, L"Display Name", name); + cfg.WriteStr(id, L"Instance ID", dev->instanceID); + if (dev->productID) { + cfg.WriteStr(id, L"Product ID", dev->productID); + } + cfg.WriteInt(id, L"API", dev->api); + cfg.WriteInt(id, L"Type", dev->type); + int ffBindingCount = 0; + int bindingCount = 0; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + for (int j=0; jpads[port][slot].numBindings; j++) { + Binding *b = dev->pads[port][slot].bindings+j; + VirtualControl *c = &dev->virtualControls[b->controlIndex]; + wsprintfW(temp, L"Binding %i", bindingCount++); + wsprintfW(temp2, L"0x%08X, %i, %i, %i, %i, %i, %i", c->uid, port, b->command, b->sensitivity, b->turbo, slot, b->deadZone); + cfg.WriteStr(id, temp, temp2); + } + + for (int j=0; jpads[port][slot].numFFBindings; j++) { + ForceFeedbackBinding *b = dev->pads[port][slot].ffBindings+j; + ForceFeedbackEffectType *eff = &dev->ffEffectTypes[b->effectIndex]; + wsprintfW(temp, L"FF Binding %i", ffBindingCount++); + wsprintfW(temp2, L"%s %i, %i, %i", eff->effectID, port, b->motor, slot); + for (int k=0; knumFFAxes; k++) { + ForceFeedbackAxis *axis = dev->ffAxes + k; + AxisEffectInfo *info = b->axes + k; + //wsprintfW(wcschr(temp2,0), L", %i, %i", axis->id, info->force); + // Not secure because I'm too lazy to compute the remaining size + wprintf(wcschr(temp2, 0), L", %i, %i", axis->id, info->force); + } + cfg.WriteStr(id, temp, temp2); + } + } + } + } + + return 0; +} + +int LoadSettings(int force, wchar_t *file) { + if (dm && !force) return 0; + + // Could just do ClearDevices() instead, but if I ever add any extra stuff, + // this will still work. + UnloadConfigs(); + dm = new InputDeviceManager(); + + CfgHelper cfg; + + for (int i=0; i= 100) break; + continue; + } + wchar_t *id2 = 0; + if (cfg.ReadStr(id, L"Product ID", temp4) && temp4[0]) + id2 = temp4; + + int api = cfg.ReadInt(id, L"API"); + int type = cfg.ReadInt(id, L"Type"); + if (!api || !type) continue; + + Device *dev = new Device((DeviceAPI)api, (DeviceType)type, temp2, temp3, id2); + dev->attached = 0; + dm->AddDevice(dev); + int j = 0; + int last = 0; + while (1) { + wsprintfW(temp, L"Binding %i", j++); + if (!cfg.ReadStr(id, temp, temp2)) { + if (j >= 100) { + if (!last) break; + last = 0; + } + continue; + } + last = 1; + unsigned int uid; + int port, command, sensitivity, turbo, slot = 0, deadZone = 0; + int w = 0; + char string[1000]; + while (temp2[w]) { + string[w] = (char)temp2[w]; + w++; + } + string[w] = 0; + int len = sscanf(string, " %i , %i , %i , %i , %i , %i , %i", &uid, &port, &command, &sensitivity, &turbo, &slot, &deadZone); + if (len >= 5 && type) { + VirtualControl *c = dev->GetVirtualControl(uid); + if (!c) c = dev->AddVirtualControl(uid, -1); + if (c) { + BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); + } + } + } + j = 0; + while (1) { + wsprintfW(temp, L"FF Binding %i", j++); + if (!cfg.ReadStr(id, temp, temp2)) { + if (j >= 10) { + if (!last) break; + last = 0; + } + continue; + } + last = 1; + int port, slot, motor; + int w = 0; + char string[1000]; + char effect[1000]; + while (temp2[w]) { + string[w] = (char)temp2[w]; + w++; + } + string[w] = 0; + // wcstok not in ntdll. More effore than its worth to shave off + // whitespace without it. + if (sscanf(string, " %s %i , %i , %i", effect, &port, &motor, &slot) == 4) { + char *s = strchr(strchr(strchr(string, ',')+1, ',')+1, ','); + if (!s) continue; + s++; + w = 0; + while (effect[w]) { + temp2[w] = effect[w]; + w++; + } + temp2[w] = 0; + ForceFeedbackEffectType *eff = dev->GetForcefeedbackEffect(temp2); + if (!eff) { + // At the moment, don't record effect types. + // Only used internally, anyways, so not an issue. + dev->AddFFEffectType(temp2, temp2, EFFECT_CONSTANT); + // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1]; + } +#if 0 + ForceFeedbackBinding *b; + CreateEffectBinding(dev, temp2, port, slot, motor, &b); + if (b) { + while (1) { + int axisID = atoi(s); + if (!(s = strchr(s, ','))) break; + s++; + int force = atoi(s); + int i; + for (i=0; inumFFAxes; i++) { + if (axisID == dev->ffAxes[i].id) break; + } + if (i == dev->numFFAxes) { + dev->AddFFAxis(L"?", axisID); + } + b->axes[i].force = force; + if (!(s = strchr(s, ','))) break; + s++; + } + } +#endif + } + } + } + config.multipleBinding = multipleBinding; + + //TODO RefreshEnabledDevicesAndDisplay(1); + RefreshEnabledDevices(1); // XXX For the moment only a subfonction + + return 0; +} + +void UnloadConfigs() { + if (dm) { + delete dm; + dm = 0; + } +} + +void RefreshEnabledDevices(int updateDeviceList) { + // Clears all device state. + static int lastXInputState = -1; + if (updateDeviceList || lastXInputState != config.gameApis.xInput) { + EnumDevices(config.gameApis.xInput); + lastXInputState = config.gameApis.xInput; + } + + for (int i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + + // XXX windows magic? + if (!dev->attached && dev->displayName[0] != '[') { + wchar_t *newName = (wchar_t*) malloc(sizeof(wchar_t) * (wcslen(dev->displayName) + 12)); + wsprintfW(newName, L"[Detached] %s", dev->displayName); + free(dev->displayName); + dev->displayName = newName; + } + + dm->EnableDevice(i); +#if 0 // windows magic? + if ((dev->type == KEYBOARD && dev->api == IGNORE_KEYBOARD) || + (dev->type == KEYBOARD && dev->api == config.keyboardApi) || + (dev->type == MOUSE && dev->api == config.mouseApi) || + (dev->type == OTHER && + ((dev->api == DI && config.gameApis.directInput) || + (dev->api == DS3 && config.gameApis.dualShock3) || + (dev->api == XINPUT && config.gameApis.xInput)))) { + dm->EnableDevice(i); + if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName && + !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) { + dm->DisableDevice(i); + } + else { + dm->EnableDevice(i); + } + } + else { + dm->DisableDevice(i); + } +#endif + } +} + +void Configure() { + // Can end up here without PADinit() being called first. + LoadSettings(); + // Can also end up here after running emulator a bit, and possibly + // disabling some devices due to focus changes, or releasing mouse. + RefreshEnabledDevices(0); +} diff --git a/plugins/LilyPad/Linux/ConfigHelper.cpp b/plugins/LilyPad/Linux/ConfigHelper.cpp new file mode 100644 index 0000000000..e7f299f65d --- /dev/null +++ b/plugins/LilyPad/Linux/ConfigHelper.cpp @@ -0,0 +1,112 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver + * + * File imported from SPU2-X (and tranformed to object) + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Linux/ConfigHelper.h" +#include + +wxString CfgHelper::m_path = L"inis/LilyPad.ini"; + +void CfgHelper::SetSettingsDir(const char* dir) +{ + m_path = wxString::FromAscii(dir) + L"/LilyPad.ini"; +} + +CfgHelper::CfgHelper() +{ + m_config = new wxFileConfig(L"", L"", m_path, L"", wxCONFIG_USE_LOCAL_FILE); +} + +CfgHelper::~CfgHelper() +{ + delete m_config; +} + +void CfgHelper::setIni(const wchar_t* Section) +{ + m_config->SetPath(wxsFormat(L"/%s", Section)); +} + + +void CfgHelper::WriteBool(const wchar_t* Section, const wchar_t* Name, bool Value) +{ + setIni(Section); + m_config->Write(Name, Value); +} + +void CfgHelper::WriteInt(const wchar_t* Section, const wchar_t* Name, int Value) +{ + setIni(Section); + m_config->Write(Name, Value); +} + +void CfgHelper::WriteFloat(const wchar_t* Section, const wchar_t* Name, float Value) +{ + setIni(Section); + m_config->Write(Name, (double)Value); +} + +void CfgHelper::WriteStr(const wchar_t* Section, const wchar_t* Name, const wxString& Data) +{ + setIni(Section); + m_config->Write(Name, Data); +} + +bool CfgHelper::ReadBool(const wchar_t *Section,const wchar_t* Name, bool Default) +{ + bool ret; + + setIni(Section); + m_config->Read(Name, &ret, Default); + + return ret; +} + +int CfgHelper::ReadInt(const wchar_t* Section, const wchar_t* Name,int Default) +{ + int ret; + + setIni(Section); + m_config->Read(Name, &ret, Default); + + return ret; +} + +float CfgHelper::ReadFloat(const wchar_t* Section, const wchar_t* Name, float Default) +{ + double ret; + + setIni(Section); + m_config->Read(Name, &ret, (double)Default); + + return (float)ret; +} + +int CfgHelper::ReadStr(const wchar_t* Section, const wchar_t* Name, wchar_t* Data, const wchar_t* Default) +{ + setIni(Section); + wcscpy(Data, m_config->Read(Name, Default).wc_str()); + return wcslen(Data); +} + +int CfgHelper::ReadStr(const wchar_t* Section, const wchar_t* Name, wxString& Data, const wchar_t* Default) +{ + setIni(Section); + Data = m_config->Read(Name, Default); + return Data.size(); +} diff --git a/plugins/LilyPad/Linux/ConfigHelper.h b/plugins/LilyPad/Linux/ConfigHelper.h new file mode 100644 index 0000000000..6a74b9ae26 --- /dev/null +++ b/plugins/LilyPad/Linux/ConfigHelper.h @@ -0,0 +1,48 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver + * + * File imported from SPU2-X (and tranformed to object) + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Global.h" +#include + +extern void CfgSetSettingsDir(const char* dir); + +class CfgHelper { + wxFileConfig* m_config; + static wxString m_path; + + void setIni(const wchar_t* Section); + + public: + CfgHelper(); + ~CfgHelper(); + + void WriteBool(const wchar_t* Section, const wchar_t* Name, bool Value); + void WriteInt(const wchar_t* Section, const wchar_t* Name, int Value); + void WriteFloat(const wchar_t* Section, const wchar_t* Name, float Value); + void WriteStr(const wchar_t* Section, const wchar_t* Name, const wxString& Data); + + bool ReadBool(const wchar_t *Section,const wchar_t* Name, bool Default = false); + int ReadStr(const wchar_t* Section, const wchar_t* Name, wxString& Data, const wchar_t* Default = 0); + int ReadStr(const wchar_t* Section, const wchar_t* Name, wchar_t* Data, const wchar_t* Default = 0); + int ReadInt(const wchar_t* Section, const wchar_t* Name,int Default = 0); + float ReadFloat(const wchar_t* Section, const wchar_t* Name, float Default = 0.0f); + + static void SetSettingsDir(const char* dir); + +}; diff --git a/plugins/LilyPad/Linux/JoyEvdev.cpp b/plugins/LilyPad/Linux/JoyEvdev.cpp new file mode 100644 index 0000000000..1d83a57252 --- /dev/null +++ b/plugins/LilyPad/Linux/JoyEvdev.cpp @@ -0,0 +1,208 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2015 PCSX2 Dev Team/ChickenLiver + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Global.h" +#include "InputManager.h" +#include "Linux/JoyEvdev.h" +#include "Linux/bitmaskros.h" + +JoyEvdev::JoyEvdev(int fd, bool ds3, const wchar_t *id) : Device(LNX_JOY, OTHER, id, id), m_fd(fd) { + // XXX LNX_JOY => DS3 or ??? + + m_abs.clear(); + m_btn.clear(); + m_rel.clear(); + int last = 0; + + uint8_t abs_bitmap[nUcharsForNBits(ABS_CNT)] = {0}; + uint8_t btn_bitmap[nUcharsForNBits(KEY_CNT)] = {0}; + uint8_t rel_bitmap[nUcharsForNBits(REL_CNT)] = {0}; + + // Add buttons + if (ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(btn_bitmap)), btn_bitmap) >= 0) { + for (int bit = BTN_MISC; bit < KEY_CNT; bit++) { + if (testBit(bit, btn_bitmap)) { + AddPhysicalControl(PSHBTN, last, 0); + m_btn.push_back(bit); + last++; + } + } + } + + // Add Absolute axis + if (ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmap)), abs_bitmap) >= 0) { + for (int bit = 0; bit < ABS_CNT; bit++) { + ControlType type = ABSAXIS; // FIXME DS3 + + if (testBit(bit, abs_bitmap)) { + input_absinfo info; + if (ioctl(m_fd, EVIOCGABS(bit), &info) < 0) { + fprintf(stderr, "Invalid IOCTL EVIOCGID\n"); + continue; + } + + AddPhysicalControl(ABSAXIS, last, 0); + last++; + if (std::abs(info.value - 127) < 2) { + fprintf(stderr, "HALF Axis info %d=>%d, current %d, flat %d, resolution %d\n", info.minimum, info.maximum, info.value, info.flat, info.resolution); + + // Half axis must be split into 2 parts... + AddPhysicalControl(ABSAXIS, last, 0); + last++; + + m_abs.push_back(abs_info(bit, info.minimum, info.value, type)); + m_abs.push_back(abs_info(bit, info.value, info.maximum, type)); + } else { + fprintf(stderr, "FULL Axis info %d=>%d, current %d, flat %d, resolution %d\n", info.minimum, info.maximum, info.value, info.flat, info.resolution); + + m_abs.push_back(abs_info(bit, info.minimum, info.maximum, type)); + } + } + } + } + + // Add relative axis + if (ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmap)), rel_bitmap) >= 0) { + for (int bit = 0; bit < REL_CNT; bit++) { + if (testBit(bit, rel_bitmap)) { + AddPhysicalControl(RELAXIS, last, last); + m_rel.push_back(bit); + last++; + + fprintf(stderr, "Add relative nb %d\n", bit); + } + } + } + + fprintf(stderr, "New device created. Found axe:%d, buttons:%d, m_rel:%d\n\n", m_abs.size(), m_btn.size(), m_rel.size()); +} + +JoyEvdev::~JoyEvdev() { + close(m_fd); +} + +int JoyEvdev::Activate(InitInfo* args) { + AllocState(); + + uint16_t size = m_abs.size()+m_rel.size()+m_btn.size(); + memset(physicalControlState, 0, sizeof(int)*size); + + active = 1; + return 1; +} + +int JoyEvdev::Update() { + struct input_event events[32]; + int len; + int status = 0; + //fprintf(stderr, "Update was called\n"); + + // Do a big read to reduce kernel validation + while ((len = read(m_fd, events, (sizeof events))) > 0) { + int evt_nb = len / sizeof(input_event); + //fprintf(stderr, "Poll %d events available\n", evt_nb); + for (int i = 0; i < evt_nb; i++) { + switch(events[i].type) { + case EV_ABS: + { + for (size_t idx = 0; idx < m_abs.size(); idx++) { + if (m_abs[idx].code == events[i].code) { + // XXX strict or not ? + if ((events[i].value >= m_abs[idx].min) && (events[i].value <= m_abs[idx].max)) { + // XXX FIX shitty api + int scale = m_abs[idx].scale(events[i].value); + fprintf(stderr, "axis value %d scaled to %d\n", events[i].value, scale); + physicalControlState[idx + m_btn.size()] = scale; + status = 1; + } + } + } + } + break; + case EV_KEY: + { + for (size_t idx = 0; idx < m_btn.size(); idx++) { + if (m_btn[idx] == events[i].code) { + fprintf(stderr, "Event KEY:%d detected with value %d\n", events[i].code, events[i].value); + physicalControlState[idx] = FULLY_DOWN * events[i].value; + status = 1; + break; + } + } + + } + break; + case EV_REL: + // XXX + break; + default: + break; + } + } + + } + + return status; +} + + +static std::wstring CorrectJoySupport(int fd) { + struct input_id id; + if (ioctl(fd, EVIOCGID, &id) < 0) { + fprintf(stderr, "Invalid IOCTL EVIOCGID\n"); + return L""; + } + + char dev_name[128]; + if (ioctl(fd, EVIOCGNAME(128), dev_name) < 0) { + fprintf(stderr, "Invalid IOCTL EVIOCGNAME\n"); + return L""; + } + + fprintf(stderr, "Found input device => bustype:%x, vendor:%x, product:%x, version:%x\n", id.bustype, id.vendor, id.product, id.version); + fprintf(stderr, "\tName:%s\n", dev_name); + + std::string s(dev_name); + return std::wstring(s.begin(), s.end()); +} + +void EnumJoystickEvdev() { + // Technically it must be done with udev but another lib for + // avoid a loop is too much for me (even if udev is mandatory + // so maybe later) + int found_devices = 0; + std::string input_root("/dev/input/event"); + for (int i = 0; i < 32; i++) { + std::string dev = input_root + std::to_string(i); + + int fd = open(dev.c_str(), O_RDWR | O_NONBLOCK); + if (fd < 0) { + continue; + } + + std::wstring id = CorrectJoySupport(fd); + if (id.size() != 0) { + bool ds3 = id.find(L"PLAYSTATION(R)3") != std::string::npos; + if (ds3) { + fprintf(stderr, "DS3 device detected !!!\n"); + } + dm->AddDevice(new JoyEvdev(fd, ds3, id.c_str())); + } else if (fd >= 0) + close(fd); + } + +} diff --git a/plugins/LilyPad/Linux/JoyEvdev.h b/plugins/LilyPad/Linux/JoyEvdev.h new file mode 100644 index 0000000000..b084aa2441 --- /dev/null +++ b/plugins/LilyPad/Linux/JoyEvdev.h @@ -0,0 +1,77 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2015 PCSX2 Dev Team/ChickenLiver + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Global.h" +#include "InputManager.h" + +#include +#include +#include +#include + +struct abs_info { + uint16_t code; + int32_t min; + int32_t max; + + int32_t factor; + int32_t translation; + + abs_info(int32_t _code, int32_t _min, int32_t _max, ControlType type) : code(_code), min(_min), max(_max) { + translation = 0; + // Note: ABSAXIS ranges from -64K to 64K + // Note: PSHBTN ranges from 0 to 64K + if ((min == 0) && (max == 255)) { + if (type == ABSAXIS) { + translation = 128; + factor = FULLY_DOWN/128; + } else { + factor = FULLY_DOWN/256; + } + } else if ((min == -1) && (max == 1)) { + factor = FULLY_DOWN; + } else if ((min == 0) && (std::abs(max - 127) < 2)) { + translation = 64; + factor = -FULLY_DOWN/64; + } else if ((max == 255) && (std::abs(min - 127) < 2)) { + translation = 64+128; + factor = FULLY_DOWN/64; + } else { + fprintf(stderr, "Scale not supported\n"); + factor = 0; + } + } + + int scale(int32_t value) { + return (value - translation) * factor; + } +}; + +class JoyEvdev : public Device { + int m_fd; + std::vector m_abs; + std::vector m_btn; + std::vector m_rel; + + public: + JoyEvdev(int fd, bool ds3, const wchar_t *id); + ~JoyEvdev(); + int Activate(InitInfo* args); + int Update(); +}; + +void EnumJoystickEvdev(); diff --git a/plugins/LilyPad/Linux/KeyboardMouse.cpp b/plugins/LilyPad/Linux/KeyboardMouse.cpp new file mode 100644 index 0000000000..efcc7de491 --- /dev/null +++ b/plugins/LilyPad/Linux/KeyboardMouse.cpp @@ -0,0 +1,78 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2015 PCSX2 Dev Team/ChickenLiver + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Linux/KeyboardMouse.h" + +// actually it is even more but it is enough to distinguish different key +#define MAX_KEYCODE (0xFF) + +LinuxKeyboard::LinuxKeyboard() : + Device(LNX_KEYBOARD, KEYBOARD, L"displayName", L"instanceID", L"deviceID") +{ + for (int i=0; i>15); + value += value&1; + if (vkey == VK_CONTROL || vkey == VK_MENU || vkey == VK_SHIFT) { + value = 0; + } + physicalControlState[vkey] = 0; + } +#endif + // Every button released + memset(physicalControlState, 0, sizeof(int)*MAX_KEYCODE); + + return 1; +} + +int LinuxKeyboard::Update() { + keyEvent event; + int status = 0; + while (R_GetQueuedKeyEvent(&event)) { + switch (event.evt) { + case KeyPress: + physicalControlState[MAX_KEYCODE & event.key] = FULLY_DOWN; + status = 1; + break; + case KeyRelease: + physicalControlState[MAX_KEYCODE & event.key] = 0; + status = 1; + break; + default: + //fprintf(stderr, "Unsupported event %x\n", event.evt); + //assert(0); + break; + } + } + + return status; // XXX ???? +} + +void EnumLnx() { + dm->AddDevice(new LinuxKeyboard()); +} diff --git a/plugins/LilyPad/Linux/KeyboardMouse.h b/plugins/LilyPad/Linux/KeyboardMouse.h new file mode 100644 index 0000000000..b336b2a842 --- /dev/null +++ b/plugins/LilyPad/Linux/KeyboardMouse.h @@ -0,0 +1,29 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2015 PCSX2 Dev Team/ChickenLiver + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Global.h" +#include "InputManager.h" +#include "KeyboardQueue.h" + +class LinuxKeyboard : public Device { + public: + LinuxKeyboard(); + int Activate(InitInfo* args); + int Update(); +}; + +void EnumLnx(); diff --git a/plugins/LilyPad/Linux/KeyboardQueue.cpp b/plugins/LilyPad/Linux/KeyboardQueue.cpp new file mode 100644 index 0000000000..aac11ad12b --- /dev/null +++ b/plugins/LilyPad/Linux/KeyboardQueue.cpp @@ -0,0 +1,61 @@ +/* LilyPad - Pad plugin for PS2 Emulator + * Copyright (C) 2002-2014 PCSX2 Dev Team/ChickenLiver + * + * PCSX2 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 Found- ation, either version 3 of the License, or (at your option) + * any later version. + * + * PCSX2 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 General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License along + * with PCSX2. If not, see . + */ + +#include "Global.h" +// This is undoubtedly completely unnecessary. +#include "KeyboardQueue.h" + +#ifdef __linux__ +// Above code is for events that go from the plugin to core +// Here we need the contrary, event that come from core to the plugin +// Yes it is a crazy ping-pong hell ! I mostly copy past with +// a R_ (which stand for reverse) + +#define R_EVENT_QUEUE_LEN 256 +static std::mutex core_event; + +static u8 R_lastQueuedEvent = 0; +static u8 R_nextQueuedEvent = 0; +static keyEvent R_queuedEvents[R_EVENT_QUEUE_LEN]; + +void R_QueueKeyEvent(const keyEvent &evt) { + std::lock_guard lock(core_event); + + R_queuedEvents[R_lastQueuedEvent] = evt; + R_lastQueuedEvent = (R_lastQueuedEvent + 1) % R_EVENT_QUEUE_LEN; + // In case someone has a severe Parkingson's disease + assert(R_nextQueuedEvent != R_lastQueuedEvent); +} + +int R_GetQueuedKeyEvent(keyEvent *event) { + if (R_lastQueuedEvent == R_nextQueuedEvent) return 0; + + std::lock_guard lock(core_event); + *event = R_queuedEvents[R_nextQueuedEvent]; + R_nextQueuedEvent = (R_nextQueuedEvent + 1) % R_EVENT_QUEUE_LEN; + return 1; +} + +void R_ClearKeyQueue() { + R_lastQueuedEvent = R_nextQueuedEvent; +} + +EXPORT_C_(void) PADWriteEvent(keyEvent &evt) +{ + R_QueueKeyEvent(evt); +} +#endif diff --git a/plugins/LilyPad/Linux/bitmaskros.h b/plugins/LilyPad/Linux/bitmaskros.h new file mode 100644 index 0000000000..2543022255 --- /dev/null +++ b/plugins/LilyPad/Linux/bitmaskros.h @@ -0,0 +1,40 @@ +/* + * bitmaskros.h + * + * Helper macros for large bit masks management + * + * Copyright (C) 2008 Jean-Philippe Meuret + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Number of bits for 1 unsigned char */ +#define nBitsPerUchar (sizeof(unsigned char) * 8) + +/* Number of unsigned chars to contain a given number of bits */ +#define nUcharsForNBits(nBits) ((((nBits)-1)/nBitsPerUchar)+1) + +/* Index=Offset of given bit in 1 unsigned char */ +#define bitOffsetInUchar(bit) ((bit)%nBitsPerUchar) + +/* Index=Offset of the unsigned char associated to the bit + at the given index=offset */ +#define ucharIndexForBit(bit) ((bit)/nBitsPerUchar) + +/* Value of an unsigned char with bit set at given index=offset */ +#define ucharValueForBit(bit) (((unsigned char)(1))<> bitOffsetInUchar(bit)) & 1) diff --git a/plugins/onepad/joystick.cpp b/plugins/onepad/joystick.cpp index 7aa7b3c5eb..e17fff0350 100644 --- a/plugins/onepad/joystick.cpp +++ b/plugins/onepad/joystick.cpp @@ -219,9 +219,9 @@ bool JoystickInfo::Init(int id) // work sometime on half axis neg others time in fulll axis. So better keep them as button for the moment u32 found_hack = devname.find(string("PLAYSTATION(R)3")); if (found_hack != string::npos) { - numbuttons = 4; - // Enable this hack is bluetooth too. It avoid to restart the onepad gui - numbuttons += 4; + numbuttons = 4; // (select, start, l3, r3) + // Enable this hack in bluetooth too. It avoid to restart the onepad gui + numbuttons += 4; // the 4 hat buttons } #if SDL_MAJOR_VERSION >= 2