├── .gitignore
├── .gitmodules
├── CMakeLists.txt
├── CMakeSettings.json
├── LICENSE
├── README.md
├── RemotePlayWhatever
├── CMakeLists.txt
├── ClickablePanel.cpp
├── ClickablePanel.h
├── FriendPanel.cpp
├── FriendPanel.h
├── FriendsListFrame.cpp
├── FriendsListFrame.h
├── QueueInviter.cpp
├── QueueInviter.h
├── RemoteApp.cpp
├── RemoteApp.h
├── RemotePlayInviteHandler.cpp
├── RemotePlayInviteHandler.h
├── RemotePlayWhatever.cpp
├── RemotePlayWhatever.h
├── SteamStuff.cpp
├── SteamStuff.h
├── appicon.ico
├── appicon.xpm
├── appresource.rc
├── wxSteamStuff.cpp
└── wxSteamStuff.h
└── assets
└── rpw_screen.png
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | .vscode/
3 | .cache/
4 | out/
5 | build/
6 | build_debug/
7 | CMakeLists.txt.user*
8 | CMakeCache.txt
9 | CMakeSettings.json
10 | CMakeFiles
11 | CMakeScripts
12 | Testing
13 | Makefile
14 | cmake_install.cmake
15 | install_manifest.txt
16 | compile_commands.json
17 | CTestTestfile.cmake
18 | _deps
19 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "open-steamworks"]
2 | path = open-steamworks
3 | url = https://github.com/m4dEngi/open-steamworks.git
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # CMakeList.txt : Top-level CMake project file, do global configuration
2 | # and include sub-projects here.
3 | #
4 | cmake_minimum_required (VERSION 3.8)
5 |
6 | project ("RemotePlayWhatever" VERSION 0.2.11)
7 |
8 | # Include sub-projects.
9 | add_subdirectory ("RemotePlayWhatever")
10 |
--------------------------------------------------------------------------------
/CMakeSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "configurations": [
3 | {
4 | "name": "x86-Debug",
5 | "generator": "Ninja",
6 | "configurationType": "Debug",
7 | "buildRoot": "${projectDir}\\out\\build\\${name}",
8 | "installRoot": "${projectDir}\\out\\install\\${name}",
9 | "cmakeCommandArgs": "",
10 | "buildCommandArgs": "",
11 | "ctestCommandArgs": "",
12 | "inheritEnvironments": [ "msvc_x86" ],
13 | "variables": []
14 | },
15 | {
16 | "name": "x86-Release",
17 | "generator": "Ninja",
18 | "configurationType": "RelWithDebInfo",
19 | "buildRoot": "${projectDir}\\out\\build\\${name}",
20 | "installRoot": "${projectDir}\\out\\install\\${name}",
21 | "cmakeCommandArgs": "",
22 | "buildCommandArgs": "",
23 | "ctestCommandArgs": "",
24 | "inheritEnvironments": [ "msvc_x86" ],
25 | "variables": []
26 | }
27 | ]
28 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 m4dEngi
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RemotePlayWhatever
2 | Tiny application that lets you force remote play together any game you have in your steam library including non-steam ones.
3 |
4 |
5 |
6 |
7 |
8 | ## How it works
9 | RemotePlayWhatever communicates with your running Steam client and instructs it to initiate a Remote Play Together session for the currently running game launched through Steam. It does this by using undocumented and potentially **unstable** internal Steam client APIs.
10 |
11 | ## ⚠️ Warning
12 | - This tool relies on internal Steam client APIs. Future Steam client updates **may break compatibility** without warning.
13 | - This project aims to support the latest **stable** version of the Steam client. If you encounter issues after a Steam update, please check for a new version of RemotePlayWhatever or report an issue.
14 | - **Use at Your Own Risk:** This tool communicates locally with Steam via its standard IPC channel and doesn't modify files. However, it relies on unstable, internal APIs used by the Steam client itself, not the stable APIs meant for games. These internal APIs can change with Steam updates, potentially breaking this tool without warning.
15 |
16 | ## Getting Started
17 | Download the latest RemotePlayWhatever [Release](https://github.com/m4dEngi/RemotePlayWhatever/releases/latest) for your system:
18 | - Linux:
19 | - **Arch**: A third-party [AUR package](https://aur.archlinux.org/packages/remoteplaywhatever) is available ( maintained by *Rojikku* ).
20 | - **Ubuntu**: Download and install the *.**deb** package.
21 | - **Any distro**: Download the *.**AppImage** and make it executable by running `chmod +x RemotePlayWhatever-*.AppImage`. \
22 | *Note*: Some systems might require `fuse2` ( or alternative ) package to be installed for *AppImages* to work.
23 | - Windows:
24 | - Download and extract the RemotePlayWhatever-*.**zip** somewhere; no installation required.
25 |
26 | ... or [build the app from source](#building-from-source)
27 |
28 | ## Usage
29 | 1. Ensure the Steam client is running.
30 | 2. Launch RemotePlayWhatever.
31 | 3. Launch the game you want to play *through the Steam client*.
32 | 4. Alt+Tab (or switch) back to the RemotePlayWhatever window.
33 | 5. Invite your friend(s):
34 | - **Direct Invite:** Select a friend from the list. They will receive the invite link via a Steam Chat message.
35 | - **Guest Link:** Click the button to generate a guest invite link. The link will be automatically copied to your clipboard, ready for you to paste and share manually.
36 | 6. Alt+Tab (or switch) back to your game.
37 |
38 | ## Command line arguments
39 | ```
40 | -a, --appid= AppID to use for non-steam games (Default is 480)
41 | -i, --invite= Send remote play invites to provided comma separated
42 | SteamID64s list ( use 0 for guest link )
43 | -g, --guestid= Guest client id
44 | -c, --cancel= Cancel invite for provided SteamID64 and GuestID
45 | ```
46 |
47 | ## Building from source
48 | ### Linux ( Debian )
49 | 1. Install the dependencies:
50 | ```bash
51 | sudo apt update
52 | sudo apt install build-essential git cmake libwxgtk3.2-dev
53 | ```
54 | 2. Clone the repository:
55 | ```bash
56 | git clone --recurse-submodules https://github.com/m4dEngi/RemotePlayWhatever.git
57 | cd RemotePlayWhatever
58 | ```
59 | 3. Configure and build:
60 | ```bash
61 | cmake -B build -DCMAKE_BUILD_TYPE=Release
62 | cmake --build build
63 | ```
64 | The executable will be placed in the `build/RemotePlayWhatever` directory.
65 | 4. **( optional )** Create and install deb package:
66 | ```bash
67 | cmake --build build --target package
68 | sudo dpkg -i build/RemotePlayWhatever-*-Linux.deb
69 | ```
70 |
71 | ### Windows
72 | These instructions assume a 32-bit build using Visual Studio. \
73 | All commands should be executed in a `Developer Command Prompt for VS`.
74 |
75 | 1. Download and install [Visual Studio](https://visualstudio.microsoft.com/) (Community Edition is free) with the "Desktop development with C++" workload installed and "Git For Windows" and "MSVC v142 - VS 2019 C++ x64/x86 build tools" components.
76 | 2. Download and install [wxWidgets](https://wxwidgets.org/) ( follow the docs ).
77 | 3. Clone the repository:
78 | ```bash
79 | git clone --recurse-submodules https://github.com/m4dEngi/RemotePlayWhatever.git &&
80 | cd RemotePlayWhatever
81 | ```
82 | 4. Configure and build:
83 | ```bash
84 | cmake -B build -A Win32 &&
85 | cmake --build build --config Release
86 | ```
87 | If build is successful, the executable will be created in the `build\RemotePlayWhatever\Release` folder.
88 |
89 | ## Contributing
90 | Contributions are welcome! If you find a bug or have a feature request, please open an issue.
91 |
92 | ## Acknowledgements
93 | - Original idea and initial testing by @Jondycz
94 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # CMakeList.txt : CMake project for RemotePlayWhatever, include source and define
2 | # project specific logic here.
3 | #
4 | cmake_minimum_required (VERSION 3.8)
5 |
6 | if(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Windows")
7 | set(SYSTEM_OS "Win")
8 | elseif(${CMAKE_HOST_SYSTEM_NAME} MATCHES "Linux")
9 | set(SYSTEM_OS "Linux")
10 | else()
11 | message(FATAL_ERROR "Unsupported platform!")
12 | endif()
13 |
14 | if(${CMAKE_SIZEOF_VOID_P} EQUAL 8)
15 | set(SYSTEM_ARCH "64")
16 | else()
17 | set(SYSTEM_ARCH "32")
18 | endif()
19 |
20 | add_custom_target(OpenSteamAPI)
21 | set(OSW_ROOT "${CMAKE_CURRENT_LIST_DIR}/../open-steamworks")
22 | set(OSW_INCLUDE_PATH "${OSW_ROOT}/OpenSteamworks")
23 | set(OSW_LIB_ROOT "${OSW_ROOT}/OpenSteamAPI")
24 | if(${SYSTEM_OS} STREQUAL "Win")
25 | if(${SYSTEM_ARCH} STREQUAL "64")
26 | set(OSW_LIB_PATH "${OSW_LIB_ROOT}/lib/${SYSTEM_OS}${SYSTEM_ARCH}/OpenSteamAPI64.lib")
27 | set(OSW_DLL_PATH "${OSW_LIB_ROOT}/lib/${SYSTEM_OS}${SYSTEM_ARCH}/OpenSteamAPI64.dll")
28 | add_custom_command(TARGET OpenSteamAPI
29 | WORKING_DIRECTORY "${OSW_ROOT}"
30 | COMMAND msbuild /p:Platform=x64 /p:Configuration=Release
31 | VERBATIM
32 | )
33 | else()
34 | set(OSW_LIB_PATH "${OSW_LIB_ROOT}/lib/${SYSTEM_OS}${SYSTEM_ARCH}/OpenSteamAPI.lib")
35 | set(OSW_DLL_PATH "${OSW_LIB_ROOT}/lib/${SYSTEM_OS}${SYSTEM_ARCH}/OpenSteamAPI.dll")
36 | add_custom_command(TARGET OpenSteamAPI
37 | WORKING_DIRECTORY "${OSW_ROOT}"
38 | COMMAND msbuild /p:Platform=Win32 /p:Configuration=Release
39 | VERBATIM
40 | )
41 | endif()
42 | else()
43 | set(OSW_LIB_PATH "${OSW_LIB_ROOT}/lib/${SYSTEM_OS}${SYSTEM_ARCH}/OpenSteamAPI.a")
44 | add_custom_command(TARGET OpenSteamAPI
45 | PRE_BUILD
46 | WORKING_DIRECTORY "${OSW_LIB_ROOT}/src"
47 | COMMAND make
48 | VERBATIM
49 | )
50 | endif()
51 |
52 | find_package(wxWidgets COMPONENTS core base adv)
53 | if(NOT wxWidgets_FOUND)
54 | message(FATAL_ERROR "wxWidgets not found!")
55 | endif()
56 |
57 | include(${wxWidgets_USE_FILE})
58 |
59 | set(APPSRCs "RemotePlayWhatever.cpp"
60 | "SteamStuff.h"
61 | "SteamStuff.cpp"
62 | "RemoteApp.h"
63 | "RemoteApp.cpp"
64 | "RemotePlayInviteHandler.h"
65 | "RemotePlayInviteHandler.cpp"
66 | "QueueInviter.cpp"
67 | "QueueInviter.h"
68 | "FriendsListFrame.h"
69 | "FriendsListFrame.cpp"
70 | "ClickablePanel.h"
71 | "ClickablePanel.cpp"
72 | "FriendPanel.h"
73 | "FriendPanel.cpp"
74 | "wxSteamStuff.h"
75 | "wxSteamStuff.cpp"
76 | )
77 |
78 |
79 | if(${SYSTEM_OS} STREQUAL "Win")
80 | add_executable(RemotePlayWhatever WIN32 ${APPSRCs} "appresource.rc")
81 | add_custom_command(TARGET RemotePlayWhatever POST_BUILD
82 | COMMAND ${CMAKE_COMMAND} -E copy ${OSW_DLL_PATH} $
83 | )
84 | else()
85 | add_executable(RemotePlayWhatever ${APPSRCs})
86 | endif()
87 | add_dependencies(RemotePlayWhatever OpenSteamAPI)
88 |
89 | target_compile_definitions(RemotePlayWhatever PRIVATE STEAMWORKS_CLIENT_INTERFACES)
90 | target_include_directories(RemotePlayWhatever PUBLIC ${OSW_INCLUDE_PATH} ${wxWidgets_INCLUDE_DIRS})
91 | target_link_libraries(RemotePlayWhatever ${OSW_LIB_PATH} ${wxWidgets_LIBRARIES})
92 | if(${SYSTEM_OS} STREQUAL "Linux")
93 | target_link_libraries(RemotePlayWhatever ${CMAKE_DL_LIBS})
94 | set_target_properties(RemotePlayWhatever PROPERTIES OUTPUT_NAME "remoteplaywhatever")
95 | install(TARGETS RemotePlayWhatever DESTINATION "bin")
96 |
97 | file(READ "/etc/issue" ISSUE)
98 | string(REGEX MATCH "Debian|Ubuntu" DISTRO ${ISSUE})
99 | if(DISTRO MATCHES "Ubuntu" OR DISTRO MATCHES "Debian")
100 | set(CPACK_GENERATOR "DEB")
101 | set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Lets you force steam remote play together for any game")
102 | set(CPACK_PACKAGE_DESCRIPTION "Tiny app that lets you force steam remote play together for any game")
103 | set(CPACK_DEBIAN_PACKAGE_MAINTAINER "m4dEngi")
104 | set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON)
105 | include(CPack)
106 | endif()
107 | endif()
108 |
109 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/ClickablePanel.cpp:
--------------------------------------------------------------------------------
1 | #include "ClickablePanel.h"
2 |
3 | wxDEFINE_EVENT(BTN_PANEL_LEFT_CLICK, wxCommandEvent);
4 |
5 | ClickablePanel::ClickablePanel(wxWindow *parent, wxWindowID winID):
6 | wxPanel(parent, winID)
7 | {
8 | }
9 |
10 | ClickablePanel::~ClickablePanel()
11 | {
12 | }
13 |
14 | void ClickablePanel::RSetForegroundColour(const wxColour &colour)
15 | {
16 | wxWindowList kidz = GetChildren();
17 | for(auto it = kidz.begin(); it != kidz.end(); ++it)
18 | {
19 | (*it)->SetForegroundColour(colour);
20 | }
21 | }
22 |
23 | void ClickablePanel::RSetBackgroundColour(const wxColour &colour)
24 | {
25 | wxWindowList kidz = GetChildren();
26 | for(auto it = kidz.begin(); it != kidz.end(); ++it)
27 | {
28 | (*it)->SetBackgroundColour(colour);
29 | }
30 | }
31 |
32 | void ClickablePanel::RBindBaseEvents()
33 | {
34 | Bind( wxEVT_ENTER_WINDOW, &ClickablePanel::OnMouseEnter, this);
35 | Bind( wxEVT_LEAVE_WINDOW, &ClickablePanel::OnMouseLeave, this);
36 | Bind( wxEVT_LEFT_DOWN , &ClickablePanel::OnMouseLeftDown, this);
37 | Bind( wxEVT_LEFT_UP , &ClickablePanel::OnMouseLeftUp, this);
38 |
39 | wxWindowList kidz = GetChildren();
40 | for(auto it = kidz.begin(); it != kidz.end(); ++it)
41 | {
42 | (*it)->Bind( wxEVT_LEFT_UP, &ClickablePanel::OnMouseLeftUp, this);
43 | (*it)->Bind( wxEVT_LEFT_DOWN, &ClickablePanel::OnMouseLeftDown, this);
44 | }
45 | }
46 |
47 | void ClickablePanel::OnMouseEnter(wxMouseEvent& event)
48 | {
49 | SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
50 | RSetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT));
51 | Refresh();
52 | }
53 |
54 | void ClickablePanel::OnMouseLeave(wxMouseEvent &event)
55 | {
56 | // check if we're actually leaving panel or just hovering over children
57 | wxPoint evtPoint = event.GetPosition();
58 | wxSize parentSize = this->GetSize();
59 | if( evtPoint.x > 0 && evtPoint.x < parentSize.x &&
60 | evtPoint.y > 0 && evtPoint.y < parentSize.y
61 | )
62 | {
63 | return;
64 | }
65 |
66 | SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
67 | RSetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
68 | Refresh();
69 | }
70 |
71 | void ClickablePanel::OnMouseLeftDown(wxMouseEvent &event)
72 | {
73 | SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE));
74 | Refresh();
75 | }
76 |
77 | void ClickablePanel::OnMouseLeftUp(wxMouseEvent &event)
78 | {
79 | SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
80 | RSetForegroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOWTEXT));
81 | Refresh();
82 |
83 | wxCommandEvent outEvt(BTN_PANEL_LEFT_CLICK, GetId());
84 | outEvt.SetEventObject(this);
85 | ProcessWindowEvent(outEvt);
86 | }
87 |
88 |
89 |
90 | BtnPanel::BtnPanel(wxWindow *parent, wxWindowID winID, wxString label):
91 | ClickablePanel(parent, winID)
92 | {
93 | SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
94 |
95 | wxBoxSizer* rootSizer = new wxBoxSizer( wxVERTICAL );
96 |
97 | rootSizer->Add( 0, 8, 1, wxEXPAND, 0 );
98 |
99 | m_label = new wxStaticText(this, wxID_ANY, label, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
100 | rootSizer->Add(m_label, 0, wxALL | wxEXPAND, 5 );
101 |
102 | rootSizer->Add( 0, 8, 1, wxEXPAND, 0 );
103 |
104 | SetSizer( rootSizer );
105 |
106 | Layout();
107 |
108 | RBindBaseEvents();
109 | }
110 |
111 | BtnPanel::~BtnPanel()
112 | {
113 |
114 | }
115 |
116 | void BtnPanel::SetLabel(wxString &label)
117 | {
118 | m_label->SetLabel(label);
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/ClickablePanel.h:
--------------------------------------------------------------------------------
1 | #ifndef CLICKABLEPANEL_H
2 | #define CLICKABLEPANEL_H
3 | #include
4 |
5 | wxDECLARE_EVENT(BTN_PANEL_LEFT_CLICK, wxCommandEvent);
6 |
7 | class ClickablePanel: public wxPanel
8 | {
9 | public:
10 | ClickablePanel(wxWindow* parent, wxWindowID winID);
11 | ~ClickablePanel();
12 |
13 | protected:
14 | void RBindBaseEvents();
15 |
16 | void OnMouseEnter(wxMouseEvent& event);
17 | void OnMouseLeave(wxMouseEvent& event);
18 | void OnMouseLeftDown(wxMouseEvent& event);
19 | void OnMouseLeftUp(wxMouseEvent& event);
20 |
21 | private:
22 | void RSetForegroundColour(const wxColour &colour);
23 | void RSetBackgroundColour(const wxColour &colour);
24 | };
25 |
26 | class BtnPanel: public ClickablePanel
27 | {
28 | public:
29 | BtnPanel(wxWindow* parent, wxWindowID winID, wxString label);
30 | ~BtnPanel();
31 |
32 | void SetLabel(wxString& label);
33 |
34 | private:
35 | wxStaticText* m_label;
36 | };
37 |
38 | #endif // CLICKABLEPANEL_H
39 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/FriendPanel.cpp:
--------------------------------------------------------------------------------
1 | #include "wxSteamStuff.h"
2 | #include "FriendPanel.h"
3 |
4 | FriendPanel::FriendPanel(wxWindow* parent, wxWindowID winID, CSteamID friendID):
5 | ClickablePanel(parent, winID),
6 | m_steamID(friendID),
7 | m_personaNameLabel(nullptr),
8 | m_personaStateLabel(nullptr)
9 | {
10 | wxBoxSizer* rootSizer = new wxBoxSizer( wxHORIZONTAL );
11 |
12 | SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW));
13 |
14 | int av = GClientContext()->SteamFriends()->GetSmallFriendAvatar(friendID);
15 | if(av)
16 | {
17 | m_personaImg = new wxStaticBitmap( this, wxID_ANY, SteamImageToBitmap(av), wxDefaultPosition, wxSize( 32,32 ), 0 );
18 | }
19 | else
20 | {
21 | m_personaImg = new wxStaticBitmap( this, wxID_ANY, wxNullBitmap, wxDefaultPosition, wxSize( 32,32 ), 0 );
22 | }
23 |
24 | rootSizer->Add( m_personaImg, 0, wxALL, 10 );
25 |
26 | wxBoxSizer* textSizer = new wxBoxSizer( wxVERTICAL );
27 |
28 | textSizer->Add( 0, 0, 1, wxEXPAND, 5 );
29 | wxString personaName;
30 | if(friendID.IsValid())
31 | {
32 | personaName = wxString(GClientContext()->SteamFriends()->GetFriendPersonaName(friendID), wxConvUTF8);
33 | }
34 | m_personaNameLabel = new wxStaticText(this, wxID_ANY, personaName, wxDefaultPosition, wxDefaultSize, 0 );
35 | textSizer->Add( m_personaNameLabel, 0, wxALL | wxEXPAND, 5 );
36 |
37 | textSizer->Add( 0, 0, 1, wxEXPAND, 5 );
38 | rootSizer->Add( textSizer, 1, wxALL|wxEXPAND, 5 );
39 |
40 | SetSizer( rootSizer );
41 | Layout();
42 |
43 | RBindBaseEvents();
44 | }
45 |
46 | FriendPanel::~FriendPanel()
47 | {
48 | }
49 |
50 | CSteamID FriendPanel::GetSteamID()
51 | {
52 | return m_steamID;
53 | }
54 |
55 | wxString FriendPanel::GetDisplayPersonaName()
56 | {
57 | return m_personaNameLabel->GetLabel();
58 | }
59 |
60 | wxString FriendPanel::GetDisplayPersonaState()
61 | {
62 | return m_personaStateLabel->GetLabel();
63 | }
64 |
65 | void FriendPanel::SetDisplayPersonaName(const wxString &name)
66 | {
67 | m_personaNameLabel->SetLabel(name);
68 | }
69 |
70 | void FriendPanel::SetDisaplayPersonaState(const wxString &state)
71 | {
72 | m_personaStateLabel->SetLabel(state);
73 | }
74 |
75 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/FriendPanel.h:
--------------------------------------------------------------------------------
1 | #ifndef FRIENDPANEL_H
2 | #define FRIENDPANEL_H
3 | #include "ClickablePanel.h"
4 | #include "wxSteamStuff.h"
5 |
6 | class FriendPanel : public ClickablePanel
7 | {
8 | public:
9 | FriendPanel(wxWindow* parent, wxWindowID winID, CSteamID friendID);
10 | ~FriendPanel();
11 |
12 | CSteamID GetSteamID();
13 |
14 | wxString GetDisplayPersonaName();
15 | wxString GetDisplayPersonaState();
16 |
17 | void SetDisplayPersonaName(const wxString& name);
18 | void SetDisaplayPersonaState(const wxString& state);
19 |
20 | private:
21 | wxStaticBitmap* m_personaImg;
22 | wxStaticText* m_personaNameLabel;
23 | wxStaticText* m_personaStateLabel;
24 |
25 | CSteamID m_steamID;
26 | };
27 |
28 | #endif // FRIENDPANEL_H
29 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/FriendsListFrame.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "FriendsListFrame.h"
5 | #include "FriendPanel.h"
6 |
7 |
8 | FriendsListFrame::FriendsListFrame(RemotePlayInviteHandler* handler):
9 | wxFrame(NULL, wxID_ANY, _("Remote Play Whatever"), wxPoint(50, 50), wxSize(275, 650)),
10 | m_remoteInviteResultCb(this, &FriendsListFrame::OnRemotePlayInviteResult),
11 | m_personaStateCb(this, &FriendsListFrame::OnPersonaStateChange)
12 | {
13 | SetSizeHints( wxDefaultSize, wxDefaultSize );
14 |
15 | SetMinSize(wxSize(275, 300));
16 | SetMaxSize(wxSize(350, -1));
17 |
18 | m_pRemoteInvite = handler;
19 |
20 | wxBoxSizer* frameSizer = new wxBoxSizer( wxVERTICAL );
21 |
22 | m_friendsListContainer = new wxScrolledWindow( this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxVSCROLL );
23 | m_friendsListContainer->SetScrollRate( 5, 5 );
24 | m_friendsListSizer = new wxBoxSizer( wxVERTICAL );
25 |
26 | BtnPanel* guestBtn = new BtnPanel(m_friendsListContainer, rpwID_INVITE_GUEST, "Create guest invite link");
27 | m_friendsListSizer->Add(guestBtn, 0, wxALL | wxEXPAND, 0);
28 |
29 | wxStaticText* flLabel = new wxStaticText(m_friendsListContainer, wxID_ANY, "or invite a friend", wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
30 | m_friendsListSizer->Add(flLabel, 0, wxALL | wxEXPAND, 10);
31 |
32 | int cFriends = GClientContext()->SteamFriends()->GetFriendCount(k_EFriendFlagImmediate);
33 | for(int i = 0; i < cFriends; ++i)
34 | {
35 | CSteamID friendSteamID = GClientContext()->SteamFriends()->GetFriendByIndex(i, k_EFriendFlagImmediate);
36 | EPersonaState personaState = GClientContext()->SteamFriends()->GetFriendPersonaState(friendSteamID);
37 | if(personaState != k_EPersonaStateOffline && personaState != k_EPersonaStateInvisible)
38 | {
39 | AddFreiendPanel(friendSteamID);
40 | }
41 | }
42 |
43 | m_friendsListContainer->SetSizer( m_friendsListSizer );
44 | m_friendsListContainer->Layout();
45 | m_friendsListSizer->Fit( m_friendsListContainer );
46 | frameSizer->Add( m_friendsListContainer, 1, wxEXPAND | wxALL, 0 );
47 |
48 | SetSizer( frameSizer );
49 | Layout();
50 |
51 | Bind(BTN_PANEL_LEFT_CLICK, &FriendsListFrame::OnFriendPanelClick, this, rpwID_INVITE_FRIEND);
52 | Bind(BTN_PANEL_LEFT_CLICK, &FriendsListFrame::OnGuestPanelClick, this, rpwID_INVITE_GUEST);
53 | }
54 |
55 | void FriendsListFrame::AddFreiendPanel(CSteamID friendID)
56 | {
57 | FriendPanel* fp = new FriendPanel(m_friendsListContainer, rpwID_INVITE_FRIEND, friendID);
58 | m_friendsListSizer->Add(fp, 0, wxBOTTOM | wxEXPAND, 1);
59 |
60 | uint64 friendID64 = friendID.ConvertToUint64();
61 | auto it = m_friendPanels.find(friendID64);
62 | if(it != m_friendPanels.end())
63 | {
64 | m_friendsListContainer->RemoveChild((*it).second);
65 | (*it).second->Destroy();
66 | }
67 | m_friendPanels[friendID.ConvertToUint64()] = fp;
68 | }
69 |
70 | void FriendsListFrame::RemoveFriendPanel(CSteamID friendID)
71 | {
72 | uint64 friendID64 = friendID.ConvertToUint64();
73 | auto it = m_friendPanels.find(friendID64);
74 | if(it != m_friendPanels.end())
75 | {
76 | m_friendsListContainer->RemoveChild((*it).second);
77 | (*it).second->Destroy();
78 | m_friendPanels.erase(it);
79 | }
80 | }
81 |
82 | void FriendsListFrame::OnPersonaStateChange(PersonaStateChange_t* personaStateCb)
83 | {
84 | if(personaStateCb->m_ulSteamID == GClientContext()->SteamUser()->GetSteamID())
85 | {
86 | return;
87 | }
88 |
89 | if(!(GClientContext()->SteamFriends()->GetFriendRelationship(personaStateCb->m_ulSteamID) &
90 | k_EFriendRelationshipFriend))
91 | {
92 | return;
93 | }
94 |
95 | if(personaStateCb->m_nChangeFlags & k_EPersonaChangeGoneOffline)
96 | {
97 | RemoveFriendPanel(personaStateCb->m_ulSteamID);
98 | Layout();
99 | }
100 | else if(personaStateCb->m_nChangeFlags & k_EPersonaChangeComeOnline)
101 | {
102 | AddFreiendPanel(personaStateCb->m_ulSteamID);
103 | Layout();
104 | }
105 | }
106 |
107 | void FriendsListFrame::OnRemotePlayInviteResult(RemotePlayInviteResult_t* inviteResultCb)
108 | {
109 | if (inviteResultCb->m_eResult != k_ERemoteClientLaunchResultOK)
110 | {
111 | wxMessageBox(
112 | wxString::Format("Could not create remote play session! (Result:%d)", inviteResultCb->m_eResult),
113 | "Remote Play Whatever",
114 | wxOK | wxICON_ERROR
115 | );
116 | }
117 | else
118 | {
119 | if(inviteResultCb->m_player.m_playerID.IsValid())
120 | {
121 | wxMessageBox(
122 | "Invite sent",
123 | "Remote Play Whatever",
124 | wxOK | wxICON_INFORMATION
125 | );
126 | }
127 | else
128 | {
129 | if(wxTheClipboard->Open())
130 | {
131 | wxTheClipboard->SetData(new wxTextDataObject(inviteResultCb->m_szConnectURL));
132 | wxMessageBox(
133 | "Guest invite link created and copied to clipboard",
134 | "Remote Play Whatever",
135 | wxOK | wxICON_INFORMATION
136 | );
137 | wxTheClipboard->Flush();
138 | wxTheClipboard->Close();
139 | }
140 | }
141 | }
142 | }
143 |
144 | void FriendsListFrame::OnFriendPanelClick(wxCommandEvent &event)
145 | {
146 | if (!GClientContext()->SteamUser()->BLoggedOn() || !GetRunningGameID().IsValid())
147 | {
148 | wxMessageBox
149 | (
150 | "Could not detect game running. Start a game first!",
151 | "No game runnunig!",
152 | wxOK | wxICON_INFORMATION
153 | );
154 |
155 | return;
156 | }
157 |
158 | FriendPanel* sender = (FriendPanel*)event.GetEventObject();
159 | CSteamID invitee = sender->GetSteamID();
160 | wxMessageDialog* dlg = new wxMessageDialog(this, "Send remote play invite to " + sender->GetDisplayPersonaName() + "?", "Confirm Remote Play Invite", wxOK | wxCANCEL);
161 | if(dlg->ShowModal() == wxID_OK)
162 | {
163 | m_pRemoteInvite->SendInvite(invitee);
164 | }
165 | delete dlg;
166 | }
167 |
168 | void FriendsListFrame::OnGuestPanelClick(wxCommandEvent &event)
169 | {
170 | if (!GClientContext()->SteamUser()->BLoggedOn() || !GetRunningGameID().IsValid())
171 | {
172 | wxMessageBox
173 | (
174 | "Could not detect game running. Start a game first!",
175 | "No game runnunig!",
176 | wxOK | wxICON_INFORMATION
177 | );
178 |
179 | return;
180 | }
181 |
182 | wxMessageDialog* dlg = new wxMessageDialog(this, "Create guest invite link?", "Confirm Remote Play Invite", wxOK | wxCANCEL);
183 | if(dlg->ShowModal() == wxID_OK)
184 | {
185 | m_pRemoteInvite->SendInvite((uint64)0);
186 | }
187 | delete dlg;
188 | }
189 |
190 |
--------------------------------------------------------------------------------
/RemotePlayWhatever/FriendsListFrame.h:
--------------------------------------------------------------------------------
1 | #ifndef FRIENDSLISTFRAME_H
2 | #define FRIENDSLISTFRAME_H
3 | #include