diff --git a/Source/Core/InputCommon/CMakeLists.txt b/Source/Core/InputCommon/CMakeLists.txt index 70e8304f37..e6359be4a4 100644 --- a/Source/Core/InputCommon/CMakeLists.txt +++ b/Source/Core/InputCommon/CMakeLists.txt @@ -20,6 +20,8 @@ elseif(APPLE) ControllerInterface/OSX/OSX.mm ControllerInterface/OSX/OSXKeyboard.mm ControllerInterface/OSX/OSXJoystick.mm + ControllerInterface/Quartz/Quartz.mm + ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm ControllerInterface/ForceFeedback/ForceFeedbackDevice.cpp) set(LIBS ${LIBS} ${COREFOUNDATION_LIBRARY} ${CARBON_LIBRARY} ${COCOA_LIBRARY}) elseif(X11_FOUND) diff --git a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp index f1e62e73cf..78393f0861 100644 --- a/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp +++ b/Source/Core/InputCommon/ControllerInterface/ControllerInterface.cpp @@ -21,6 +21,7 @@ #endif #ifdef CIFACE_USE_OSX #include "InputCommon/ControllerInterface/OSX/OSX.h" +#include "InputCommon/ControllerInterface/Quartz/Quartz.h" #endif #ifdef CIFACE_USE_SDL #include "InputCommon/ControllerInterface/SDL/SDL.h" @@ -70,6 +71,7 @@ void ControllerInterface::Initialize(void* const hwnd) #endif #ifdef CIFACE_USE_OSX ciface::OSX::Init(hwnd); + ciface::Quartz::Init(hwnd); #endif #ifdef CIFACE_USE_SDL ciface::SDL::Init(); @@ -117,6 +119,7 @@ void ControllerInterface::Shutdown() #endif #ifdef CIFACE_USE_OSX ciface::OSX::DeInit(); + ciface::Quartz::DeInit(); #endif #ifdef CIFACE_USE_SDL // TODO: there seems to be some sort of memory leak with SDL, quit isn't freeing everything up diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h new file mode 100644 index 0000000000..779ab8090e --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.h @@ -0,0 +1,14 @@ +// Copyright 2010 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +namespace ciface +{ +namespace Quartz +{ +void Init(void* window); +void DeInit(); +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm new file mode 100644 index 0000000000..651b82feac --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/Quartz.mm @@ -0,0 +1,30 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include +#include +#include + +#include "Common/StringUtil.h" + +#include "InputCommon/ControllerInterface/ControllerInterface.h" +#include "InputCommon/ControllerInterface/Quartz/Quartz.h" +#include "InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h" + +#include + +namespace ciface +{ +namespace Quartz +{ +void Init(void* window) +{ + g_controller_interface.AddDevice(std::make_shared(window)); +} + +void DeInit() +{ +} +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h new file mode 100644 index 0000000000..00ede72726 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h @@ -0,0 +1,73 @@ +// Copyright 2010 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include "InputCommon/ControllerInterface/Device.h" + +namespace ciface +{ +namespace Quartz +{ +class KeyboardAndMouse : public Core::Device +{ +private: + class Key : public Input + { + public: + Key(CGKeyCode keycode); + std::string GetName() const override; + ControlState GetState() const override; + + private: + CGKeyCode m_keycode; + std::string m_name; + }; + + class Cursor : public Input + { + public: + Cursor(u8 index, const float& axis, const bool positive) + : m_axis(axis), m_index(index), m_positive(positive) + { + } + std::string GetName() const override; + bool IsDetectable() override { return false; } + ControlState GetState() const override; + + private: + const float& m_axis; + const u8 m_index; + const bool m_positive; + }; + + class Button : public Input + { + public: + Button(CGMouseButton button) : m_button(button) {} + std::string GetName() const override; + ControlState GetState() const override; + + private: + CGMouseButton m_button; + }; + +public: + void UpdateInput() override; + + KeyboardAndMouse(void* window); + + std::string GetName() const override; + std::string GetSource() const override; + +private: + struct + { + float x, y; + } m_cursor; + + uint32_t m_windowid; +}; +} +} diff --git a/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm new file mode 100644 index 0000000000..e996b63d56 --- /dev/null +++ b/Source/Core/InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.mm @@ -0,0 +1,241 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include +#include +#include + +#include "InputCommon/ControllerInterface/Quartz/QuartzKeyboardAndMouse.h" + +namespace ciface +{ +namespace Quartz +{ + +KeyboardAndMouse::Key::Key(CGKeyCode keycode) : m_keycode(keycode) +{ + static const struct PrettyKeys + { + const CGKeyCode code; + const char* const name; + } named_keys[] = { + {kVK_ANSI_A, "A"}, + {kVK_ANSI_B, "B"}, + {kVK_ANSI_C, "C"}, + {kVK_ANSI_D, "D"}, + {kVK_ANSI_E, "E"}, + {kVK_ANSI_F, "F"}, + {kVK_ANSI_G, "G"}, + {kVK_ANSI_H, "H"}, + {kVK_ANSI_I, "I"}, + {kVK_ANSI_J, "J"}, + {kVK_ANSI_K, "K"}, + {kVK_ANSI_L, "L"}, + {kVK_ANSI_M, "M"}, + {kVK_ANSI_N, "N"}, + {kVK_ANSI_O, "O"}, + {kVK_ANSI_P, "P"}, + {kVK_ANSI_Q, "Q"}, + {kVK_ANSI_R, "R"}, + {kVK_ANSI_S, "S"}, + {kVK_ANSI_T, "T"}, + {kVK_ANSI_U, "U"}, + {kVK_ANSI_V, "V"}, + {kVK_ANSI_W, "W"}, + {kVK_ANSI_X, "X"}, + {kVK_ANSI_Y, "Y"}, + {kVK_ANSI_Z, "Z"}, + {kVK_ANSI_1, "1"}, + {kVK_ANSI_2, "2"}, + {kVK_ANSI_3, "3"}, + {kVK_ANSI_4, "4"}, + {kVK_ANSI_5, "5"}, + {kVK_ANSI_6, "6"}, + {kVK_ANSI_7, "7"}, + {kVK_ANSI_8, "8"}, + {kVK_ANSI_9, "9"}, + {kVK_ANSI_0, "0"}, + {kVK_Return, "Return"}, + {kVK_Escape, "Escape"}, + {kVK_Delete, "Backspace"}, + {kVK_Tab, "Tab"}, + {kVK_Space, "Space"}, + {kVK_ANSI_Minus, "-"}, + {kVK_ANSI_Equal, "="}, + {kVK_ANSI_LeftBracket, "["}, + {kVK_ANSI_RightBracket, "]"}, + {kVK_ANSI_Backslash, "\\"}, + {kVK_ANSI_Semicolon, ";"}, + {kVK_ANSI_Quote, "'"}, + {kVK_ANSI_Grave, "Tilde"}, + {kVK_ANSI_Comma, ","}, + {kVK_ANSI_Period, "."}, + {kVK_ANSI_Slash, "/"}, + {kVK_CapsLock, "Caps Lock"}, + {kVK_F1, "F1"}, + {kVK_F2, "F2"}, + {kVK_F3, "F3"}, + {kVK_F4, "F4"}, + {kVK_F5, "F5"}, + {kVK_F6, "F6"}, + {kVK_F7, "F7"}, + {kVK_F8, "F8"}, + {kVK_F9, "F9"}, + {kVK_F10, "F10"}, + {kVK_F11, "F11"}, + {kVK_F12, "F12"}, + // {kHIDUsage_KeyboardInsert, "Insert"}, + {kVK_Home, "Home"}, + {kVK_PageUp, "Page Up"}, + {kVK_ForwardDelete, "Delete"}, + {kVK_End, "End"}, + {kVK_PageDown, "Page Down"}, + {kVK_RightArrow, "Right Arrow"}, + {kVK_LeftArrow, "Left Arrow"}, + {kVK_DownArrow, "Down Arrow"}, + {kVK_UpArrow, "Up Arrow"}, + {kVK_ANSI_KeypadDivide, "Keypad /"}, + {kVK_ANSI_KeypadMultiply, "Keypad *"}, + {kVK_ANSI_KeypadMinus, "Keypad -"}, + {kVK_ANSI_KeypadPlus, "Keypad +"}, + {kVK_ANSI_KeypadEnter, "Keypad Enter"}, + {kVK_ANSI_Keypad1, "Keypad 1"}, + {kVK_ANSI_Keypad2, "Keypad 2"}, + {kVK_ANSI_Keypad3, "Keypad 3"}, + {kVK_ANSI_Keypad4, "Keypad 4"}, + {kVK_ANSI_Keypad5, "Keypad 5"}, + {kVK_ANSI_Keypad6, "Keypad 6"}, + {kVK_ANSI_Keypad7, "Keypad 7"}, + {kVK_ANSI_Keypad8, "Keypad 8"}, + {kVK_ANSI_Keypad9, "Keypad 9"}, + {kVK_ANSI_Keypad0, "Keypad 0"}, + {kVK_ANSI_KeypadDecimal, "Keypad ."}, + // {kHIDUsage_KeyboardNonUSBackslash, "Paragraph"}, + {kVK_ANSI_KeypadEquals, "Keypad ="}, + // {kHIDUsage_KeypadComma, "Keypad ,"}, + {kVK_Control, "Left Control"}, + {kVK_Shift, "Left Shift"}, + {kVK_Option, "Left Alt"}, + {kVK_Command, "Left Command"}, + {kVK_RightControl, "Right Control"}, + {kVK_RightShift, "Right Shift"}, + {kVK_RightOption, "Right Alt"}, + // {kVK_RightCommand, "Right Command"}, + }; + + for (auto& named_key : named_keys) + { + if (named_key.code == m_keycode) + { + m_name = named_key.name; + return; + } + } + + std::stringstream ss; + ss << "Key " << keycode; + m_name = ss.str(); +} + +ControlState KeyboardAndMouse::Key::GetState() const +{ + return CGEventSourceKeyState(kCGEventSourceStateHIDSystemState, m_keycode) != 0; +} + +std::string KeyboardAndMouse::Key::GetName() const +{ + return m_name; +} + +KeyboardAndMouse::KeyboardAndMouse(void* window) +{ + // All ANSI keycodes in are 0x7e or lower + for (int i = 0; i < 0x80; i++) + AddInput(new Key(i)); + + m_windowid = [[reinterpret_cast(window) window] windowNumber]; + + // cursor, with a hax for-loop + for (unsigned int i = 0; i < 4; ++i) + AddInput(new Cursor(!!(i & 2), (&m_cursor.x)[i / 2], !!(i & 1))); + + AddInput(new Button(kCGMouseButtonLeft)); + AddInput(new Button(kCGMouseButtonRight)); + AddInput(new Button(kCGMouseButtonCenter)); +} + +void KeyboardAndMouse::UpdateInput() +{ + CGRect bounds = CGRectZero; + uint32_t windowid[1] = {m_windowid}; + CFArrayRef windowArray = CFArrayCreate(nullptr, (const void**)windowid, 1, nullptr); + CFArrayRef windowDescriptions = CGWindowListCreateDescriptionFromArray(windowArray); + CFDictionaryRef windowDescription = + (CFDictionaryRef)CFArrayGetValueAtIndex((CFArrayRef)windowDescriptions, 0); + + if (CFDictionaryContainsKey(windowDescription, kCGWindowBounds)) + { + CFDictionaryRef boundsDictionary = + (CFDictionaryRef)CFDictionaryGetValue(windowDescription, kCGWindowBounds); + + if (boundsDictionary != nullptr) + CGRectMakeWithDictionaryRepresentation(boundsDictionary, &bounds); + } + + CFRelease(windowDescriptions); + CFRelease(windowArray); + + CGEventRef event = CGEventCreate(nil); + CGPoint loc = CGEventGetLocation(event); + CFRelease(event); + + loc.x -= bounds.origin.x; + loc.y -= bounds.origin.y; + m_cursor.x = loc.x / bounds.size.width * 2 - 1.0; + m_cursor.y = loc.y / bounds.size.height * 2 - 1.0; +} + +std::string KeyboardAndMouse::GetName() const +{ + return "Keyboard & Mouse"; +} + +std::string KeyboardAndMouse::GetSource() const +{ + return "Quartz"; +} + +ControlState KeyboardAndMouse::Cursor::GetState() const +{ + return std::max(0.0, ControlState(m_axis) / (m_positive ? 1.0 : -1.0)); +} + +ControlState KeyboardAndMouse::Button::GetState() const +{ + return CGEventSourceButtonState(kCGEventSourceStateHIDSystemState, m_button) != 0; + // return (m_button != 0); +} + +std::string KeyboardAndMouse::Cursor::GetName() const +{ + static char tmpstr[] = "Cursor .."; + tmpstr[7] = (char)('X' + m_index); + tmpstr[8] = (m_positive ? '+' : '-'); + return tmpstr; +} + +std::string KeyboardAndMouse::Button::GetName() const +{ + if (m_button == kCGMouseButtonLeft) + return "Left Click"; + if (m_button == kCGMouseButtonCenter) + return "Middle Click"; + if (m_button == kCGMouseButtonRight) + return "Right Click"; + return std::string("Click ") + char('0' + m_button); +} +} +} diff --git a/Source/Core/InputCommon/InputConfig.cpp b/Source/Core/InputCommon/InputConfig.cpp index e1869ca465..9df0b71e62 100644 --- a/Source/Core/InputCommon/InputConfig.cpp +++ b/Source/Core/InputCommon/InputConfig.cpp @@ -126,7 +126,8 @@ bool InputConfig::IsControllerControlledByGamepadDevice(int index) const const auto& controller = m_controllers.at(index).get()->default_device; // Filter out anything which obviously not a gamepad - return !((controller.source == "Keyboard") // OSX Keyboard/Mouse + return !((controller.source == "Keyboard") // OSX IOKit Keyboard/Mouse + || (controller.source == "Quartz") // OSX Quartz Keyboard/Mouse || (controller.source == "XInput2") // Linux and BSD Keyboard/Mouse || (controller.source == "Android" && controller.name == "Touchscreen") // Android Touchscreen