Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ jobs:
export CC=gcc CXX=g++
meson configure -Db_coverage=true build-gcc
meson test -C build-gcc --suite gamescope -v
gcovr -f src --xml -o coverage.xml -s build-gcc
gcovr -f src -e src/reshade --xml -o coverage.xml -s build-gcc
- name: Upload coverage report
uses: actions/upload-artifact@v7
with:
Expand Down
5 changes: 2 additions & 3 deletions src/Backends/OpenVRBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "edid.h"
#include "Ratio.h"
#include "LibInputHandler.h"
#include "Utils/Process.h"

#include <signal.h>
#include <string.h>
Expand Down Expand Up @@ -124,8 +125,6 @@ namespace vr

extern std::atomic<uint32_t> g_unCurrentVRSceneAppId;

uint32_t get_appid_from_pid( pid_t pid );

///////////////////////////////////////////////
// Josh:
// GetVulkanInstanceExtensionsRequired and GetVulkanDeviceExtensionsRequired return *space separated* exts :(
Expand Down Expand Up @@ -1179,7 +1178,7 @@ namespace gamescope
if (m_uCurrentScenePid != vrEvent.data.process.pid)
{
m_uCurrentScenePid = vrEvent.data.process.pid;
m_uCurrentSceneAppId = get_appid_from_pid(m_uCurrentScenePid);
m_uCurrentSceneAppId = gamescope::Process::GetAppIdFromPid(m_uCurrentScenePid);
g_unCurrentVRSceneAppId = m_uCurrentSceneAppId;

openvr_log.debugf("SceneApplicationChanged -> pid: %u appid: %u", m_uCurrentScenePid, m_uCurrentSceneAppId);
Expand Down
47 changes: 45 additions & 2 deletions src/Utils/Process.cpp
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#include "Process.h"
#include "../Utils/Algorithm.h"
#include "Algorithm.h"
#include "../convar.h"
#include "../log.hpp"
#include "../Utils/Defer.h"

#include <algorithm>
#include <array>
#include <cstdio>
#include <cstring>
#include <fstream>
#include <istream>
#include <string>

#include <errno.h>
#include <pthread.h>
Expand Down Expand Up @@ -538,4 +542,43 @@ namespace gamescope::Process
return __progname;
}

uint32_t GetAppIdFromCgroup( std::istream &stream )
{
std::string line;
while ( std::getline( stream, line ) )
{
// cgroup line format: hierarchy-ID:controller-list:cgroup-path
size_t first_colon = line.find( ':' );
if ( first_colon == std::string::npos )
continue;
size_t second_colon = line.find( ':', first_colon + 1 );
if ( second_colon == std::string::npos )
continue;

const char *path = line.c_str() + second_colon + 1;
const char *last_slash = strrchr( path, '/' );
const char *scope = last_slash ? last_slash + 1 : path;

pid_t reaperpid = 0;
uint32_t appid = 0;
if ( sscanf( scope, "app-steam-app%u-%d.scope", &appid, &reaperpid ) == 2 && appid != 0 )
return appid;
}
return 0;
}

uint32_t GetAppIdFromPid( pid_t pid )
{
char filename[256];
snprintf( filename, sizeof( filename ), "/proc/%i/cgroup", pid );
std::ifstream cgroup_file( filename );

if ( !cgroup_file.is_open() || cgroup_file.bad() )
return 0;

uint32_t appid = GetAppIdFromCgroup( cgroup_file );
if ( appid != 0 )
s_ProcessLog.debugf( "AppID %u derived from cgroup for pid %d", appid, pid );
return appid;
}
}
8 changes: 6 additions & 2 deletions src/Utils/Process.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#pragma once

#include <optional>
#include <cstdint>
#include <functional>
#include <iosfwd>
#include <optional>
#include <span>

#include <sys/types.h>
Expand Down Expand Up @@ -43,4 +45,6 @@ namespace gamescope::Process

const char *GetProcessName();

}
uint32_t GetAppIdFromCgroup( std::istream &stream );
uint32_t GetAppIdFromPid( pid_t pid );
}
99 changes: 1 addition & 98 deletions src/steamcompmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,11 @@
#include <condition_variable>
#include <mutex>
#include <atomic>
#include <vector>
#include <algorithm>
#include <array>
#include <iostream>
#include <fstream>
#include <string>
#include <queue>
#include <filesystem>
#include <variant>
#include <unordered_set>

#include <assert.h>
Expand Down Expand Up @@ -97,7 +93,6 @@
#include "reshade_effect_manager.hpp"
#include "BufferMemo.h"
#include "Utils/Process.h"
#include "Utils/Algorithm.h"

#include "wlr_begin.hpp"
#include "wlr/types/wlr_pointer_constraints_v1.h"
Expand Down Expand Up @@ -4932,98 +4927,6 @@ get_name_from_pid( pid_t pid )
return procNameStr;
}

uint32_t
get_appid_from_pid( pid_t pid )
{
uint32_t unFoundAppId = 0;

char filename[256];
pid_t next_pid = pid;

while ( 1 )
{
snprintf( filename, sizeof( filename ), "/proc/%i/stat", next_pid );
std::ifstream proc_stat_file( filename );

if (!proc_stat_file.is_open() || proc_stat_file.bad())
break;

std::string proc_stat;

std::getline( proc_stat_file, proc_stat );

char *procName = nullptr;
char *lastParens = nullptr;

for ( uint32_t i = 0; i < proc_stat.length(); i++ )
{
if ( procName == nullptr && proc_stat[ i ] == '(' )
{
procName = &proc_stat[ i + 1 ];
}

if ( proc_stat[ i ] == ')' )
{
lastParens = &proc_stat[ i ];
}
}

if (!lastParens)
break;

*lastParens = '\0';
char state;
int parent_pid = -1;

sscanf( lastParens + 1, " %c %d", &state, &parent_pid );

if ( strcmp( "reaper", procName ) == 0 )
{
snprintf( filename, sizeof( filename ), "/proc/%i/cmdline", next_pid );
std::ifstream proc_cmdline_file( filename );
std::string proc_cmdline;

bool bSteamLaunch = false;
uint32_t unAppId = 0;

std::getline( proc_cmdline_file, proc_cmdline );

for ( uint32_t j = 0; j < proc_cmdline.length(); j++ )
{
if ( proc_cmdline[ j ] == '\0' && j + 1 < proc_cmdline.length() )
{
if ( strcmp( "SteamLaunch", &proc_cmdline[ j + 1 ] ) == 0 )
{
bSteamLaunch = true;
}
else if ( sscanf( &proc_cmdline[ j + 1 ], "AppId=%u", &unAppId ) == 1 && unAppId != 0 )
{
if ( bSteamLaunch == true )
{
unFoundAppId = unAppId;
}
}
else if ( strcmp( "--", &proc_cmdline[ j + 1 ] ) == 0 )
{
break;
}
}
}
}

if ( parent_pid == -1 || parent_pid == 0 )
{
break;
}
else
{
next_pid = parent_pid;
}
}

return unFoundAppId;
}

static pid_t
get_win_pid(xwayland_ctx_t *ctx, Window id)
{
Expand Down Expand Up @@ -5113,7 +5016,7 @@ add_win(xwayland_ctx_t *ctx, Window id, Window prev, unsigned long sequence)
{
if ( new_win->pid != -1 )
{
new_win->appID = get_appid_from_pid( new_win->pid );
new_win->appID = gamescope::Process::GetAppIdFromPid( new_win->pid );
}
else
{
Expand Down
6 changes: 2 additions & 4 deletions src/wlserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
#include "InputEmulation.h"
#include "commit.h"
#include "Timeline.h"
#include "Utils/NonCopyable.h"
#include "Utils/Process.h"

#if HAVE_PIPEWIRE
#include "pipewire.hpp"
Expand Down Expand Up @@ -1845,8 +1845,6 @@ void xdg_toplevel_new(struct wl_listener *listener, void *data)
{
}

uint32_t get_appid_from_pid( pid_t pid );

wlserver_xdg_surface_info* waylandy_type_surface_new(struct wl_client *client, struct wlr_surface *surface)
{
wlserver_wl_surface_info *wlserver_surface = get_wl_surface_info(surface);
Expand All @@ -1868,7 +1866,7 @@ wlserver_xdg_surface_info* waylandy_type_surface_new(struct wl_client *client, s
{
pid_t nPid = 0;
wl_client_get_credentials( client, &nPid, nullptr, nullptr );
window->appID = get_appid_from_pid( nPid );
window->appID = gamescope::Process::GetAppIdFromPid( nPid );
}
window->_window_types.emplace<steamcompmgr_xdg_win_t>();

Expand Down
20 changes: 16 additions & 4 deletions tests/meson.build
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
catch2_dep = dependency('catch2-with-main', required: true)

srcdir = '../src'
unittest_src = []

convar_unittest_src = []
foreach src : gamescope_core_src + ['Script/Script.cpp', 'convar_script.cpp']
unittest_src += srcdir / src
convar_unittest_src += srcdir / src
endforeach

test_convar = executable(
'test_convar',
unittest_src + ['test_convar.cpp'],
convar_unittest_src + ['test_convar.cpp'],
include_directories: [srcdir, sol2_include],
dependencies: [catch2_dep, luajit_dep, cap_dep, drm_dep, glm_dep, wlroots_dep],
cpp_args: gamescope_cpp_args,
)
test('convar', test_convar)

utils_process_unittest_src = []
foreach src : gamescope_core_src
utils_process_unittest_src += srcdir / src
endforeach
test_utils_process = executable(
'test_utils_process',
utils_process_unittest_src + ['test_utils_process.cpp'],
include_directories: [srcdir],
dependencies: [catch2_dep, cap_dep],
)
test('utils/process', test_utils_process)
44 changes: 44 additions & 0 deletions tests/test_utils_process.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <catch2/catch_test_macros.hpp>

#include <sstream>

#include "Utils/Process.h"

using namespace gamescope;

TEST_CASE("GetAppIdFromCgroup", "[appid]") {
SECTION("steam app scope") {
std::istringstream stream("0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-steam-app12345-9876.scope\n");
REQUIRE(Process::GetAppIdFromCgroup(stream) == 12345u);
}

SECTION("no matching scope") {
std::istringstream stream("0::/user.slice/user-1000.slice/session.scope\n");
REQUIRE(Process::GetAppIdFromCgroup(stream) == 0u);
}

SECTION("multiple lines, one matching") {
std::istringstream stream(
"12:devices:/user.slice\n"
"11:memory:/user.slice/user-1000.slice/session-1.scope\n"
"0::/user.slice/user-1000.slice/user@1000.service/app.slice/app-steam-app567-42.scope\n"
);
REQUIRE(Process::GetAppIdFromCgroup(stream) == 567u);
}

SECTION("empty stream") {
std::istringstream stream("");
REQUIRE(Process::GetAppIdFromCgroup(stream) == 0u);
}

SECTION("malformed lines") {
std::istringstream stream("not-a-valid-cgroup-line\n");
REQUIRE(Process::GetAppIdFromCgroup(stream) == 0u);

stream = std::istringstream("not-a-valid:cgroup-line\n");
REQUIRE(Process::GetAppIdFromCgroup(stream) == 0u);

stream = std::istringstream("not-a-valid:cgroup-line:\n");
REQUIRE(Process::GetAppIdFromCgroup(stream) == 0u);
}
}