From 1655055f61592d272a1ba147c9f26e8b964d3d30 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 23 Jan 2015 23:14:09 +0100 Subject: [PATCH 01/18] onepad: update old comment --- plugins/onepad/joystick.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 2471306fc4282e5b0b3467b54496a87afcf5e2e9 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 24 Jan 2015 23:15:34 +0100 Subject: [PATCH 02/18] cmake: actually onepad requires X11 --- cmake/SelectPcsx2Plugins.cmake | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index 726dc2d97c..c401129dcd 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) @@ -209,8 +209,9 @@ 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) From 76ff56a4e35a018a4cb036a7294abb58e40be559 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 24 Jan 2015 23:06:43 +0100 Subject: [PATCH 03/18] lilypad: add a linux fork Yes it is bad! --- plugins/LilyPad/Linux/Config.cpp | 278 +++++ plugins/LilyPad/Linux/KeyboardQueue.cpp | 68 ++ plugins/LilyPad/Linux/LilyPad.cpp | 1275 +++++++++++++++++++++++ 3 files changed, 1621 insertions(+) create mode 100644 plugins/LilyPad/Linux/Config.cpp create mode 100644 plugins/LilyPad/Linux/KeyboardQueue.cpp create mode 100644 plugins/LilyPad/Linux/LilyPad.cpp diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp new file mode 100644 index 0000000000..ec506baf44 --- /dev/null +++ b/plugins/LilyPad/Linux/Config.cpp @@ -0,0 +1,278 @@ +/* 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" + +GeneralConfig config; +u8 ps2e = 0; + +int LoadSettings(int force, wchar_t *file) { + if (dm && !force) return 0; + +#if 0 + if( createIniDir ) + { + CreateDirectory(L"inis", 0); + createIniDir = false; + } +#endif + + // Could just do ClearDevices() instead, but if I ever add any extra stuff, + // this will still work. + UnloadConfigs(); + dm = new InputDeviceManager(); + +#ifdef _MSC_VER + if (!file) { + file = iniFile; + GetPrivateProfileStringW(L"General Settings", L"Last Config Path", L"inis", config.lastSaveConfigPath, sizeof(config.lastSaveConfigPath), file); + GetPrivateProfileStringW(L"General Settings", L"Last Config Name", L"LilyPad.lily", config.lastSaveConfigFileName, sizeof(config.lastSaveConfigFileName), file); + } + else { + wchar_t *c = wcsrchr(file, '\\'); + if (c) { + *c = 0; + wcscpy(config.lastSaveConfigPath, file); + wcscpy(config.lastSaveConfigFileName, c+1); + *c = '\\'; + WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile); + WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile); + } + } + + for (int i=0; i= 100) break; + continue; + } + wchar_t *id2 = 0; + if (GetPrivateProfileStringW(id, L"Product ID", 0, temp4, 1000, file) && temp4[0]) + id2 = temp4; + + int api = GetPrivateProfileIntW(id, L"API", 0, file); + int type = GetPrivateProfileIntW(id, L"Type", 0, file); + 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 (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { + 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 (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { + 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]; + } + 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++; + } + } + } + } + } + config.multipleBinding = multipleBinding; + + RefreshEnabledDevicesAndDisplay(1); +#endif + + 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]; + + 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; + } + + 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)))) { + 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); + } + } +} + +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/KeyboardQueue.cpp b/plugins/LilyPad/Linux/KeyboardQueue.cpp new file mode 100644 index 0000000000..3718834f3c --- /dev/null +++ b/plugins/LilyPad/Linux/KeyboardQueue.cpp @@ -0,0 +1,68 @@ +/* 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" + +// What MS calls a single process Mutex. Faster, supposedly. +// More importantly, can be abbreviated, amusingly, as cSection. +static std::mutex cSection; + +#define EVENT_QUEUE_LEN 16 +// Actually points one beyond the last queued event. +static u8 lastQueuedEvent = 0; +static u8 nextQueuedEvent = 0; +static keyEvent queuedEvents[EVENT_QUEUE_LEN]; + +void QueueKeyEvent(int key, int event) { + std::lock_guard lock(cSection); + + // 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. + if (nextQueuedEvent == lastQueuedEvent || + queuedEvents[nextQueuedEvent].key != VK_ESCAPE || + queuedEvents[nextQueuedEvent].evt != KEYPRESS) { + // Clear queue on escape down, bringing escape to front. May do something + // with shift/ctrl/alt and F-keys, later. + if (event == KEYPRESS && key == VK_ESCAPE) { + nextQueuedEvent = lastQueuedEvent; + } + + queuedEvents[lastQueuedEvent].key = key; + queuedEvents[lastQueuedEvent].evt = event; + + lastQueuedEvent = (lastQueuedEvent + 1) % EVENT_QUEUE_LEN; + // If queue wrapped around, remove last element. + if (nextQueuedEvent == lastQueuedEvent) { + nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; + } + } +} + +int GetQueuedKeyEvent(keyEvent *event) { + if (lastQueuedEvent == nextQueuedEvent) return 0; + + std::lock_guard lock(cSection); + *event = queuedEvents[nextQueuedEvent]; + nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; + return 1; +} + +void ClearKeyQueue() { + lastQueuedEvent = nextQueuedEvent; +} diff --git a/plugins/LilyPad/Linux/LilyPad.cpp b/plugins/LilyPad/Linux/LilyPad.cpp new file mode 100644 index 0000000000..51d6a7b7a8 --- /dev/null +++ b/plugins/LilyPad/Linux/LilyPad.cpp @@ -0,0 +1,1275 @@ +/* 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" + +// For escape timer, so as not to break GSDX+DX9. +#include +#include "resource.h" +#include "InputManager.h" +#include "Config.h" + +#define PADdefs + +#include "DeviceEnumerator.h" +#include "KeyboardQueue.h" +#include "svnrev.h" +#include "DualShock3.h" +#include "HidDevice.h" + +#define WMA_FORCE_UPDATE (WM_APP + 0x537) +#define FORCE_UPDATE_WPARAM ((WPARAM)0x74328943) +#define FORCE_UPDATE_LPARAM ((LPARAM)0x89437437) + +// LilyPad version. +#define VERSION ((0<<8) | 11 | (0<<24)) + +// Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking +// havoc on each other... +#ifdef _MSC_VER +CRITICAL_SECTION updateLock; +#else +static std::mutex updateLock; +#endif + +// Used to toggle mouse listening. +u8 miceEnabled; + +// 2 when both pads are initialized, 1 for one pad, etc. +int openCount = 0; + +int activeWindow = 0; +int windowThreadId = 0; +int updateQueued = 0; + +int bufSize = 0; +unsigned char outBuf[50]; +unsigned char inBuf[50]; + +// windowThreadId = GetWindowThreadProcessId(hWnd, 0); + +#define MODE_DIGITAL 0x41 +#define MODE_ANALOG 0x73 +#define MODE_DS2_NATIVE 0x79 + + +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) { + DWORD junk; + WriteFile(hFile, text, strlen(text), &junk, 0); + 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) { + int i; + char temp[1500]; + char *end = temp; + sprintf(end, "%02X (%02X) ", inBuf[0], inBuf[1]); + end += 8; + for (i=2; i 0). + u8 enabled; +} pads[2][4]; + +// Active slots for each port. +int slots[2]; +// Which ports we're running on. +int portInitialized[2]; + +// Force value to be from 0 to 255. +u8 Cap (int i) { + if (i<0) return 0; + if (i>255) return 255; + return (u8) i; +} + +inline void ReleaseModifierKeys() { + QueueKeyEvent(VK_SHIFT, KEYRELEASE); + QueueKeyEvent(VK_MENU, KEYRELEASE); + QueueKeyEvent(VK_CONTROL, KEYRELEASE); +} + +// RefreshEnabledDevices() enables everything that can potentially +// be bound to, as well as the "Ignore keyboard" device. +// +// This enables everything that input should be read from while the +// emulator is running. Takes into account mouse and focus state +// and which devices have bindings for enabled pads. Releases +// keyboards if window is not focused. Releases game devices if +// background monitoring is not checked. +// And releases games if not focused and config.background is not set. +void UpdateEnabledDevices(int updateList = 0) { + // Enable all devices I might want. Can ignore the rest. + RefreshEnabledDevices(updateList); + // Figure out which pads I'm getting input for. + for (int port = 0; port<2; port++) { + for (int slot = 0; slot<4; slot++) { + if (slot && !config.multitap[port]) { + pads[port][slot].enabled = 0; + } + else { + pads[port][slot].enabled = pads[port][slot].initialized && config.padConfigs[port][slot].type != DisabledPad; + } + } + } + for (int i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + + if (!dev->enabled) continue; + if (!dev->attached) { + dm->DisableDevice(i); + continue; + } + + // Disable ignore keyboard if don't have focus or there are no keys to ignore. + if (dev->api == IGNORE_KEYBOARD) { + if ((!config.vistaVolume && (config.keyboardApi == NO_API || !dev->pads[0][0].numBindings)) || !activeWindow) { + dm->DisableDevice(i); + } + continue; + } + // Keep for PCSX2 keyboard shotcuts, unless unfocused. + if (dev->type == KEYBOARD) { + if (!activeWindow) dm->DisableDevice(i); + } + // Keep for cursor hiding consistency, unless unfocused. + // miceEnabled tracks state of mouse enable/disable button, not if mouse API is set to disabled. + else if (dev->type == MOUSE) { + if (!miceEnabled || !activeWindow) dm->DisableDevice(i); + } + else if (!activeWindow && !config.background) dm->DisableDevice(i); + else { + int numActiveBindings = 0; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + if (pads[port][slot].enabled) { + numActiveBindings += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings; + } + } + } + if (!numActiveBindings) + dm->DisableDevice(i); + } + } +} + +void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { + if (!delta) return; + if (cmd<0x14) { + sum->buttons[cmd-0x10] += delta; + } + // D-pad. Command numbering is based on ordering of digital values. + else if (cmd < 0x18) { + if (cmd == 0x14) { + sum->sticks[0].vert -= delta; + } + else if (cmd == 0x15) { + sum->sticks[0].horiz += delta; + } + else if (cmd == 0x16) { + sum->sticks[0].vert += delta; + } + else if (cmd == 0x17) { + sum->sticks[0].horiz -= delta; + } + } + else if (cmd < 0x20) { + sum->buttons[cmd-0x10-4] += delta; + } + // Left stick. + else if (cmd < 0x24) { + if (cmd == 32) { + sum->sticks[2].vert -= delta; + } + else if (cmd == 33) { + sum->sticks[2].horiz += delta; + } + else if (cmd == 34) { + sum->sticks[2].vert += delta; + } + else if (cmd == 35) { + sum->sticks[2].horiz -= delta; + } + } + // Right stick. + else if (cmd < 0x28) { + if (cmd == 36) { + sum->sticks[1].vert -= delta; + } + else if (cmd == 37) { + sum->sticks[1].horiz += delta; + } + else if (cmd == 38) { + sum->sticks[1].vert += delta; + } + else if (cmd == 39) { + sum->sticks[1].horiz -= delta; + } + } +} + +void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { + if (value < b->deadZone || !value) return; + +#ifdef _MSC_VER + if ( config.turboKeyHack == 1 ){ // send a tabulator keypress to emulator + //printf("%x\n", b->command); + if ( b->command == 0x11 ){ // L3 button + static unsigned int LastCheck = 0; + unsigned int t = timeGetTime(); + if (t - LastCheck < 300 ) return; + QueueKeyEvent(VK_TAB, KEYPRESS); + LastCheck = t; + } + } +#endif + + int sensitivity = b->sensitivity; + if (sensitivity < 0) { + sensitivity = -sensitivity; + value = (1<<16)-value; + } + if (value < 0) return; + + /* Note: Value ranges of FULLY_DOWN, and sensitivity of + * BASE_SENSITIVITY corresponds to an axis/button being exactly fully down. + * Math in next line takes care of those two conditions, rounding as necessary. + * Done using __int64s because overflows will occur when + * sensitivity > BASE_SENSITIVITY and/or value > FULLY_DOWN. Latter only happens + * for relative axis. + */ + int force = (int)((((sensitivity*(255*(__int64)value)) + BASE_SENSITIVITY/2)/BASE_SENSITIVITY + FULLY_DOWN/2)/FULLY_DOWN); + AddForce(sum, b->command, force); +} + +// Restricts d-pad/analog stick values to be from -255 to 255 and button values to be from 0 to 255. +// With D-pad in DS2 native mode, the negative and positive ranges are both independently from 0 to 255, +// which is why I use 9 bits of all sticks. For left and right sticks, I have to remove a bit before sending. +void CapSum(ButtonSum *sum) { + int i; + for (i=0; i<3; i++) { + int div = std::max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); + if (div > 255) { + sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; + sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; + } + } + for (i=0; i<12; i++) { + sum->buttons[i] = Cap(sum->buttons[i]); + } +} + +// Counter similar to stateUpdated for each pad, except used for PADkeyEvent instead. +// Only matters when GS thread updates is disabled (Just like summed pad values +// for pads beyond the first slot). + +// Values, in order, correspond to PADkeyEvent, PADupdate(0), PADupdate(1), and +// WndProc(WMA_FORCE_UPDATE). Last is always 0. +char padReadKeyUpdated[4] = {0, 0, 0, 0}; + +#define LOCK_DIRECTION 2 +#define LOCK_BUTTONS 4 +#define LOCK_BOTH 1 + +void Update(unsigned int port, unsigned int slot) { + char *stateUpdated; + if (port < 2) { + stateUpdated = &pads[port][slot].stateUpdated; + } + else if (port < 6) { + stateUpdated = padReadKeyUpdated+port-2; + } + else return; + + if (*stateUpdated > 0) { + stateUpdated[0] --; + return; + } + + // Lock prior to timecheck code to avoid pesky race conditions. + std::lock_guard lock(updateLock); + +#ifdef _MSC_VER + static unsigned int LastCheck = 0; + unsigned int t = timeGetTime(); + if (t - LastCheck < 15 || !openCount) return; +#endif + +#ifdef _MSC_VER + if (windowThreadId != GetCurrentThreadId()) { + if (stateUpdated[0] < 0) { + if (!updateQueued) { + updateQueued = 1; + PostMessage(hWnd, WMA_FORCE_UPDATE, FORCE_UPDATE_WPARAM, FORCE_UPDATE_LPARAM); + } + } else + { + stateUpdated[0] --; + } + return; + } + + LastCheck = t; +#endif + + int i; + ButtonSum s[2][4]; + u8 lockStateChanged[2][4]; + memset(lockStateChanged, 0, sizeof(lockStateChanged)); + + for (i=0; i<8; i++) { + s[i&1][i>>1] = pads[i&1][i>>1].lockedSum; + } +#ifdef _MSC_VER + InitInfo info = { + 0, 0, hWndTop, &hWndGSProc + }; + + dm->Update(&info); +#endif + static int turbo = 0; + turbo++; + for (i=0; inumDevices; i++) { + Device *dev = dm->devices[i]; + // Skip both disabled devices and inactive enabled devices. + // Shouldn't be any of the latter, in general, but just in case... + if (!dev->active) continue; + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; + for (int j=0; jpads[port][slot].numBindings; j++) { + Binding *b = dev->pads[port][slot].bindings+j; + int cmd = b->command; + int state = dev->virtualControlState[b->controlIndex]; + if (!(turbo & b->turbo)) { + if (cmd > 0x0F && cmd != 0x28) { + ProcessButtonBinding(b, &s[port][slot], state); + } + else if ((state>>15) && !(dev->oldVirtualControlState[b->controlIndex]>>15)) { + if (cmd == 0x0F) { + miceEnabled = !miceEnabled; + UpdateEnabledDevices(); + } + else if (cmd == 0x0C) { + lockStateChanged[port][slot] |= LOCK_BUTTONS; + } + else if (cmd == 0x0E) { + lockStateChanged[port][slot] |= LOCK_DIRECTION; + } + else if (cmd == 0x0D) { + lockStateChanged[port][slot] |= LOCK_BOTH; + } + else if (cmd == 0x28) { + if (!pads[port][slot].modeLock) { + if (pads[port][slot].mode != MODE_DIGITAL) + pads[port][slot].mode = MODE_DIGITAL; + else + pads[port][slot].mode = MODE_ANALOG; + } + } + } + } + } + } + } + } + dm->PostRead(); + + { + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + for (int motor=0; motor<2; motor++) { + // TODO: Probably be better to send all of these at once. + if (pads[port][slot].nextVibrate[motor] | pads[port][slot].currentVibrate[motor]) { + pads[port][slot].currentVibrate[motor] = pads[port][slot].nextVibrate[motor]; + dm->SetEffect(port,slot, motor, pads[port][slot].nextVibrate[motor]); + } + } + } + } + + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + pads[port][slot].stateUpdated = 1; + if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; + if (config.padConfigs[port][slot].type == GuitarPad) { + if (!config.GH2) { + s[port][slot].sticks[1].vert = -s[port][slot].sticks[1].vert; + } + // GH2 hack. + else if (config.GH2) { + const unsigned int oldIdList[5] = {ID_R2, ID_CIRCLE, ID_TRIANGLE, ID_CROSS, ID_SQUARE}; + const unsigned int idList[5] = {ID_L2, ID_L1, ID_R1, ID_R2, ID_CROSS}; + int values[5]; + int i; + for (i=0; i<5; i++) { + int id = oldIdList[i] - 0x1104; + values[i] = s[port][slot].buttons[id]; + s[port][slot].buttons[id] = 0; + } + s[port][slot].buttons[ID_TRIANGLE-0x1104] = values[1]; + for (i=0; i<5; i++) { + int id = idList[i] - 0x1104; + s[port][slot].buttons[id] = values[i]; + } + if (abs(s[port][slot].sticks[0].vert) <= 48) { + for (int i=0; i<5; i++) { + unsigned int id = idList[i] - 0x1104; + if (pads[port][slot].sum.buttons[id] < s[port][slot].buttons[id]) { + s[port][slot].buttons[id] = pads[port][slot].sum.buttons[id]; + } + } + } + else if (abs(pads[port][slot].sum.sticks[0].vert) <= 48) { + for (int i=0; i<5; i++) { + unsigned int id = idList[i] - 0x1104; + if (pads[port][slot].sum.buttons[id]) { + s[port][slot].buttons[id] = 0; + } + } + } + } + } + + if (pads[port][slot].mode == 0x41) { + for (int i=1; i<=2; i++) { + if (abs(s[port][slot].sticks[i].horiz) >= 100) + s[port][slot].sticks[0].horiz += s[port][slot].sticks[i].horiz; + if (abs(s[port][slot].sticks[i].vert) >= 100) + s[port][slot].sticks[0].vert += s[port][slot].sticks[i].vert; + } + } + + CapSum(&s[port][slot]); + if (lockStateChanged[port][slot]) { + if (lockStateChanged[port][slot] & LOCK_BOTH) { + if (pads[port][slot].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) { + // Enable the one that's not enabled. + lockStateChanged[port][slot] ^= pads[port][slot].lockedState^(LOCK_DIRECTION | LOCK_BUTTONS); + } + else { + // Disable both + lockStateChanged[port][slot] ^= LOCK_DIRECTION | LOCK_BUTTONS; + } + } + if (lockStateChanged[port][slot] & LOCK_DIRECTION) { + if (pads[port][slot].lockedState & LOCK_DIRECTION) { + memset(pads[port][slot].lockedSum.sticks, 0, sizeof(pads[port][slot].lockedSum.sticks)); + } + else { + memcpy(pads[port][slot].lockedSum.sticks, s[port][slot].sticks, sizeof(pads[port][slot].lockedSum.sticks)); + } + pads[port][slot].lockedState ^= LOCK_DIRECTION; + } + if (lockStateChanged[port][slot] & LOCK_BUTTONS) { + if (pads[port][slot].lockedState & LOCK_BUTTONS) { + memset(pads[port][slot].lockedSum.buttons, 0, sizeof(pads[port][slot].lockedSum.buttons)); + } + else { + memcpy(pads[port][slot].lockedSum.buttons, s[port][slot].buttons, sizeof(pads[port][slot].lockedSum.buttons)); + } + pads[port][slot].lockedState ^= LOCK_BUTTONS; + } + for (i=0; i>1].sum = s[i&1][i>>1]; + } + + padReadKeyUpdated[0] = padReadKeyUpdated[1] = padReadKeyUpdated[2] = 1; + + if( stateUpdated[0] > 0 ) + --stateUpdated[0]; +} + +void CALLBACK PADupdate(int port) { + Update(port+3, 0); +} + +inline void SetVibrate(int port, int slot, int motor, u8 val) { + pads[port][slot].nextVibrate[motor] = val; +} + +u32 CALLBACK PS2EgetLibType(void) { + ps2e = 1; + return PS2E_LT_PAD; +} + +u32 CALLBACK PS2EgetLibVersion2(u32 type) { + ps2e = 1; + if (type == PS2E_LT_PAD) + return (PS2E_PAD_VERSION<<16) | VERSION; + return 0; +} + +char* CALLBACK PSEgetLibName() { +#if defined(PCSX2_DEBUG) + static char version[50]; + sprintf(version, "LilyPad Debug (%lld)", SVN_REV); + return version; +#else + static char version[50]; + sprintf(version, "LilyPad (%lld)", SVN_REV); + return version; +#endif +} + +char* CALLBACK PS2EgetLibName(void) { + ps2e = 1; + return PSEgetLibName(); +} + +//void CALLBACK PADgsDriverInfo(GSdriverInfo *info) { +// info=info; +//} + +void CALLBACK PADshutdown() { + DEBUG_TEXT_OUT("LilyPad shutdown.\n\n"); + for (int i=0; i<8; i++) + pads[i&1][i>>1].initialized = 0; + portInitialized[0] = portInitialized[1] = 0; + UnloadConfigs(); +} + +inline void StopVibrate() { + for (int i=0; i<8; i++) { + SetVibrate(i&1, i>>1, 0, 0); + SetVibrate(i&1, i>>1, 1, 0); + } +} + +inline void ResetVibrate(int port, int slot) { + SetVibrate(port, slot, 0, 0); + SetVibrate(port, slot, 1, 0); + ((int*)(pads[port][slot].vibrate))[0] = 0xFFFFFF5A; + ((int*)(pads[port][slot].vibrate))[1] = 0xFFFFFFFF; +} + +void ResetPad(int port, int slot) { + // Lines before memset currently don't do anything useful, + // but allow this function to be called at any time. + + // Need to backup, so can be called at any point. + u8 enabled = pads[port][slot].enabled; + + // Currently should never do anything. + SetVibrate(port, slot, 0, 0); + SetVibrate(port, slot, 1, 0); + + memset(&pads[port][slot], 0, sizeof(pads[0][0])); + pads[port][slot].mode = MODE_DIGITAL; + pads[port][slot].umask[0] = pads[port][slot].umask[1] = 0xFF; + // Sets up vibrate variable. + ResetVibrate(port, slot); + if (config.padConfigs[port][slot].autoAnalog && !ps2e) { + pads[port][slot].mode = MODE_ANALOG; + } + pads[port][slot].initialized = 1; + + pads[port][slot].enabled = enabled; +} + + +struct QueryInfo { + u8 port; + u8 slot; + u8 lastByte; + u8 currentCommand; + u8 numBytes; + u8 queryDone; + u8 response[42]; +} query = {0,0,0,0, 0,0xFF, 0xF3}; + +s32 CALLBACK PADinit(u32 flags) { + // Note: Won't load settings if already loaded. + if (LoadSettings() < 0) { + return -1; + } + int port = (flags & 3); + if (port == 3) { + if (PADinit(1) == -1) return -1; + return PADinit(2); + } + + #if defined(PCSX2_DEBUG) && defined(_MSC_VER) + int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); + tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; + _CrtSetDbgFlag( tmpFlag ); + #endif + + port --; + + for (int i=0; i<4; i++) { + ResetPad(port, i); + } + slots[port] = 0; + portInitialized[port] = 1; + + query.lastByte = 1; + query.numBytes = 0; + ClearKeyQueue(); + // Just in case, when resuming emulation. + ReleaseModifierKeys(); + + DEBUG_TEXT_OUT("LilyPad initialized\n\n"); + return 0; +} + + + +// Note to self: Has to be a define for the sizeof() to work right. +// Note to self 2: All are the same size, anyways, except for longer full DS2 response +// and shorter digital mode response. +#define SET_RESULT(a) { \ + memcpy(query.response+2, a, sizeof(a)); \ + query.numBytes = 2+sizeof(a); \ +} + +#define SET_FINAL_RESULT(a) { \ + memcpy(query.response+2, a, sizeof(a));\ + query.numBytes = 2+sizeof(a); \ + query.queryDone = 1; \ +} + +static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +//static const u8 ConfigExit[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; + +static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; +static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A}; +//static const u8 DSNonNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + +// DS2 +static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; +// DS1 +static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; + +static const u8 queryAct[2][7] = {{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, + {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; + +static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; + +static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + + +static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; + +void CALLBACK PADconfigure() { + if (openCount) { + return; + } + Configure(); +} + +s32 CALLBACK PADopen(void *pDsp) { + if (openCount++) return 0; + DEBUG_TEXT_OUT("LilyPad opened\n\n"); + + miceEnabled = !config.mouseUnfocus; + + 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)); + memset(&pads[port][slot].lockedSum, 0, sizeof(pads[port][slot].lockedSum)); + pads[port][slot].lockedState = 0; + } + } + + // 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, + // if any window did (topmost in this thread?). Which isn't what I want, and doesn't seem + // to be what it actually does. + // activeWindow = GetActiveWindow() == hWnd; + + // activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); + activeWindow = 1; + UpdateEnabledDevices(); + return 0; +} + +void CALLBACK PADclose() { + if (openCount && !--openCount) { + DEBUG_TEXT_OUT("LilyPad closed\n\n"); + updateQueued = 0; + ClearKeyQueue(); + } +} + +u8 CALLBACK PADstartPoll(int port) { + DEBUG_NEW_SET(); + port--; + if ((unsigned int)port <= 1 && pads[port][slots[port]].enabled) { + query.queryDone = 0; + query.port = port; + query.slot = slots[port]; + query.numBytes = 2; + query.lastByte = 0; + DEBUG_IN(port); + DEBUG_OUT(0xFF); + DEBUG_IN(slots[port]); + DEBUG_OUT(pads[port][slots[port]].enabled); + return 0xFF; + } + else { + query.queryDone = 1; + query.numBytes = 0; + query.lastByte = 1; + DEBUG_IN(0); + DEBUG_OUT(0); + DEBUG_IN(port); + DEBUG_OUT(0); + return 0; + } +} + +inline int IsDualshock2(u8 port, u8 slot) { + return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || + (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); +} + +u8 CALLBACK PADpoll(u8 value) { + DEBUG_IN(value); + if (query.lastByte+1 >= query.numBytes) { + DEBUG_OUT(0); + return 0; + } + if (query.lastByte && query.queryDone) { + DEBUG_OUT(query.response[1+query.lastByte]); + return query.response[++query.lastByte]; + } + + int i; + Pad *pad = &pads[query.port][query.slot]; + if (query.lastByte == 0) { + query.lastByte++; + query.currentCommand = value; + switch(value) { + // CONFIG_MODE + case 0x43: + if (pad->config) { + // In config mode. Might not actually be leaving it. + SET_RESULT(ConfigExit); + DEBUG_OUT(0xF3); + return 0xF3; + } + // READ_DATA_AND_VIBRATE + case 0x42: + query.response[2] = 0x5A; + { + Update(query.port, query.slot); + ButtonSum *sum = &pad->sum; + + u8 b1 = 0xFF, b2 = 0xFF; + for (i = 0; i<4; i++) { + b1 -= (sum->buttons[i] > 0) << i; + } + for (i = 0; i<8; i++) { + b2 -= (sum->buttons[i+4] > 0) << i; + } + if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) { + sum->sticks[0].horiz = -255; + // Not sure about this. Forces wammy to be from 0 to 0x7F. + // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; + } + b1 -= ((sum->sticks[0].vert < 0) << 4); + b1 -= ((sum->sticks[0].horiz > 0) << 5); + b1 -= ((sum->sticks[0].vert > 0) << 6); + b1 -= ((sum->sticks[0].horiz < 0) << 7); + query.response[3] = b1; + query.response[4] = b2; + + query.numBytes = 5; + if (pad->mode != MODE_DIGITAL) { + query.response[5] = Cap((sum->sticks[1].horiz+255)/2); + query.response[6] = Cap((sum->sticks[1].vert+255)/2); + query.response[7] = Cap((sum->sticks[2].horiz+255)/2); + query.response[8] = Cap((sum->sticks[2].vert+255)/2); + + query.numBytes = 9; + if (pad->mode != MODE_ANALOG) { + // Good idea? No clue. + //query.response[3] &= pad->mask[0]; + //query.response[4] &= pad->mask[1]; + + // Each value is from -255 to 255, so have to use cap to convert + // negative values to 0. + query.response[9] = Cap(sum->sticks[0].horiz); + query.response[10] = Cap(-sum->sticks[0].horiz); + query.response[11] = Cap(-sum->sticks[0].vert); + query.response[12] = Cap(sum->sticks[0].vert); + + // No need to cap these, already done int CapSum(). + query.response[13] = (unsigned char) sum->buttons[8]; + query.response[14] = (unsigned char) sum->buttons[9]; + query.response[15] = (unsigned char) sum->buttons[10]; + query.response[16] = (unsigned char) sum->buttons[11]; + query.response[17] = (unsigned char) sum->buttons[6]; + query.response[18] = (unsigned char) sum->buttons[7]; + query.response[19] = (unsigned char) sum->buttons[4]; + query.response[20] = (unsigned char) sum->buttons[5]; + query.numBytes = 21; + } + } + } + + query.lastByte=1; + DEBUG_OUT(pad->mode); + return pad->mode; + // SET_VREF_PARAM + case 0x40: + SET_FINAL_RESULT(noclue); + break; + // QUERY_DS2_ANALOG_MODE + case 0x41: + // Right? Wrong? No clue. + if (pad->mode == MODE_DIGITAL) { + queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; + queryMaskMode[6] = 0x00; + } + else { + queryMaskMode[1] = pad->umask[0]; + queryMaskMode[2] = pad->umask[1]; + queryMaskMode[3] = 0x03; + // Not entirely sure about this. + //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; + queryMaskMode[6] = 0x5A; + } + SET_FINAL_RESULT(queryMaskMode); + break; + // SET_MODE_AND_LOCK + case 0x44: + SET_RESULT(setMode); + ResetVibrate(query.port, query.slot); + break; + // QUERY_MODEL_AND_MODE + case 0x45: + if (IsDualshock2(query.port, query.slot)) { + SET_FINAL_RESULT(queryModelDS2) + } + else { + SET_FINAL_RESULT(queryModelDS1); + } + // Not digital mode. + query.response[5] = (pad->mode & 0xF) != 1; + break; + // QUERY_ACT + case 0x46: + SET_RESULT(queryAct[0]); + break; + // QUERY_COMB + case 0x47: + SET_FINAL_RESULT(queryComb); + break; + // QUERY_MODE + case 0x4C: + SET_RESULT(queryMode); + break; + // VIBRATION_TOGGLE + case 0x4D: + memcpy(query.response+2, pad->vibrate, 7); + query.numBytes = 9; + ResetVibrate(query.port, query.slot); + break; + // SET_DS2_NATIVE_MODE + case 0x4F: + if (IsDualshock2(query.port, query.slot)) { + SET_RESULT(setNativeMode); + } + else { + SET_FINAL_RESULT(setNativeMode); + } + break; + default: + query.numBytes = 0; + query.queryDone = 1; + break; + } + DEBUG_OUT(0xF3); + return 0xF3; + } + else { + query.lastByte++; + switch (query.currentCommand) { + // READ_DATA_AND_VIBRATE + case 0x42: + if (query.lastByte == pad->vibrateI[0]) { + SetVibrate(query.port, query.slot, 1, 255*(0!=value)); + } + else if (query.lastByte == pad->vibrateI[1]) { + SetVibrate(query.port, query.slot, 0, value); + } + break; + // CONFIG_MODE + case 0x43: + if (query.lastByte == 3) { + query.queryDone = 1; + pad->config = value; + } + break; + // SET_MODE_AND_LOCK + case 0x44: + if (query.lastByte == 3 && value < 2) { + static const u8 modes[2] = {MODE_DIGITAL, MODE_ANALOG}; + pad->mode = modes[value]; + } + else if (query.lastByte == 4) { + if (value == 3) { + pad->modeLock = 3; + } + else { + pad->modeLock = 0; + if (pad->mode == MODE_DIGITAL && config.padConfigs[query.port][query.slot].autoAnalog && !ps2e) { + pad->mode = MODE_ANALOG; + } + } + query.queryDone = 1; + } + break; + // QUERY_ACT + case 0x46: + if (query.lastByte == 3) { + if (value<2) SET_RESULT(queryAct[value]) + // bunch of 0's + // else SET_RESULT(setMode); + query.queryDone = 1; + } + break; + // QUERY_MODE + case 0x4C: + if (query.lastByte == 3 && value<2) { + query.response[6] = 4+value*3; + query.queryDone = 1; + } + // bunch of 0's + //else data = setMode; + break; + // VIBRATION_TOGGLE + case 0x4D: + if (query.lastByte>=3) { + if (value == 0) { + pad->vibrateI[0] = (u8)query.lastByte; + } + else if (value == 1) { + pad->vibrateI[1] = (u8)query.lastByte; + } + pad->vibrate[query.lastByte-2] = value; + } + break; + // SET_DS2_NATIVE_MODE + case 0x4F: + if (query.lastByte == 3 || query.lastByte == 4) { + pad->umask[query.lastByte-3] = value; + } + else if (query.lastByte == 5) { + if (!(value & 1)) { + pad->mode = MODE_DIGITAL; + } + else if (!(value & 2)) { + pad->mode = MODE_ANALOG; + } + else { + pad->mode = MODE_DS2_NATIVE; + } + } + break; + default: + DEBUG_OUT(0); + return 0; + } + DEBUG_OUT(query.response[query.lastByte]); + return query.response[query.lastByte]; + } +} + +// returns: 1 if supports pad1 +// 2 if supports pad2 +// 3 if both are supported +u32 CALLBACK PADquery() { + return 3; +} + +void CALLBACK PADabout() { +} + +s32 CALLBACK PADtest() { + return 0; +} + +keyEvent* CALLBACK PADkeyEvent() { + // If running both pads, ignore every other call. So if two keys pressed in same interval... + static char eventCount = 0; + eventCount++; + if (eventCount < openCount) { + return 0; + } + eventCount = 0; + + Update(2, 0); + static keyEvent ev; + if (!GetQueuedKeyEvent(&ev)) return 0; + + return &ev; +} + +struct PadPluginFreezeData { + char format[8]; + // Currently all different versions are incompatible. + // May split into major/minor with some compatibility rules. + u32 version; + // So when loading, know which plugin's settings I'm loading. + // Not a big deal. Use a static variable when saving to figure it out. + u8 port; + // active slot for port + u8 slot[2]; + PadFreezeData padData[2][4]; + QueryInfo query; +}; + +s32 CALLBACK PADfreeze(int mode, freezeData *data) { + if (!data) + { + printf("LilyPad savestate null pointer!\n"); + return -1; + } + + if (mode == FREEZE_SIZE) { + data->size = sizeof(PadPluginFreezeData); + } + else if (mode == FREEZE_LOAD) { + PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); + StopVibrate(); + if (data->size != sizeof(PadPluginFreezeData) || + pdata.version != PAD_SAVE_STATE_VERSION || + strcmp(pdata.format, "PadMode")) return 0; + + if( pdata.port >= 2 ) return 0; + + query = pdata.query; + if (pdata.query.slot < 4) { + query = pdata.query; + } + + // Tales of the Abyss - pad fix + // - restore data for both ports + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + u8 mode = pdata.padData[port][slot].mode; + + if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE) { + break; + } + + // Not sure if the cast is strictly necessary, but feel safest with it there... + *(PadFreezeData*)&pads[port][slot] = pdata.padData[port][slot]; + } + + if (pdata.slot[port] < 4) + slots[port] = pdata.slot[port]; + } + } + else if (mode == FREEZE_SAVE) { + if (data->size != sizeof(PadPluginFreezeData)) return 0; + PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); + + + // Tales of the Abyss - pad fix + // - PCSX2 only saves port0 (save #1), then port1 (save #2) + + memset(&pdata, 0, sizeof(pdata)); + strcpy(pdata.format, "PadMode"); + pdata.version = PAD_SAVE_STATE_VERSION; + pdata.port = 0; + pdata.query = query; + + for (int port=0; port<2; port++) { + for (int slot=0; slot<4; slot++) { + pdata.padData[port][slot] = pads[port][slot]; + } + + pdata.slot[port] = slots[port]; + } + } + else return -1; + return 0; +} + +u32 CALLBACK PADreadPort1 (PadDataS* pads) { + PADstartPoll(1); + PADpoll(0x42); + memcpy(pads, query.response+1, 7); + pads->controllerType = pads[0].controllerType>>4; + memset (pads+7, 0, sizeof(PadDataS)-7); + return 0; +} + +u32 CALLBACK PADreadPort2 (PadDataS* pads) { + PADstartPoll(2); + PADpoll(0x42); + memcpy(pads, query.response+1, 7); + pads->controllerType = pads->controllerType>>4; + memset (pads+7, 0, sizeof(PadDataS)-7); + return 0; +} + +u32 CALLBACK PSEgetLibType() { + return 8; +} + +u32 CALLBACK PSEgetLibVersion() { + return (VERSION & 0xFFFFFF); +} + +s32 CALLBACK PADqueryMtap(u8 port) { + port--; + if (port > 1) return 0; + return config.multitap[port]; +} + +s32 CALLBACK PADsetSlot(u8 port, u8 slot) { + port--; + slot--; + if (port > 1 || slot > 3) { + return 0; + } + // Even if no pad there, record the slot, as it is the active slot regardless. + slots[port] = slot; + // First slot always allowed. + // return pads[port][slot].enabled | !slot; + return 1; +} + +// Little funkiness to handle rounding floating points to ints without the C runtime. +// Unfortunately, means I can't use /GL optimization option when NO_CRT is defined. +#ifdef NO_CRT +extern "C" long _cdecl _ftol(); +extern "C" long _cdecl _ftol2_sse() { + return _ftol(); +} +extern "C" long _cdecl _ftol2() { + return _ftol(); +} +#endif + From 147353a177f9d96417988168c3b73f4be304ad83 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 24 Jan 2015 23:06:08 +0100 Subject: [PATCH 04/18] lilypad: add cmake file --- plugins/CMakeLists.txt | 76 ++++++++++++---------------------- plugins/LilyPad/CMakeLists.txt | 70 +++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 50 deletions(-) create mode 100644 plugins/LilyPad/CMakeLists.txt 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..86bd2da6b4 --- /dev/null +++ b/plugins/LilyPad/CMakeLists.txt @@ -0,0 +1,70 @@ +# 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 + Linux/Config.cpp + Linux/KeyboardQueue.cpp + Linux/LilyPad.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}") From 3661f51bcbd972bf7656df52013084c7d9aaae66 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 24 Jan 2015 23:10:01 +0100 Subject: [PATCH 05/18] lilypad: minor c++ fixes Must be checked on windows --- plugins/LilyPad/Config.h | 2 -- plugins/LilyPad/Global.h | 1 + plugins/LilyPad/InputManager.cpp | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) 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/Global.h b/plugins/LilyPad/Global.h index 7c6162d13b..0398ae94c5 100644 --- a/plugins/LilyPad/Global.h +++ b/plugins/LilyPad/Global.h @@ -106,6 +106,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..beaec4ff54 100644 --- a/plugins/LilyPad/InputManager.cpp +++ b/plugins/LilyPad/InputManager.cpp @@ -221,7 +221,7 @@ void Device::CalcVirtualState() { double East = sin(angle); double South = -cos(angle); // Normalize so greatest direction is 1. - double mul = FULLY_DOWN / max(fabs(South), fabs(East)); + double mul = FULLY_DOWN / std::max(fabs(South), fabs(East)); iEast = (int) floor(East * mul + 0.5); iSouth = (int) floor(South * mul + 0.5); } From 156f66ef62968c55fbcb98d9a23797b59ed1aecc Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 24 Jan 2015 23:10:51 +0100 Subject: [PATCH 06/18] lilypad: make it compile on linux * Ifdef windows code * Add a windows crap to linux crap translation layer --- plugins/LilyPad/DeviceEnumerator.cpp | 2 + plugins/LilyPad/Global.h | 75 +++++++++++++++++++++++++++- plugins/LilyPad/InputManager.cpp | 2 + plugins/LilyPad/InputManager.h | 4 ++ plugins/LilyPad/Linux/LilyPad.cpp | 10 +--- 5 files changed, 83 insertions(+), 10 deletions(-) diff --git a/plugins/LilyPad/DeviceEnumerator.cpp b/plugins/LilyPad/DeviceEnumerator.cpp index ca5b4076df..01e6b9705f 100644 --- a/plugins/LilyPad/DeviceEnumerator.cpp +++ b/plugins/LilyPad/DeviceEnumerator.cpp @@ -33,12 +33,14 @@ void EnumDevices(int hideDXXinput) { InputDeviceManager *oldDm = dm; dm = new InputDeviceManager(); +#ifdef _MSC_VER EnumHookDevices(); EnumWindowsMessagingDevices(); EnumRawInputDevices(); EnumDualShock3s(); EnumXInputDevices(); EnumDirectInputDevices(hideDXXinput); +#endif dm->CopyBindings(oldDm->numDevices, oldDm->devices); diff --git a/plugins/LilyPad/Global.h b/plugins/LilyPad/Global.h index 0398ae94c5..f09526ed28 100644 --- a/plugins/LilyPad/Global.h +++ b/plugins/LilyPad/Global.h @@ -20,6 +20,67 @@ // 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 ... +} + +#endif + + #define DIRECTINPUT_VERSION 0x0800 #ifdef NO_CRT @@ -40,9 +101,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 +123,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); diff --git a/plugins/LilyPad/InputManager.cpp b/plugins/LilyPad/InputManager.cpp index beaec4ff54..80fbe67766 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; diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index 78ae4d4f1e..e68ad2b205 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -196,12 +196,14 @@ 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; +#endif }; @@ -216,9 +218,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/Linux/LilyPad.cpp b/plugins/LilyPad/Linux/LilyPad.cpp index 51d6a7b7a8..994c47bed5 100644 --- a/plugins/LilyPad/Linux/LilyPad.cpp +++ b/plugins/LilyPad/Linux/LilyPad.cpp @@ -40,11 +40,7 @@ // Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking // havoc on each other... -#ifdef _MSC_VER -CRITICAL_SECTION updateLock; -#else static std::mutex updateLock; -#endif // Used to toggle mouse listening. u8 miceEnabled; @@ -325,7 +321,6 @@ void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { if (value < b->deadZone || !value) return; -#ifdef _MSC_VER if ( config.turboKeyHack == 1 ){ // send a tabulator keypress to emulator //printf("%x\n", b->command); if ( b->command == 0x11 ){ // L3 button @@ -336,7 +331,6 @@ void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { LastCheck = t; } } -#endif int sensitivity = b->sensitivity; if (sensitivity < 0) { @@ -403,11 +397,9 @@ void Update(unsigned int port, unsigned int slot) { // Lock prior to timecheck code to avoid pesky race conditions. std::lock_guard lock(updateLock); -#ifdef _MSC_VER static unsigned int LastCheck = 0; unsigned int t = timeGetTime(); if (t - LastCheck < 15 || !openCount) return; -#endif #ifdef _MSC_VER if (windowThreadId != GetCurrentThreadId()) { @@ -422,9 +414,9 @@ void Update(unsigned int port, unsigned int slot) { } return; } +#endif LastCheck = t; -#endif int i; ButtonSum s[2][4]; From 4f013a2960488a229084d975dffcf190c1df0da9 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Mon, 26 Jan 2015 23:45:15 +0100 Subject: [PATCH 07/18] lilypad: import common utility to manage config Based work on spu2x equivalent file. Only convert the file to a CfgHelper object --- plugins/LilyPad/CMakeLists.txt | 1 + plugins/LilyPad/Global.h | 4 + plugins/LilyPad/Linux/Config.cpp | 14 ++-- plugins/LilyPad/Linux/ConfigHelper.cpp | 112 +++++++++++++++++++++++++ plugins/LilyPad/Linux/ConfigHelper.h | 48 +++++++++++ 5 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 plugins/LilyPad/Linux/ConfigHelper.cpp create mode 100644 plugins/LilyPad/Linux/ConfigHelper.h diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt index 86bd2da6b4..f7073171f1 100644 --- a/plugins/LilyPad/CMakeLists.txt +++ b/plugins/LilyPad/CMakeLists.txt @@ -29,6 +29,7 @@ set(lilypadSources DeviceEnumerator.cpp InputManager.cpp Linux/Config.cpp + Linux/ConfigHelper.cpp Linux/KeyboardQueue.cpp Linux/LilyPad.cpp) diff --git a/plugins/LilyPad/Global.h b/plugins/LilyPad/Global.h index f09526ed28..799eb5f55a 100644 --- a/plugins/LilyPad/Global.h +++ b/plugins/LilyPad/Global.h @@ -78,6 +78,10 @@ static inline unsigned int timeGetTime() { return (ms & 0xFFFFFFFF); // MS code is u32 ... } +#include "Utilities/Dependencies.h" +#include "Utilities/StringHelpers.h" +#include "Utilities/Path.h" + #endif diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp index ec506baf44..55849f689c 100644 --- a/plugins/LilyPad/Linux/Config.cpp +++ b/plugins/LilyPad/Linux/Config.cpp @@ -20,21 +20,19 @@ #include "InputManager.h" #include "Config.h" #include "DeviceEnumerator.h" +#include "Linux/ConfigHelper.h" GeneralConfig config; u8 ps2e = 0; +void CALLBACK PADsetSettingsDir( const char *dir ) +{ + CfgSetSettingsDir(dir); +} + int LoadSettings(int force, wchar_t *file) { if (dm && !force) return 0; -#if 0 - if( createIniDir ) - { - CreateDirectory(L"inis", 0); - createIniDir = false; - } -#endif - // Could just do ClearDevices() instead, but if I ever add any extra stuff, // this will still work. UnloadConfigs(); 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); + +}; From 087f252909ca4e69f00f2f659b044b35b6b456d6 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Wed, 28 Jan 2015 00:42:56 +0100 Subject: [PATCH 08/18] lilypad: add plumbering config Note: it miss interaction with any dialog --- plugins/LilyPad/Linux/Config.cpp | 185 ++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 55 deletions(-) diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp index 55849f689c..725efa2e76 100644 --- a/plugins/LilyPad/Linux/Config.cpp +++ b/plugins/LilyPad/Linux/Config.cpp @@ -25,9 +25,119 @@ GeneralConfig config; u8 ps2e = 0; +struct GeneralSettingsBool { + const wchar_t *name; + unsigned int ControlId; + u8 defaultValue; +}; + +// 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 ) { - CfgSetSettingsDir(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) { @@ -38,62 +148,28 @@ int LoadSettings(int force, wchar_t *file) { UnloadConfigs(); dm = new InputDeviceManager(); -#ifdef _MSC_VER - if (!file) { - file = iniFile; - GetPrivateProfileStringW(L"General Settings", L"Last Config Path", L"inis", config.lastSaveConfigPath, sizeof(config.lastSaveConfigPath), file); - GetPrivateProfileStringW(L"General Settings", L"Last Config Name", L"LilyPad.lily", config.lastSaveConfigFileName, sizeof(config.lastSaveConfigFileName), file); - } - else { - wchar_t *c = wcsrchr(file, '\\'); - if (c) { - *c = 0; - wcscpy(config.lastSaveConfigPath, file); - wcscpy(config.lastSaveConfigFileName, c+1); - *c = '\\'; - WritePrivateProfileStringW(L"General Settings", L"Last Config Path", config.lastSaveConfigPath, iniFile); - WritePrivateProfileStringW(L"General Settings", L"Last Config Name", config.lastSaveConfigFileName, iniFile); - } - } + CfgHelper cfg; for (int i=0; i= 100) break; continue; } wchar_t *id2 = 0; - if (GetPrivateProfileStringW(id, L"Product ID", 0, temp4, 1000, file) && temp4[0]) + if (cfg.ReadStr(id, L"Product ID", temp4) && temp4[0]) id2 = temp4; - int api = GetPrivateProfileIntW(id, L"API", 0, file); - int type = GetPrivateProfileIntW(id, L"Type", 0, file); + 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); @@ -125,7 +201,7 @@ int LoadSettings(int force, wchar_t *file) { int last = 0; while (1) { wsprintfW(temp, L"Binding %i", j++); - if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { + if (!cfg.ReadStr(id, temp, temp2)) { if (j >= 100) { if (!last) break; last = 0; @@ -147,14 +223,14 @@ int LoadSettings(int force, wchar_t *file) { VirtualControl *c = dev->GetVirtualControl(uid); if (!c) c = dev->AddVirtualControl(uid, -1); if (c) { - BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); + //TODO BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); } } } j = 0; while (1) { wsprintfW(temp, L"FF Binding %i", j++); - if (!GetPrivateProfileStringW(id, temp, 0, temp2, 1000, file)) { + if (!cfg.ReadStr(id, temp, temp2)) { if (j >= 10) { if (!last) break; last = 0; @@ -191,7 +267,7 @@ int LoadSettings(int force, wchar_t *file) { // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1]; } ForceFeedbackBinding *b; - CreateEffectBinding(dev, temp2, port, slot, motor, &b); + //TODO CreateEffectBinding(dev, temp2, port, slot, motor, &b); if (b) { while (1) { int axisID = atoi(s); @@ -215,8 +291,7 @@ int LoadSettings(int force, wchar_t *file) { } config.multipleBinding = multipleBinding; - RefreshEnabledDevicesAndDisplay(1); -#endif + //TODO RefreshEnabledDevicesAndDisplay(1); return 0; } From aec43f0bee0a44e4d0098a184d440c0f98e8f621 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 30 Jan 2015 20:44:52 +0100 Subject: [PATCH 09/18] lilypad: add x11 display pointer If we move all window management hack into the core it would be useless (for input) --- plugins/LilyPad/Global.h | 5 +++++ plugins/LilyPad/InputManager.h | 4 ++++ plugins/LilyPad/Linux/LilyPad.cpp | 22 +++++++++++++--------- 3 files changed, 22 insertions(+), 9 deletions(-) diff --git a/plugins/LilyPad/Global.h b/plugins/LilyPad/Global.h index 799eb5f55a..8201d34daf 100644 --- a/plugins/LilyPad/Global.h +++ b/plugins/LilyPad/Global.h @@ -82,6 +82,11 @@ static inline unsigned int timeGetTime() { #include "Utilities/StringHelpers.h" #include "Utilities/Path.h" +#include + +extern Display *GSdsp; +extern Window GSwin; + #endif diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index e68ad2b205..87600703b8 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -203,6 +203,10 @@ struct InitInfo { //HWND hWndButton; WndProcEater* hWndProc; +#else + // Linux equivalent to HWND + Display *GSdsp; + Window GSwin; #endif }; diff --git a/plugins/LilyPad/Linux/LilyPad.cpp b/plugins/LilyPad/Linux/LilyPad.cpp index 994c47bed5..fb7a7fca73 100644 --- a/plugins/LilyPad/Linux/LilyPad.cpp +++ b/plugins/LilyPad/Linux/LilyPad.cpp @@ -38,6 +38,9 @@ // LilyPad version. #define VERSION ((0<<8) | 11 | (0<<24)) +Display *GSdsp; +Window GSwin; + // Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking // havoc on each other... static std::mutex updateLock; @@ -430,9 +433,13 @@ void Update(unsigned int port, unsigned int slot) { InitInfo info = { 0, 0, hWndTop, &hWndGSProc }; - - dm->Update(&info); +#else + InitInfo info = { + 0, 0, GSdsp, GSwin + }; #endif + dm->Update(&info); + static int turbo = 0; turbo++; for (i=0; inumDevices; i++) { @@ -789,14 +796,11 @@ s32 CALLBACK PADopen(void *pDsp) { } } - // 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, - // if any window did (topmost in this thread?). Which isn't what I want, and doesn't seem - // to be what it actually does. - // activeWindow = GetActiveWindow() == hWnd; + updateQueued = 0; + + GSdsp = *(Display**)pDsp; + GSwin = (Window)*(((uptr*)pDsp)+1); - // activeWindow = (GetAncestor(hWnd, GA_ROOT) == GetAncestor(GetForegroundWindow(), GA_ROOT)); activeWindow = 1; UpdateEnabledDevices(); return 0; From 3096e946163a9a1a388aef01d0c94a9d272af631 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 30 Jan 2015 23:44:51 +0100 Subject: [PATCH 10/18] lilypad : plug a basic keyboard device Add a reverse fifo to transfer event from core to plugin Long story short, on linux there is only one event queue in core --- plugins/LilyPad/CMakeLists.txt | 1 + plugins/LilyPad/DeviceEnumerator.cpp | 6 ++ plugins/LilyPad/InputManager.h | 2 + plugins/LilyPad/KeyboardQueue.h | 6 ++ plugins/LilyPad/Linux/Config.cpp | 9 ++- plugins/LilyPad/Linux/KeyboardMouse.cpp | 81 +++++++++++++++++++++++++ plugins/LilyPad/Linux/KeyboardMouse.h | 29 +++++++++ plugins/LilyPad/Linux/KeyboardQueue.cpp | 43 +++++++++++++ plugins/LilyPad/Linux/LilyPad.cpp | 2 + 9 files changed, 177 insertions(+), 2 deletions(-) create mode 100644 plugins/LilyPad/Linux/KeyboardMouse.cpp create mode 100644 plugins/LilyPad/Linux/KeyboardMouse.h diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt index f7073171f1..7920f22ef4 100644 --- a/plugins/LilyPad/CMakeLists.txt +++ b/plugins/LilyPad/CMakeLists.txt @@ -30,6 +30,7 @@ set(lilypadSources InputManager.cpp Linux/Config.cpp Linux/ConfigHelper.cpp + Linux/KeyboardMouse.cpp Linux/KeyboardQueue.cpp Linux/LilyPad.cpp) diff --git a/plugins/LilyPad/DeviceEnumerator.cpp b/plugins/LilyPad/DeviceEnumerator.cpp index 01e6b9705f..c06088c3d8 100644 --- a/plugins/LilyPad/DeviceEnumerator.cpp +++ b/plugins/LilyPad/DeviceEnumerator.cpp @@ -27,6 +27,10 @@ #include "HidDevice.h" #include "DualShock3.h" +#ifdef __linux__ +#include "Linux/KeyboardMouse.h" +#endif + void EnumDevices(int hideDXXinput) { // Needed for enumeration of some device types. dm->ReleaseInput(); @@ -40,6 +44,8 @@ void EnumDevices(int hideDXXinput) { EnumDualShock3s(); EnumXInputDevices(); EnumDirectInputDevices(hideDXXinput); +#else + EnumLnx(); #endif dm->CopyBindings(oldDm->numDevices, oldDm->devices); diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index 87600703b8..62e3111da9 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -130,6 +130,8 @@ enum DeviceAPI { // to ignore individual buttons. Wrapper itself takes care // of ignoring bound keys. Otherwise, works normally. IGNORE_KEYBOARD = 7, + // XXX + LNX_KEYBOARD = 16, }; enum DeviceType { 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/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp index 725efa2e76..0b40914353 100644 --- a/plugins/LilyPad/Linux/Config.cpp +++ b/plugins/LilyPad/Linux/Config.cpp @@ -158,8 +158,8 @@ int LoadSettings(int force, wchar_t *file) { config.closeHacks = (u8)cfg.ReadInt(L"General Settings", L"Close Hacks"); if (config.closeHacks&1) config.closeHacks &= ~2; - config.keyboardApi = (DeviceAPI)cfg.ReadInt(L"General Settings", L"Keyboard Mode", WM); - if (!config.keyboardApi) config.keyboardApi = WM; + config.keyboardApi = (DeviceAPI)cfg.ReadInt(L"General Settings", L"Keyboard Mode", LNX_KEYBOARD); + if (!config.keyboardApi) config.keyboardApi = LNX_KEYBOARD; config.mouseApi = (DeviceAPI) cfg.ReadInt(L"General Settings", L"Mouse Mode"); config.volume = cfg.ReadInt(L"General Settings", L"Volume", 100); @@ -292,6 +292,7 @@ int LoadSettings(int force, wchar_t *file) { config.multipleBinding = multipleBinding; //TODO RefreshEnabledDevicesAndDisplay(1); + RefreshEnabledDevices(1); // XXX For the moment only a subfonction return 0; } @@ -314,6 +315,7 @@ void RefreshEnabledDevices(int updateDeviceList) { 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); @@ -328,6 +330,8 @@ void RefreshEnabledDevices(int updateDeviceList) { ((dev->api == DI && config.gameApis.directInput) || (dev->api == DS3 && config.gameApis.dualShock3) || (dev->api == XINPUT && config.gameApis.xInput)))) { + dm->EnableDevice(i); +#if 0 // windows magic? if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName && !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) { dm->DisableDevice(i); @@ -335,6 +339,7 @@ void RefreshEnabledDevices(int updateDeviceList) { else { dm->EnableDevice(i); } +#endif } else { dm->DisableDevice(i); diff --git a/plugins/LilyPad/Linux/KeyboardMouse.cpp b/plugins/LilyPad/Linux/KeyboardMouse.cpp new file mode 100644 index 0000000000..c520227f2d --- /dev/null +++ b/plugins/LilyPad/Linux/KeyboardMouse.cpp @@ -0,0 +1,81 @@ +/* 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") +{ + fprintf(stderr, "Create a new device YES\n"); + 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: + fprintf(stderr, "key pressed %x\n", event.key); + physicalControlState[MAX_KEYCODE & event.key] = FULLY_DOWN; + status = 1; + break; + case KeyRelease: + fprintf(stderr, "key released %x\n", event.key); + 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 index 3718834f3c..55fffd8a2b 100644 --- a/plugins/LilyPad/Linux/KeyboardQueue.cpp +++ b/plugins/LilyPad/Linux/KeyboardQueue.cpp @@ -66,3 +66,46 @@ int GetQueuedKeyEvent(keyEvent *event) { void ClearKeyQueue() { lastQueuedEvent = nextQueuedEvent; } + + +#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(cSection); + + 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) +{ + //fprintf(stderr, "Received evt:%x key:%x\n", evt.evt, evt.key); + R_QueueKeyEvent(evt); +} +#endif diff --git a/plugins/LilyPad/Linux/LilyPad.cpp b/plugins/LilyPad/Linux/LilyPad.cpp index fb7a7fca73..c802d7e57f 100644 --- a/plugins/LilyPad/Linux/LilyPad.cpp +++ b/plugins/LilyPad/Linux/LilyPad.cpp @@ -729,6 +729,7 @@ s32 CALLBACK PADinit(u32 flags) { query.lastByte = 1; query.numBytes = 0; ClearKeyQueue(); + R_ClearKeyQueue(); // Just in case, when resuming emulation. ReleaseModifierKeys(); @@ -811,6 +812,7 @@ void CALLBACK PADclose() { DEBUG_TEXT_OUT("LilyPad closed\n\n"); updateQueued = 0; ClearKeyQueue(); + R_ClearKeyQueue(); } } From 42b9932bd0d275c8d9a8ec5d8952b2b7a6537e02 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Tue, 3 Feb 2015 22:38:39 +0100 Subject: [PATCH 11/18] lilypad: add code to bind key --- plugins/LilyPad/Linux/Config.cpp | 161 ++++++++++++++++++++++++++++++- 1 file changed, 159 insertions(+), 2 deletions(-) diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp index 0b40914353..f45a3468d3 100644 --- a/plugins/LilyPad/Linux/Config.cpp +++ b/plugins/LilyPad/Linux/Config.cpp @@ -25,12 +25,167 @@ 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[] = { @@ -223,7 +378,7 @@ int LoadSettings(int force, wchar_t *file) { VirtualControl *c = dev->GetVirtualControl(uid); if (!c) c = dev->AddVirtualControl(uid, -1); if (c) { - //TODO BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); + BindCommand(dev, uid, port, slot, command, sensitivity, turbo, deadZone); } } } @@ -266,8 +421,9 @@ int LoadSettings(int force, wchar_t *file) { dev->AddFFEffectType(temp2, temp2, EFFECT_CONSTANT); // eff = &dev->ffEffectTypes[dev->numFFEffectTypes-1]; } +#if 0 ForceFeedbackBinding *b; - //TODO CreateEffectBinding(dev, temp2, port, slot, motor, &b); + CreateEffectBinding(dev, temp2, port, slot, motor, &b); if (b) { while (1) { int axisID = atoi(s); @@ -286,6 +442,7 @@ int LoadSettings(int force, wchar_t *file) { s++; } } +#endif } } } From 7985f5114c211f9920deb85438887efe6f564dad Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sat, 24 Jan 2015 23:14:35 +0100 Subject: [PATCH 12/18] cmake: enable lilypad I manage to have x/start button working with 0/1 key. Here the reference configuration. [General\ Settings] Force\ Cursor\ Hide=0 Mouse\ Unfocus=0 Background=0 Multiple\ Bindings=0 DirectInput\ Game\ Devices=0 XInput=0 DualShock\ 3=0 Multitap\ 1=0 Multitap\ 2=0 Escape\ Fullscreen\ Hack=0 Disable\ Screen\ Saver=0 Logging=0 Save\ State\ in\ Title=0 GH2=0 Turbo\ Key\ Hack=0 Vista\ Volume=0 Close\ Hacks=0 Keyboard\ Mode=16 Mouse\ Mode=0 Volume=0 [Pad\ 0\ 0] Mode=1 Auto\ Analog=0 [Pad\ 0\ 1] Mode=1 Auto\ Analog=0 [Pad\ 0\ 2] Mode=1 Auto\ Analog=0 [Pad\ 0\ 3] Mode=1 Auto\ Analog=0 [Pad\ 1\ 0] Mode=1 Auto\ Analog=0 [Pad\ 1\ 1] Mode=1 Auto\ Analog=0 [Pad\ 1\ 2] Mode=1 Auto\ Analog=0 [Pad\ 1\ 3] Mode=1 Auto\ Analog=0 [Device\ 0] Display\ Name=displayName Instance\ ID=instanceID Product\ ID=deviceID API=16 Type=1 Binding 0=0x00040030, 0, 31, 65536, 0, 0, 0 Binding 1=0x00040031, 0, 19, 65536, 0, 0, 0 Binding 2=0x00040002, 0, 50, 65536, 0, 0, 0 Binding 3=0x00040003, 0, 51, 65536, 0, 0, 0 Binding 4=0x00040004, 0, 52, 65536, 0, 0, 0 Binding 5=0x00040005, 0, 53, 65536, 0, 0, 0 Binding 6=0x00040006, 0, 54, 65536, 0, 0, 0 Binding 7=0x00040007, 0, 55, 65536, 0, 0, 0 Binding 8=0x00040008, 0, 56, 65536, 0, 0, 0 Binding 9=0x00040009, 0, 57, 65536, 0, 0, 0 Binding 10=0x0004000C, 0, 58, 65536, 0, 0, 0 Binding 11=0x0004000D, 0, 59, 65536, 0, 0, 0 Binding 12=0x0004000E, 0, 60, 65536, 0, 0, 0 Binding 13=0x0004000F, 0, 61, 65536, 0, 0, 0 Binding 14=0x00200010, 0, 62, 65536, 0, 0, 1 Binding 15=0x00200011, 0, 63, 65536, 0, 0, 1 Binding 16=0x01020012, 0, 33, 65536, 0, 0, 13172 Binding 17=0x02020012, 0, 35, 65536, 0, 0, 13172 Binding 18=0x02020014, 0, 39, 65536, 0, 0, 13172 Binding 19=0x02020015, 0, 38, 65536, 0, 0, 13172 --- cmake/SelectPcsx2Plugins.cmake | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/cmake/SelectPcsx2Plugins.cmake b/cmake/SelectPcsx2Plugins.cmake index c401129dcd..1b965f3cbc 100644 --- a/cmake/SelectPcsx2Plugins.cmake +++ b/cmake/SelectPcsx2Plugins.cmake @@ -205,6 +205,15 @@ if(GTKn_FOUND AND EXTRA_PLUGINS) endif() #--------------------------------------- +#--------------------------------------- +# LilyPad +# requires: -X11 +#--------------------------------------- +if(GTKn_FOUND AND X11_FOUND) + set(LilyPad TRUE) +endif() +#--------------------------------------- + #--------------------------------------- # onepad #--------------------------------------- @@ -287,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) From 349bb810f0268d1278bb75ee8982610060962ef1 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Wed, 4 Feb 2015 22:20:58 +0100 Subject: [PATCH 13/18] lilypad: merge LilyPad.cpp (ifdef, ifdef) Avoid duplication for linux --- plugins/LilyPad/CMakeLists.txt | 7 ++-- plugins/LilyPad/LilyPad.cpp | 74 +++++++++++++++++++++++++++++++--- 2 files changed, 72 insertions(+), 9 deletions(-) diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt index 7920f22ef4..58bd80a0df 100644 --- a/plugins/LilyPad/CMakeLists.txt +++ b/plugins/LilyPad/CMakeLists.txt @@ -28,11 +28,12 @@ endif() set(lilypadSources DeviceEnumerator.cpp InputManager.cpp + LilyPad.cpp Linux/Config.cpp Linux/ConfigHelper.cpp - Linux/KeyboardMouse.cpp + Linux/KeyboardMouse.cpp Linux/KeyboardQueue.cpp - Linux/LilyPad.cpp) + ) # lilypad headers set(lilypadHeaders @@ -59,7 +60,7 @@ endif() set(lilypadFinalLibs #${lilypadFinalLibs} #${GTK2_LIBRARIES} - ${X11_LIBRARIES} + #${X11_LIBRARIES} ) set(lilypadFinalSources diff --git a/plugins/LilyPad/LilyPad.cpp b/plugins/LilyPad/LilyPad.cpp index 58725ccb41..f7d4046ff5 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,7 @@ void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { void CapSum(ButtonSum *sum) { int i; for (i=0; i<3; i++) { - int div = max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); + int div = std::max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); 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 +444,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 +457,7 @@ struct EnterScopedSection LeaveCriticalSection( &m_cs ); } }; +#endif void Update(unsigned int port, unsigned int slot) { char *stateUpdated; @@ -452,12 +475,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 +498,7 @@ void Update(unsigned int port, unsigned int slot) { } return; } +#endif LastCheck = t; @@ -481,10 +510,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 +703,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 +714,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 +796,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 +811,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 +828,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 +877,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 +985,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 +994,7 @@ void CALLBACK PADconfigure() { Configure(); } - +#ifdef _MSC_VER DWORD WINAPI RenameWindowThreadProc(void *lpParameter) { wchar_t newTitle[200]; if (hWndTop) { @@ -973,12 +1016,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 +1081,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 +1090,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 +1099,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 +1112,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 +1419,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 +1432,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 +1455,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 +1514,7 @@ keyEvent* CALLBACK PADkeyEvent() { ev.key = VK_MENU; altDown = (ev.evt == KEYPRESS); } +#endif return &ev; } From 13bb1ae0282ab74e455025ba0ce7fe309ad34a56 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Wed, 4 Feb 2015 22:27:39 +0100 Subject: [PATCH 14/18] lilypad: Keep original KeyboardQueue (more ifdef) It would be nice if windows support basic std::mutex The reverse fifo queue is kept in Linux/KeyboardQueue.cpp --- plugins/LilyPad/CMakeLists.txt | 1 + plugins/LilyPad/KeyboardQueue.cpp | 18 +++++++++ plugins/LilyPad/Linux/KeyboardQueue.cpp | 51 +------------------------ 3 files changed, 20 insertions(+), 50 deletions(-) diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt index 58bd80a0df..d34f7994e3 100644 --- a/plugins/LilyPad/CMakeLists.txt +++ b/plugins/LilyPad/CMakeLists.txt @@ -28,6 +28,7 @@ endif() set(lilypadSources DeviceEnumerator.cpp InputManager.cpp + KeyboardQueue.cpp LilyPad.cpp Linux/Config.cpp Linux/ConfigHelper.cpp 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/Linux/KeyboardQueue.cpp b/plugins/LilyPad/Linux/KeyboardQueue.cpp index 55fffd8a2b..16087d7f98 100644 --- a/plugins/LilyPad/Linux/KeyboardQueue.cpp +++ b/plugins/LilyPad/Linux/KeyboardQueue.cpp @@ -19,55 +19,6 @@ // This is undoubtedly completely unnecessary. #include "KeyboardQueue.h" -// What MS calls a single process Mutex. Faster, supposedly. -// More importantly, can be abbreviated, amusingly, as cSection. -static std::mutex cSection; - -#define EVENT_QUEUE_LEN 16 -// Actually points one beyond the last queued event. -static u8 lastQueuedEvent = 0; -static u8 nextQueuedEvent = 0; -static keyEvent queuedEvents[EVENT_QUEUE_LEN]; - -void QueueKeyEvent(int key, int event) { - std::lock_guard lock(cSection); - - // 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. - if (nextQueuedEvent == lastQueuedEvent || - queuedEvents[nextQueuedEvent].key != VK_ESCAPE || - queuedEvents[nextQueuedEvent].evt != KEYPRESS) { - // Clear queue on escape down, bringing escape to front. May do something - // with shift/ctrl/alt and F-keys, later. - if (event == KEYPRESS && key == VK_ESCAPE) { - nextQueuedEvent = lastQueuedEvent; - } - - queuedEvents[lastQueuedEvent].key = key; - queuedEvents[lastQueuedEvent].evt = event; - - lastQueuedEvent = (lastQueuedEvent + 1) % EVENT_QUEUE_LEN; - // If queue wrapped around, remove last element. - if (nextQueuedEvent == lastQueuedEvent) { - nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; - } - } -} - -int GetQueuedKeyEvent(keyEvent *event) { - if (lastQueuedEvent == nextQueuedEvent) return 0; - - std::lock_guard lock(cSection); - *event = queuedEvents[nextQueuedEvent]; - nextQueuedEvent = (nextQueuedEvent + 1) % EVENT_QUEUE_LEN; - return 1; -} - -void ClearKeyQueue() { - lastQueuedEvent = nextQueuedEvent; -} - - #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 @@ -82,7 +33,7 @@ static u8 R_nextQueuedEvent = 0; static keyEvent R_queuedEvents[R_EVENT_QUEUE_LEN]; void R_QueueKeyEvent(const keyEvent &evt) { - std::lock_guard lock(cSection); + std::lock_guard lock(core_event); R_queuedEvents[R_lastQueuedEvent] = evt; R_lastQueuedEvent = (R_lastQueuedEvent + 1) % R_EVENT_QUEUE_LEN; From 8350dc2c68922b1b8c6633b04d4adfa4bb5ae6b1 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Wed, 4 Feb 2015 22:31:12 +0100 Subject: [PATCH 15/18] lilypad: remove debug printf --- plugins/LilyPad/Linux/KeyboardMouse.cpp | 5 +---- plugins/LilyPad/Linux/KeyboardQueue.cpp | 1 - 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/plugins/LilyPad/Linux/KeyboardMouse.cpp b/plugins/LilyPad/Linux/KeyboardMouse.cpp index c520227f2d..efcc7de491 100644 --- a/plugins/LilyPad/Linux/KeyboardMouse.cpp +++ b/plugins/LilyPad/Linux/KeyboardMouse.cpp @@ -23,7 +23,6 @@ LinuxKeyboard::LinuxKeyboard() : Device(LNX_KEYBOARD, KEYBOARD, L"displayName", L"instanceID", L"deviceID") { - fprintf(stderr, "Create a new device YES\n"); for (int i=0; i Date: Wed, 4 Feb 2015 23:08:34 +0100 Subject: [PATCH 16/18] lilypad: removed old linux/LilyPad.cpp file --- plugins/LilyPad/Linux/LilyPad.cpp | 1273 ----------------------------- 1 file changed, 1273 deletions(-) delete mode 100644 plugins/LilyPad/Linux/LilyPad.cpp diff --git a/plugins/LilyPad/Linux/LilyPad.cpp b/plugins/LilyPad/Linux/LilyPad.cpp deleted file mode 100644 index c802d7e57f..0000000000 --- a/plugins/LilyPad/Linux/LilyPad.cpp +++ /dev/null @@ -1,1273 +0,0 @@ -/* 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" - -// For escape timer, so as not to break GSDX+DX9. -#include -#include "resource.h" -#include "InputManager.h" -#include "Config.h" - -#define PADdefs - -#include "DeviceEnumerator.h" -#include "KeyboardQueue.h" -#include "svnrev.h" -#include "DualShock3.h" -#include "HidDevice.h" - -#define WMA_FORCE_UPDATE (WM_APP + 0x537) -#define FORCE_UPDATE_WPARAM ((WPARAM)0x74328943) -#define FORCE_UPDATE_LPARAM ((LPARAM)0x89437437) - -// LilyPad version. -#define VERSION ((0<<8) | 11 | (0<<24)) - -Display *GSdsp; -Window GSwin; - -// Keeps the various sources for Update polling (PADpoll, PADupdate, etc) from wreaking -// havoc on each other... -static std::mutex updateLock; - -// Used to toggle mouse listening. -u8 miceEnabled; - -// 2 when both pads are initialized, 1 for one pad, etc. -int openCount = 0; - -int activeWindow = 0; -int windowThreadId = 0; -int updateQueued = 0; - -int bufSize = 0; -unsigned char outBuf[50]; -unsigned char inBuf[50]; - -// windowThreadId = GetWindowThreadProcessId(hWnd, 0); - -#define MODE_DIGITAL 0x41 -#define MODE_ANALOG 0x73 -#define MODE_DS2_NATIVE 0x79 - - -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) { - DWORD junk; - WriteFile(hFile, text, strlen(text), &junk, 0); - 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) { - int i; - char temp[1500]; - char *end = temp; - sprintf(end, "%02X (%02X) ", inBuf[0], inBuf[1]); - end += 8; - for (i=2; i 0). - u8 enabled; -} pads[2][4]; - -// Active slots for each port. -int slots[2]; -// Which ports we're running on. -int portInitialized[2]; - -// Force value to be from 0 to 255. -u8 Cap (int i) { - if (i<0) return 0; - if (i>255) return 255; - return (u8) i; -} - -inline void ReleaseModifierKeys() { - QueueKeyEvent(VK_SHIFT, KEYRELEASE); - QueueKeyEvent(VK_MENU, KEYRELEASE); - QueueKeyEvent(VK_CONTROL, KEYRELEASE); -} - -// RefreshEnabledDevices() enables everything that can potentially -// be bound to, as well as the "Ignore keyboard" device. -// -// This enables everything that input should be read from while the -// emulator is running. Takes into account mouse and focus state -// and which devices have bindings for enabled pads. Releases -// keyboards if window is not focused. Releases game devices if -// background monitoring is not checked. -// And releases games if not focused and config.background is not set. -void UpdateEnabledDevices(int updateList = 0) { - // Enable all devices I might want. Can ignore the rest. - RefreshEnabledDevices(updateList); - // Figure out which pads I'm getting input for. - for (int port = 0; port<2; port++) { - for (int slot = 0; slot<4; slot++) { - if (slot && !config.multitap[port]) { - pads[port][slot].enabled = 0; - } - else { - pads[port][slot].enabled = pads[port][slot].initialized && config.padConfigs[port][slot].type != DisabledPad; - } - } - } - for (int i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - - if (!dev->enabled) continue; - if (!dev->attached) { - dm->DisableDevice(i); - continue; - } - - // Disable ignore keyboard if don't have focus or there are no keys to ignore. - if (dev->api == IGNORE_KEYBOARD) { - if ((!config.vistaVolume && (config.keyboardApi == NO_API || !dev->pads[0][0].numBindings)) || !activeWindow) { - dm->DisableDevice(i); - } - continue; - } - // Keep for PCSX2 keyboard shotcuts, unless unfocused. - if (dev->type == KEYBOARD) { - if (!activeWindow) dm->DisableDevice(i); - } - // Keep for cursor hiding consistency, unless unfocused. - // miceEnabled tracks state of mouse enable/disable button, not if mouse API is set to disabled. - else if (dev->type == MOUSE) { - if (!miceEnabled || !activeWindow) dm->DisableDevice(i); - } - else if (!activeWindow && !config.background) dm->DisableDevice(i); - else { - int numActiveBindings = 0; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - if (pads[port][slot].enabled) { - numActiveBindings += dev->pads[port][slot].numBindings + dev->pads[port][slot].numFFBindings; - } - } - } - if (!numActiveBindings) - dm->DisableDevice(i); - } - } -} - -void AddForce(ButtonSum *sum, u8 cmd, int delta = 255) { - if (!delta) return; - if (cmd<0x14) { - sum->buttons[cmd-0x10] += delta; - } - // D-pad. Command numbering is based on ordering of digital values. - else if (cmd < 0x18) { - if (cmd == 0x14) { - sum->sticks[0].vert -= delta; - } - else if (cmd == 0x15) { - sum->sticks[0].horiz += delta; - } - else if (cmd == 0x16) { - sum->sticks[0].vert += delta; - } - else if (cmd == 0x17) { - sum->sticks[0].horiz -= delta; - } - } - else if (cmd < 0x20) { - sum->buttons[cmd-0x10-4] += delta; - } - // Left stick. - else if (cmd < 0x24) { - if (cmd == 32) { - sum->sticks[2].vert -= delta; - } - else if (cmd == 33) { - sum->sticks[2].horiz += delta; - } - else if (cmd == 34) { - sum->sticks[2].vert += delta; - } - else if (cmd == 35) { - sum->sticks[2].horiz -= delta; - } - } - // Right stick. - else if (cmd < 0x28) { - if (cmd == 36) { - sum->sticks[1].vert -= delta; - } - else if (cmd == 37) { - sum->sticks[1].horiz += delta; - } - else if (cmd == 38) { - sum->sticks[1].vert += delta; - } - else if (cmd == 39) { - sum->sticks[1].horiz -= delta; - } - } -} - -void ProcessButtonBinding(Binding *b, ButtonSum *sum, int value) { - if (value < b->deadZone || !value) return; - - if ( config.turboKeyHack == 1 ){ // send a tabulator keypress to emulator - //printf("%x\n", b->command); - if ( b->command == 0x11 ){ // L3 button - static unsigned int LastCheck = 0; - unsigned int t = timeGetTime(); - if (t - LastCheck < 300 ) return; - QueueKeyEvent(VK_TAB, KEYPRESS); - LastCheck = t; - } - } - - int sensitivity = b->sensitivity; - if (sensitivity < 0) { - sensitivity = -sensitivity; - value = (1<<16)-value; - } - if (value < 0) return; - - /* Note: Value ranges of FULLY_DOWN, and sensitivity of - * BASE_SENSITIVITY corresponds to an axis/button being exactly fully down. - * Math in next line takes care of those two conditions, rounding as necessary. - * Done using __int64s because overflows will occur when - * sensitivity > BASE_SENSITIVITY and/or value > FULLY_DOWN. Latter only happens - * for relative axis. - */ - int force = (int)((((sensitivity*(255*(__int64)value)) + BASE_SENSITIVITY/2)/BASE_SENSITIVITY + FULLY_DOWN/2)/FULLY_DOWN); - AddForce(sum, b->command, force); -} - -// Restricts d-pad/analog stick values to be from -255 to 255 and button values to be from 0 to 255. -// With D-pad in DS2 native mode, the negative and positive ranges are both independently from 0 to 255, -// which is why I use 9 bits of all sticks. For left and right sticks, I have to remove a bit before sending. -void CapSum(ButtonSum *sum) { - int i; - for (i=0; i<3; i++) { - int div = std::max(abs(sum->sticks[i].horiz), abs(sum->sticks[i].vert)); - if (div > 255) { - sum->sticks[i].horiz = sum->sticks[i].horiz * 255 / div; - sum->sticks[i].vert = sum->sticks[i].vert * 255 / div; - } - } - for (i=0; i<12; i++) { - sum->buttons[i] = Cap(sum->buttons[i]); - } -} - -// Counter similar to stateUpdated for each pad, except used for PADkeyEvent instead. -// Only matters when GS thread updates is disabled (Just like summed pad values -// for pads beyond the first slot). - -// Values, in order, correspond to PADkeyEvent, PADupdate(0), PADupdate(1), and -// WndProc(WMA_FORCE_UPDATE). Last is always 0. -char padReadKeyUpdated[4] = {0, 0, 0, 0}; - -#define LOCK_DIRECTION 2 -#define LOCK_BUTTONS 4 -#define LOCK_BOTH 1 - -void Update(unsigned int port, unsigned int slot) { - char *stateUpdated; - if (port < 2) { - stateUpdated = &pads[port][slot].stateUpdated; - } - else if (port < 6) { - stateUpdated = padReadKeyUpdated+port-2; - } - else return; - - if (*stateUpdated > 0) { - stateUpdated[0] --; - return; - } - - // Lock prior to timecheck code to avoid pesky race conditions. - std::lock_guard lock(updateLock); - - 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) { - updateQueued = 1; - PostMessage(hWnd, WMA_FORCE_UPDATE, FORCE_UPDATE_WPARAM, FORCE_UPDATE_LPARAM); - } - } else - { - stateUpdated[0] --; - } - return; - } -#endif - - LastCheck = t; - - int i; - ButtonSum s[2][4]; - u8 lockStateChanged[2][4]; - memset(lockStateChanged, 0, sizeof(lockStateChanged)); - - for (i=0; i<8; i++) { - s[i&1][i>>1] = pads[i&1][i>>1].lockedSum; - } -#ifdef _MSC_VER - InitInfo info = { - 0, 0, hWndTop, &hWndGSProc - }; -#else - InitInfo info = { - 0, 0, GSdsp, GSwin - }; -#endif - dm->Update(&info); - - static int turbo = 0; - turbo++; - for (i=0; inumDevices; i++) { - Device *dev = dm->devices[i]; - // Skip both disabled devices and inactive enabled devices. - // Shouldn't be any of the latter, in general, but just in case... - if (!dev->active) continue; - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; - for (int j=0; jpads[port][slot].numBindings; j++) { - Binding *b = dev->pads[port][slot].bindings+j; - int cmd = b->command; - int state = dev->virtualControlState[b->controlIndex]; - if (!(turbo & b->turbo)) { - if (cmd > 0x0F && cmd != 0x28) { - ProcessButtonBinding(b, &s[port][slot], state); - } - else if ((state>>15) && !(dev->oldVirtualControlState[b->controlIndex]>>15)) { - if (cmd == 0x0F) { - miceEnabled = !miceEnabled; - UpdateEnabledDevices(); - } - else if (cmd == 0x0C) { - lockStateChanged[port][slot] |= LOCK_BUTTONS; - } - else if (cmd == 0x0E) { - lockStateChanged[port][slot] |= LOCK_DIRECTION; - } - else if (cmd == 0x0D) { - lockStateChanged[port][slot] |= LOCK_BOTH; - } - else if (cmd == 0x28) { - if (!pads[port][slot].modeLock) { - if (pads[port][slot].mode != MODE_DIGITAL) - pads[port][slot].mode = MODE_DIGITAL; - else - pads[port][slot].mode = MODE_ANALOG; - } - } - } - } - } - } - } - } - dm->PostRead(); - - { - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - for (int motor=0; motor<2; motor++) { - // TODO: Probably be better to send all of these at once. - if (pads[port][slot].nextVibrate[motor] | pads[port][slot].currentVibrate[motor]) { - pads[port][slot].currentVibrate[motor] = pads[port][slot].nextVibrate[motor]; - dm->SetEffect(port,slot, motor, pads[port][slot].nextVibrate[motor]); - } - } - } - } - - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - pads[port][slot].stateUpdated = 1; - if (config.padConfigs[port][slot].type == DisabledPad || !pads[port][slot].initialized) continue; - if (config.padConfigs[port][slot].type == GuitarPad) { - if (!config.GH2) { - s[port][slot].sticks[1].vert = -s[port][slot].sticks[1].vert; - } - // GH2 hack. - else if (config.GH2) { - const unsigned int oldIdList[5] = {ID_R2, ID_CIRCLE, ID_TRIANGLE, ID_CROSS, ID_SQUARE}; - const unsigned int idList[5] = {ID_L2, ID_L1, ID_R1, ID_R2, ID_CROSS}; - int values[5]; - int i; - for (i=0; i<5; i++) { - int id = oldIdList[i] - 0x1104; - values[i] = s[port][slot].buttons[id]; - s[port][slot].buttons[id] = 0; - } - s[port][slot].buttons[ID_TRIANGLE-0x1104] = values[1]; - for (i=0; i<5; i++) { - int id = idList[i] - 0x1104; - s[port][slot].buttons[id] = values[i]; - } - if (abs(s[port][slot].sticks[0].vert) <= 48) { - for (int i=0; i<5; i++) { - unsigned int id = idList[i] - 0x1104; - if (pads[port][slot].sum.buttons[id] < s[port][slot].buttons[id]) { - s[port][slot].buttons[id] = pads[port][slot].sum.buttons[id]; - } - } - } - else if (abs(pads[port][slot].sum.sticks[0].vert) <= 48) { - for (int i=0; i<5; i++) { - unsigned int id = idList[i] - 0x1104; - if (pads[port][slot].sum.buttons[id]) { - s[port][slot].buttons[id] = 0; - } - } - } - } - } - - if (pads[port][slot].mode == 0x41) { - for (int i=1; i<=2; i++) { - if (abs(s[port][slot].sticks[i].horiz) >= 100) - s[port][slot].sticks[0].horiz += s[port][slot].sticks[i].horiz; - if (abs(s[port][slot].sticks[i].vert) >= 100) - s[port][slot].sticks[0].vert += s[port][slot].sticks[i].vert; - } - } - - CapSum(&s[port][slot]); - if (lockStateChanged[port][slot]) { - if (lockStateChanged[port][slot] & LOCK_BOTH) { - if (pads[port][slot].lockedState != (LOCK_DIRECTION | LOCK_BUTTONS)) { - // Enable the one that's not enabled. - lockStateChanged[port][slot] ^= pads[port][slot].lockedState^(LOCK_DIRECTION | LOCK_BUTTONS); - } - else { - // Disable both - lockStateChanged[port][slot] ^= LOCK_DIRECTION | LOCK_BUTTONS; - } - } - if (lockStateChanged[port][slot] & LOCK_DIRECTION) { - if (pads[port][slot].lockedState & LOCK_DIRECTION) { - memset(pads[port][slot].lockedSum.sticks, 0, sizeof(pads[port][slot].lockedSum.sticks)); - } - else { - memcpy(pads[port][slot].lockedSum.sticks, s[port][slot].sticks, sizeof(pads[port][slot].lockedSum.sticks)); - } - pads[port][slot].lockedState ^= LOCK_DIRECTION; - } - if (lockStateChanged[port][slot] & LOCK_BUTTONS) { - if (pads[port][slot].lockedState & LOCK_BUTTONS) { - memset(pads[port][slot].lockedSum.buttons, 0, sizeof(pads[port][slot].lockedSum.buttons)); - } - else { - memcpy(pads[port][slot].lockedSum.buttons, s[port][slot].buttons, sizeof(pads[port][slot].lockedSum.buttons)); - } - pads[port][slot].lockedState ^= LOCK_BUTTONS; - } - for (i=0; i>1].sum = s[i&1][i>>1]; - } - - padReadKeyUpdated[0] = padReadKeyUpdated[1] = padReadKeyUpdated[2] = 1; - - if( stateUpdated[0] > 0 ) - --stateUpdated[0]; -} - -void CALLBACK PADupdate(int port) { - Update(port+3, 0); -} - -inline void SetVibrate(int port, int slot, int motor, u8 val) { - pads[port][slot].nextVibrate[motor] = val; -} - -u32 CALLBACK PS2EgetLibType(void) { - ps2e = 1; - return PS2E_LT_PAD; -} - -u32 CALLBACK PS2EgetLibVersion2(u32 type) { - ps2e = 1; - if (type == PS2E_LT_PAD) - return (PS2E_PAD_VERSION<<16) | VERSION; - return 0; -} - -char* CALLBACK PSEgetLibName() { -#if defined(PCSX2_DEBUG) - static char version[50]; - sprintf(version, "LilyPad Debug (%lld)", SVN_REV); - return version; -#else - static char version[50]; - sprintf(version, "LilyPad (%lld)", SVN_REV); - return version; -#endif -} - -char* CALLBACK PS2EgetLibName(void) { - ps2e = 1; - return PSEgetLibName(); -} - -//void CALLBACK PADgsDriverInfo(GSdriverInfo *info) { -// info=info; -//} - -void CALLBACK PADshutdown() { - DEBUG_TEXT_OUT("LilyPad shutdown.\n\n"); - for (int i=0; i<8; i++) - pads[i&1][i>>1].initialized = 0; - portInitialized[0] = portInitialized[1] = 0; - UnloadConfigs(); -} - -inline void StopVibrate() { - for (int i=0; i<8; i++) { - SetVibrate(i&1, i>>1, 0, 0); - SetVibrate(i&1, i>>1, 1, 0); - } -} - -inline void ResetVibrate(int port, int slot) { - SetVibrate(port, slot, 0, 0); - SetVibrate(port, slot, 1, 0); - ((int*)(pads[port][slot].vibrate))[0] = 0xFFFFFF5A; - ((int*)(pads[port][slot].vibrate))[1] = 0xFFFFFFFF; -} - -void ResetPad(int port, int slot) { - // Lines before memset currently don't do anything useful, - // but allow this function to be called at any time. - - // Need to backup, so can be called at any point. - u8 enabled = pads[port][slot].enabled; - - // Currently should never do anything. - SetVibrate(port, slot, 0, 0); - SetVibrate(port, slot, 1, 0); - - memset(&pads[port][slot], 0, sizeof(pads[0][0])); - pads[port][slot].mode = MODE_DIGITAL; - pads[port][slot].umask[0] = pads[port][slot].umask[1] = 0xFF; - // Sets up vibrate variable. - ResetVibrate(port, slot); - if (config.padConfigs[port][slot].autoAnalog && !ps2e) { - pads[port][slot].mode = MODE_ANALOG; - } - pads[port][slot].initialized = 1; - - pads[port][slot].enabled = enabled; -} - - -struct QueryInfo { - u8 port; - u8 slot; - u8 lastByte; - u8 currentCommand; - u8 numBytes; - u8 queryDone; - u8 response[42]; -} query = {0,0,0,0, 0,0xFF, 0xF3}; - -s32 CALLBACK PADinit(u32 flags) { - // Note: Won't load settings if already loaded. - if (LoadSettings() < 0) { - return -1; - } - int port = (flags & 3); - if (port == 3) { - if (PADinit(1) == -1) return -1; - return PADinit(2); - } - - #if defined(PCSX2_DEBUG) && defined(_MSC_VER) - int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG ); - tmpFlag |= _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF; - _CrtSetDbgFlag( tmpFlag ); - #endif - - port --; - - for (int i=0; i<4; i++) { - ResetPad(port, i); - } - slots[port] = 0; - portInitialized[port] = 1; - - query.lastByte = 1; - query.numBytes = 0; - ClearKeyQueue(); - R_ClearKeyQueue(); - // Just in case, when resuming emulation. - ReleaseModifierKeys(); - - DEBUG_TEXT_OUT("LilyPad initialized\n\n"); - return 0; -} - - - -// Note to self: Has to be a define for the sizeof() to work right. -// Note to self 2: All are the same size, anyways, except for longer full DS2 response -// and shorter digital mode response. -#define SET_RESULT(a) { \ - memcpy(query.response+2, a, sizeof(a)); \ - query.numBytes = 2+sizeof(a); \ -} - -#define SET_FINAL_RESULT(a) { \ - memcpy(query.response+2, a, sizeof(a));\ - query.numBytes = 2+sizeof(a); \ - query.queryDone = 1; \ -} - -static const u8 ConfigExit[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -//static const u8 ConfigExit[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00}; - -static const u8 noclue[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x00, 0x5A}; -static u8 queryMaskMode[7] = {0x5A, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x5A}; -//static const u8 DSNonNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; -static const u8 setMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - -// DS2 -static const u8 queryModelDS2[7] = {0x5A, 0x03, 0x02, 0x00, 0x02, 0x01, 0x00}; -// DS1 -static const u8 queryModelDS1[7] = {0x5A, 0x01, 0x02, 0x00, 0x02, 0x01, 0x00}; - -static const u8 queryAct[2][7] = {{0x5A, 0x00, 0x00, 0x01, 0x02, 0x00, 0x0A}, - {0x5A, 0x00, 0x00, 0x01, 0x01, 0x01, 0x14}}; - -static const u8 queryComb[7] = {0x5A, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00}; - -static const u8 queryMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - - -static const u8 setNativeMode[7] = {0x5A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5A}; - -void CALLBACK PADconfigure() { - if (openCount) { - return; - } - Configure(); -} - -s32 CALLBACK PADopen(void *pDsp) { - if (openCount++) return 0; - DEBUG_TEXT_OUT("LilyPad opened\n\n"); - - miceEnabled = !config.mouseUnfocus; - - 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)); - memset(&pads[port][slot].lockedSum, 0, sizeof(pads[port][slot].lockedSum)); - pads[port][slot].lockedState = 0; - } - } - - updateQueued = 0; - - GSdsp = *(Display**)pDsp; - GSwin = (Window)*(((uptr*)pDsp)+1); - - activeWindow = 1; - UpdateEnabledDevices(); - return 0; -} - -void CALLBACK PADclose() { - if (openCount && !--openCount) { - DEBUG_TEXT_OUT("LilyPad closed\n\n"); - updateQueued = 0; - ClearKeyQueue(); - R_ClearKeyQueue(); - } -} - -u8 CALLBACK PADstartPoll(int port) { - DEBUG_NEW_SET(); - port--; - if ((unsigned int)port <= 1 && pads[port][slots[port]].enabled) { - query.queryDone = 0; - query.port = port; - query.slot = slots[port]; - query.numBytes = 2; - query.lastByte = 0; - DEBUG_IN(port); - DEBUG_OUT(0xFF); - DEBUG_IN(slots[port]); - DEBUG_OUT(pads[port][slots[port]].enabled); - return 0xFF; - } - else { - query.queryDone = 1; - query.numBytes = 0; - query.lastByte = 1; - DEBUG_IN(0); - DEBUG_OUT(0); - DEBUG_IN(port); - DEBUG_OUT(0); - return 0; - } -} - -inline int IsDualshock2(u8 port, u8 slot) { - return config.padConfigs[query.port][query.slot].type == Dualshock2Pad || - (config.padConfigs[query.port][query.slot].type == GuitarPad && config.GH2); -} - -u8 CALLBACK PADpoll(u8 value) { - DEBUG_IN(value); - if (query.lastByte+1 >= query.numBytes) { - DEBUG_OUT(0); - return 0; - } - if (query.lastByte && query.queryDone) { - DEBUG_OUT(query.response[1+query.lastByte]); - return query.response[++query.lastByte]; - } - - int i; - Pad *pad = &pads[query.port][query.slot]; - if (query.lastByte == 0) { - query.lastByte++; - query.currentCommand = value; - switch(value) { - // CONFIG_MODE - case 0x43: - if (pad->config) { - // In config mode. Might not actually be leaving it. - SET_RESULT(ConfigExit); - DEBUG_OUT(0xF3); - return 0xF3; - } - // READ_DATA_AND_VIBRATE - case 0x42: - query.response[2] = 0x5A; - { - Update(query.port, query.slot); - ButtonSum *sum = &pad->sum; - - u8 b1 = 0xFF, b2 = 0xFF; - for (i = 0; i<4; i++) { - b1 -= (sum->buttons[i] > 0) << i; - } - for (i = 0; i<8; i++) { - b2 -= (sum->buttons[i+4] > 0) << i; - } - if (config.padConfigs[query.port][query.slot].type == GuitarPad && !config.GH2) { - sum->sticks[0].horiz = -255; - // Not sure about this. Forces wammy to be from 0 to 0x7F. - // if (sum->sticks[2].vert > 0) sum->sticks[2].vert = 0; - } - b1 -= ((sum->sticks[0].vert < 0) << 4); - b1 -= ((sum->sticks[0].horiz > 0) << 5); - b1 -= ((sum->sticks[0].vert > 0) << 6); - b1 -= ((sum->sticks[0].horiz < 0) << 7); - query.response[3] = b1; - query.response[4] = b2; - - query.numBytes = 5; - if (pad->mode != MODE_DIGITAL) { - query.response[5] = Cap((sum->sticks[1].horiz+255)/2); - query.response[6] = Cap((sum->sticks[1].vert+255)/2); - query.response[7] = Cap((sum->sticks[2].horiz+255)/2); - query.response[8] = Cap((sum->sticks[2].vert+255)/2); - - query.numBytes = 9; - if (pad->mode != MODE_ANALOG) { - // Good idea? No clue. - //query.response[3] &= pad->mask[0]; - //query.response[4] &= pad->mask[1]; - - // Each value is from -255 to 255, so have to use cap to convert - // negative values to 0. - query.response[9] = Cap(sum->sticks[0].horiz); - query.response[10] = Cap(-sum->sticks[0].horiz); - query.response[11] = Cap(-sum->sticks[0].vert); - query.response[12] = Cap(sum->sticks[0].vert); - - // No need to cap these, already done int CapSum(). - query.response[13] = (unsigned char) sum->buttons[8]; - query.response[14] = (unsigned char) sum->buttons[9]; - query.response[15] = (unsigned char) sum->buttons[10]; - query.response[16] = (unsigned char) sum->buttons[11]; - query.response[17] = (unsigned char) sum->buttons[6]; - query.response[18] = (unsigned char) sum->buttons[7]; - query.response[19] = (unsigned char) sum->buttons[4]; - query.response[20] = (unsigned char) sum->buttons[5]; - query.numBytes = 21; - } - } - } - - query.lastByte=1; - DEBUG_OUT(pad->mode); - return pad->mode; - // SET_VREF_PARAM - case 0x40: - SET_FINAL_RESULT(noclue); - break; - // QUERY_DS2_ANALOG_MODE - case 0x41: - // Right? Wrong? No clue. - if (pad->mode == MODE_DIGITAL) { - queryMaskMode[1] = queryMaskMode[2] = queryMaskMode[3] = 0; - queryMaskMode[6] = 0x00; - } - else { - queryMaskMode[1] = pad->umask[0]; - queryMaskMode[2] = pad->umask[1]; - queryMaskMode[3] = 0x03; - // Not entirely sure about this. - //queryMaskMode[3] = 0x01 | (pad->mode == MODE_DS2_NATIVE)*2; - queryMaskMode[6] = 0x5A; - } - SET_FINAL_RESULT(queryMaskMode); - break; - // SET_MODE_AND_LOCK - case 0x44: - SET_RESULT(setMode); - ResetVibrate(query.port, query.slot); - break; - // QUERY_MODEL_AND_MODE - case 0x45: - if (IsDualshock2(query.port, query.slot)) { - SET_FINAL_RESULT(queryModelDS2) - } - else { - SET_FINAL_RESULT(queryModelDS1); - } - // Not digital mode. - query.response[5] = (pad->mode & 0xF) != 1; - break; - // QUERY_ACT - case 0x46: - SET_RESULT(queryAct[0]); - break; - // QUERY_COMB - case 0x47: - SET_FINAL_RESULT(queryComb); - break; - // QUERY_MODE - case 0x4C: - SET_RESULT(queryMode); - break; - // VIBRATION_TOGGLE - case 0x4D: - memcpy(query.response+2, pad->vibrate, 7); - query.numBytes = 9; - ResetVibrate(query.port, query.slot); - break; - // SET_DS2_NATIVE_MODE - case 0x4F: - if (IsDualshock2(query.port, query.slot)) { - SET_RESULT(setNativeMode); - } - else { - SET_FINAL_RESULT(setNativeMode); - } - break; - default: - query.numBytes = 0; - query.queryDone = 1; - break; - } - DEBUG_OUT(0xF3); - return 0xF3; - } - else { - query.lastByte++; - switch (query.currentCommand) { - // READ_DATA_AND_VIBRATE - case 0x42: - if (query.lastByte == pad->vibrateI[0]) { - SetVibrate(query.port, query.slot, 1, 255*(0!=value)); - } - else if (query.lastByte == pad->vibrateI[1]) { - SetVibrate(query.port, query.slot, 0, value); - } - break; - // CONFIG_MODE - case 0x43: - if (query.lastByte == 3) { - query.queryDone = 1; - pad->config = value; - } - break; - // SET_MODE_AND_LOCK - case 0x44: - if (query.lastByte == 3 && value < 2) { - static const u8 modes[2] = {MODE_DIGITAL, MODE_ANALOG}; - pad->mode = modes[value]; - } - else if (query.lastByte == 4) { - if (value == 3) { - pad->modeLock = 3; - } - else { - pad->modeLock = 0; - if (pad->mode == MODE_DIGITAL && config.padConfigs[query.port][query.slot].autoAnalog && !ps2e) { - pad->mode = MODE_ANALOG; - } - } - query.queryDone = 1; - } - break; - // QUERY_ACT - case 0x46: - if (query.lastByte == 3) { - if (value<2) SET_RESULT(queryAct[value]) - // bunch of 0's - // else SET_RESULT(setMode); - query.queryDone = 1; - } - break; - // QUERY_MODE - case 0x4C: - if (query.lastByte == 3 && value<2) { - query.response[6] = 4+value*3; - query.queryDone = 1; - } - // bunch of 0's - //else data = setMode; - break; - // VIBRATION_TOGGLE - case 0x4D: - if (query.lastByte>=3) { - if (value == 0) { - pad->vibrateI[0] = (u8)query.lastByte; - } - else if (value == 1) { - pad->vibrateI[1] = (u8)query.lastByte; - } - pad->vibrate[query.lastByte-2] = value; - } - break; - // SET_DS2_NATIVE_MODE - case 0x4F: - if (query.lastByte == 3 || query.lastByte == 4) { - pad->umask[query.lastByte-3] = value; - } - else if (query.lastByte == 5) { - if (!(value & 1)) { - pad->mode = MODE_DIGITAL; - } - else if (!(value & 2)) { - pad->mode = MODE_ANALOG; - } - else { - pad->mode = MODE_DS2_NATIVE; - } - } - break; - default: - DEBUG_OUT(0); - return 0; - } - DEBUG_OUT(query.response[query.lastByte]); - return query.response[query.lastByte]; - } -} - -// returns: 1 if supports pad1 -// 2 if supports pad2 -// 3 if both are supported -u32 CALLBACK PADquery() { - return 3; -} - -void CALLBACK PADabout() { -} - -s32 CALLBACK PADtest() { - return 0; -} - -keyEvent* CALLBACK PADkeyEvent() { - // If running both pads, ignore every other call. So if two keys pressed in same interval... - static char eventCount = 0; - eventCount++; - if (eventCount < openCount) { - return 0; - } - eventCount = 0; - - Update(2, 0); - static keyEvent ev; - if (!GetQueuedKeyEvent(&ev)) return 0; - - return &ev; -} - -struct PadPluginFreezeData { - char format[8]; - // Currently all different versions are incompatible. - // May split into major/minor with some compatibility rules. - u32 version; - // So when loading, know which plugin's settings I'm loading. - // Not a big deal. Use a static variable when saving to figure it out. - u8 port; - // active slot for port - u8 slot[2]; - PadFreezeData padData[2][4]; - QueryInfo query; -}; - -s32 CALLBACK PADfreeze(int mode, freezeData *data) { - if (!data) - { - printf("LilyPad savestate null pointer!\n"); - return -1; - } - - if (mode == FREEZE_SIZE) { - data->size = sizeof(PadPluginFreezeData); - } - else if (mode == FREEZE_LOAD) { - PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); - StopVibrate(); - if (data->size != sizeof(PadPluginFreezeData) || - pdata.version != PAD_SAVE_STATE_VERSION || - strcmp(pdata.format, "PadMode")) return 0; - - if( pdata.port >= 2 ) return 0; - - query = pdata.query; - if (pdata.query.slot < 4) { - query = pdata.query; - } - - // Tales of the Abyss - pad fix - // - restore data for both ports - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - u8 mode = pdata.padData[port][slot].mode; - - if (mode != MODE_DIGITAL && mode != MODE_ANALOG && mode != MODE_DS2_NATIVE) { - break; - } - - // Not sure if the cast is strictly necessary, but feel safest with it there... - *(PadFreezeData*)&pads[port][slot] = pdata.padData[port][slot]; - } - - if (pdata.slot[port] < 4) - slots[port] = pdata.slot[port]; - } - } - else if (mode == FREEZE_SAVE) { - if (data->size != sizeof(PadPluginFreezeData)) return 0; - PadPluginFreezeData &pdata = *(PadPluginFreezeData*)(data->data); - - - // Tales of the Abyss - pad fix - // - PCSX2 only saves port0 (save #1), then port1 (save #2) - - memset(&pdata, 0, sizeof(pdata)); - strcpy(pdata.format, "PadMode"); - pdata.version = PAD_SAVE_STATE_VERSION; - pdata.port = 0; - pdata.query = query; - - for (int port=0; port<2; port++) { - for (int slot=0; slot<4; slot++) { - pdata.padData[port][slot] = pads[port][slot]; - } - - pdata.slot[port] = slots[port]; - } - } - else return -1; - return 0; -} - -u32 CALLBACK PADreadPort1 (PadDataS* pads) { - PADstartPoll(1); - PADpoll(0x42); - memcpy(pads, query.response+1, 7); - pads->controllerType = pads[0].controllerType>>4; - memset (pads+7, 0, sizeof(PadDataS)-7); - return 0; -} - -u32 CALLBACK PADreadPort2 (PadDataS* pads) { - PADstartPoll(2); - PADpoll(0x42); - memcpy(pads, query.response+1, 7); - pads->controllerType = pads->controllerType>>4; - memset (pads+7, 0, sizeof(PadDataS)-7); - return 0; -} - -u32 CALLBACK PSEgetLibType() { - return 8; -} - -u32 CALLBACK PSEgetLibVersion() { - return (VERSION & 0xFFFFFF); -} - -s32 CALLBACK PADqueryMtap(u8 port) { - port--; - if (port > 1) return 0; - return config.multitap[port]; -} - -s32 CALLBACK PADsetSlot(u8 port, u8 slot) { - port--; - slot--; - if (port > 1 || slot > 3) { - return 0; - } - // Even if no pad there, record the slot, as it is the active slot regardless. - slots[port] = slot; - // First slot always allowed. - // return pads[port][slot].enabled | !slot; - return 1; -} - -// Little funkiness to handle rounding floating points to ints without the C runtime. -// Unfortunately, means I can't use /GL optimization option when NO_CRT is defined. -#ifdef NO_CRT -extern "C" long _cdecl _ftol(); -extern "C" long _cdecl _ftol2_sse() { - return _ftol(); -} -extern "C" long _cdecl _ftol2() { - return _ftol(); -} -#endif - From 642371996ad5735ed76fba0ddb3685310ba6a0d0 Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Sun, 8 Feb 2015 15:08:26 +0100 Subject: [PATCH 17/18] lilypad: add joy support based on linux evdev I wanted to play with linux interface :) It is far from perfect but joysticks are detected. The biggest issue is the correct generation of the config file! --- plugins/LilyPad/CMakeLists.txt | 1 + plugins/LilyPad/DeviceEnumerator.cpp | 2 + plugins/LilyPad/InputManager.h | 1 + plugins/LilyPad/Linux/Config.cpp | 5 +- plugins/LilyPad/Linux/JoyEvdev.cpp | 208 +++++++++++++++++++++++++++ plugins/LilyPad/Linux/JoyEvdev.h | 77 ++++++++++ plugins/LilyPad/Linux/bitmaskros.h | 40 ++++++ 7 files changed, 332 insertions(+), 2 deletions(-) create mode 100644 plugins/LilyPad/Linux/JoyEvdev.cpp create mode 100644 plugins/LilyPad/Linux/JoyEvdev.h create mode 100644 plugins/LilyPad/Linux/bitmaskros.h diff --git a/plugins/LilyPad/CMakeLists.txt b/plugins/LilyPad/CMakeLists.txt index d34f7994e3..799ada8207 100644 --- a/plugins/LilyPad/CMakeLists.txt +++ b/plugins/LilyPad/CMakeLists.txt @@ -32,6 +32,7 @@ set(lilypadSources LilyPad.cpp Linux/Config.cpp Linux/ConfigHelper.cpp + Linux/JoyEvdev.cpp Linux/KeyboardMouse.cpp Linux/KeyboardQueue.cpp ) diff --git a/plugins/LilyPad/DeviceEnumerator.cpp b/plugins/LilyPad/DeviceEnumerator.cpp index c06088c3d8..a848095d3f 100644 --- a/plugins/LilyPad/DeviceEnumerator.cpp +++ b/plugins/LilyPad/DeviceEnumerator.cpp @@ -29,6 +29,7 @@ #ifdef __linux__ #include "Linux/KeyboardMouse.h" +#include "Linux/JoyEvdev.h" #endif void EnumDevices(int hideDXXinput) { @@ -46,6 +47,7 @@ void EnumDevices(int hideDXXinput) { EnumDirectInputDevices(hideDXXinput); #else EnumLnx(); + EnumJoystickEvdev(); #endif dm->CopyBindings(oldDm->numDevices, oldDm->devices); diff --git a/plugins/LilyPad/InputManager.h b/plugins/LilyPad/InputManager.h index 62e3111da9..f78ddf99ab 100644 --- a/plugins/LilyPad/InputManager.h +++ b/plugins/LilyPad/InputManager.h @@ -132,6 +132,7 @@ enum DeviceAPI { IGNORE_KEYBOARD = 7, // XXX LNX_KEYBOARD = 16, + LNX_JOY = 17, }; enum DeviceType { diff --git a/plugins/LilyPad/Linux/Config.cpp b/plugins/LilyPad/Linux/Config.cpp index f45a3468d3..eaf9a5e4f2 100644 --- a/plugins/LilyPad/Linux/Config.cpp +++ b/plugins/LilyPad/Linux/Config.cpp @@ -480,6 +480,8 @@ void RefreshEnabledDevices(int updateDeviceList) { 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) || @@ -488,7 +490,6 @@ void RefreshEnabledDevices(int updateDeviceList) { (dev->api == DS3 && config.gameApis.dualShock3) || (dev->api == XINPUT && config.gameApis.xInput)))) { dm->EnableDevice(i); -#if 0 // windows magic? if (config.gameApis.dualShock3 && dev->api == DI && dev->displayName && !wcsicmp(dev->displayName, L"DX PLAYSTATION(R)3 Controller")) { dm->DisableDevice(i); @@ -496,11 +497,11 @@ void RefreshEnabledDevices(int updateDeviceList) { else { dm->EnableDevice(i); } -#endif } else { dm->DisableDevice(i); } +#endif } } 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/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) From 8d76586deca1a9578ca1e7ceeef95811f4672d8e Mon Sep 17 00:00:00 2001 From: Gregory Hainaut Date: Fri, 20 Feb 2015 23:04:27 +0100 Subject: [PATCH 18/18] lilypad: fix windows build I think lilypad used libc instead of libc++ --- plugins/LilyPad/InputManager.cpp | 4 ++++ plugins/LilyPad/LilyPad.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/plugins/LilyPad/InputManager.cpp b/plugins/LilyPad/InputManager.cpp index 80fbe67766..ccf9ed4636 100644 --- a/plugins/LilyPad/InputManager.cpp +++ b/plugins/LilyPad/InputManager.cpp @@ -223,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/LilyPad.cpp b/plugins/LilyPad/LilyPad.cpp index f7d4046ff5..6fc998ef4d 100644 --- a/plugins/LilyPad/LilyPad.cpp +++ b/plugins/LilyPad/LilyPad.cpp @@ -421,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;