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 +