2020-01-22 02:23:18 +00:00
# include <sstream>
# include <iomanip>
2020-01-07 22:15:55 +00:00
2020-01-31 19:57:34 +00:00
# include <ws2tcpip.h>
# include <winsock2.h>
2020-01-16 19:00:25 +00:00
# include <windows.h>
# include <winuser.h>
2020-01-22 02:23:18 +00:00
# include <iphlpapi.h>
2020-01-16 19:00:25 +00:00
2020-01-18 22:52:22 +00:00
# include <ViGEm/Client.h>
2020-01-16 19:00:25 +00:00
# include "sunshine/main.h"
2020-01-03 19:25:21 +00:00
# include "common.h"
namespace platf {
using namespace std : : literals ;
2020-01-18 22:52:22 +00:00
2020-01-22 02:23:18 +00:00
using adapteraddrs_t = util : : c_ptr < IP_ADAPTER_ADDRESSES > ;
2020-01-18 22:52:22 +00:00
class vigem_t {
public :
using client_t = util : : safe_ptr < _VIGEM_CLIENT_T , vigem_free > ;
using target_t = util : : safe_ptr < _VIGEM_TARGET_T , vigem_target_free > ;
int init ( ) {
VIGEM_ERROR status ;
client . reset ( vigem_alloc ( ) ) ;
status = vigem_connect ( client . get ( ) ) ;
if ( ! VIGEM_SUCCESS ( status ) ) {
BOOST_LOG ( warning ) < < " Couldn't setup connection to ViGEm for gamepad support [ " sv < < util : : hex ( status ) . to_string_view ( ) < < ' ] ' ;
return - 1 ;
}
2020-01-25 19:46:14 +00:00
x360s . resize ( MAX_GAMEPADS ) ;
2020-01-18 22:52:22 +00:00
2020-01-31 19:57:34 +00:00
return 0 ;
}
2020-01-18 22:52:22 +00:00
2020-01-31 19:57:34 +00:00
int alloc_x360 ( int nr ) {
auto & x360 = x360s [ nr ] ;
assert ( ! x360 ) ;
x360 . reset ( vigem_target_x360_alloc ( ) ) ;
auto status = vigem_target_add ( client . get ( ) , x360 . get ( ) ) ;
if ( ! VIGEM_SUCCESS ( status ) ) {
BOOST_LOG ( error ) < < " Couldn't add Gamepad to ViGEm connection [ " sv < < util : : hex ( status ) . to_string_view ( ) < < ' ] ' ;
return - 1 ;
2020-01-18 22:52:22 +00:00
}
return 0 ;
}
2020-01-31 19:57:34 +00:00
void free_target ( int nr ) {
auto & x360 = x360s [ nr ] ;
if ( x360 & & vigem_target_is_attached ( x360 . get ( ) ) ) {
auto status = vigem_target_remove ( client . get ( ) , x360 . get ( ) ) ;
if ( ! VIGEM_SUCCESS ( status ) ) {
BOOST_LOG ( warning ) < < " Couldn't detach gamepad from ViGEm [ " sv < < util : : hex ( status ) . to_string_view ( ) < < ' ] ' ;
}
}
x360 . reset ( ) ;
}
2020-01-18 22:52:22 +00:00
~ vigem_t ( ) {
if ( client ) {
2020-01-25 00:27:47 +00:00
for ( auto & x360 : x360s ) {
if ( x360 & & vigem_target_is_attached ( x360 . get ( ) ) ) {
auto status = vigem_target_remove ( client . get ( ) , x360 . get ( ) ) ;
if ( ! VIGEM_SUCCESS ( status ) ) {
BOOST_LOG ( warning ) < < " Couldn't detach gamepad from ViGEm [ " sv < < util : : hex ( status ) . to_string_view ( ) < < ' ] ' ;
}
2020-01-18 22:52:22 +00:00
}
}
vigem_disconnect ( client . get ( ) ) ;
}
}
2020-01-25 00:27:47 +00:00
std : : vector < target_t > x360s ;
2020-01-18 22:52:22 +00:00
client_t client ;
} ;
2020-02-08 15:26:38 +00:00
std : : string from_sockaddr ( const sockaddr * const socket_address ) {
2020-01-22 02:23:18 +00:00
char data [ INET6_ADDRSTRLEN ] ;
2020-02-08 15:26:38 +00:00
auto family = socket_address - > sa_family ;
2020-01-22 02:23:18 +00:00
if ( family = = AF_INET6 ) {
2020-02-08 15:26:38 +00:00
inet_ntop ( AF_INET6 , & ( ( sockaddr_in6 * ) socket_address ) - > sin6_addr , data , INET6_ADDRSTRLEN ) ;
2020-01-22 02:23:18 +00:00
}
if ( family = = AF_INET ) {
2020-02-08 15:26:38 +00:00
inet_ntop ( AF_INET , & ( ( sockaddr_in * ) socket_address ) - > sin_addr , data , INET_ADDRSTRLEN ) ;
2020-01-22 02:23:18 +00:00
}
return std : : string { data } ;
}
2020-02-12 10:28:27 +00:00
std : : pair < std : : uint16_t , std : : string > from_sockaddr_ex ( const sockaddr * const ip_addr ) {
char data [ INET6_ADDRSTRLEN ] ;
auto family = ip_addr - > sa_family ;
std : : uint16_t port ;
if ( family = = AF_INET6 ) {
inet_ntop ( AF_INET6 , & ( ( sockaddr_in6 * ) ip_addr ) - > sin6_addr , data , INET6_ADDRSTRLEN ) ;
port = ( ( sockaddr_in6 * ) ip_addr ) - > sin6_port ;
}
if ( family = = AF_INET ) {
inet_ntop ( AF_INET , & ( ( sockaddr_in * ) ip_addr ) - > sin_addr , data , INET_ADDRSTRLEN ) ;
port = ( ( sockaddr_in * ) ip_addr ) - > sin_port ;
}
return { port , std : : string { data } } ;
}
2020-01-22 02:23:18 +00:00
adapteraddrs_t get_adapteraddrs ( ) {
adapteraddrs_t info { nullptr } ;
ULONG size = 0 ;
while ( GetAdaptersAddresses ( AF_UNSPEC , 0 , nullptr , info . get ( ) , & size ) = = ERROR_BUFFER_OVERFLOW ) {
info . reset ( ( PIP_ADAPTER_ADDRESSES ) malloc ( size ) ) ;
}
return info ;
}
2020-01-21 01:34:22 +00:00
std : : string get_mac_address ( const std : : string_view & address ) {
2020-01-22 02:23:18 +00:00
adapteraddrs_t info = get_adapteraddrs ( ) ;
for ( auto adapter_pos = info . get ( ) ; adapter_pos ! = nullptr ; adapter_pos = adapter_pos - > Next ) {
for ( auto addr_pos = adapter_pos - > FirstUnicastAddress ; addr_pos ! = nullptr ; addr_pos = addr_pos - > Next ) {
2020-02-08 15:26:38 +00:00
if ( adapter_pos - > PhysicalAddressLength ! = 0 & & address = = from_sockaddr ( addr_pos - > Address . lpSockaddr ) ) {
2020-01-22 02:23:18 +00:00
std : : stringstream mac_addr ;
mac_addr < < std : : hex ;
for ( int i = 0 ; i < adapter_pos - > PhysicalAddressLength ; i + + ) {
if ( i > 0 ) {
mac_addr < < ' : ' ;
}
mac_addr < < std : : setw ( 2 ) < < std : : setfill ( ' 0 ' ) < < ( int ) adapter_pos - > PhysicalAddress [ i ] ;
}
return mac_addr . str ( ) ;
}
}
}
BOOST_LOG ( warning ) < < " Unable to find MAC address for " sv < < address ;
2020-01-21 01:34:22 +00:00
return " 00:00:00:00:00:00 " s ;
}
2020-01-03 19:25:21 +00:00
input_t input ( ) {
2020-01-18 22:52:22 +00:00
input_t result { new vigem_t { } } ;
auto vigem = ( vigem_t * ) result . get ( ) ;
if ( vigem - > init ( ) ) {
return nullptr ;
}
return result ;
2020-01-03 19:25:21 +00:00
}
2020-01-16 19:00:25 +00:00
void move_mouse ( input_t & input , int deltaX , int deltaY ) {
INPUT i { } ;
i . type = INPUT_MOUSE ;
auto & mi = i . mi ;
mi . dwFlags = MOUSEEVENTF_MOVE ;
mi . dx = deltaX ;
mi . dy = deltaY ;
auto send = SendInput ( 1 , & i , sizeof ( INPUT ) ) ;
if ( send ! = 1 ) {
BOOST_LOG ( warning ) < < " Couldn't send mouse movement input " sv ;
}
}
void button_mouse ( input_t & input , int button , bool release ) {
2020-01-17 14:36:06 +00:00
constexpr SHORT KEY_STATE_DOWN = 0x8000 ;
2020-01-16 19:00:25 +00:00
INPUT i { } ;
i . type = INPUT_MOUSE ;
auto & mi = i . mi ;
2020-01-17 14:36:06 +00:00
int mouse_button ;
2020-01-16 19:00:25 +00:00
if ( button = = 1 ) {
mi . dwFlags = release ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_LEFTDOWN ;
2020-01-17 14:36:06 +00:00
mouse_button = VK_LBUTTON ;
2020-01-16 19:00:25 +00:00
}
else if ( button = = 2 ) {
mi . dwFlags = release ? MOUSEEVENTF_MIDDLEUP : MOUSEEVENTF_MIDDLEDOWN ;
2020-01-17 14:36:06 +00:00
mouse_button = VK_MBUTTON ;
2020-01-16 19:00:25 +00:00
}
2020-01-19 00:08:07 +00:00
else if ( button = = 3 ) {
2020-01-16 19:00:25 +00:00
mi . dwFlags = release ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_RIGHTDOWN ;
2020-01-17 14:36:06 +00:00
mouse_button = VK_RBUTTON ;
}
2020-01-19 00:08:07 +00:00
else if ( button = = 4 ) {
mi . dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN ;
mi . mouseData = XBUTTON1 ;
mouse_button = VK_XBUTTON1 ;
}
else {
mi . dwFlags = release ? MOUSEEVENTF_XUP : MOUSEEVENTF_XDOWN ;
mi . mouseData = XBUTTON2 ;
mouse_button = VK_XBUTTON2 ;
}
2020-01-17 14:36:06 +00:00
auto key_state = GetAsyncKeyState ( mouse_button ) ;
bool key_state_down = ( key_state & KEY_STATE_DOWN ) ! = 0 ;
if ( key_state_down ! = release ) {
BOOST_LOG ( warning ) < < " Button state of mouse_button [ " sv < < button < < " ] does not match the desired state " sv ;
return ;
2020-01-16 19:00:25 +00:00
}
auto send = SendInput ( 1 , & i , sizeof ( INPUT ) ) ;
if ( send ! = 1 ) {
BOOST_LOG ( warning ) < < " Couldn't send mouse button input " sv ;
}
}
void scroll ( input_t & input , int distance ) {
INPUT i { } ;
i . type = INPUT_MOUSE ;
auto & mi = i . mi ;
mi . dwFlags = MOUSEEVENTF_WHEEL ;
2020-01-16 20:44:56 +00:00
mi . mouseData = distance ;
2020-01-16 19:00:25 +00:00
auto send = SendInput ( 1 , & i , sizeof ( INPUT ) ) ;
if ( send ! = 1 ) {
BOOST_LOG ( warning ) < < " Couldn't send moue movement input " sv ;
}
}
void keyboard ( input_t & input , uint16_t modcode , bool release ) {
2020-01-17 14:36:06 +00:00
constexpr SHORT KEY_STATE_DOWN = 0x8000 ;
2020-01-16 19:00:25 +00:00
if ( modcode = = VK_RMENU ) {
2020-01-16 22:36:49 +00:00
modcode = VK_LWIN ;
2020-01-16 19:00:25 +00:00
}
2020-01-17 14:36:06 +00:00
auto key_state = GetAsyncKeyState ( modcode ) ;
bool key_state_down = ( key_state & KEY_STATE_DOWN ) ! = 0 ;
if ( key_state_down ! = release ) {
BOOST_LOG ( warning ) < < " Key state of vkey [ " sv < < util : : hex ( modcode ) . to_string_view ( ) < < " ] does not match the desired state [ " sv < < ( release ? " on] " sv : " off] " sv ) ;
return ;
}
2020-01-16 19:00:25 +00:00
INPUT i { } ;
i . type = INPUT_KEYBOARD ;
auto & ki = i . ki ;
2020-01-16 22:57:36 +00:00
// For some reason, MapVirtualKey(VK_LWIN, MAPVK_VK_TO_VSC) doesn't seem to work :/
2020-01-22 04:36:49 +00:00
if ( modcode ! = VK_LWIN & & modcode ! = VK_RWIN & & modcode ! = VK_PAUSE ) {
2020-01-16 22:57:36 +00:00
ki . wScan = MapVirtualKey ( modcode , MAPVK_VK_TO_VSC ) ;
ki . dwFlags = KEYEVENTF_SCANCODE ;
}
else {
ki . wVk = modcode ;
}
2020-01-22 04:36:49 +00:00
// https://docs.microsoft.com/en-us/windows/win32/inputdev/about-keyboard-input#keystroke-message-flags
switch ( modcode ) {
case VK_RMENU :
case VK_RCONTROL :
case VK_INSERT :
case VK_DELETE :
case VK_HOME :
case VK_END :
case VK_PRIOR :
case VK_NEXT :
case VK_UP :
case VK_DOWN :
case VK_LEFT :
case VK_RIGHT :
case VK_DIVIDE :
ki . dwFlags | = KEYEVENTF_EXTENDEDKEY ;
break ;
default :
break ;
}
2020-01-16 19:00:25 +00:00
if ( release ) {
2020-01-17 14:36:06 +00:00
ki . dwFlags | = KEYEVENTF_KEYUP ;
2020-01-16 19:00:25 +00:00
}
auto send = SendInput ( 1 , & i , sizeof ( INPUT ) ) ;
if ( send ! = 1 ) {
BOOST_LOG ( warning ) < < " Couldn't send moue movement input " sv ;
}
}
2020-01-03 19:25:21 +00:00
2020-01-31 19:57:34 +00:00
int alloc_gamepad ( input_t & input , int nr ) {
if ( ! input ) {
return 0 ;
}
return ( ( vigem_t * ) input . get ( ) ) - > alloc_x360 ( nr ) ;
}
void free_gamepad ( input_t & input , int nr ) {
if ( ! input ) {
return ;
}
( ( vigem_t * ) input . get ( ) ) - > free_target ( nr ) ;
}
2020-01-25 19:46:14 +00:00
void gamepad ( input_t & input , int nr , const gamepad_state_t & gamepad_state ) {
2020-01-18 22:52:22 +00:00
// If there is no gamepad support
if ( ! input ) {
return ;
}
auto vigem = ( vigem_t * ) input . get ( ) ;
2020-01-25 00:27:47 +00:00
2020-01-18 22:52:22 +00:00
auto & xusb = * ( PXUSB_REPORT ) & gamepad_state ;
2020-01-25 19:46:14 +00:00
auto & x360 = vigem - > x360s [ nr ] ;
2020-01-18 22:52:22 +00:00
2020-01-25 00:27:47 +00:00
auto status = vigem_target_x360_update ( vigem - > client . get ( ) , x360 . get ( ) , xusb ) ;
2020-01-18 22:52:22 +00:00
if ( ! VIGEM_SUCCESS ( status ) ) {
BOOST_LOG ( fatal ) < < " Couldn't send gamepad input to ViGEm [ " sv < < util : : hex ( status ) . to_string_view ( ) < < ' ] ' ;
log_flush ( ) ;
std : : abort ( ) ;
}
}
void freeInput ( void * p ) {
auto vigem = ( vigem_t * ) p ;
delete vigem ;
}
2020-01-03 19:25:21 +00:00
}