diff --git a/desmume/src/wx/InputCommon/Configuration.cpp b/desmume/src/wx/InputCommon/Configuration.cpp new file mode 100644 index 000000000..b3bfa2fba --- /dev/null +++ b/desmume/src/wx/InputCommon/Configuration.cpp @@ -0,0 +1,278 @@ + +// Project description +// ------------------- +// Name: Input Configuration and Calibration +// Description: Common SDL Input Functions +// +// Author: Falcon4ever (nJoy@falcon4ever.com, www.multigesture.net), JPeterson etc +// Copyright (C) 2003 Dolphin Project. +// + +// +// Licensetype: GNU General Public License (GPL) +// +// 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, version 2.0. +// +// 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 2.0 for more details. +// +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ +// +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// + + + + + +// Include +// ------------------- +#if defined HAVE_WX && HAVE_WX + #include +#endif + +#ifdef _WIN32 +#define NOMINMAX +#include +#endif + +#include "SDL.h" // Local + + + +namespace InputCommon +{ + + + +// Degree to radian and back +// ------------- +float Deg2Rad(float Deg) +{ + return Deg * (float)M_PI / 180.0f; +} +float Rad2Deg(float Rad) +{ + return Rad * 180.0f / (float)M_PI; +} + +// Check if the pad is within the dead zone, we assume the range is 0x8000 +// ---------------- +float CoordinatesToRadius(int x, int y) +{ + return sqrt(pow((float)x, 2) + pow((float)y, 2)); +} + +bool IsDeadZone(float DeadZone, int x, int y) +{ + // Get the distance from the center + float Distance = CoordinatesToRadius(x, y) / 32767.0f; + + //Console::Print("%f\n", Distance); + + // Check if it's within the dead zone + if (Distance <= DeadZone) + return true; + else + return false; +} + + + + +// Scale down stick values from 0x8000 to 0x80 +/* ---------------- + The value returned by SDL_JoystickGetAxis is a signed integer s16 + (-32768 to 32767). The value used for the gamecube controller is an unsigned + char u8 (0 to 255) with neutral at 0x80 (128), so that it's equivalent to a signed + -128 to 127. +*/ +// --------------------- +int Pad_Convert(int _val) +{ + /* If the limits on PadState[].axis[] actually is a u16 then we don't need this + but if it's not actually limited to that we need to apply these limits */ + if(_val > 32767) _val = 32767; // upper limit + if(_val < -32768) _val = -32768; // lower limit + + // Convert the range (-0x8000 to 0x7fff) to (0 to 0xffff) + _val = 0x8000 +_val; + + // Convert the range (0 to 0xffff) to (0 to 0xff) + _val = _val >> 8; + + //Console::Print("0x%04x %06i\n\n", _val, _val); + + return _val; +} + + + + +// Adjust the radius +// ---------------- +void RadiusAdjustment(int &_x, int &_y, int _pad, std::string SRadius) +{ + // Get the radius setting + int Tmp = atoi (SRadius.substr(0, SRadius.length() - 1).c_str()); + float RadiusSetting = Tmp / 100.0f; + + // Get current angle + float Deg = Rad2Deg(atan2((float)_y, (float)_x)); + // Get the current radius + float Radius = sqrt((float)(_x*_x + _y*_y)); + // Adjust radius + Radius = Radius * RadiusSetting; + // Produce new coordinates + float x = Radius * cos(Deg2Rad(Deg)); + float y = Radius * sin(Deg2Rad(Deg)); + // Update values + _x = (int)x, _y = (int)y; +} + + + + +/* Convert the stick raidus from a square or rounded box to a circular radius. I don't know what + input values the actual GC controller produce for the GC, it may be a square, a circle or + something in between. But one thing that is certain is that PC pads differ in their output + (as shown in the list below), so it may be beneficiary to convert whatever radius they + produce to the radius the GC games expect. This is the first implementation of this + that convert a square radius to a circual radius. Use the advanced settings to enable + and calibrate it. + + Observed diagonals: + Perfect circle: 71% = sin(45) + Logitech Dual Action: 100% + PS2 Dual Shock 2 (Original) with Super Dual Box Pro: 90% + XBox 360 Wireless: 85% + GameCube Controller (Third Party) with EMS Trio Linker Plus II: 60% +*/ +// --------------------- + +/* Calculate the distance from the outer edges of the box to the outer edges of the circle inside the box + at any angle from 0 to 360. The returned value is 1 + Distance, for example at most sqrt(2) in the + corners and at least 1.0 at the horizontal and vertical angles. */ +float Square2CircleDistance(float deg) +{ + // See if we have to adjust the angle + deg = abs(deg); + if( (deg > 45 && deg < 135) ) deg = deg - 90; + + // Calculate distance from center + float val = abs(cos(Deg2Rad(deg))); + float Distance = 1 / val; + + //m_frame->m_pStatusBar2->SetLabel(wxString::Format("Deg:%f Val:%f Dist:%f", deg, val, dist)); + + return Distance; +} +// Produce a perfect circle from an original square or rounded box +void Square2Circle(int &_x, int &_y, int _pad, std::string SDiagonal, bool Circle2Square) +{ + // Do we need to do this check? + if(_x > 32767) _x = 32767; if(_y > 32767) _y = 32767; // upper limit + if(_x < -32768) _x = -32768; if(_y < -32768) _y = -32768; // lower limit + + // ==================================== + // Convert to circle + // ----------- + // Get the manually configured diagonal distance + int Tmp = atoi (SDiagonal.substr(0, SDiagonal.length() - 1).c_str()); + float Diagonal = Tmp / 100.0f; + + // First make a perfect square in case we don't have one already + float OrigDist = sqrt( pow((float)_y, 2) + pow((float)_x, 2) ); // Get current distance + float deg = Rad2Deg(atan2((float)_y, (float)_x)); // Get current angle + + /* Calculate the actual distance between the maxium diagonal values, and the outer edges of the + square. A diagonal of 85% means a maximum distance of 0.85 * sqrt(2) ~1.2 in the diagonals. */ + float corner_circle_dist = ( Diagonal / sin(Deg2Rad(45.0f)) ); + float SquareDist = Square2CircleDistance(deg); + // The original-to-square distance adjustment + float adj_ratio1; + // The circle-to-square distance adjustment + float adj_ratio2 = SquareDist; + // The resulting distance + float result_dist; + + // Calculate the corner-to-square adjustment ratio + if(corner_circle_dist < SquareDist) adj_ratio1 = SquareDist / corner_circle_dist; + else adj_ratio1 = 1; + + // Calculate the resulting distance + if(Circle2Square) + result_dist = OrigDist * adj_ratio1; + else + result_dist = OrigDist * adj_ratio1 / adj_ratio2; + + // Calculate x and y and return it + float x = result_dist * cos(Deg2Rad(deg)); + float y = result_dist * sin(Deg2Rad(deg)); + // Update coordinates + _x = (int)floor(x); + _y = (int)floor(y); + // Boundaries + if (_x < -32768) _x = -32768; if (_x > 32767) _x = 32767; + if (_y < -32768) _y = -32768; if (_y > 32767) _y = 32767; + + // Debugging + //Console::Print("%f %f %i", corner_circle_dist, Diagonal, Tmp)); +} + + + + +// Windows Virtual Key Codes Names +// --------------------- +#ifdef _WIN32 +std::string VKToString(int keycode) +{ +#ifdef _WIN32 + // Default value + char KeyStr[64] = {0}; + std::string KeyString; + + // TODO: Switch to unicode GetKeyNameText? + if (keycode < 256) // Keyboard + GetKeyNameTextA(MapVirtualKey(keycode, MAPVK_VK_TO_VSC) << 16, KeyStr, 64); + else // Pad + sprintf(KeyStr, "PAD: %d", keycode - 0x1000); + + switch(keycode) + { + // Give it some help with a few keys + case VK_END: return "END"; + case VK_INSERT: return "INS"; + case VK_DELETE: return "DEL"; + case VK_PRIOR: return "PGUP"; + case VK_NEXT: return "PGDN"; + + case VK_UP: return "UP"; + case VK_DOWN: return "DOWN"; + case VK_LEFT: return "LEFT"; + case VK_RIGHT: return "RIGHT"; + + case VK_LSHIFT: return "LEFT SHIFT"; + case VK_LCONTROL: return "LEFT CTRL"; + case VK_RCONTROL: return "RIGHT CTRL"; + case VK_LMENU: return "LEFT ALT"; + + default: return KeyString = KeyStr; + } +#else + // An equivalent name translation can probably be used on other systems to? + return ""; +#endif +} +#endif + + +} + diff --git a/desmume/src/wx/InputCommon/DirectInputBase.cpp b/desmume/src/wx/InputCommon/DirectInputBase.cpp new file mode 100644 index 000000000..3e2c67b84 --- /dev/null +++ b/desmume/src/wx/InputCommon/DirectInputBase.cpp @@ -0,0 +1,195 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + + +// Include +// ------------------- +#include "DirectInputBase.h" + + + +DInput::DInput() + : g_pDI(NULL), + g_pKeyboard(NULL) +{} + + +DInput::~DInput() +{ + Free(); +} + +void DInput::DIKToString(unsigned int keycode, char *keyStr) +{ + switch(keycode) { + case DIK_RETURN: + sprintf(keyStr, "Enter"); + break; + case DIK_UP: + sprintf(keyStr, "Up"); + break; + case DIK_DOWN: + sprintf(keyStr, "Down"); + break; + case DIK_LEFT: + sprintf(keyStr, "Left"); + break; + case DIK_RIGHT: + sprintf(keyStr, "Right"); + break; + case DIK_HOME: + strcpy(keyStr, "Home"); + break; + case DIK_END: + strcpy(keyStr, "End"); + break; + case DIK_INSERT: + strcpy(keyStr, "Ins"); + break; + case DIK_DELETE: + strcpy(keyStr, "Del"); + break; + case DIK_PGUP: + strcpy(keyStr, "PgUp"); + break; + case DIK_PGDN: + strcpy(keyStr, "PgDn"); + break; + case DIK_NUMPAD0: + strcpy(keyStr, "Num 0"); + break; + case DIK_NUMPAD1: + strcpy(keyStr, "Num 1"); + break; + case DIK_NUMPAD2: + strcpy(keyStr, "Num 2"); + break; + case DIK_NUMPAD3: + strcpy(keyStr, "Num 3"); + break; + case DIK_NUMPAD4: + strcpy(keyStr, "Num 4"); + break; + case DIK_NUMPAD5: + strcpy(keyStr, "Num 5"); + break; + case DIK_NUMPAD6: + strcpy(keyStr, "Num 6"); + break; + case DIK_NUMPAD7: + strcpy(keyStr, "Num 7"); + break; + case DIK_NUMPAD8: + strcpy(keyStr, "Num 8"); + break; + case DIK_NUMPAD9: + strcpy(keyStr, "Num 9"); + break; + case DIK_NUMPADSLASH: + strcpy(keyStr, "Num /"); + break; + default: + // TODO: Switch to unicode GetKeyNameText? + GetKeyNameTextA(keycode << 16, keyStr, 64); + break; + } +} + +HRESULT DInput::Init(HWND hWnd) +{ + HRESULT hr; + DWORD dwCoopFlags; + dwCoopFlags = DISCL_FOREGROUND | DISCL_NOWINKEY; + + // Create a DInput object + if (FAILED(hr = DirectInput8Create(GetModuleHandle(NULL), DIRECTINPUT_VERSION, + IID_IDirectInput8, (VOID* *)&g_pDI, NULL))) + { + MessageBox(0, "Direct Input Create Failed", 0, MB_ICONERROR); + return(hr); + } + + if (FAILED(hr = g_pDI->CreateDevice(GUID_SysKeyboard, &g_pKeyboard, NULL))) + { + MessageBox(0, "Couldn't access keyboard", 0, MB_ICONERROR); + Free(); + return(hr); + } + + g_pKeyboard->SetDataFormat(&c_dfDIKeyboard); + g_pKeyboard->SetCooperativeLevel(hWnd, dwCoopFlags); + g_pKeyboard->Acquire(); + + return(S_OK); +} + +void DInput::Free() +{ + if (g_pKeyboard) + { + g_pKeyboard->Unacquire(); + g_pKeyboard->Release(); + g_pKeyboard = 0; + } + + if (g_pDI) + { + g_pDI->Release(); + g_pDI = 0; + } +} + +// Desc: Read the input device's state when in immediate mode and display it. +HRESULT DInput::Read() +{ + HRESULT hr; + + if (NULL == g_pKeyboard) + { + return(S_OK); + } + + // Get the input's device state, and put the state in dims + ZeroMemory(diks, sizeof(diks)); + hr = g_pKeyboard->GetDeviceState(sizeof(diks), diks); + +// for (int i=0; i<256; i++) +// if (diks[i]) MessageBox(0,"DSFJDKSF|",0,0); + if (FAILED(hr)) + { + // DirectInput may be telling us that the input stream has been + // interrupted. We aren't tracking any state between polls, so + // we don't have any special reset that needs to be done. + // We just re-acquire and try again. + + // If input is lost then acquire and keep trying + hr = g_pKeyboard->Acquire(); + + while (hr == DIERR_INPUTLOST) + { + hr = g_pKeyboard->Acquire(); + } + + // hr may be DIERR_OTHERAPPHASPRIO or other errors. This + // may occur when the app is minimized or in the process of + // switching, so just try again later + return(S_OK); + } + + return(S_OK); +} diff --git a/desmume/src/wx/InputCommon/DirectInputBase.h b/desmume/src/wx/InputCommon/DirectInputBase.h new file mode 100644 index 000000000..770fa258d --- /dev/null +++ b/desmume/src/wx/InputCommon/DirectInputBase.h @@ -0,0 +1,58 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef _DIRECTINPUTBASE_H +#define _DIRECTINPUTBASE_H + + + +// Include +// ------------------- +#include // System +#include + +#define DIRECTINPUT_VERSION 0x0800 // DirectInput +#include + +//#include "ConsoleWindow.h" // Common + + + +class DInput +{ + public: + + DInput(); + ~DInput(); + + static void DInput::DIKToString(unsigned int keycode, char *keyStr); + + HRESULT Init(HWND hWnd); + void Free(); + HRESULT Read(); + + + BYTE diks[256]; // DirectInput keyboard state buffer + + private: + + LPDIRECTINPUT8 g_pDI; // The DirectInput object + LPDIRECTINPUTDEVICE8 g_pKeyboard; // The keyboard device +}; + +#endif + diff --git a/desmume/src/wx/InputCommon/Event.hpp b/desmume/src/wx/InputCommon/Event.hpp new file mode 100644 index 000000000..f9a028c57 --- /dev/null +++ b/desmume/src/wx/InputCommon/Event.hpp @@ -0,0 +1,264 @@ +// SFML - Simple and Fast Multimedia Library +// Copyright (C) 2007-2008 Laurent Gomila (laurent.gom@gmail.com) +// +// This software is provided 'as-is', without any express or implied warranty. +// In no event will the authors be held liable for any damages arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it freely, +// subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; +// you must not claim that you wrote the original software. +// If you use this software in a product, an acknowledgment +// in the product documentation would be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, +// and must not be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source distribution. + +#ifndef SFML_EVENT_HPP +#define SFML_EVENT_HPP + + +namespace sf +{ + namespace Key + { + enum Code + { + A = 'a', + B = 'b', + C = 'c', + D = 'd', + E = 'e', + F = 'f', + G = 'g', + H = 'h', + I = 'i', + J = 'j', + K = 'k', + L = 'l', + M = 'm', + N = 'n', + O = 'o', + P = 'p', + Q = 'q', + R = 'r', + S = 's', + T = 't', + U = 'u', + V = 'v', + W = 'w', + X = 'x', + Y = 'y', + Z = 'z', + Num0 = '0', + Num1 = '1', + Num2 = '2', + Num3 = '3', + Num4 = '4', + Num5 = '5', + Num6 = '6', + Num7 = '7', + Num8 = '8', + Num9 = '9', + Escape = 256, + LControl, + LShift, + LAlt, + LSystem, + RControl, + RShift, + RAlt, + RSystem, + Menu, + LBracket, + RBracket, + SemiColon, + Comma, + Period, + Quote, + Slash, + BackSlash, + Tilde, + Equal, + Dash, + Space, + Return, + Back, + Tab, + PageUp, + PageDown, + End, + Home, + Insert, + Delete, + Add, + Subtract, + Multiply, + Divide, + Left, + Right, + Up, + Down, + Numpad0, + Numpad1, + Numpad2, + Numpad3, + Numpad4, + Numpad5, + Numpad6, + Numpad7, + Numpad8, + Numpad9, + F1, + F2, + F3, + F4, + F5, + F6, + F7, + F8, + F9, + F10, + F11, + F12, + F13, + F14, + F15, + Pause, + + Count // For internal use + }; + } + + + namespace Mouse + { + enum Button + { + Left, + Right, + Middle, + XButton1, + XButton2, + + Count // For internal use + }; + } + + + namespace Joy + { + enum Axis + { + AxisX, + AxisY, + AxisZ, + AxisR, + AxisU, + AxisV, + AxisPOV, + + Count // For internal use + }; + } + + + class Event + { + public : + + struct KeyEvent + { + Key::Code Code; + bool Alt; + bool Control; + bool Shift; + }; + + struct TextEvent + { + // I'm not sure we need this... + unsigned short Unicode; + }; + + struct MouseMoveEvent + { + int X; + int Y; + }; + + struct MouseButtonEvent + { + Mouse::Button Button; + int X; + int Y; + }; + + struct MouseWheelEvent + { + int Delta; + }; + + struct JoyMoveEvent + { + unsigned int JoystickId; + Joy::Axis Axis; + float Position; + }; + + struct JoyButtonEvent + { + unsigned int JoystickId; + unsigned int Button; + }; + + struct SizeEvent + { + unsigned int Width; + unsigned int Height; + }; + + enum EventType + { + Closed, + Resized, + LostFocus, + GainedFocus, + TextEntered, + KeyPressed, + KeyReleased, + MouseWheelMoved, + MouseButtonPressed, + MouseButtonReleased, + MouseMoved, + MouseEntered, + MouseLeft, + JoyButtonPressed, + JoyButtonReleased, + JoyMoved + }; + + // Member data + EventType Type; + + union + { + KeyEvent Key; + TextEvent Text; + MouseMoveEvent MouseMove; + MouseButtonEvent MouseButton; + MouseWheelEvent MouseWheel; + JoyMoveEvent JoyMove; + JoyButtonEvent JoyButton; + SizeEvent Size; + }; + }; + +} // namespace sf + + +#endif // SFML_EVENT_HPP diff --git a/desmume/src/wx/InputCommon/EventHandler.cpp b/desmume/src/wx/InputCommon/EventHandler.cpp new file mode 100644 index 000000000..f2b210322 --- /dev/null +++ b/desmume/src/wx/InputCommon/EventHandler.cpp @@ -0,0 +1,316 @@ +#include "EventHandler.h" +#include +#include + +#if defined HAVE_WX && HAVE_WX +#include +#endif + +EventHandler *EventHandler::m_Instance = 0; + +EventHandler::EventHandler() { + memset(keys, 0, sizeof(keys)); + memset(mouse, 0, sizeof(mouse)); + memset(joys, 0, sizeof(joys)); +} + +EventHandler::~EventHandler() { +} + +EventHandler *EventHandler::GetInstance() { + // fprintf(stderr, "handler instance %p\n", m_Instance); + + if (! m_Instance) + m_Instance = new EventHandler(); + return m_Instance; +} + +void EventHandler::Destroy() { + if (m_Instance) + delete m_Instance; + // fprintf(stderr, "deleting instance %p\n", m_Instance); + m_Instance = 0; +} + +bool EventHandler::RegisterEventListener(listenFuncPtr func, Keys key) { + if (key.inputType == KeyboardInput) { + // fprintf(stderr, "Registering %d:%d %p %p \n", key.keyCode, key.mods, func, this); + if (key.keyCode == sf::Key::Count || key.mods >= NUMMODS || + key.keyCode >= NUMKEYS) + return false; + if (keys[key.keyCode][key.mods] && keys[key.keyCode][key.mods] != func) + return false +; + keys[key.keyCode][key.mods] = func; + } else if (key.inputType == MouseInput) { + if (mouse[key.mouseButton]) + return false; + mouse[key.mouseButton] = func; + } + + return true; +} + +bool EventHandler::RemoveEventListener(Keys key) { + if (key.inputType == KeyboardInput) { + if ((key.keyCode == sf::Key::Count || key.keyCode >= NUMKEYS + || key.mods >= NUMMODS) && ! keys[key.keyCode][key.mods]) + return false; + keys[key.keyCode][key.mods] = NULL; + } else if (key.inputType == MouseInput) { + if (! mouse[key.mouseButton]) + return false; + mouse[key.mouseButton] = NULL; + } + + return true; +} + +void EventHandler::Update() { + for (unsigned int i = 0; i < eventQueue.size();i++) { + sf::Event ev = eventQueue.front(); + eventQueue.pop(); + fprintf(stderr, "Updating event type %d code %d mod %d func %p %p\n", ev.Type, ev.Key.Code, ev.Key.Alt+2*ev.Key.Shift+4*ev.Key.Control, keys[ev.Key.Code][ev.Key.Alt+2*ev.Key.Shift+4*ev.Key.Control], this); + if(keys[ev.Key.Code][ev.Key.Alt+2*ev.Key.Shift+4*ev.Key.Control]) + keys[ev.Key.Code][ev.Key.Alt+2*ev.Key.Shift+4*ev.Key.Control](ev); + } +} + +bool EventHandler::addEvent(sf::Event *ev) { + eventQueue.push(*ev); + fprintf(stderr, "Got event type %d code %d %p\n", ev->Type, ev->Key.Code, this); + return true; +} + + +bool EventHandler::TestEvent (Keys k, sf::Event e) +{ + //Mouse event + if (k.inputType==MouseInput && k.eventType==e.Type && k.mouseButton==e.MouseButton.Button) + { + return (true); + } + //Keyboard event + if (k.inputType==KeyboardInput && k.eventType==e.Type && k.keyCode==e.Key.Code) + { + return (true); + } + return (false); +} + +#if defined HAVE_WX && HAVE_WX +// Taken from wxw source code +sf::Key::Code EventHandler::wxCharCodeToSF(int id) +{ + sf::Key::Code sfKey; + + switch (id) { +// case WXK_CANCEL: sfKey = sf::Key::Cancel; break; +// case WXK_BACK: sfKey = sf::Key::BackSpace; break; + case WXK_TAB: sfKey = sf::Key::Tab; break; +// case WXK_CLEAR: sfKey = sf::Key::Clear; break; + case WXK_RETURN: sfKey = sf::Key::Return; break; + case WXK_SHIFT: sfKey = sf::Key::LShift; break; + case WXK_CONTROL: sfKey = sf::Key::LControl; break; + case WXK_ALT: sfKey = sf::Key::LAlt; break; +// case WXK_CAPITAL: sfKey = sf::Key::Caps_Lock; break; + case WXK_MENU : sfKey = sf::Key::Menu; break; + case WXK_PAUSE: sfKey = sf::Key::Pause; break; + case WXK_ESCAPE: sfKey = sf::Key::Escape; break; + case WXK_SPACE: sfKey = sf::Key::Space; break; + case WXK_PAGEUP: sfKey = sf::Key::PageUp; break; + case WXK_PAGEDOWN: sfKey = sf::Key::PageDown; break; + case WXK_END: sfKey = sf::Key::End; break; + case WXK_HOME : sfKey = sf::Key::Home; break; + case WXK_LEFT : sfKey = sf::Key::Left; break; + case WXK_UP: sfKey = sf::Key::Up; break; + case WXK_RIGHT: sfKey = sf::Key::Right; break; + case WXK_DOWN : sfKey = sf::Key::Down; break; +// case WXK_SELECT: sfKey = sf::Key::Select; break; +// case WXK_PRINT: sfKey = sf::Key::Print; break; +// case WXK_EXECUTE: sfKey = sf::Key::Execute; break; + case WXK_INSERT: sfKey = sf::Key::Insert; break; + case WXK_DELETE: sfKey = sf::Key::Delete; break; +// case WXK_HELP : sfKey = sf::Key::Help; break; + case WXK_NUMPAD0: sfKey = sf::Key::Numpad0; break; + case WXK_NUMPAD_INSERT: sfKey = sf::Key::Insert; break; + case WXK_NUMPAD1: sfKey = sf::Key::Numpad1; break; + case WXK_NUMPAD_END: sfKey = sf::Key::End; break; + case WXK_NUMPAD2: sfKey = sf::Key::Numpad2; break; + case WXK_NUMPAD_DOWN: sfKey = sf::Key::Down; break; + case WXK_NUMPAD3: sfKey = sf::Key::Numpad3; break; + case WXK_NUMPAD_PAGEDOWN: sfKey = sf::Key::PageDown; break; + case WXK_NUMPAD4: sfKey = sf::Key::Numpad4; break; + case WXK_NUMPAD_LEFT: sfKey = sf::Key::Left; break; + case WXK_NUMPAD5: sfKey = sf::Key::Numpad5; break; + case WXK_NUMPAD6: sfKey = sf::Key::Numpad6; break; + case WXK_NUMPAD_RIGHT: sfKey = sf::Key::Right; break; + case WXK_NUMPAD7: sfKey = sf::Key::Numpad7; break; + case WXK_NUMPAD_HOME: sfKey = sf::Key::Home; break; + case WXK_NUMPAD8: sfKey = sf::Key::Numpad8; break; + case WXK_NUMPAD_UP: sfKey = sf::Key::Up; break; + case WXK_NUMPAD9: sfKey = sf::Key::Numpad9; break; + case WXK_NUMPAD_PAGEUP: sfKey = sf::Key::PageUp; break; +// case WXK_NUMPAD_DECIMAL: sfKey = sf::Key::Decimal; break; + case WXK_NUMPAD_DELETE: sfKey = sf::Key::Delete; break; + case WXK_NUMPAD_MULTIPLY: sfKey = sf::Key::Multiply; break; + case WXK_NUMPAD_ADD: sfKey = sf::Key::Add; break; + case WXK_NUMPAD_SUBTRACT: sfKey = sf::Key::Subtract; break; + case WXK_NUMPAD_DIVIDE: sfKey = sf::Key::Divide; break; + case WXK_NUMPAD_ENTER: sfKey = sf::Key::Return; break; +// case WXK_NUMPAD_SEPARATOR:sfKey = sf::Key::Separator; break; + case WXK_F1: sfKey = sf::Key::F1; break; + case WXK_F2: sfKey = sf::Key::F2; break; + case WXK_F3: sfKey = sf::Key::F3; break; + case WXK_F4: sfKey = sf::Key::F4; break; + case WXK_F5: sfKey = sf::Key::F5; break; + case WXK_F6: sfKey = sf::Key::F6; break; + case WXK_F7: sfKey = sf::Key::F7; break; + case WXK_F8: sfKey = sf::Key::F8; break; + case WXK_F9: sfKey = sf::Key::F9; break; + case WXK_F10: sfKey = sf::Key::F10; break; + case WXK_F11: sfKey = sf::Key::F11; break; + case WXK_F12: sfKey = sf::Key::F12; break; + case WXK_F13: sfKey = sf::Key::F13; break; + case WXK_F14: sfKey = sf::Key::F14; break; + case WXK_F15: sfKey = sf::Key::F15; break; +// case WXK_NUMLOCK: sfKey = sf::Key::Num_Lock; break; +// case WXK_SCROLL: sfKey = sf::Key::Scroll_Lock; break; + default: + + // To lower (will tolower work on windows?) + if (id >= 'A' && id <= 'Z') + id = id - 'A' + 'a'; + + if ((id >= 'a' && id <= 'z') || + (id >= '0' && id <= '9')) + sfKey = (sf::Key::Code)id; + else + sfKey = sf::Key::Count; // Invalid key + + } + + return sfKey; +} +#endif + +void EventHandler::SFKeyToString(sf::Key::Code keycode, char *keyStr) { + switch (keycode) { +/* case sf::Key::A = 'a': sprintf(keyStr, "UP"); break; + case sf::Key::B = 'b': sprintf(keyStr, "UP"); break; + case sf::Key::C = 'c': sprintf(keyStr, "UP"); break; + case sf::Key::D = 'd': sprintf(keyStr, "UP"); break; + case sf::Key::E = 'e': sprintf(keyStr, "UP"); break; + case sf::Key::F = 'f': sprintf(keyStr, "UP"); break; + case sf::Key::G = 'g': sprintf(keyStr, "UP"); break; + case sf::Key::H = 'h': sprintf(keyStr, "UP"); break; + case sf::Key::I = 'i': sprintf(keyStr, "UP"); break; + case sf::Key::J = 'j': sprintf(keyStr, "UP"); break; + case sf::Key::K = 'k': sprintf(keyStr, "UP"); break; + case sf::Key::L = 'l': sprintf(keyStr, "UP"); break; + case sf::Key::M = 'm': sprintf(keyStr, "UP"); break; + case sf::Key::N = 'n': sprintf(keyStr, "UP"); break; + case sf::Key::O = 'o': sprintf(keyStr, "UP"); break; + case sf::Key::P = 'p': sprintf(keyStr, "UP"); break; + case sf::Key::Q = 'q': sprintf(keyStr, "UP"); break; + case sf::Key::R = 'r': sprintf(keyStr, "UP"); break; + case sf::Key::S = 's': sprintf(keyStr, "UP"); break; + case sf::Key::T = 't': sprintf(keyStr, "UP"); break; + case sf::Key::U = 'u': sprintf(keyStr, "UP"); break; + case sf::Key::V = 'v': sprintf(keyStr, "UP"); break; + case sf::Key::W = 'w': sprintf(keyStr, "UP"); break; + case sf::Key::X = 'x': sprintf(keyStr, "UP"); break; + case sf::Key::Y = 'y': sprintf(keyStr, "UP"); break; + case sf::Key::Z = 'z': sprintf(keyStr, "UP"); break; + case sf::Key::Num0 = '0': sprintf(keyStr, "UP"); break; + case sf::Key::Num1 = '1': sprintf(keyStr, "UP"); break; + case sf::Key::Num2 = '2': sprintf(keyStr, "UP"); break; + case sf::Key::Num3 = '3': sprintf(keyStr, "UP"); break; + case sf::Key::Num4 = '4': sprintf(keyStr, "UP"); break; + case sf::Key::Num5 = '5': sprintf(keyStr, "UP"); break; + case sf::Key::Num6 = '6': sprintf(keyStr, "UP"); break; + case sf::Key::Num7 = '7': sprintf(keyStr, "UP"); break; + case sf::Key::Num8 = '8': sprintf(keyStr, "UP"); break; + case sf::Key::Num9 = '9': sprintf(keyStr, "UP"); break;*/ + case sf::Key::Escape: sprintf(keyStr, "Escape"); break; + case sf::Key::LControl: sprintf(keyStr, "LControl"); break; + case sf::Key::LShift: sprintf(keyStr, "LShift"); break; + case sf::Key::LAlt: sprintf(keyStr, "LAlt"); break; + case sf::Key::LSystem: sprintf(keyStr, "LSystem"); break; + case sf::Key::RControl: sprintf(keyStr, "RControl"); break; + case sf::Key::RShift: sprintf(keyStr, "RShift"); break; + case sf::Key::RAlt: sprintf(keyStr, "RAlt"); break; + case sf::Key::RSystem: sprintf(keyStr, "RSystem"); break; + case sf::Key::Menu: sprintf(keyStr, "Menu"); break; + case sf::Key::LBracket: sprintf(keyStr, "LBracket"); break; + case sf::Key::RBracket: sprintf(keyStr, "RBracket"); break; + case sf::Key::SemiColon: sprintf(keyStr, ";"); break; + case sf::Key::Comma: sprintf(keyStr, ","); break; + case sf::Key::Period: sprintf(keyStr, "."); break; + case sf::Key::Quote: sprintf(keyStr, "\'"); break; + case sf::Key::Slash: sprintf(keyStr, "/"); break; + case sf::Key::BackSlash: sprintf(keyStr, "\\"); break; + case sf::Key::Tilde: sprintf(keyStr, "~"); break; + case sf::Key::Equal: sprintf(keyStr, "="); break; + case sf::Key::Dash: sprintf(keyStr, "-"); break; + case sf::Key::Space: sprintf(keyStr, "Space"); break; + case sf::Key::Return: sprintf(keyStr, "Return"); break; + case sf::Key::Back: sprintf(keyStr, "Back"); break; + case sf::Key::Tab: sprintf(keyStr, "Tab"); break; + case sf::Key::PageUp: sprintf(keyStr, "Page Up"); break; + case sf::Key::PageDown: sprintf(keyStr, "Page Down"); break; + case sf::Key::End: sprintf(keyStr, "End"); break; + case sf::Key::Home: sprintf(keyStr, "Home"); break; + case sf::Key::Insert: sprintf(keyStr, "Insert"); break; + case sf::Key::Delete: sprintf(keyStr, "Delete"); break; + case sf::Key::Add: sprintf(keyStr, "+"); break; + case sf::Key::Subtract: sprintf(keyStr, "-"); break; + case sf::Key::Multiply: sprintf(keyStr, "*"); break; + case sf::Key::Divide: sprintf(keyStr, "/"); break; + case sf::Key::Left: sprintf(keyStr, "Left"); break; + case sf::Key::Right: sprintf(keyStr, "Right"); break; + case sf::Key::Up: sprintf(keyStr, "Up"); break; + case sf::Key::Down: sprintf(keyStr, "Down"); break; + case sf::Key::Numpad0: sprintf(keyStr, "NP 0"); break; + case sf::Key::Numpad1: sprintf(keyStr, "NP 1"); break; + case sf::Key::Numpad2: sprintf(keyStr, "NP 2"); break; + case sf::Key::Numpad3: sprintf(keyStr, "NP 3"); break; + case sf::Key::Numpad4: sprintf(keyStr, "NP 4"); break; + case sf::Key::Numpad5: sprintf(keyStr, "NP 5"); break; + case sf::Key::Numpad6: sprintf(keyStr, "NP 6"); break; + case sf::Key::Numpad7: sprintf(keyStr, "NP 7"); break; + case sf::Key::Numpad8: sprintf(keyStr, "NP 8"); break; + case sf::Key::Numpad9: sprintf(keyStr, "NP 9"); break; + case sf::Key::F1: sprintf(keyStr, "F1"); break; + case sf::Key::F2: sprintf(keyStr, "F2"); break; + case sf::Key::F3: sprintf(keyStr, "F3"); break; + case sf::Key::F4: sprintf(keyStr, "F4"); break; + case sf::Key::F5: sprintf(keyStr, "F5"); break; + case sf::Key::F6: sprintf(keyStr, "F6"); break; + case sf::Key::F7: sprintf(keyStr, "F7"); break; + case sf::Key::F8: sprintf(keyStr, "F8"); break; + case sf::Key::F9: sprintf(keyStr, "F9"); break; + case sf::Key::F10: sprintf(keyStr, "F10"); break; + case sf::Key::F11: sprintf(keyStr, "F11"); break; + case sf::Key::F12: sprintf(keyStr, "F12"); break; + case sf::Key::F13: sprintf(keyStr, "F13"); break; + case sf::Key::F14: sprintf(keyStr, "F14"); break; + case sf::Key::F15: sprintf(keyStr, "F15"); break; + case sf::Key::Pause: sprintf(keyStr, "Pause"); break; + default: + if (keycode > sf::Key::Escape) + sprintf(keyStr, "Invalid Key"); + else + sprintf(keyStr, "%c", toupper(keycode)); + break; + } +} + +class EventHandlerCleaner +{ +public: + ~EventHandlerCleaner() + { + //EventHandler::Destroy(); + } +} EventHandlerCleanerInst; diff --git a/desmume/src/wx/InputCommon/EventHandler.h b/desmume/src/wx/InputCommon/EventHandler.h new file mode 100644 index 000000000..ff5421939 --- /dev/null +++ b/desmume/src/wx/InputCommon/EventHandler.h @@ -0,0 +1,63 @@ +#ifndef EVENTHANDER_H +#define EVENTHANDER_H 1 +#include "Common.h" +#include +#include "Event.hpp" + +#define NUMKEYS 300 +#define NUMMODS 8 + +typedef bool (*listenFuncPtr) (sf::Event); +enum InputType +{ + KeyboardInput, + MouseInput, + JoystickInput +}; + +enum Modifiers { + UseAlt = 1, + UseShift = 2, + UseCtrl = 4 +}; + +struct Keys { + InputType inputType; + sf::Event::EventType eventType; + sf::Key::Code keyCode; + int mods; + sf::Mouse::Button mouseButton; +}; + +class EventHandler { + +private: + listenFuncPtr keys[NUMKEYS][NUMMODS]; + listenFuncPtr mouse[sf::Mouse::Count+1]; + listenFuncPtr joys[sf::Joy::Count+1]; + std::queue eventQueue; + static EventHandler *m_Instance; + +protected: + EventHandler(const EventHandler&); + EventHandler& operator= (const EventHandler&); + + EventHandler(); + ~EventHandler(); + +public: + + bool RegisterEventListener(listenFuncPtr func, Keys key); + bool RemoveEventListener(Keys key); + void Update(); + static EventHandler *GetInstance(); + static void Destroy(); + bool addEvent(sf::Event *e); + static bool TestEvent (Keys k, sf::Event e); +#if defined HAVE_WX && HAVE_WX + static sf::Key::Code wxCharCodeToSF(int id); +#endif + static void SFKeyToString(sf::Key::Code keycode, char *keyStr); +}; + +#endif diff --git a/desmume/src/wx/InputCommon/InputCommon.cpp b/desmume/src/wx/InputCommon/InputCommon.cpp new file mode 100644 index 000000000..8ec366a9c --- /dev/null +++ b/desmume/src/wx/InputCommon/InputCommon.cpp @@ -0,0 +1,16 @@ +#include "EventHandler.h" + +//EventHandler *eventHandler = NULL; + +namespace InputCommon +{ + void Init() { + // init the event handler + //EventHandler::GetInstance(); + } + + void Shutdown() { + //if (eventHandler) + // delete eventHandler; + } +} diff --git a/desmume/src/wx/InputCommon/InputCommon.h b/desmume/src/wx/InputCommon/InputCommon.h new file mode 100644 index 000000000..edfcf3c01 --- /dev/null +++ b/desmume/src/wx/InputCommon/InputCommon.h @@ -0,0 +1,9 @@ +#include "EventHandler.h" + +//extern EventHandler *eventHandler; + +namespace InputCommon +{ + void Init(); + void Shutdown(); +} diff --git a/desmume/src/wx/InputCommon/SDL.cpp b/desmume/src/wx/InputCommon/SDL.cpp new file mode 100644 index 000000000..cc905e9cb --- /dev/null +++ b/desmume/src/wx/InputCommon/SDL.cpp @@ -0,0 +1,375 @@ + +// Project description +// ------------------- +// Name: SDL Input +// Description: Common SDL Input Functions +// +// Author: Falcon4ever (nJoy@falcon4ever.com, www.multigesture.net), JPeterson etc +// Copyright (C) 2003 Dolphin Project. +// + +// +// Licensetype: GNU General Public License (GPL) +// +// 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, version 2.0. +// +// 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 2.0 for more details. +// +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ +// +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// + + + + +// Include +// ------------------- +#define _SDL_MAIN_ // Avoid certain declarations in SDL.h +#include "SDL.h" // Local +#include "XInput.h" + + + + +// Definitions +// ------------------- +int g_LastPad = 0; + + + +namespace InputCommon +{ + + +// Definitions +// ------------------- + + + + + +// Search attached devices. Populate joyinfo for all attached physical devices. +// ----------------------- +bool SearchDevices(std::vector &_joyinfo, int &_NumPads, int &_NumGoodPads) +{ + if (!SDL_WasInit(0)) +#if SDL_VERSION_ATLEAST(1, 3, 0) + if (SDL_Init(SDL_INIT_JOYSTICK | SDL_INIT_HAPTIC) < 0) +#else + if (SDL_Init(SDL_INIT_JOYSTICK) < 0) +#endif + { + PanicAlert("Could not initialize SDL: %s", SDL_GetError()); + return false; + } + + // Get device status + int numjoy = SDL_NumJoysticks(); + for (int i = 0; i < numjoy; i++ ) + { + CONTROLLER_INFO Tmp; + + Tmp.joy = SDL_JoystickOpen(i); + Tmp.ID = i; + Tmp.NumAxes = SDL_JoystickNumAxes(Tmp.joy); + Tmp.NumButtons = SDL_JoystickNumButtons(Tmp.joy); + Tmp.NumBalls = SDL_JoystickNumBalls(Tmp.joy); + Tmp.NumHats = SDL_JoystickNumHats(Tmp.joy); + Tmp.Name = SDL_JoystickName(i); + + // Check if the device is okay + if ( Tmp.NumAxes == 0 + && Tmp.NumBalls == 0 + && Tmp.NumButtons == 0 + && Tmp.NumHats == 0 + ) + { + Tmp.Good = false; + } + else + { + _NumGoodPads++; + Tmp.Good = true; + } + + _joyinfo.push_back(Tmp); + + // We have now read the values we need so we close the device + if (SDL_JoystickOpened(i)) SDL_JoystickClose(_joyinfo[i].joy); + } + + _NumPads = (int)_joyinfo.size(); + + return true; +} + + + + +// Supporting functions +// ---------------- + +// Read current joystick status +/* ---------------- + The value PadMapping[].buttons[] is the number of the assigned joypad button, + PadState[].buttons[] is the status of the button, it becomes 0 (no pressed) or 1 (pressed) */ + + +// Read buttons status. Called from GetJoyState(). +// ---------------------- +void ReadButton(CONTROLLER_STATE &_PadState, CONTROLLER_MAPPING _PadMapping, int button, int NumButtons) +{ + int ctl_button = _PadMapping.buttons[button]; + if (ctl_button < NumButtons) + { + _PadState.buttons[button] = SDL_JoystickGetButton(_PadState.joy, ctl_button); + } +} + +// Request joystick state. +// ---------------------- +/* Called from: PAD_GetStatus() + Input: The virtual device 0, 1, 2 or 3 + Function: Updates the PadState struct with the current pad status. The input value "controller" is + for a virtual controller 0 to 3. */ +void GetJoyState(CONTROLLER_STATE &_PadState, CONTROLLER_MAPPING _PadMapping, int Controller, int NumButtons) +{ + // Update the gamepad status + SDL_JoystickUpdate(); + + // Update axis states. It doesn't hurt much if we happen to ask for nonexisting axises here. + _PadState.axis[CTL_MAIN_X] = SDL_JoystickGetAxis(_PadState.joy, _PadMapping.axis[CTL_MAIN_X]); + _PadState.axis[CTL_MAIN_Y] = SDL_JoystickGetAxis(_PadState.joy, _PadMapping.axis[CTL_MAIN_Y]); + _PadState.axis[CTL_SUB_X] = SDL_JoystickGetAxis(_PadState.joy, _PadMapping.axis[CTL_SUB_X]); + _PadState.axis[CTL_SUB_Y] = SDL_JoystickGetAxis(_PadState.joy, _PadMapping.axis[CTL_SUB_Y]); + + // Update the analog trigger axis values +#ifdef _WIN32 + if (_PadMapping.triggertype == CTL_TRIGGER_SDL) + { +#endif + // If we are using SDL analog triggers the buttons have to be mapped as 1000 or up, otherwise they are not used + if(_PadMapping.buttons[CTL_L_SHOULDER] >= 1000) _PadState.axis[CTL_L_SHOULDER] = SDL_JoystickGetAxis(_PadState.joy, _PadMapping.buttons[CTL_L_SHOULDER] - 1000); else _PadState.axis[CTL_L_SHOULDER] = 0; + if(_PadMapping.buttons[CTL_R_SHOULDER] >= 1000) _PadState.axis[CTL_R_SHOULDER] = SDL_JoystickGetAxis(_PadState.joy, _PadMapping.buttons[CTL_R_SHOULDER] - 1000); else _PadState.axis[CTL_R_SHOULDER] = 0; +#ifdef _WIN32 + } + else + { + // XInput triggers for Xbox360 pads + _PadState.axis[CTL_L_SHOULDER] = XInput::GetXI(0, _PadMapping.buttons[CTL_L_SHOULDER] - 1000); + _PadState.axis[CTL_R_SHOULDER] = XInput::GetXI(0, _PadMapping.buttons[CTL_R_SHOULDER] - 1000); + } +#endif + + // Update button states to on or off + ReadButton(_PadState, _PadMapping, CTL_L_SHOULDER, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_R_SHOULDER, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_A_BUTTON, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_B_BUTTON, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_X_BUTTON, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_Y_BUTTON, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_Z_TRIGGER, NumButtons); + ReadButton(_PadState, _PadMapping, CTL_START, NumButtons); + + // Update Halfpress state, this one is not in the standard _PadState.buttons array + if (_PadMapping.halfpress < NumButtons && _PadMapping.halfpress >= 0) + _PadState.halfpress = SDL_JoystickGetButton(_PadState.joy, _PadMapping.halfpress); + else + _PadState.halfpress = 0; + + + // Check if we have an analog or digital joypad + if (_PadMapping.controllertype == CTL_DPAD_HAT) + { + _PadState.dpad = SDL_JoystickGetHat(_PadState.joy, _PadMapping.dpad); + } + else + { + // Only do this if the assigned button is in range (to allow for the current way of saving keyboard + // keys in the same array) + if(_PadMapping.dpad2[CTL_D_PAD_UP] <= NumButtons) + _PadState.dpad2[CTL_D_PAD_UP] = SDL_JoystickGetButton(_PadState.joy, _PadMapping.dpad2[CTL_D_PAD_UP]); + if(_PadMapping.dpad2[CTL_D_PAD_DOWN] <= NumButtons) + _PadState.dpad2[CTL_D_PAD_DOWN] = SDL_JoystickGetButton(_PadState.joy, _PadMapping.dpad2[CTL_D_PAD_DOWN]); + if(_PadMapping.dpad2[CTL_D_PAD_LEFT] <= NumButtons) + _PadState.dpad2[CTL_D_PAD_LEFT] = SDL_JoystickGetButton(_PadState.joy, _PadMapping.dpad2[CTL_D_PAD_LEFT]); + if(_PadMapping.dpad2[CTL_D_PAD_RIGHT] <= NumButtons) + _PadState.dpad2[CTL_D_PAD_RIGHT] = SDL_JoystickGetButton(_PadState.joy, _PadMapping.dpad2[CTL_D_PAD_RIGHT]); + } + +#ifdef SHOW_PAD_STATUS + // Show the status of all connected pads + //ConsoleListener* Console = LogManager::GetInstance()->getConsoleListener(); + //if ((g_LastPad == 0 && Controller == 0) || Controller < g_LastPad) Console->ClearScreen(); + g_LastPad = Controller; + NOTICE_LOG(CONSOLE, + "Pad | Number:%i Enabled:%i Handle:%i\n" + "Main Stick | X:%03i Y:%03i\n" + "C Stick | X:%03i Y:%03i\n" + "Trigger | Type:%s DigitalL:%i DigitalR:%i AnalogL:%03i AnalogR:%03i HalfPress:%i\n" + "Buttons | A:%i X:%i\n" + "D-Pad | Type:%s Hat:%i U:%i D:%i\n" + "======================================================\n", + + Controller, _PadMapping.enabled, _PadState.joy, + + _PadState.axis[InputCommon::CTL_MAIN_X], _PadState.axis[InputCommon::CTL_MAIN_Y], + _PadState.axis[InputCommon::CTL_SUB_X], _PadState.axis[InputCommon::CTL_SUB_Y], + + (_PadMapping.triggertype ? "CTL_TRIGGER_XINPUT" : "CTL_TRIGGER_SDL"), + _PadState.buttons[InputCommon::CTL_L_SHOULDER], _PadState.buttons[InputCommon::CTL_R_SHOULDER], + _PadState.axis[InputCommon::CTL_L_SHOULDER], _PadState.axis[InputCommon::CTL_R_SHOULDER], + _PadState.halfpress, + + _PadState.buttons[InputCommon::CTL_A_BUTTON], _PadState.buttons[InputCommon::CTL_X_BUTTON], + + (_PadMapping.controllertype ? "CTL_DPAD_CUSTOM" : "CTL_DPAD_HAT"), + _PadState.dpad, + _PadState.dpad2[InputCommon::CTL_D_PAD_UP], _PadState.dpad2[InputCommon::CTL_D_PAD_DOWN] + ); +#endif +} + + + + + + +// Configure button mapping +// ---------- + +// Avoid extreme axis values +// --------------------- +/* Function: We have to avoid very big values to becuse some triggers are -0x8000 in the + unpressed state (and then go from -0x8000 to 0x8000 as they are fully pressed) */ +bool AvoidValues(int value, bool NoTriggerFilter) +{ + // Avoid detecting very small or very big (for triggers) values + if( (value > -0x2000 && value < 0x2000) // Small values + || ((value < -0x6000 || value > 0x6000) && !NoTriggerFilter)) // Big values + return true; // Avoid + else + return false; // Keep +} + + +// Detect a pressed button +// --------------------- +void GetButton(SDL_Joystick *joy, int ControllerID, int buttons, int axes, int hats, + int &KeyboardKey, int &value, int &type, int &pressed, bool &Succeed, bool &Stop, + bool LeftRight, bool Axis, bool XInput, bool Button, bool Hat, bool NoTriggerFilter) +{ + // It needs the wxWidgets excape keycode + static const int WXK_ESCAPE = 27; + + // Update the internal status + SDL_JoystickUpdate(); + + // For the triggers we accept both a digital or an analog button + if(Axis) + { + for(int i = 0; i < axes; i++) + { + value = SDL_JoystickGetAxis(joy, i); + + if(AvoidValues(value, NoTriggerFilter)) continue; // Avoid values + + pressed = i + (LeftRight ? 1000 : 0); // Identify the analog triggers + type = InputCommon::CTL_AXIS; + Succeed = true; + } + } + + // Check for a hat + if(Hat) + { + for(int i = 0; i < hats; i++) + { + value = SDL_JoystickGetHat(joy, i); + if(value) + { + pressed = i; + type = InputCommon::CTL_HAT; + Succeed = true; + } + } + } + + // Check for a button + if(Button) + { + for(int i = 0; i < buttons; i++) + { + // Some kind of bug in SDL 1.3 would give button 9 and 10 (nonexistent) the value 48 on the 360 pad + if (SDL_JoystickGetButton(joy, i) > 1) continue; + + if(SDL_JoystickGetButton(joy, i)) + { + pressed = i; + type = InputCommon::CTL_BUTTON; + Succeed = true; + } + } + } + + // Check for a XInput trigger + #ifdef _WIN32 + if(XInput) + { + for(int i = 0; i <= InputCommon::XI_TRIGGER_R; i++) + { + if(XInput::GetXI(0, i)) + { + pressed = i + 1000; + type = InputCommon::CTL_AXIS; + Succeed = true; + } + } + } + #endif + + // Check for keyboard action + if (KeyboardKey) + { + if(Button) + { + // Todo: Add a separate keyboard vector to remove this restriction + if(KeyboardKey >= buttons) + { + pressed = KeyboardKey; + type = InputCommon::CTL_BUTTON; + Succeed = true; + KeyboardKey = 0; + if(pressed == WXK_ESCAPE) pressed = -1; // Check for the escape key + } + // Else show the error message + else + { + pressed = KeyboardKey; + KeyboardKey = -1; + Stop = true; + } + } + // Only accept the escape key + else if (KeyboardKey == WXK_ESCAPE) + { + Succeed = true; + KeyboardKey = 0; + pressed = -1; + } + } +} +/////////////////////////////////////////////////////////// Configure button mapping + + + +} // InputCommon + diff --git a/desmume/src/wx/InputCommon/SDL.h b/desmume/src/wx/InputCommon/SDL.h new file mode 100644 index 000000000..18855c2dd --- /dev/null +++ b/desmume/src/wx/InputCommon/SDL.h @@ -0,0 +1,269 @@ + +// Project description +// ------------------- +// Name: SDL Input +// Description: Common SDL Input Functions +// +// Author: Falcon4ever (nJoy@falcon4ever.com, www.multigesture.net), JPeterson etc +// Copyright (C) 2003 Dolphin Project. +// + +// +// Licensetype: GNU General Public License (GPL) +// +// 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, version 2.0. +// +// 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 2.0 for more details. +// +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ +// +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// + + +#ifndef _SDL_h +#define _SDL_h + + +// Include +// ------------------- +#include // System +#include +#include + +#ifdef _WIN32 +#include // Externals +#include +#if SDL_VERSION_ATLEAST(1, 3, 0) + #include +#endif +#else +#include +#include +#if SDL_VERSION_ATLEAST(1, 3, 0) + #include +#endif +#endif + +#include "Common.h" // Common + + + +namespace InputCommon +{ + + +// Settings +// ---------- +// Show a status window with the detected axes, buttons and so on +//#define SHOW_PAD_STATUS + + + +// Structures +/* ------------------- + CONTROLLER_STATE buttons (PadState) = 0 or 1 + CONTROLLER_MAPPING buttons (joystick) = 0 or 1, 2, 3, 4, a certain joypad button + + Please remember: The axis limit is hardcoded here, if you allow more axises (for + example for analog A and B buttons) you must first incrase the size of the axis array + size here +*/ +struct CONTROLLER_STATE // GC PAD INFO/STATE +{ + int buttons[8]; // Amount of buttons (A B X Y Z, L-Trigger R-Trigger Start) might need to change the triggers buttons + int dpad; // Automatic SDL D-Pad (8 directions + neutral) + int dpad2[4]; // D-pad using buttons + int axis[6]; // 2 x 2 Axes (Main & Sub) + int halfpress; // Halfpress... you know, like not fully pressed ;)... + SDL_Joystick *joy; // SDL joystick device +}; + +struct CONTROLLER_MAPPING // GC PAD MAPPING +{ + int buttons[8]; // (See above) + int dpad; // (See above) + int dpad2[4]; // (See above) + int axis[6]; // (See above) + int halfpress; // (See above) + int deadzone; // Deadzone... what else? + int ID; // SDL joystick device ID + int controllertype; // Hat: Hat or custom buttons + int triggertype; // Triggers range + std::string SRadius, SDiagonal, SRadiusC, SDiagonalC; + bool bRadiusOnOff, bSquareToCircle, bRadiusOnOffC, bSquareToCircleC; + bool rumble; + bool enable; + int eventnum; // Linux Event Number, Can't be found dynamically yet +}; + +struct CONTROLLER_INFO // CONNECTED WINDOWS DEVICES INFO +{ + int NumAxes; // Amount of Axes + int NumButtons; // Amount of Buttons + int NumBalls; // Amount of Balls + int NumHats; // Amount of Hats (POV) + std::string Name; // Joypad/stickname + int ID; // SDL joystick device ID + bool Good; // Pad is good (it has at least one button or axis) + SDL_Joystick *joy; // SDL joystick device +}; +enum +{ + // CTL_L_SHOULDER and CTL_R_SHOULDER = 0 and 1 + CTL_MAIN_X = 2, + CTL_MAIN_Y, + CTL_SUB_X, + CTL_SUB_Y +}; + +enum +{ + CTL_L_SHOULDER = 0, + CTL_R_SHOULDER, + CTL_A_BUTTON, + CTL_B_BUTTON, + CTL_X_BUTTON, + CTL_Y_BUTTON, + CTL_Z_TRIGGER, + CTL_START +}; +// DPad Type +enum +{ + CTL_DPAD_HAT = 0, // Automatically use the first hat that SDL finds + CTL_DPAD_CUSTOM // Custom directional pad settings +}; +// Trigger Type +enum +{ + CTL_TRIGGER_SDL = 0, // + CTL_TRIGGER_XINPUT // The XBox 360 pad +}; +enum +{ + CTL_D_PAD_UP = 0, + CTL_D_PAD_DOWN, + CTL_D_PAD_LEFT, + CTL_D_PAD_RIGHT +}; +// Button type for the configuration +enum +{ + CTL_AXIS = 0, + CTL_HAT, + CTL_BUTTON, + CTL_KEY +}; +// XInput buttons +enum +{ + XI_TRIGGER_L = 0, + XI_TRIGGER_R +}; + + +union PadAxis +{ + int keyForControls[6]; + struct + { + int Lx; + int Ly; + int Rx; + int Ry; + int Tl; // Trigger + int Tr; // Trigger + }; +}; +struct PadWiimote +{ + int keyForControls[16]; + // Order is A, B, 1, 2, +, -, Home + // L, R, U, D, RollL, RollR, PitchU, PitchD, Shake +}; +struct PadNunchuck +{ + int keyForControls[7]; + // Order is Z, C, L, R, U, D, Shake +}; +struct PadClassicController +{ + int keyForControls[23]; + // Order is A, B, X, Y, +, -, Home + // Tl, Zl, Zr, Tr, Dl, Dr, Du, Dd + // Ll, Lr, Lu, Ld, Rl, Rr, Ru, Rd +}; +struct PadGH3Controller +{ + int keyForControls[14]; + // Order is Green, Red, Yellow, Blue, Orange, + // +, -, Whammy, + // Al, Ar, Au, Ad, + // StrumUp, StrumDown +}; +struct CONTROLLER_STATE_NEW // GC PAD INFO/STATE +{ + PadAxis Axis; // 6 Axes (Main, Sub, Triggers) + SDL_Joystick *joy; // SDL joystick device +}; +struct CONTROLLER_MAPPING_NEW // GC PAD MAPPING +{ + PadAxis Axis; // (See above) + PadWiimote Wm; + PadNunchuck Nc; + PadClassicController Cc; + PadGH3Controller GH3c; + bool enabled; // Pad attached? + bool Rumble; + int RumbleStrength; + int DeadZoneL; // Analog 1 Deadzone + int DeadZoneR; // Analog 2 Deadzone + int ID; // SDL joystick device ID + int controllertype; // D-Pad type: Hat or custom buttons + int triggertype; // SDL or XInput trigger + std::string SDiagonal; + bool bSquareToCircle; + bool bCircle2Square; +}; + + + + +// Declarations +// --------- + +// General functions +bool SearchDevices(std::vector &_joyinfo, int &NumPads, int &NumGoodPads); +void GetJoyState(CONTROLLER_STATE &_PadState, CONTROLLER_MAPPING _PadMapping, int controller, int NumButtons); +void GetButton(SDL_Joystick*, int,int,int,int, int&,int&,int&,int&,bool&,bool&, bool,bool,bool,bool,bool,bool); + +// Value conversion +float Deg2Rad(float Deg); +float Rad2Deg(float Rad); +int Pad_Convert(int _val); +float SquareDistance(float deg); +bool IsDeadZone(float DeadZone, int x, int y); +void Square2Circle(int &_x, int &_y, int _pad, std::string SDiagonal, bool Circle2Square = false); +void RadiusAdjustment(int &_x, int &_y, int _pad, std::string SRadius); + +// Input configuration +std::string VKToString(int keycode); + +#ifndef _SDL_MAIN_ + extern int g_LastPad; +#endif + + + +} // InputCommon + + +#endif // _SDL_h diff --git a/desmume/src/wx/InputCommon/X11InputBase.cpp b/desmume/src/wx/InputCommon/X11InputBase.cpp new file mode 100644 index 000000000..636ec6950 --- /dev/null +++ b/desmume/src/wx/InputCommon/X11InputBase.cpp @@ -0,0 +1,156 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "X11InputBase.h" +#include +#include +#include +namespace InputCommon +{ +#if defined(HAVE_WX) && HAVE_WX +// Taken from wxw source code +KeySym wxCharCodeWXToX(int id) +{ + KeySym keySym; + + switch (id) + { + case WXK_CANCEL: keySym = XK_Cancel; break; + case WXK_BACK: keySym = XK_BackSpace; break; + case WXK_TAB: keySym = XK_Tab; break; + case WXK_CLEAR: keySym = XK_Clear; break; + case WXK_RETURN: keySym = XK_Return; break; + case WXK_SHIFT: keySym = XK_Shift_L; break; + case WXK_CONTROL: keySym = XK_Control_L; break; + case WXK_ALT: keySym = XK_Meta_L; break; + case WXK_CAPITAL: keySym = XK_Caps_Lock; break; + case WXK_MENU : keySym = XK_Menu; break; + case WXK_PAUSE: keySym = XK_Pause; break; + case WXK_ESCAPE: keySym = XK_Escape; break; + case WXK_SPACE: keySym = ' '; break; + case WXK_PAGEUP: keySym = XK_Prior; break; + case WXK_PAGEDOWN: keySym = XK_Next; break; + case WXK_END: keySym = XK_End; break; + case WXK_HOME : keySym = XK_Home; break; + case WXK_LEFT : keySym = XK_Left; break; + case WXK_UP: keySym = XK_Up; break; + case WXK_RIGHT: keySym = XK_Right; break; + case WXK_DOWN : keySym = XK_Down; break; + case WXK_SELECT: keySym = XK_Select; break; + case WXK_PRINT: keySym = XK_Print; break; + case WXK_EXECUTE: keySym = XK_Execute; break; + case WXK_INSERT: keySym = XK_Insert; break; + case WXK_DELETE: keySym = XK_Delete; break; + case WXK_HELP : keySym = XK_Help; break; + case WXK_NUMPAD0: keySym = XK_KP_0; break; case WXK_NUMPAD_INSERT: keySym = XK_KP_Insert; break; + case WXK_NUMPAD1: keySym = XK_KP_1; break; case WXK_NUMPAD_END: keySym = XK_KP_End; break; + case WXK_NUMPAD2: keySym = XK_KP_2; break; case WXK_NUMPAD_DOWN: keySym = XK_KP_Down; break; + case WXK_NUMPAD3: keySym = XK_KP_3; break; case WXK_NUMPAD_PAGEDOWN: keySym = XK_KP_Page_Down; break; + case WXK_NUMPAD4: keySym = XK_KP_4; break; case WXK_NUMPAD_LEFT: keySym = XK_KP_Left; break; + case WXK_NUMPAD5: keySym = XK_KP_5; break; + case WXK_NUMPAD6: keySym = XK_KP_6; break; case WXK_NUMPAD_RIGHT: keySym = XK_KP_Right; break; + case WXK_NUMPAD7: keySym = XK_KP_7; break; case WXK_NUMPAD_HOME: keySym = XK_KP_Home; break; + case WXK_NUMPAD8: keySym = XK_KP_8; break; case WXK_NUMPAD_UP: keySym = XK_KP_Up; break; + case WXK_NUMPAD9: keySym = XK_KP_9; break; case WXK_NUMPAD_PAGEUP: keySym = XK_KP_Page_Up; break; + case WXK_NUMPAD_DECIMAL: keySym = XK_KP_Decimal; break; case WXK_NUMPAD_DELETE: keySym = XK_KP_Delete; break; + case WXK_NUMPAD_MULTIPLY: keySym = XK_KP_Multiply; break; + case WXK_NUMPAD_ADD: keySym = XK_KP_Add; break; + case WXK_NUMPAD_SUBTRACT: keySym = XK_KP_Subtract; break; + case WXK_NUMPAD_DIVIDE: keySym = XK_KP_Divide; break; + case WXK_NUMPAD_ENTER: keySym = XK_KP_Enter; break; + case WXK_NUMPAD_SEPARATOR: keySym = XK_KP_Separator; break; + case WXK_F1: keySym = XK_F1; break; + case WXK_F2: keySym = XK_F2; break; + case WXK_F3: keySym = XK_F3; break; + case WXK_F4: keySym = XK_F4; break; + case WXK_F5: keySym = XK_F5; break; + case WXK_F6: keySym = XK_F6; break; + case WXK_F7: keySym = XK_F7; break; + case WXK_F8: keySym = XK_F8; break; + case WXK_F9: keySym = XK_F9; break; + case WXK_F10: keySym = XK_F10; break; + case WXK_F11: keySym = XK_F11; break; + case WXK_F12: keySym = XK_F12; break; + case WXK_F13: keySym = XK_F13; break; + case WXK_F14: keySym = XK_F14; break; + case WXK_F15: keySym = XK_F15; break; + case WXK_F16: keySym = XK_F16; break; + case WXK_F17: keySym = XK_F17; break; + case WXK_F18: keySym = XK_F18; break; + case WXK_F19: keySym = XK_F19; break; + case WXK_F20: keySym = XK_F20; break; + case WXK_F21: keySym = XK_F21; break; + case WXK_F22: keySym = XK_F22; break; + case WXK_F23: keySym = XK_F23; break; + case WXK_F24: keySym = XK_F24; break; + case WXK_NUMLOCK: keySym = XK_Num_Lock; break; + case WXK_SCROLL: keySym = XK_Scroll_Lock; break; + default: keySym = id <= 255 ? (KeySym)id : 0; + } + + return keySym; +} +#else +KeySym wxCharCodeWXToX(int id) +{ + return NULL; +} +#endif +void XKeyToString(unsigned int keycode, char *keyStr) { + switch (keycode) { + + case XK_Left: + sprintf(keyStr, "LEFT"); + break; + case XK_Up: + sprintf(keyStr, "UP"); + break; + case XK_Right: + sprintf(keyStr, "RIGHT"); + break; + case XK_Down: + sprintf(keyStr, "DOWN"); + break; + case XK_Return: + sprintf(keyStr, "RETURN"); + break; + case XK_KP_Enter: + sprintf(keyStr, "KP ENTER"); + break; + case XK_KP_Left: + sprintf(keyStr, "KP LEFT"); + break; + case XK_KP_Up: + sprintf(keyStr, "KP UP"); + break; + case XK_KP_Right: + sprintf(keyStr, "KP RIGHT"); + break; + case XK_KP_Down: + sprintf(keyStr, "KP DOWN"); + break; + case XK_Shift_L: + sprintf(keyStr, "LShift"); + break; + case XK_Control_L: + sprintf(keyStr, "LControl"); + break; + default: + sprintf(keyStr, "%c", toupper(keycode)); + } +} +} diff --git a/desmume/src/wx/InputCommon/X11InputBase.h b/desmume/src/wx/InputCommon/X11InputBase.h new file mode 100644 index 000000000..a17fbe356 --- /dev/null +++ b/desmume/src/wx/InputCommon/X11InputBase.h @@ -0,0 +1,32 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef XINPUTBASE_H +#define XINPUTBASE_H + +#include +#include +#include "Config.h" +#if defined(HAVE_WX) && HAVE_WX +#include +#endif +namespace InputCommon +{ +KeySym wxCharCodeWXToX(int id); +void XKeyToString(unsigned int keycode, char *keyStr); +} +#endif diff --git a/desmume/src/wx/InputCommon/XInput.cpp b/desmume/src/wx/InputCommon/XInput.cpp new file mode 100644 index 000000000..3c762ccf0 --- /dev/null +++ b/desmume/src/wx/InputCommon/XInput.cpp @@ -0,0 +1,136 @@ + +// +// Licensetype: GNU General Public License (GPL) +// +// 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, version 2.0. +// +// 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 2.0 for more details. +// +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ +// +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// + + + + +// File description +/* ------------------- + Function: This file will get the status of the analog triggers of any connected XInput device. + This code was made with the help of SimpleController.cpp in the June 2008 Microsoft DirectX SDK + Samples. + +///////////////////////////////////////////////////// */ + +#ifdef _WIN32 + + +// Includes +// ------------------- +#include +#include // XInput API + +#include "SDL.h" // Local + + + +namespace XInput +{ + + + +// Declarations +// ------------------- + +#define MAX_CONTROLLERS 4 // XInput handles up to 4 controllers + +struct CONTROLER_STATE +{ + XINPUT_STATE state; + bool bConnected; +}; +CONTROLER_STATE g_Controllers[MAX_CONTROLLERS]; + + + + +// Init +// ------------------- +/* Function: Calculate the number of connected XInput devices + Todo: Implement this to figure out if there are multiple XInput controllers connected, + we currently only try to connect to XInput device 0 */ +void Init() +{ + // Init state + //ZeroMemory( g_Controllers, sizeof( CONTROLER_STATE ) * MAX_CONTROLLERS ); + + // Declaration + DWORD dwResult; + + // Calculate the number of connected XInput devices + for( DWORD i = 0; i < MAX_CONTROLLERS; i++ ) + { + // Simply get the state of the controller from XInput. + dwResult = XInputGetState( i, &g_Controllers[i].state ); + + if( dwResult == ERROR_SUCCESS ) + g_Controllers[i].bConnected = true; + else + g_Controllers[i].bConnected = false; + } + +} + + + + +// Get the trigger status +// ------------------- +int GetXI(int Controller, int Button) +{ + // Update the internal status + DWORD dwResult; + dwResult = XInputGetState( Controller, &g_Controllers[Controller].state ); + + if( dwResult != ERROR_SUCCESS ) return -1; + + switch(Button) + { + case InputCommon::XI_TRIGGER_L: + return g_Controllers[0].state.Gamepad.bLeftTrigger; + + case InputCommon::XI_TRIGGER_R: + return g_Controllers[0].state.Gamepad.bRightTrigger; + + default: + return 0; + } +} + + + + +// Check if a certain controller is connected +// ------------------- +bool IsConnected(int Controller) +{ + DWORD dwResult = XInputGetState( Controller, &g_Controllers[Controller].state ); + + // Update the connected status + if( dwResult == ERROR_SUCCESS ) + return true; + else + return false; +} + + +} // XInput + +#endif \ No newline at end of file diff --git a/desmume/src/wx/InputCommon/XInput.h b/desmume/src/wx/InputCommon/XInput.h new file mode 100644 index 000000000..25905afa7 --- /dev/null +++ b/desmume/src/wx/InputCommon/XInput.h @@ -0,0 +1,46 @@ + +// +// Licensetype: GNU General Public License (GPL) +// +// 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, version 2.0. +// +// 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 2.0 for more details. +// +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ +// +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// + + +#ifdef _WIN32 + + +// Includes +// ---------- +#include + + + + +namespace XInput +{ + + +// Declarations +// ---------- +void Init(); +int GetXI(int Controller, int Button); +bool IsConnected(int Controller); + + +} // XInput + +#endif + diff --git a/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp b/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp new file mode 100644 index 000000000..207255a04 --- /dev/null +++ b/desmume/src/wx/PadSimple/GUI/ConfigDlg.cpp @@ -0,0 +1,557 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "ConfigDlg.h" +#include "../PadSimple.h" + +#ifdef _WIN32 +#include "XInput.h" +#include "../../InputCommon/DirectInputBase.h" // Core + +DInput m_dinput; +#endif + +BEGIN_EVENT_TABLE(PADConfigDialogSimple,wxDialog) + EVT_SHOW(PADConfigDialogSimple::OnShow) + EVT_CLOSE(PADConfigDialogSimple::OnClose) + EVT_BUTTON(ID_CLOSE,PADConfigDialogSimple::OnCloseClick) + EVT_BUTTON(ID_PAD_ABOUT,PADConfigDialogSimple::DllAbout) + + EVT_CHECKBOX(ID_X360PAD,PADConfigDialogSimple::ControllerSettingsChanged) + EVT_CHOICE(ID_X360PAD_CHOICE,PADConfigDialogSimple::ControllerSettingsChanged) + EVT_CHECKBOX(ID_RUMBLE,PADConfigDialogSimple::ControllerSettingsChanged) + EVT_CHECKBOX(ID_DISABLE,PADConfigDialogSimple::ControllerSettingsChanged) + + // Input recording +#ifdef RERECORDING + EVT_CHECKBOX(ID_RECORDING,PADConfigDialogSimple::ControllerSettingsChanged) + EVT_CHECKBOX(ID_PLAYBACK,PADConfigDialogSimple::ControllerSettingsChanged) + EVT_BUTTON(ID_SAVE_RECORDING,PADConfigDialogSimple::ControllerSettingsChanged) +#endif + + EVT_BUTTON(CTL_A, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_B, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_X, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_Y, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_Z, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_START, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_L, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_R, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_L_SEMI, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_R_SEMI, PADConfigDialogSimple::OnButtonClick) + EVT_SLIDER(ID_TRIGGER_SEMIVALUE, PADConfigDialogSimple::ControllerSettingsChanged) + EVT_BUTTON(CTL_MAINUP, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_MAINDOWN, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_MAINLEFT, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_MAINRIGHT, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_MAIN_SEMI, PADConfigDialogSimple::OnButtonClick) + EVT_SLIDER(ID_MAIN_SEMIVALUE, PADConfigDialogSimple::ControllerSettingsChanged) + EVT_BUTTON(CTL_SUBUP, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_SUBDOWN, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_SUBLEFT, PADConfigDialogSimple::OnButtonClick) + EVT_SLIDER(ID_SUB_SEMIVALUE, PADConfigDialogSimple::ControllerSettingsChanged) + EVT_BUTTON(CTL_SUBRIGHT, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_SUB_SEMI, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_DPADUP, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_DPADDOWN, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_DPADLEFT, PADConfigDialogSimple::OnButtonClick) + EVT_BUTTON(CTL_DPADRIGHT, PADConfigDialogSimple::OnButtonClick) +END_EVENT_TABLE() + +PADConfigDialogSimple::PADConfigDialogSimple(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) +: wxDialog(parent, id, title, position, size, style) +{ + ClickedButton = NULL; + CreateGUIControls(); + Fit(); + + // Connect keydown to the window + wxTheApp->Connect(wxID_ANY, wxEVT_KEY_DOWN, + wxKeyEventHandler(PADConfigDialogSimple::OnKeyDown), + (wxObject*)NULL, this); + + Show(); +} + +PADConfigDialogSimple::~PADConfigDialogSimple() +{ +} + +// Create input button controls +// ------------------- +inline void AddControl(wxPanel *pan, wxButton **button, + wxStaticBoxSizer *sizer, const char *name, int ctl, int controller) +{ + wxBoxSizer *hButton = new wxBoxSizer(wxHORIZONTAL); + char keyStr[10] = {0}; + + // Add the label + hButton->Add(new wxStaticText(pan, 0, wxString::FromAscii(name), + wxDefaultPosition, wxDefaultSize), 0, + wxALIGN_CENTER_VERTICAL|wxALL); + + // Give it the mapped key name +#ifdef _WIN32 + DInput::DIKToString(pad[controller].keyForControl[ctl], keyStr); +#elif defined(HAVE_X11) && HAVE_X11 + InputCommon::XKeyToString(pad[controller].keyForControl[ctl], keyStr); +#endif + + // Add the button to its sizer + *button = new wxButton(pan, ctl, wxString::FromAscii(keyStr), + wxDefaultPosition, wxDefaultSize, wxWANTS_CHARS); + hButton->Add(*button, 0, wxALIGN_RIGHT|wxALL); + sizer->Add(hButton, 0, wxALIGN_RIGHT|wxALL); +} + +// Create input slider controls +// ------------------- +inline void PADConfigDialogSimple::AddSlider(wxPanel *pan, wxSlider **slider, + wxStaticBoxSizer *sizer, const char *name, int ctl, int controller) +{ + wxBoxSizer *hSlider = new wxBoxSizer(wxHORIZONTAL); + int semivalue, maxvalue; + std::stringstream ss; + + // Add the label + hSlider->Add(new wxStaticText(pan, 0, wxString::FromAscii(name), + wxDefaultPosition, wxDefaultSize), 0, + wxALIGN_CENTER_VERTICAL|wxALL); + + // Do everything else + switch (ctl) + { + case ID_TRIGGER_SEMIVALUE: + semivalue = pad[controller].Trigger_semivalue; + maxvalue = TRIGGER_FULL; + // Add the slider to its sizer + *slider = new wxSlider(pan, ctl, semivalue, 0, maxvalue, wxDefaultPosition, wxSize(100,-1)); + (**slider).SetPageSize(32); + hSlider->Add(*slider, 0, wxALIGN_RIGHT|wxALL); + sizer->Add(hSlider, 0, wxALIGN_RIGHT|wxALL); + // Add numeric value label to sizer + ss << pad[controller].Trigger_semivalue; + m_Trigger_SemiValue_Label[controller] = new wxStaticText(pan, 0, + wxString::FromAscii( ss.str().c_str() ), wxDefaultPosition, wxSize(25, -1)); + hSlider->Add(m_Trigger_SemiValue_Label[controller], + 0, wxALIGN_CENTER_VERTICAL|wxALL); + break; + case ID_MAIN_SEMIVALUE: + semivalue = pad[controller].Main_stick_semivalue; + maxvalue = STICK_FULL; + // Add the slider to its sizer + *slider = new wxSlider(pan, ctl, semivalue, 0, maxvalue, wxDefaultPosition, wxSize(100, -1)); + (**slider).SetPageSize(10); + hSlider->Add(*slider, 0, wxALIGN_RIGHT|wxALL); + sizer->Add(hSlider, 0, wxALIGN_RIGHT|wxALL); + // Add numeric value label to sizer + ss << pad[controller].Main_stick_semivalue; + m_Stick_SemiValue_Label[controller] = new wxStaticText(pan, 0, + wxString::FromAscii( ss.str().c_str() ), wxDefaultPosition, wxSize(25, -1)); + hSlider->Add(m_Stick_SemiValue_Label[controller], + 0, wxALIGN_CENTER_VERTICAL|wxALL); + break; + case ID_SUB_SEMIVALUE: + semivalue = pad[controller].Sub_stick_semivalue; + maxvalue = STICK_FULL; + // Add the slider to its sizer + *slider = new wxSlider(pan, ctl, semivalue, 0, maxvalue, wxDefaultPosition, wxSize(100,-1)); + (**slider).SetPageSize(10); + hSlider->Add(*slider, 0, wxALIGN_RIGHT|wxALL); + sizer->Add(hSlider, 0, wxALIGN_RIGHT|wxALL); + // Add numeric value label to sizer + ss << pad[controller].Sub_stick_semivalue; + m_CStick_SemiValue_Label[controller] = new wxStaticText(pan, 0, + wxString::FromAscii( ss.str().c_str() ), wxDefaultPosition, wxSize(25, -1)); + hSlider->Add(m_CStick_SemiValue_Label[controller], + 0, wxALIGN_CENTER_VERTICAL|wxALL); + break; + } +} + +void PADConfigDialogSimple::CreateGUIControls() +{ + // Notebook + m_Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize); + + // Controller pages + m_Controller[0] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE1, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Controller[0], wxT("Controller 1")); + m_Controller[1] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE2, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Controller[1], wxT("Controller 2")); + m_Controller[2] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE3, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Controller[2], wxT("Controller 3")); + m_Controller[3] = new wxPanel(m_Notebook, ID_CONTROLLERPAGE4, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Controller[3], wxT("Controller 4")); + + // Standard buttons + m_Close = new wxButton(this, ID_CLOSE, wxT("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_About = new wxButton(this, ID_PAD_ABOUT, wxT("About"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + // Put notebook and standard buttons in sizers + wxBoxSizer* sSButtons; + sSButtons = new wxBoxSizer(wxHORIZONTAL); + sSButtons->Add(m_About,0,wxALL, 5); + sSButtons->Add(0, 0, 1, wxEXPAND, 5); + sSButtons->Add(m_Close, 0, wxALL, 5); + + wxBoxSizer* sMain; + sMain = new wxBoxSizer(wxVERTICAL); + sMain->Add(m_Notebook, 1, wxEXPAND|wxALL, 5); + sMain->Add(sSButtons, 0, wxEXPAND, 5); + + this->SetSizer(sMain); + this->Layout(); + +#ifdef _WIN32 + // Add connected XPads + for (int x = 0; x < 4; x++) + { + XINPUT_STATE xstate; + DWORD xresult = XInputGetState(x, &xstate); + + if (xresult == ERROR_SUCCESS) + { + arrayStringFor_X360Pad.Add(wxString::Format(wxT("%i"), x+1)); + } + } +#endif + + for(int i = 0; i < 4; i++) + { + // -------------------------------------------------------------------- + // Settings + // ----------------------------- + // Main horizontal container + sDevice[i] = new wxBoxSizer(wxHORIZONTAL); + + sbDevice[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Controller Settings")); + m_Disable[i] = new wxCheckBox(m_Controller[i], ID_DISABLE, wxT("Disable when Dolphin is not in focus"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + sbDevice[i]->Add(m_Disable[i], 0, wxEXPAND|wxALL, 1); + +#ifdef _WIN32 + m_SizeXInput[i] = new wxStaticBoxSizer(wxHORIZONTAL, m_Controller[i], wxT("XInput Pad")); + m_X360Pad[i] = new wxCheckBox(m_Controller[i], ID_X360PAD, wxT("Enable X360Pad"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_X360PadC[i] = new wxChoice(m_Controller[i], ID_X360PAD_CHOICE, wxDefaultPosition, wxDefaultSize, arrayStringFor_X360Pad, 0, wxDefaultValidator); + m_Rumble[i] = new wxCheckBox(m_Controller[i], ID_RUMBLE, wxT("Enable rumble"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_SizeXInput[i]->Add(m_X360Pad[i], 0, wxEXPAND | wxALL, 1); + m_SizeXInput[i]->Add(m_X360PadC[i], 0, wxEXPAND | wxALL, 1); + m_SizeXInput[i]->Add(m_Rumble[i], 0, wxEXPAND | wxALL, 1); +#endif + // Set values + m_Disable[i]->SetValue(pad[i].bDisable); + +#ifdef _WIN32 + // Check if any XInput pad was found + if (arrayStringFor_X360Pad.IsEmpty()) + { + m_X360Pad[i]->SetLabel(wxT("Enable X360Pad - No pad connected")); + m_X360Pad[i]->SetValue(false); + m_X360Pad[i]->Enable(false); + pad[i].bEnableXPad = false; + m_X360PadC[i]->Hide(); + m_Rumble[i]->Hide(); + } + else + { + m_X360Pad[i]->SetValue(pad[i].bEnableXPad); + m_X360PadC[i]->SetSelection(pad[i].XPadPlayer); + m_X360PadC[i]->Enable(m_X360Pad[i]->IsChecked()); + m_Rumble[i]->SetValue(pad[i].bRumble); + m_Rumble[i]->Enable(m_X360Pad[i]->IsChecked()); + } +#endif + + // Sizers + sDevice[i]->Add(sbDevice[i], 0, wxEXPAND | wxALL, 1); + //sDevice[i]->AddStretchSpacer(); +#ifdef _WIN32 + sDevice[i]->Add(m_SizeXInput[i], 0, wxEXPAND | wxALL, 1); +#endif + // ----------------------------------- + + + + // Rerecording + // --------- +#ifdef RERECORDING + // Create controls + m_SizeRecording[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Input Recording")); + m_CheckRecording[i] = new wxCheckBox(m_Controller[i], ID_RECORDING, wxT("Record input")); + m_CheckPlayback[i] = new wxCheckBox(m_Controller[i], ID_PLAYBACK, wxT("Play back input")); + m_BtnSaveRecording[i] = new wxButton(m_Controller[i], ID_SAVE_RECORDING, wxT("Save recording"), wxDefaultPosition, wxDefaultSize); + + // Tool tips + m_CheckRecording[i]->SetToolTip(wxT("Your recording will be saved to pad-record.bin in the Dolphin dir when you stop the game")); + m_CheckPlayback[i]->SetToolTip(wxT("Play back the pad-record.bin file from the Dolphin dir")); + m_BtnSaveRecording[i]->SetToolTip(wxT( + "This will save the current recording to pad-record.bin. Your recording will\n" + "also be automatically saved every 60 * 10 frames. And when you shut down the\n" + "game.")); + + // Sizers + m_SizeRecording[i]->Add(m_CheckRecording[i], 0, wxEXPAND | wxALL, 1); + m_SizeRecording[i]->Add(m_CheckPlayback[i], 0, wxEXPAND | wxALL, 1); + m_SizeRecording[i]->Add(m_BtnSaveRecording[i], 0, wxEXPAND | wxALL, 1); + + // Only enable these options for pad 0 + m_CheckRecording[i]->Enable(false); m_CheckRecording[0]->Enable(true); + m_CheckPlayback[i]->Enable(false); m_CheckPlayback[0]->Enable(true); + m_BtnSaveRecording[i]->Enable(false); m_BtnSaveRecording[0]->Enable(true); + // Don't allow saving when we are not recording + m_BtnSaveRecording[i]->Enable(g_EmulatorRunning && pad[0].bRecording); + sDevice[i]->Add(m_SizeRecording[i], 0, wxEXPAND | wxALL, 1); + + // Set values + m_CheckRecording[0]->SetValue(pad[0].bRecording); + m_CheckPlayback[0]->SetValue(pad[0].bPlayback); + + //DEBUG_LOG(CONSOLE, "m_CheckRecording: %i\n", pad[0].bRecording, pad[0].bPlayback); +#endif + + + + // -------------------------------------------------------------------- + // Buttons + // ----------------------------- + sButtons[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Buttons")); + + AddControl(m_Controller[i], &(m_ButtonA[i]), sButtons[i], "A: ", CTL_A, i); + AddControl(m_Controller[i], &(m_ButtonB[i]), sButtons[i], "B: ", CTL_B, i); + AddControl(m_Controller[i], &(m_ButtonX[i]), sButtons[i], "X: ", CTL_X, i); + AddControl(m_Controller[i], &(m_ButtonY[i]), sButtons[i], "Y: ", CTL_Y, i); + AddControl(m_Controller[i], &(m_ButtonZ[i]), sButtons[i], "Z: ", CTL_Z, i); + AddControl(m_Controller[i], &(m_ButtonStart[i]), sButtons[i], "Start: ", CTL_START, i); + + sTriggers[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Triggers")); + + AddControl(m_Controller[i], &(m_ButtonL[i]), sTriggers[i], "L: ", CTL_L, i); + AddControl(m_Controller[i], &(m_ButtonR[i]), sTriggers[i], "R: ", CTL_R, i); + AddControl(m_Controller[i], &(m_ButtonL_Semi[i]), sTriggers[i], "Semi-L: ", CTL_L_SEMI, i); + AddControl(m_Controller[i], &(m_ButtonR_Semi[i]), sTriggers[i], "Semi-R: ", CTL_R_SEMI, i); + AddSlider(m_Controller[i], &(m_Trigger_SemiValue[i]), sTriggers[i], "Semi: ", ID_TRIGGER_SEMIVALUE, i); + + sStick[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("Main Stick")); + + AddControl(m_Controller[i], &(m_StickUp[i]), sStick[i], "Up: ", CTL_MAINUP, i); + AddControl(m_Controller[i], &(m_StickDown[i]), sStick[i], "Down: ", CTL_MAINDOWN, i); + AddControl(m_Controller[i], &(m_StickLeft[i]), sStick[i], "Left: ", CTL_MAINLEFT, i); + AddControl(m_Controller[i], &(m_StickRight[i]), sStick[i], "Right: ", CTL_MAINRIGHT, i); + AddControl(m_Controller[i], &(m_Stick_Semi[i]), sStick[i], "Semi-press: ", CTL_MAIN_SEMI, i); + AddSlider(m_Controller[i], &(m_Stick_SemiValue[i]), sStick[i], "Semi: ", ID_MAIN_SEMIVALUE, i); + + sDPad[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("D-Pad")); + + AddControl(m_Controller[i], &(m_DPadUp[i]), sDPad[i], "Up: ", CTL_DPADUP, i); + AddControl(m_Controller[i], &(m_DPadDown[i]), sDPad[i], "Down: ", CTL_DPADDOWN, i); + AddControl(m_Controller[i], &(m_DPadLeft[i]), sDPad[i], "Left: ", CTL_DPADLEFT, i); + AddControl(m_Controller[i], &(m_DPadRight[i]), sDPad[i], "Right: ", CTL_DPADRIGHT, i); + + sCStick[i] = new wxStaticBoxSizer(wxVERTICAL, m_Controller[i], wxT("C-Stick")); + + AddControl(m_Controller[i], &(m_CStickUp[i]), sCStick[i], "Up: ", CTL_SUBUP, i); + AddControl(m_Controller[i], &(m_CStickDown[i]), sCStick[i], "Down: ", CTL_SUBDOWN, i); + AddControl(m_Controller[i], &(m_CStickLeft[i]), sCStick[i], "Left: ", CTL_SUBLEFT, i); + AddControl(m_Controller[i], &(m_CStickRight[i]), sCStick[i], "Right: ", CTL_SUBRIGHT, i); + AddControl(m_Controller[i], &(m_CStick_Semi[i]), sCStick[i], "Semi-press: ", CTL_SUB_SEMI, i); + AddSlider(m_Controller[i], &(m_CStick_SemiValue[i]), sCStick[i], "Semi: ", ID_SUB_SEMIVALUE, i); + + // -------------------------------------------------------------------- + // Sizers + // ----------------------------- + sPage[i] = new wxGridBagSizer(0, 0); + sPage[i]->SetFlexibleDirection(wxBOTH); + sPage[i]->SetNonFlexibleGrowMode(wxFLEX_GROWMODE_SPECIFIED); + sPage[i]->Add(sDevice[i], wxGBPosition(0, 0), wxGBSpan(1, 5), wxEXPAND|wxALL, 1); + sPage[i]->Add(sButtons[i], wxGBPosition(1, 0), wxGBSpan(2, 1), wxALL, 1); + sPage[i]->Add(sTriggers[i], wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 1); + sPage[i]->Add(sStick[i], wxGBPosition(1, 2), wxGBSpan(2, 1), wxALL, 1); + sPage[i]->Add(sDPad[i], wxGBPosition(1, 3), wxGBSpan(2, 1), wxALL, 1); + sPage[i]->Add(sCStick[i], wxGBPosition(1, 4), wxGBSpan(2, 1), wxALL, 1); + m_Controller[i]->SetSizer(sPage[i]); + sPage[i]->Layout(); + } +} + +void PADConfigDialogSimple::OnClose(wxCloseEvent& event) +{ +#ifdef _WIN32 + m_dinput.Free(); +#endif + EndModal(0); +} + +void PADConfigDialogSimple::OnCloseClick(wxCommandEvent& event) +{ + Close(); +} + +void PADConfigDialogSimple::OnShow(wxShowEvent& event) +{ +#ifdef _WIN32 + m_dinput.Init((HWND)this->GetParent()); +#endif +} + +void PADConfigDialogSimple::OnKeyDown(wxKeyEvent& event) +{ + char keyStr[10] = {0}; + if(ClickedButton) + { + // Get the selected notebook page + int page = m_Notebook->GetSelection(); + +#ifdef _WIN32 + m_dinput.Read(); + if (m_dinput.diks[DIK_ESCAPE]) + { + pad[page].keyForControl[ClickedButton->GetId()] = 0x00; + ClickedButton->SetLabel(wxString::FromAscii("")); + } + else + { + for(int i = 0; i < 255; i++) + { + if(m_dinput.diks[i]) + { + // Save the mapped key + pad[page].keyForControl[ClickedButton->GetId()] = i; + // Get the key name + DInput::DIKToString(i, keyStr); + ClickedButton->SetLabel(wxString::FromAscii(keyStr)); + break; + } + } + } + +#elif defined(HAVE_X11) && HAVE_X11 + if (event.GetKeyCode() == (XK_Escape & 0xFF)) + { + pad[page].keyForControl[ClickedButton->GetId()] = InputCommon::wxCharCodeWXToX(0x00); + ClickedButton->SetLabel(wxString::FromAscii("")); + } + else + { + pad[page].keyForControl[ClickedButton->GetId()] = InputCommon::wxCharCodeWXToX(event.GetKeyCode()); + InputCommon::XKeyToString(pad[page].keyForControl[ClickedButton->GetId()], keyStr); + ClickedButton->SetLabel(wxString::FromAscii(keyStr)); + } +#endif + ClickedButton->Disconnect(); + } + // Reset + ClickedButton = NULL; + //event.Skip(); +} + +// We have clicked a button +void PADConfigDialogSimple::OnButtonClick(wxCommandEvent& event) +{ + // Check if the Space key was set, to solve the problem that the Space key calls this function +#ifdef _WIN32 + if (m_dinput.diks[DIK_SPACE]) { m_dinput.diks[DIK_SPACE] = 0; return; } +#endif + + // If we come here again before any key was set + if(ClickedButton) ClickedButton->SetLabel(oldLabel); + + // Save the old button label so we can reapply it if necessary + ClickedButton = (wxButton *)event.GetEventObject(); + oldLabel = ClickedButton->GetLabel(); + ClickedButton->SetLabel(_("Press Key/Esc")); + + ClickedButton->SetWindowStyle(wxWANTS_CHARS); +} + +void PADConfigDialogSimple::ControllerSettingsChanged(wxCommandEvent& event) +{ + int page = m_Notebook->GetSelection(); + std::stringstream ss; + + switch (event.GetId()) + { + // General settings + case ID_DISABLE: + pad[page].bDisable = m_Disable[page]->GetValue(); + break; + + // XInput + case ID_X360PAD: + pad[page].bEnableXPad = event.IsChecked(); + m_Rumble[page]->Enable(event.IsChecked()); + m_X360PadC[page]->Enable(event.IsChecked()); + break; + case ID_X360PAD_CHOICE: + pad[page].XPadPlayer = event.GetSelection(); + break; + case ID_RUMBLE: + pad[page].bRumble = m_Rumble[page]->GetValue(); + break; + + // Semi-press adjustment + case ID_TRIGGER_SEMIVALUE: + pad[page].Trigger_semivalue = m_Trigger_SemiValue[page]->GetValue(); + ss << pad[page].Trigger_semivalue; + (*m_Trigger_SemiValue_Label[page]).SetLabel(wxString::FromAscii( ss.str().c_str() )); + break; + case ID_MAIN_SEMIVALUE: + pad[page].Main_stick_semivalue = m_Stick_SemiValue[page]->GetValue(); + ss << pad[page].Main_stick_semivalue; + (*m_Stick_SemiValue_Label[page]).SetLabel(wxString::FromAscii( ss.str().c_str() )); + break; + case ID_SUB_SEMIVALUE: + pad[page].Sub_stick_semivalue = m_CStick_SemiValue[page]->GetValue(); + ss << pad[page].Sub_stick_semivalue; + (*m_CStick_SemiValue_Label[page]).SetLabel(wxString::FromAscii( ss.str().c_str() )); + break; + + // Input recording +#ifdef RERECORDING + case ID_RECORDING: + pad[page].bRecording = m_CheckRecording[page]->GetValue(); + // Turn off the other option + pad[page].bPlayback = false; m_CheckPlayback[page]->SetValue(false); + break; + case ID_PLAYBACK: + pad[page].bPlayback = m_CheckPlayback[page]->GetValue(); + // Turn off the other option + pad[page].bRecording = false; m_CheckRecording[page]->SetValue(false); + break; + case ID_SAVE_RECORDING: + // Double check again that we are still running a game + if (g_EmulatorRunning) SaveRecord(); + break; +#endif + } +} + +void PADConfigDialogSimple::DllAbout(wxCommandEvent& event) +{ + wxString message; +#ifdef _WIN32 + message = _("A simple keyboard and XInput plugin for dolphin."); +#else + message = _("A simple keyboard plugin for dolphin."); +#endif + + wxMessageBox(_T("Dolphin PadSimple Plugin\nBy ector and F|RES\n\n" + message), + _T("Dolphin PadSimple"), wxOK, this); +} diff --git a/desmume/src/wx/PadSimple/GUI/ConfigDlg.h b/desmume/src/wx/PadSimple/GUI/ConfigDlg.h new file mode 100644 index 000000000..d7fb70808 --- /dev/null +++ b/desmume/src/wx/PadSimple/GUI/ConfigDlg.h @@ -0,0 +1,149 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef __CONFIGDLG_H__ +#define __CONFIGDLG_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_X11) && HAVE_X11 +#include "X11InputBase.h" +#endif + +class PADConfigDialogSimple : public wxDialog +{ + + + public: + PADConfigDialogSimple(wxWindow *parent, wxWindowID id = 1, const wxString &title = wxT("Pad Configuration"), + const wxPoint& pos = wxDefaultPosition, const wxSize& size = wxDefaultSize, long style = wxDEFAULT_DIALOG_STYLE); + + virtual ~PADConfigDialogSimple(); + + private: + DECLARE_EVENT_TABLE(); + wxNotebook *m_Notebook; + wxPanel *m_Controller[4]; + wxButton *m_About; + wxButton *m_Close; + + wxStaticBoxSizer *sbDevice[4], *m_SizeXInput[4], *m_SizeRecording[4]; + wxBoxSizer *sDevice[4]; + wxGridBagSizer *sPage[4]; + wxStaticBoxSizer *sButtons[4]; + wxStaticBoxSizer *sTriggers[4]; + wxStaticBoxSizer *sStick[4]; + wxStaticBoxSizer *sCStick[4]; + wxStaticBoxSizer *sDPad[4]; + + wxArrayString arrayStringFor_X360Pad; + wxCheckBox *m_X360Pad[4]; + wxChoice *m_X360PadC[4]; + wxCheckBox *m_Disable[4]; + wxCheckBox *m_Rumble[4]; + + // Recording + wxCheckBox *m_CheckRecording[4]; + wxCheckBox *m_CheckPlayback[4]; + wxButton *m_BtnSaveRecording[4]; + + wxButton *m_ButtonA[4]; + wxButton *m_ButtonB[4]; + wxButton *m_ButtonX[4]; + wxButton *m_ButtonY[4]; + wxButton *m_ButtonZ[4]; + wxButton *m_ButtonStart[4]; + wxButton *m_ButtonL[4]; + wxButton *m_ButtonR[4]; + wxButton *m_ButtonL_Semi[4]; + wxButton *m_ButtonR_Semi[4]; + wxSlider *m_Trigger_SemiValue[4]; + wxStaticText *m_Trigger_SemiValue_Label[4]; + wxButton *m_StickUp[4]; + wxButton *m_StickDown[4]; + wxButton *m_StickLeft[4]; + wxButton *m_StickRight[4]; + wxButton *m_Stick_Semi[4]; + wxSlider *m_Stick_SemiValue[4]; + wxStaticText *m_Stick_SemiValue_Label[4]; + wxButton *m_CStickUp[4]; + wxButton *m_CStickDown[4]; + wxButton *m_CStickLeft[4]; + wxButton *m_CStickRight[4]; + wxButton *m_CStick_Semi[4]; + wxSlider *m_CStick_SemiValue[4]; + wxStaticText *m_CStick_SemiValue_Label[4]; + wxButton *m_DPadUp[4]; + wxButton *m_DPadDown[4]; + wxButton *m_DPadLeft[4]; + wxButton *m_DPadRight[4]; + + enum + { + ////GUI Enum Control ID Start + ID_CLOSE = 1000, + ID_NOTEBOOK, + ID_CONTROLLERPAGE1, + ID_CONTROLLERPAGE2, + ID_CONTROLLERPAGE3, + ID_CONTROLLERPAGE4, + + // XInput pad + ID_X360PAD_CHOICE, + ID_X360PAD, + ID_RUMBLE, + + // Semi-press values + ID_TRIGGER_SEMIVALUE, + ID_MAIN_SEMIVALUE, + ID_SUB_SEMIVALUE, + + // Input recording + ID_RECORDING, + ID_PLAYBACK, + ID_SAVE_RECORDING, + + // General settings + ID_DISABLE, + ID_PAD_ABOUT, + }; + + void OnClose(wxCloseEvent& event); + void CreateGUIControls(); + void OnCloseClick(wxCommandEvent& event); + void OnKeyDown(wxKeyEvent& event); + void ControllerSettingsChanged(wxCommandEvent& event); + void OnButtonClick(wxCommandEvent& event); + void DllAbout(wxCommandEvent& event); + void OnShow(wxShowEvent& event); + void AddSlider(wxPanel *pan, wxSlider **slider, + wxStaticBoxSizer *sizer, const char *name, int ctl, int controller); + + wxButton *ClickedButton; + wxString oldLabel; +}; + +#endif diff --git a/desmume/src/wx/PadSimple/PadSimple.cpp b/desmume/src/wx/PadSimple/PadSimple.cpp new file mode 100644 index 000000000..2da512dcb --- /dev/null +++ b/desmume/src/wx/PadSimple/PadSimple.cpp @@ -0,0 +1,994 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + + +// Include +#include +#include + +#include "Common.h" +//#include "LogManager.h" +#include "pluginspecs_pad.h" +#include "PadSimple.h" +//#include "IniFile.h" +//#include "StringUtil.h" +//#include "FileUtil.h" +//#include "ChunkFile.h" + +#if defined(HAVE_WX) && HAVE_WX + #include "GUI/ConfigDlg.h" + PADConfigDialogSimple* m_ConfigFrame = NULL; +#endif + +#ifdef _WIN32 + #include "XInput.h" + #include "../InputCommon/DirectInputBase.h" // Core + + DInput dinput; + //#elif defined(USE_SDL) && USE_SDL + //#include + +#elif defined(HAVE_X11) && HAVE_X11 + + #include + #include + #include + #include + + Display* GXdsp; + bool KeyStatus[NUMCONTROLS]; +#elif defined(HAVE_COCOA) && HAVE_COCOA + #include + bool KeyStatus[NUMCONTROLS]; +#endif + + + +// Declarations +SPads pad[4]; +SPADInitialize g_PADInitialize; + + +// Standard crap to make wxWidgets happy +#ifdef _WIN32 +//HINSTANCE g_hInstance; + +#if defined(HAVE_WX) && HAVE_WX +class wxDLLApp : public wxApp +{ + bool OnInit() + { + return true; + } +}; +IMPLEMENT_APP_NO_MAIN(wxDLLApp) +WXDLLIMPEXP_BASE void wxSetInstance(HINSTANCE hInst); +#endif + +BOOL APIENTRY aMain(HINSTANCE hinstDLL, // DLL module handle + DWORD dwReason, // reason called + LPVOID lpvReserved) // reserved +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + { +#if defined(HAVE_WX) && HAVE_WX + wxSetInstance((HINSTANCE)hinstDLL); + int argc = 0; + char **argv = NULL; + wxEntryStart(argc, argv); + if (!wxTheApp || !wxTheApp->CallOnInit()) + return FALSE; +#endif + } + break; + + case DLL_PROCESS_DETACH: +#if defined(HAVE_WX) && HAVE_WX + wxEntryCleanup(); +#endif + break; + default: + break; + } + + //g_hInstance = hinstDLL; + return TRUE; +} +#endif + +#if defined(HAVE_WX) && HAVE_WX +wxWindow* GetParentedWxWindow(HWND Parent) +{ +#ifdef _WIN32 +// wxSetInstance((HINSTANCE)g_hInstance); +#endif + wxWindow *win = new wxWindow(); +#ifdef _WIN32 + win->SetHWND((WXHWND)Parent); + win->AdoptAttributesFromHWND(); +#endif + return win; +} +#endif + + + +// Input Recording + +// Enable these to record or play back +//#define RECORD_REPLAY +//#define RECORD_STORE +#if 0 +// Pre defined maxium storage limit +#define RECORD_SIZE (1024 * 128) +PLUGIN_GLOBALS* globals = NULL; +SPADStatus recordBuffer[RECORD_SIZE]; +int count = 0; +bool g_EmulatorRunning = false; + +//****************************************************************************** +// Supporting functions +//****************************************************************************** + +void RecordInput(const SPADStatus& _rPADStatus) +{ + if (count >= RECORD_SIZE) return; + recordBuffer[count++] = _rPADStatus; + + // Logging + //u8 TmpData[sizeof(SPADStatus)]; + //memcpy(TmpData, &recordBuffer[count - 1], sizeof(SPADStatus)); + //Console::Print("RecordInput(%i): %s\n", count, ArrayToString(TmpData, sizeof(SPADStatus), 0, 30).c_str()); + + // Auto save every ten seconds + if (count % (60 * 10) == 0) SaveRecord(); +} + +const SPADStatus& PlayRecord() +{ + // Logging + //Console::Print("PlayRecord(%i)\n", count); + + if (count >= RECORD_SIZE) + { + // Todo: Make the recording size unlimited? + //PanicAlert("The recording reached its end"); + return(recordBuffer[0]); + } + return(recordBuffer[count++]); +} + +void LoadRecord() +{ + FILE* pStream = fopen("pad-record.bin", "rb"); + + if (pStream != NULL) + { + fread(recordBuffer, 1, RECORD_SIZE * sizeof(SPADStatus), pStream); + fclose(pStream); + } + else + { + PanicAlert("SimplePad: Could not open pad-record.bin"); + } + + //Console::Print("LoadRecord()"); +} + +void SaveRecord() +{ + // Open the file in a way that clears all old data + FILE* pStream = fopen("pad-record.bin", "wb"); + + if (pStream != NULL) + { + fwrite(recordBuffer, 1, RECORD_SIZE * sizeof(SPADStatus), pStream); + fclose(pStream); + } + else + { + PanicAlert("SimplePad: Could not save pad-record.bin"); + } + //PanicAlert("SaveRecord()"); + //Console::Print("SaveRecord()"); +} + +#endif +// Check if Dolphin is in focus +bool IsFocus() +{ +#ifdef _WIN32 + HWND Parent = GetParent(g_PADInitialize.hWnd); + HWND TopLevel = GetParent(Parent); + // Support both rendering to main window and not + if (GetForegroundWindow() == TopLevel || GetForegroundWindow() == g_PADInitialize.hWnd) + return true; + else + return false; +#else + return true; +#endif +} + +// Implement circular deadzone +const float kDeadZone = 0.1f; +void ScaleStickValues(unsigned char* outx, + unsigned char* outy, + short inx, short iny) +{ + float x = ((float)inx + 0.5f) / 32767.5f; + float y = ((float)iny + 0.5f) / 32767.5f; + + if ((x == 0.0f) && (y == 0.0f)) // to be safe + { + *outx = 0; + *outy = 0; + return; + } + + float magnitude = sqrtf(x * x + y * y); + float nx = x / magnitude; + float ny = y / magnitude; + + if (magnitude < kDeadZone){magnitude = kDeadZone;} + + magnitude = (magnitude - kDeadZone) / (1.0f - kDeadZone); + magnitude *= magnitude; // another power may be more appropriate + nx *= magnitude; + ny *= magnitude; + int ix = (int)(nx * 100); + int iy = (int)(ny * 100); + *outx = 0x80 + ix; + *outy = 0x80 + iy; +} + +// for same displacement should be sqrt(2)/2 (in theory) +// 3/4 = 0.75 is a little faster than sqrt(2)/2 = 0.7071... +// In SMS, 17/20 = 0.85 is perfect; in WW, 7/10 = 0.70 is closer. +#define DIAGONAL_SCALE 0.70710678 +void EmulateAnalogStick(unsigned char *stickX, unsigned char *stickY, bool buttonUp, bool buttonDown, bool buttonLeft, bool buttonRight, int magnitude) { + int mainY = 0; + int mainX = 0; + if (buttonUp) + mainY = magnitude; + else if (buttonDown) + mainY = -magnitude; + if (buttonLeft) + mainX = -magnitude; + else if (buttonRight) + mainX = magnitude; + // only update if there is some action + // this allows analog stick to still work + // disable for now, enable later if any platform supports both methods of input + //if ((mainX != 0) && (mainY != 0)) { + if (true) { + if ((mainX == 0) || (mainY == 0)) + { + *stickX += mainX; + *stickY += mainY; + } + else + { + *stickX += mainX*DIAGONAL_SCALE; + *stickY += mainY*DIAGONAL_SCALE; + } + } +} + +//****************************************************************************** +// Input +//****************************************************************************** + + +#ifdef _WIN32 +void DInput_Read(int _numPAD, SPADStatus* _pPADStatus) +{ + dinput.Read(); + +// for (int i = 0; i < 200; i++) +// if(dinput.diks[i]) +// printf("DInput_Read"); + + // Analog stick values based on semi-press keys + int mainstickvalue = (dinput.diks[pad[_numPAD].keyForControl[CTL_MAIN_SEMI]] & 0xFF) ? pad[_numPAD].Main_stick_semivalue : STICK_FULL; + int substickvalue = (dinput.diks[pad[_numPAD].keyForControl[CTL_SUB_SEMI]] & 0xFF) ? pad[_numPAD].Sub_stick_semivalue : STICK_FULL; + // Buttons (A/B/X/Y/Z/Start) + if (dinput.diks[pad[_numPAD].keyForControl[CTL_A]] & 0xFF) + { + _pPADStatus->button |= PAD_BUTTON_A; + _pPADStatus->analogA = BUTTON_FULL; + } + if (dinput.diks[pad[_numPAD].keyForControl[CTL_B]] & 0xFF) + { + _pPADStatus->button |= PAD_BUTTON_B; + _pPADStatus->analogB = BUTTON_FULL; + } + if (dinput.diks[pad[_numPAD].keyForControl[CTL_X]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_X;} + if (dinput.diks[pad[_numPAD].keyForControl[CTL_Y]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_Y;} + if (dinput.diks[pad[_numPAD].keyForControl[CTL_Z]] & 0xFF){_pPADStatus->button |= PAD_TRIGGER_Z;} + if (dinput.diks[pad[_numPAD].keyForControl[CTL_START]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_START;} + // Triggers (L/R) + if (dinput.diks[pad[_numPAD].keyForControl[CTL_L]] & 0xFF) + { + _pPADStatus->button |= PAD_TRIGGER_L; + _pPADStatus->triggerLeft = TRIGGER_FULL; + } + if (dinput.diks[pad[_numPAD].keyForControl[CTL_R]] & 0xFF) + { + _pPADStatus->button |= PAD_TRIGGER_R; + _pPADStatus->triggerRight = TRIGGER_FULL; + } + if (dinput.diks[pad[_numPAD].keyForControl[CTL_L_SEMI]] & 0xFF) + { + _pPADStatus->triggerLeft = pad[_numPAD].Trigger_semivalue; + if (pad[_numPAD].Trigger_semivalue > TRIGGER_THRESHOLD) + _pPADStatus->button |= PAD_TRIGGER_L; + } + if (dinput.diks[pad[_numPAD].keyForControl[CTL_R_SEMI]] & 0xFF) + { + _pPADStatus->triggerRight = pad[_numPAD].Trigger_semivalue; + if (pad[_numPAD].Trigger_semivalue > TRIGGER_THRESHOLD) + _pPADStatus->button |= PAD_TRIGGER_R; + } + // Main stick + EmulateAnalogStick( + &_pPADStatus->stickX, + &_pPADStatus->stickY, + !!dinput.diks[pad[_numPAD].keyForControl[CTL_MAINUP]], + !!dinput.diks[pad[_numPAD].keyForControl[CTL_MAINDOWN]], + !!dinput.diks[pad[_numPAD].keyForControl[CTL_MAINLEFT]], + !!dinput.diks[pad[_numPAD].keyForControl[CTL_MAINRIGHT]], + mainstickvalue ); + + // Sub-stick (C-stick) + EmulateAnalogStick( + &_pPADStatus->substickX, + &_pPADStatus->substickY, + !!dinput.diks[pad[_numPAD].keyForControl[CTL_SUBUP]], + !!dinput.diks[pad[_numPAD].keyForControl[CTL_SUBDOWN]], + !!dinput.diks[pad[_numPAD].keyForControl[CTL_SUBLEFT]], + !!dinput.diks[pad[_numPAD].keyForControl[CTL_SUBRIGHT]], + substickvalue ); + // D-pad + if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADUP]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_UP;} + if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADDOWN]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_DOWN;} + if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADLEFT]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_LEFT;} + if (dinput.diks[pad[_numPAD].keyForControl[CTL_DPADRIGHT]] & 0xFF){_pPADStatus->button |= PAD_BUTTON_RIGHT;} + // Mic key + _pPADStatus->MicButton = (dinput.diks[pad[_numPAD].keyForControl[CTL_MIC]] & 0xFF) ? true : false; +} + +bool XInput_Read(int XPadPlayer, SPADStatus* _pPADStatus) +{ + const int base = 0x80; + XINPUT_STATE xstate; + DWORD xresult = XInputGetState(XPadPlayer, &xstate); + + // Let's .. yes, let's use XINPUT! + if (xresult == ERROR_SUCCESS) + { + const XINPUT_GAMEPAD& xpad = xstate.Gamepad; + + if ((_pPADStatus->stickX == base) && (_pPADStatus->stickY == base)) + { + ScaleStickValues( + &_pPADStatus->stickX, + &_pPADStatus->stickY, + xpad.sThumbLX, + xpad.sThumbLY); + } + + if ((_pPADStatus->substickX == base) && (_pPADStatus->substickY == base)) + { + ScaleStickValues( + &_pPADStatus->substickX, + &_pPADStatus->substickY, + xpad.sThumbRX, + xpad.sThumbRY); + } + + if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_UP) {_pPADStatus->button |= PAD_BUTTON_UP;} + if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_DOWN) {_pPADStatus->button |= PAD_BUTTON_DOWN;} + if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_LEFT) {_pPADStatus->button |= PAD_BUTTON_LEFT;} + if (xpad.wButtons & XINPUT_GAMEPAD_DPAD_RIGHT) {_pPADStatus->button |= PAD_BUTTON_RIGHT;} + + _pPADStatus->triggerLeft = xpad.bLeftTrigger; + _pPADStatus->triggerRight = xpad.bRightTrigger; + if (xpad.bLeftTrigger > TRIGGER_THRESHOLD) {_pPADStatus->button |= PAD_TRIGGER_L;} + if (xpad.bRightTrigger > TRIGGER_THRESHOLD) {_pPADStatus->button |= PAD_TRIGGER_R;} + if (xpad.wButtons & XINPUT_GAMEPAD_START) {_pPADStatus->button |= PAD_BUTTON_START;} + if (xpad.wButtons & XINPUT_GAMEPAD_A) {_pPADStatus->button |= PAD_BUTTON_A;} + if (xpad.wButtons & XINPUT_GAMEPAD_B) {_pPADStatus->button |= PAD_BUTTON_X;} + if (xpad.wButtons & XINPUT_GAMEPAD_X) {_pPADStatus->button |= PAD_BUTTON_B;} + if (xpad.wButtons & XINPUT_GAMEPAD_Y) {_pPADStatus->button |= PAD_BUTTON_Y;} + if (xpad.wButtons & XINPUT_GAMEPAD_RIGHT_SHOULDER){_pPADStatus->button |= PAD_TRIGGER_Z;} + + //_pPADStatus->MicButton = (xpad.wButtons & XINPUT_GAMEPAD_LEFT_SHOULDER) ? true : false; + + return true; + } + else + { + return false; + } +} +#endif + +#if (defined(HAVE_X11) && HAVE_X11) || (defined(HAVE_COCOA) && HAVE_COCOA) +#if defined(HAVE_X11) && HAVE_X11 +// The graphics plugin in the PCSX2 design leaves a lot of the window processing to the pad plugin, weirdly enough. +void X11_Read(int _numPAD, SPADStatus* _pPADStatus) +{ + // Do all the stuff we need to do once per frame here + if (_numPAD != 0) + return; + // This code is from Zerofrog's pcsx2 pad plugin + XEvent E; + //int keyPress=0, keyRelease=0; + KeySym key; + + // keyboard input + int num_events; + for (num_events = XPending(GXdsp);num_events > 0;num_events--) + { + XNextEvent(GXdsp, &E); + switch (E.type) + { + case KeyPress: + //_KeyPress(pad, XLookupKeysym((XKeyEvent *)&E, 0)); + //break; + key = XLookupKeysym((XKeyEvent*)&E, 0); + + if((key >= XK_F1 && key <= XK_F9) || + key == XK_Shift_L || key == XK_Shift_R || + key == XK_Control_L || key == XK_Control_R) + { + XPutBackEvent(GXdsp, &E); + break; + } + int i; + for (i = 0; i < NUMCONTROLS; i++) { + if (key == pad[_numPAD].keyForControl[i]) { + KeyStatus[i] = true; + break; + } + } + break; + case KeyRelease: + key = XLookupKeysym((XKeyEvent*)&E, 0); + if((key >= XK_F1 && key <= XK_F9) || + key == XK_Shift_L || key == XK_Shift_R || + key == XK_Control_L || key == XK_Control_R) + { + XPutBackEvent(GXdsp, &E); + break; + } + //_KeyRelease(pad, XLookupKeysym((XKeyEvent *)&E, 0)); + for (i = 0; i < NUMCONTROLS; i++) + { + if (key == pad[_numPAD].keyForControl[i]) + { + KeyStatus[i] = false; + break; + } + } + break; + } + } +#elif defined(HAVE_COCOA) && HAVE_COCOA +void cocoa_Read(int _numPAD, SPADStatus* _pPADStatus) +{ + // Do all the stuff we need to do once per frame here + if (_numPAD != 0) + return; + //get event from main thread + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + NSConnection *conn; + NSDistantObject *proxy; + + conn = [NSConnection connectionWithRegisteredName:@"DolphinCocoaEvent" host:nil]; + //if (!conn) { + //printf("error creating cnx event client\n"); + //} + proxy = [conn rootProxy]; + //if (!proxy) { + // printf("error prox client\n"); + //} + + long cocoaKey = (long)[proxy keyCode]; + + int i; + if ((long)[proxy type] == 10) + { + for (i = 0; i < NUMCONTROLS; i++) + { + if (cocoaKey == pad[_numPAD].keyForControl[i]) + { + KeyStatus[i] = true; + break; + } + } + } + else + { + for (i = 0; i < NUMCONTROLS; i++) + { + if (cocoaKey == pad[_numPAD].keyForControl[i]) + { + KeyStatus[i] = false; + break; + } + } + } +#endif + // Analog stick values based on semi-press keys + int mainstickvalue = (KeyStatus[CTL_MAIN_SEMI]) ? pad[_numPAD].Main_stick_semivalue : STICK_FULL; + int substickvalue = (KeyStatus[CTL_SUB_SEMI]) ? pad[_numPAD].Sub_stick_semivalue : STICK_FULL; + // Buttons (A/B/X/Y/Z/Start) + if (KeyStatus[CTL_A]) + { + _pPADStatus->button |= PAD_BUTTON_A; + _pPADStatus->analogA = BUTTON_FULL; + } + if (KeyStatus[CTL_B]) + { + _pPADStatus->button |= PAD_BUTTON_B; + _pPADStatus->analogB = BUTTON_FULL; + } + if (KeyStatus[CTL_X]){_pPADStatus->button |= PAD_BUTTON_X;} + if (KeyStatus[CTL_Y]){_pPADStatus->button |= PAD_BUTTON_Y;} + if (KeyStatus[CTL_Z]){_pPADStatus->button |= PAD_TRIGGER_Z;} + if (KeyStatus[CTL_START]){_pPADStatus->button |= PAD_BUTTON_START;} + // Triggers (L/R) + if (KeyStatus[CTL_L]){_pPADStatus->triggerLeft = TRIGGER_FULL;} + if (KeyStatus[CTL_R]){_pPADStatus->triggerRight = TRIGGER_FULL;} + if (KeyStatus[CTL_L_SEMI]){_pPADStatus->triggerLeft = pad[_numPAD].Trigger_semivalue;} + if (KeyStatus[CTL_R_SEMI]){_pPADStatus->triggerRight = pad[_numPAD].Trigger_semivalue;} + // Main stick + EmulateAnalogStick( + &_pPADStatus->stickX, + &_pPADStatus->stickY, + KeyStatus[CTL_MAINUP], + KeyStatus[CTL_MAINDOWN], + KeyStatus[CTL_MAINLEFT], + KeyStatus[CTL_MAINRIGHT], + mainstickvalue ); + EmulateAnalogStick( + &_pPADStatus->substickX, + &_pPADStatus->substickY, + KeyStatus[CTL_SUBUP], + KeyStatus[CTL_SUBDOWN], + KeyStatus[CTL_SUBLEFT], + KeyStatus[CTL_SUBRIGHT], + substickvalue ); + // D-pad + if (KeyStatus[CTL_DPADUP]) {_pPADStatus->button |= PAD_BUTTON_UP;} + if (KeyStatus[CTL_DPADDOWN]) {_pPADStatus->button |= PAD_BUTTON_DOWN;} + if (KeyStatus[CTL_DPADLEFT]) {_pPADStatus->button |= PAD_BUTTON_LEFT;} + if (KeyStatus[CTL_DPADRIGHT]){_pPADStatus->button |= PAD_BUTTON_RIGHT;} + // Mic key + _pPADStatus->MicButton = KeyStatus[CTL_MIC]; +#if defined(HAVE_X11) && HAVE_X11 +} +#elif defined(HAVE_COCOA) && HAVE_COCOA + [pool release]; +} +#endif +#endif + +//****************************************************************************** +// Plugin specification functions +//****************************************************************************** +#if 0 +void GetDllInfo(PLUGIN_INFO* _PluginInfo) +{ + _PluginInfo->Version = 0x0100; + _PluginInfo->Type = PLUGIN_TYPE_PAD; + +#ifdef DEBUGFAST + sprintf(_PluginInfo->Name, "Dolphin KB/X360pad (DebugFast)"); +#else +#ifndef _DEBUG + sprintf(_PluginInfo->Name, "Dolphin KB/X360pad"); +#else + sprintf(_PluginInfo->Name, "Dolphin KB/X360pad (Debug)"); +#endif +#endif + +} + +void SetDllGlobals(PLUGIN_GLOBALS* _pPluginGlobals) +{ + globals = _pPluginGlobals; + LogManager::SetInstance((LogManager *)globals->logManager); +} + +void DllConfig(HWND _hParent) +{ + // Load configuration + LoadConfig(); + + // Show wxDialog +#if defined(HAVE_WX) && HAVE_WX + if (!m_ConfigFrame) + m_ConfigFrame = new PADConfigDialogSimple(GetParentedWxWindow(_hParent)); + else if (!m_ConfigFrame->GetParent()->IsShown()) + m_ConfigFrame->Close(true); + + // Only allow one open at a time + if (!m_ConfigFrame->IsShown()) + m_ConfigFrame->ShowModal(); + else + m_ConfigFrame->Hide(); +#endif + + // Save configuration + SaveConfig(); +} + +void DllDebugger(HWND _hParent, bool Show) {} +#endif +void Initialize(void *init) +{ + // We are now running a game +// g_EmulatorRunning = true; + + // Load configuration + LoadConfig(); + +#ifdef RERECORDING + /* Check if we are starting the pad to record the input, and an old file exists. In that case ask + if we really want to start the recording and eventually overwrite the file */ + if (pad[0].bRecording && File::Exists("pad-record.bin")) + { + if (!AskYesNo("An old version of '%s' aleady exists in your Dolphin directory. You can" + " now make a copy of it before you start a new recording and overwrite the file." + " Select Yes to continue and overwrite the file. Select No to turn off the input" + " recording and continue without recording anything.", + "pad-record.bin")) + { + // Turn off recording and continue + pad[0].bRecording = false; + } + } + + // Load recorded input if we are to play it back, otherwise begin with a blank recording + if (pad[0].bPlayback) LoadRecord(); +#endif + + g_PADInitialize = *(SPADInitialize*)init; + + #ifdef _WIN32 + dinput.Init((HWND)g_PADInitialize.hWnd); + #elif defined(HAVE_X11) && HAVE_X11 + GXdsp = (Display*)g_PADInitialize.hWnd; + #elif defined(HAVE_COCOA) && HAVE_COCOA + + #endif +} + +void DoState(unsigned char **ptr, int mode) +{ +#ifdef RERECORDING + // Load or save the counter + PointerWrap p(ptr, mode); + p.Do(count); + + //Console::Print("count: %i\n", count); + + // Update the frame counter for the sake of the status bar + if (mode == PointerWrap::MODE_READ) + { + #ifdef _WIN32 + // This only works when rendering to the main window, I think + PostMessage(GetParent(g_PADInitialize.hWnd), WM_USER, INPUT_FRAME_COUNTER, count); + #endif + } +#endif +} + +void Shutdown() +{ + // Save the recording and reset the counter +#ifdef RERECORDING + // Save recording + if (pad[0].bRecording) SaveRecord(); + // Reset the counter + count = 0; +#endif + + // We have stopped the game + //g_EmulatorRunning = false; + +#ifdef _WIN32 + dinput.Free(); + // Kill xpad rumble + XINPUT_VIBRATION vib; + vib.wLeftMotorSpeed = 0; + vib.wRightMotorSpeed = 0; + for (int i = 0; i < 4; i++) + if (pad[i].bRumble) + XInputSetState(pad[i].XPadPlayer, &vib); +#endif + SaveConfig(); +} + + +// Set buttons status from wxWidgets in the main application +void PAD_Input(u16 _Key, u8 _UpDown) {} + + +void PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus) +{ + // Check if all is okay + if (_pPADStatus == NULL) return; + + // Play back input instead of accepting any user input +#ifdef RERECORDING + if (pad[0].bPlayback) + { + *_pPADStatus = PlayRecord(); + return; + } +#endif + + const int base = 0x80; + // Clear pad + memset(_pPADStatus, 0, sizeof(SPADStatus)); + + _pPADStatus->stickY = base; + _pPADStatus->stickX = base; + _pPADStatus->substickX = base; + _pPADStatus->substickY = base; + _pPADStatus->button |= PAD_USE_ORIGIN; +#ifdef _WIN32 + // Only update pad on focus, don't do this when recording + if (pad[_numPAD].bDisable && !pad[0].bRecording && !IsFocus()) return; + + // Dolphin doesn't really care about the pad error codes anyways... + _pPADStatus->err = PAD_ERR_NONE; + + // Read XInput + if (pad[_numPAD].bEnableXPad) + XInput_Read(pad[_numPAD].XPadPlayer, _pPADStatus); + + // Read Direct Input + DInput_Read(_numPAD, _pPADStatus); + +#elif defined(HAVE_X11) && HAVE_X11 + _pPADStatus->err = PAD_ERR_NONE; + X11_Read(_numPAD, _pPADStatus); +#elif defined(HAVE_COCOA) && HAVE_COCOA + _pPADStatus->err = PAD_ERR_NONE; + cocoa_Read(_numPAD, _pPADStatus); +#endif + +#ifdef RERECORDING + // Record input + if (pad[0].bRecording) RecordInput(*_pPADStatus); +#endif +} + + +// Rough approximation of GC behaviour - needs improvement. +void PAD_Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength) +{ +#ifdef _WIN32 + if (pad[_numPAD].bEnableXPad) + { + static int a = 0; + + if ((_uType == 0) || (_uType == 2)) + { + a = 0; + } + else if (_uType == 1) + { + a = _uStrength > 2 ? pad[_numPAD].RumbleStrength : 0; + } + + a = int ((float)a * 0.96f); + + if (!pad[_numPAD].bRumble) + { + a = 0; + } + + XINPUT_VIBRATION vib; + vib.wLeftMotorSpeed = a; //_uStrength*100; + vib.wRightMotorSpeed = a; //_uStrength*100; + XInputSetState(pad[_numPAD].XPadPlayer, &vib); + } +#endif +} + +//****************************************************************************** +// Load and save the configuration +//****************************************************************************** + +void LoadConfig() +{ + // Initialize first pad to standard controls +#ifdef _WIN32 + const int defaultKeyForControl[NUMCONTROLS] = + { + DIK_X, // A + DIK_Z, // B + DIK_C, // X + DIK_S, // Y + DIK_D, // Z + DIK_RETURN, // Start + DIK_Q, // L + DIK_W, // R + 0x00, // L semi-press + 0x00, // R semi-press + DIK_UP, // Main stick up + DIK_DOWN, // Main stick down + DIK_LEFT, // Main stick left + DIK_RIGHT, // Main stick right + DIK_LSHIFT, // Main stick semi-press + DIK_I, // C-stick up + DIK_K, // C-stick down + DIK_J, // C-stick left + DIK_L, // C-stick right + DIK_LCONTROL, // C-stick semi-press + DIK_T, // D-pad up + DIK_G, // D-pad down + DIK_F, // D-pad left + DIK_H, // D-pad right + DIK_M, // Mic + }; +#elif defined(HAVE_X11) && HAVE_X11 + const int defaultKeyForControl[NUMCONTROLS] = + { + XK_x, // A + XK_z, // B + XK_c, // X + XK_s, // Y + XK_d, // Z + XK_Return, // Start + XK_q, // L + XK_w, // R + 0x00, // L semi-press + 0x00, // R semi-press + XK_Up, // Main stick up + XK_Down, // Main stick down + XK_Left, // Main stick left + XK_Right, // Main stick right + XK_Shift_L, // Main stick semi-press + XK_i, // C-stick up + XK_k, // C-stick down + XK_j, // C-stick left + XK_l, // C-stick right + XK_Control_L, // C-stick semi-press + XK_t, // D-pad up + XK_g, // D-pad down + XK_f, // D-pad left + XK_h, // D-pad right + XK_m, // Mic + }; +#elif defined(HAVE_COCOA) && HAVE_COCOA + // Reference for Cocoa key codes: + // http://boredzo.org/blog/archives/2007-05-22/virtual-key-codes + const int defaultKeyForControl[NUMCONTROLS] = + { + 7, // A (x) + 6, // B (z) + 8, // X (c) + 1, // Y (s) + 2, // Z (d) + 36, // Start (return) + 12, // L (q) + 13, // R (w) + -1, // L semi-press (none) + -1, // R semi-press (none) + 126, // Main stick up (up) + 125, // Main stick down (down) + 123, // Main stick left (left) + 124, // Main stick right (right) + 56, // Main stick semi-press (left shift) + 34, // C-stick up (i) + 40, // C-stick down (k) + 38, // C-stick left (j) + 37, // C-stick right (l) + 59, // C-stick semi-press (left control) + 17, // D-pad up (t) + 5, // D-pad down (g) + 3, // D-pad left (f) + 4, // D-pad right (h) + 46, // Mic (m) + }; +#endif + //TODO +#if 0 + IniFile file; + file.Load(FULL_CONFIG_DIR "pad.ini"); + + for(int i = 0; i < 4; i++) + { + char SectionName[32]; + sprintf(SectionName, "PAD%i", i+1); + + file.Get(SectionName, "UseXPad", &pad[i].bEnableXPad, i==0); + file.Get(SectionName, "DisableOnBackground", &pad[i].bDisable, false); + file.Get(SectionName, "Rumble", &pad[i].bRumble, true); + file.Get(SectionName, "RumbleStrength", &pad[i].RumbleStrength, 8000); + file.Get(SectionName, "XPad#", &pad[i].XPadPlayer); + + file.Get(SectionName, "Trigger_semivalue", &pad[i].Trigger_semivalue, TRIGGER_HALF_DEFAULT); + file.Get(SectionName, "Main_stick_semivalue", &pad[i].Main_stick_semivalue, STICK_HALF_DEFAULT); + file.Get(SectionName, "Sub_stick_semivalue", &pad[i].Sub_stick_semivalue, STICK_HALF_DEFAULT); + + #ifdef RERECORDING + file.Get(SectionName, "Recording", &pad[0].bRecording, false); + file.Get(SectionName, "Playback", &pad[0].bPlayback, false); + #endif + + for (int x = 0; x < NUMCONTROLS; x++) + { + file.Get(SectionName, controlNames[x], + &pad[i].keyForControl[x], + (i==0) ? defaultKeyForControl[x] : 0); +#if defined(HAVE_X11) && HAVE_X11 + // In linux we have a problem assigning the upper case of the + // keys because they're not being recognized + pad[i].keyForControl[x] = tolower(pad[i].keyForControl[x]); +#endif + } + } +#endif +} + + +void SaveConfig() +{ + //TODO +#if 0 + IniFile file; + file.Load(FULL_CONFIG_DIR "pad.ini"); + + for(int i = 0; i < 4; i++) + { + char SectionName[32]; + sprintf(SectionName, "PAD%i", i+1); + + file.Set(SectionName, "UseXPad", pad[i].bEnableXPad); + file.Set(SectionName, "DisableOnBackground", pad[i].bDisable); + file.Set(SectionName, "Rumble", pad[i].bRumble); + file.Set(SectionName, "RumbleStrength", pad[i].RumbleStrength); + file.Set(SectionName, "XPad#", pad[i].XPadPlayer); + + file.Set(SectionName, "Trigger_semivalue", pad[i].Trigger_semivalue); + file.Set(SectionName, "Main_stick_semivalue", pad[i].Main_stick_semivalue); + file.Set(SectionName, "Sub_stick_semivalue", pad[i].Sub_stick_semivalue); + + #ifdef RERECORDING + file.Set(SectionName, "Recording", pad[0].bRecording); + file.Set(SectionName, "Playback", pad[0].bPlayback); + #endif + + for (int x = 0; x < NUMCONTROLS; x++) + { + file.Set(SectionName, controlNames[x], pad[i].keyForControl[x]); + } + } + file.Save(FULL_CONFIG_DIR "pad.ini"); +#endif +} diff --git a/desmume/src/wx/PadSimple/PadSimple.h b/desmume/src/wx/PadSimple/PadSimple.h new file mode 100644 index 000000000..4eb8a7299 --- /dev/null +++ b/desmume/src/wx/PadSimple/PadSimple.h @@ -0,0 +1,117 @@ +// Copyright (C) 2003 Dolphin Project. + +// 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, version 2.0. + +// 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 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifndef __PADSIMPLE_H__ +#define __PADSIMPLE_H__ + +//#include "Setup.h" // Common + +// Constants for full-press sticks and triggers +const int BUTTON_FULL = 255; +const int STICK_FULL = 100; +const int STICK_HALF_DEFAULT = 50; +const int TRIGGER_FULL = 255; +const int TRIGGER_HALF_DEFAULT = 128; +const int TRIGGER_THRESHOLD = 230; + +// Controls +enum +{ + CTL_A = 0, + CTL_B, + CTL_X, + CTL_Y, + CTL_Z, + CTL_START, + CTL_L, + CTL_R, + CTL_L_SEMI, + CTL_R_SEMI, + CTL_MAINUP, + CTL_MAINDOWN, + CTL_MAINLEFT, + CTL_MAINRIGHT, + CTL_MAIN_SEMI, + CTL_SUBUP, + CTL_SUBDOWN, + CTL_SUBLEFT, + CTL_SUBRIGHT, + CTL_SUB_SEMI, + CTL_DPADUP, + CTL_DPADDOWN, + CTL_DPADLEFT, + CTL_DPADRIGHT, + CTL_MIC, + NUMCONTROLS, +}; + +// Control names +static const char* controlNames[] = +{ + "A_button", + "B_button", + "X_button", + "Y_button", + "Z_trigger", + "Start", + "L_button", + "R_button", + "L_button_semi", + "R_button_semi", + "Main_stick_up", + "Main_stick_down", + "Main_stick_left", + "Main_stick_right", + "Main_stick_semi", + "Sub_stick_up", + "Sub_stick_down", + "Sub_stick_left", + "Sub_stick_right", + "Sub_stick_semi", + "D-Pad_up", + "D-Pad_down", + "D-Pad_left", + "D-Pad_right", + "Mic-button", +}; + +struct SPads +{ + bool bEnableXPad; // Use an XPad in addition to the keyboard? + bool bDisable; // Disabled when dolphin isn't in focus + bool bRumble; // Rumble for xpad + u32 RumbleStrength; // Rumble strength + bool bRecording; // Record input? + bool bPlayback; // Playback input? + s32 XPadPlayer; // Player# of the xpad + u32 keyForControl[NUMCONTROLS]; // Keyboard mapping + u32 Trigger_semivalue; // Semi-press value for triggers + u32 Main_stick_semivalue; // Semi-press value for main stick + u32 Sub_stick_semivalue; // Semi-press value for sub-stick +}; + +extern SPads pad[]; +//extern bool g_EmulatorRunning; + +void LoadConfig(); +void SaveConfig(); +bool IsFocus(); + +// Input Recording +void SaveRecord(); + +#endif diff --git a/desmume/src/wx/PadSimple/pluginspecs_pad.h b/desmume/src/wx/PadSimple/pluginspecs_pad.h new file mode 100644 index 000000000..6ed8a8450 --- /dev/null +++ b/desmume/src/wx/PadSimple/pluginspecs_pad.h @@ -0,0 +1,86 @@ +//__________________________________________________________________________________________________ +// Common pad plugin spec, version #1.0 maintained by F|RES +// + +#ifndef _PAD_H_INCLUDED__ +#define _PAD_H_INCLUDED__ + +//#include "PluginSpecs.h" + +//#include "ExportProlog.h" + +#define PAD_ERR_NONE 0 +#define PAD_ERR_NO_CONTROLLER -1 +#define PAD_ERR_NOT_READY -2 +#define PAD_ERR_TRANSFER -3 + +#define PAD_USE_ORIGIN 0x0080 + +#define PAD_BUTTON_LEFT 0x0001 +#define PAD_BUTTON_RIGHT 0x0002 +#define PAD_BUTTON_DOWN 0x0004 +#define PAD_BUTTON_UP 0x0008 +#define PAD_TRIGGER_Z 0x0010 +#define PAD_TRIGGER_R 0x0020 +#define PAD_TRIGGER_L 0x0040 +#define PAD_BUTTON_A 0x0100 +#define PAD_BUTTON_B 0x0200 +#define PAD_BUTTON_X 0x0400 +#define PAD_BUTTON_Y 0x0800 +#define PAD_BUTTON_START 0x1000 + +typedef void (*TLog)(const char* _pMessage); + +typedef struct +{ + HWND hWnd; + TLog pLog; + int padNumber; +} SPADInitialize; + +typedef struct +{ + unsigned short button; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits + unsigned char stickX; // 0 <= stickX <= 255 + unsigned char stickY; // 0 <= stickY <= 255 + unsigned char substickX; // 0 <= substickX <= 255 + unsigned char substickY; // 0 <= substickY <= 255 + unsigned char triggerLeft; // 0 <= triggerLeft <= 255 + unsigned char triggerRight; // 0 <= triggerRight <= 255 + unsigned char analogA; // 0 <= analogA <= 255 + unsigned char analogB; // 0 <= analogB <= 255 + bool MicButton; // HAX + signed char err; // one of PAD_ERR_* number +} SPADStatus; + +#define EXPORT +#define CALL + +// I N T E R F A C E + +// __________________________________________________________________________________________________ +// Function: +// Purpose: +// input: +// output: +// +EXPORT void CALL PAD_GetStatus(u8 _numPAD, SPADStatus* _pPADStatus); + +// __________________________________________________________________________________________________ +// Function: Send keyboard input to the plugin +// Purpose: +// input: The key and if it's pressed or released +// output: None +// +EXPORT void CALL PAD_Input(u16 _Key, u8 _UpDown); + +// __________________________________________________________________________________________________ +// Function: PAD_Rumble +// Purpose: Pad rumble! +// input: PAD number, Command type (Stop=0, Rumble=1, Stop Hard=2) and strength of Rumble +// output: none +// +EXPORT void CALL PAD_Rumble(u8 _numPAD, unsigned int _uType, unsigned int _uStrength); + +//#include "ExportEpilog.h" +#endif diff --git a/desmume/src/wx/wxMain.cpp b/desmume/src/wx/wxMain.cpp index 86c3cfe95..6fc258e4d 100644 --- a/desmume/src/wx/wxMain.cpp +++ b/desmume/src/wx/wxMain.cpp @@ -16,6 +16,8 @@ #include "wx/stdpaths.h" #include "LuaWindow.h" +#include "PadSimple/GUI/ConfigDlg.h" +#include "PadSimple/pluginspecs_pad.h" #ifndef WX_PRECOMP #include "wx/wx.h" @@ -45,13 +47,40 @@ public: this); } - bool up,down,left,right,x,y,a,b,l,r,start,select; - void applyInput(){ - //TODO - return; - u16 pad = (0 | + bool up,down,left,right,x,y,a,b,l,r,start,select; + + up = down = left = right = x = y = a = b = l = r = start = select = false; + + SPADStatus s; + memset(&s,0,sizeof(s)); + PAD_GetStatus(0, &s); + + if(s.button & PAD_BUTTON_LEFT) + left = true; + if(s.button & PAD_BUTTON_RIGHT) + right = true; + if(s.button & PAD_BUTTON_UP) + up = true; + if(s.button & PAD_BUTTON_DOWN) + down = true; + if(s.button & PAD_BUTTON_A) + a = true; + if(s.button & PAD_BUTTON_B) + b = true; + if(s.button & PAD_BUTTON_X) + x = true; + if(s.button & PAD_BUTTON_Y) + y = true; + if(s.button & PAD_TRIGGER_L) + l = true; + if(s.button & PAD_TRIGGER_R) + r = true; + if(s.button & PAD_BUTTON_START) + start = true; + + u16 pad1 = (0 | ((a ? 0 : 0x80) >> 7) | ((b ? 0 : 0x80) >> 6) | ((select? 0 : 0x80) >> 5) | @@ -63,8 +92,8 @@ public: ((r ? 0 : 0x80) << 1) | ((l ? 0 : 0x80) << 2)) ; - ((u16 *)MMU.ARM9_REG)[0x130>>1] = (u16)pad; - ((u16 *)MMU.ARM7_REG)[0x130>>1] = (u16)pad; + ((u16 *)MMU.ARM9_REG)[0x130>>1] = (u16)pad1; + ((u16 *)MMU.ARM7_REG)[0x130>>1] = (u16)pad1; bool debug = false; bool lidClosed = false; @@ -79,79 +108,6 @@ public: ((u16 *)MMU.ARM7_REG)[0x136>>1] = (u16)padExt; } - void OnKeyDown(wxKeyEvent &event){ - - switch(event.m_keyCode) { -case WXK_UP: - up = true; - break; -case WXK_DOWN: - down = true; - break; -case WXK_LEFT: - left = true; - break; -case WXK_RIGHT: - right = true; - break; -case WXK_RETURN: - start = true; - break; -case WXK_SHIFT: - select = true; - break; -case 65://a - y = true; - break; -case 83://s - x = true; - break; -case 90://z - b = true; - break; -case 88://x - a = true; - break; - } - // printf("%d",event.m_keyCode); - } - - void OnKeyUp(wxKeyEvent &event){ - - switch(event.m_keyCode) { -case WXK_UP: - up = false; - break; -case WXK_DOWN: - down = false; - break; -case WXK_LEFT: - left = false; - break; -case WXK_RIGHT: - right = false; - break; -case WXK_RETURN: - start = false; - break; -case WXK_SHIFT: - select = false; - break; -case 65://a - y = false; - break; -case 83://s - x = false; - break; -case 90://z - b = false; - break; -case 88://x - a = false; - break; - } - } - //TODO integrate paths system? void LoadRom(wxCommandEvent& event){ wxFileDialog dialog(this,"Load Rom",wxGetHomeDir(),"","*.nds",wxFD_OPEN, wxDefaultPosition, wxDefaultSize); @@ -313,6 +269,11 @@ case 88://x new wxLuaWindow(this, wxDefaultPosition, wxSize(600, 390)); } + void OnOpenControllerConfiguration(wxCommandEvent& WXUNUSED (event)) + { + new PADConfigDialogSimple(this); + } + wxMenu* MakeStatesSubMenu( int baseid ) const { wxMenu* mnuSubstates = new wxMenu(); @@ -369,7 +330,8 @@ enum wQuickScreenshot, wLuaWindow, wLoadState01, - wSaveState01 = wLoadState01+20 + wSaveState01 = wLoadState01+20, + wConfigureControls }; void DesmumeFrame::Menu_SaveStates(wxCommandEvent &event){savestate_slot(event.GetId() - wSaveState01 - 1);} @@ -389,8 +351,6 @@ EVT_MENU(wDisplayInput,DesmumeFrame::displayInput) EVT_MENU(wDisplayGraphicalInput,DesmumeFrame::displayGraphicalInput) EVT_MENU(wDisplayLagCounter,DesmumeFrame::displayLagCounter) EVT_MENU(wDisplayMicrophone,DesmumeFrame::displayMicrophone) -EVT_KEY_DOWN(DesmumeFrame::OnKeyDown) -EVT_KEY_UP(DesmumeFrame::OnKeyUp) EVT_MENU(wMainGPU,DesmumeFrame::mainGPU) EVT_MENU(wMainBG0,DesmumeFrame::mainBG0) @@ -425,6 +385,8 @@ EVT_MENU(w3dView,DesmumeFrame::_3dView) EVT_MENU(wLuaWindow,DesmumeFrame::OnOpenLuaWindow) +EVT_MENU(wConfigureControls,DesmumeFrame::OnOpenControllerConfiguration) + END_EVENT_TABLE() IMPLEMENT_APP(Desmume) @@ -447,9 +409,15 @@ bool Desmume::OnInit() DesmumeFrame *frame = new DesmumeFrame((char*)EMU_DESMUME_NAME_AND_VERSION()); frame->Show(true); + SPADInitialize PADInitialize; + PADInitialize.padNumber = 1; + extern void Initialize(void *init); + Initialize(&PADInitialize); + //TODO addon_type = NDS_ADDON_NONE; addonsChangePak(addon_type); + return true; } @@ -478,8 +446,9 @@ DesmumeFrame::DesmumeFrame(const wxString& title) fileMenu->AppendSubMenu(loads,"Load State"); #define ConnectMenuRange( id_start, inc, handler ) \ Connect( id_start, id_start + inc, wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(DesmumeFrame::handler) ) - ConnectMenuRange(wLoadState01+1, 10, Menu_LoadStates); - ConnectMenuRange(wSaveState01+1, 10, Menu_SaveStates); + //TODO something is wrong here +// ConnectMenuRange(wLoadState01+1, 10, Menu_LoadStates); +// ConnectMenuRange(wSaveState01+1, 10, Menu_SaveStates); } fileMenu->AppendSeparator(); fileMenu->Append(wImportBackupMemory, "Import Backup Memory..."); @@ -522,6 +491,9 @@ DesmumeFrame::DesmumeFrame(const wxString& title) layersMenu->Append(wSubBG2,"Sub BG 2"); layersMenu->Append(wSubBG3,"Sub BG 3"); } + + configMenu->Append(wConfigureControls,"Controls"); + toolsMenu->AppendSeparator(); toolsMenu->AppendSubMenu(layersMenu,"View Layers");