Compare commits

...

16 Commits

Author SHA1 Message Date
Ehren Bendler
af07e169d5 fix static analyzer error that is blocking CI 2025-05-02 08:30:35 +01:00
Ehren Bendler
6e5c950141 fix clang-format error that is blocking CI 2025-05-01 15:35:50 +01:00
MithicSpirit
499af4c7bb Prevent platform profile error on unsupported systems
If a system does not support setting the platform profile (i.e., does
not have the file /sys/firmware/acpi/platform_profile), then everything
that interacts with it is skipped to prevent errors. This situation is
more common than I expected.[1]

[1] https://github.com/FeralInteractive/gamemode/issues/524
2025-04-30 19:12:51 +01:00
Ehren Bendler
5f691c3171 Potential fix for code scanning alert no. 2: Workflow does not contain permissions
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
2025-04-30 19:11:59 +01:00
Ehren Bendler
f5fbdcf014 Take advanted of imminent EOL of Ubuntu 20 to update GH runner config. Also make a pass over the development section of the README to reflect current reality. Update inih to r60. Update meson settings to eliminate deprecated functions. Set minimum Meson version to 1.3.1 to match Ubuntu 24 and OpenSUSE Leap 15.6. 2025-04-30 19:11:59 +01:00
Richard Martin
7c49d6e1db docs: a change was made to remove the supported games list, but it was still referenced from one location in the README.md 2025-03-14 10:23:06 +00:00
MithicSpirit
d84c5ded1c Extract split lock mitigation into common file
This reduces duplication of the split lock mitigation path.
Additionally, it allows extending the functionality of procsysctl into
also getting the split lock mitigation state.
2025-02-24 15:43:15 +00:00
MithicSpirit
2d71be6a94 Save all state before any of it is changed
The platform profile may restrict what values the governor can take, so
we need to save all state first to ensure the restored state is correct.
Then, the platform profile is the first thing we set (and restore) to
ensure all other changes are made to the best of our ability.
2025-02-24 15:43:15 +00:00
MithicSpirit
1e3e55c5d8 Add tests for platform profile
Note that the platform profile feature is considered required. Maybe
this shouldn't be the case, as I'm not sure how standard this feature is
yet.
2025-02-24 15:43:15 +00:00
MithicSpirit
e75f2c3942 Support setting the platform profile
The platform profile lives in /sys/firmware/acpi/platform_profile. The
desiredprof and defaultprof options correspond to the values for the
platform profile set when gamescope begins and ends, respectively.

HACK: The platform profile may restrict what values the governor can
take, so we choose to set the governor before the platform profile in
order to store the correct default governor, and restore the platform
profile before the governor. This is done to maximize correctness after
restoration, but it can cause issues if the previous platform profile
restricts the governor.

TODO: Save all the state we care about before any of it is changed. In
thsi case, we should set (and restore) the platform profile before the
governor.
2025-02-24 15:43:15 +00:00
Ahsan Fayaz
81d26c79b4 Remove list of supported apps
GameMode integration is widespread nowadays, and it works out of the box
for the most common use cases.

There is no longer a need for us to maintain a list of supported games
and applications, allowing us to streamline the readme file.
2025-02-24 15:15:17 +00:00
Ahsan Fayaz
5b3b4ae638 Add "GameMode contributors" to copyright notice
Attribute the contributions made by members of the community, but avoid
unweildy and inconsistent copyright notices at the top of each file.

Existing contributor copyright notices have been left as-is.
2025-02-24 15:14:21 +00:00
Henrik Holst
1d9c1e6a72
Use better kernel api for P/E-cores (#515)
There's a proper kernel way to check for the presence of P- and E-cores so we don't have to check the frequency differences.

Kept the frequency check in case the kernel way does not exist on the users kernel but upped the fail-safe to 10% from 5% to close #498
2025-01-27 08:25:32 +00:00
Kaydax
2eecd513ac Update README.md
PolyMC seems to have been removed for the reason "PolyMC died" yet the project is still active. Requesting this be added back as it still supports gamemode even on the latest version
2025-01-13 11:43:31 +00:00
afayaz-feral
086b3fe5b4
Update build-and-test.yml
Update to upload-artifact@v4
2024-11-06 11:54:08 +00:00
Andrew Koroluk
2f69f7c023 Add suggestion message to failed CPU governernor test 2024-11-04 11:04:05 +00:00
54 changed files with 716 additions and 134 deletions

View File

@ -1,15 +1,18 @@
name: Build and test
on: [push, pull_request]
permissions:
contents: read
actions: write
jobs:
build-and-test:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2
- name: Install dependencies
run: |
sudo apt install build-essential meson appstream clang clang-format clang-tools libdbus-1-dev libinih-dev libsystemd-dev
sudo DEBIAN_FRONTEND=noninteractive apt-get install -y build-essential meson appstream clang clang-format clang-tools libdbus-1-dev libinih-dev libsystemd-dev systemd-dev
- name: Check format
env:
CI: "true"
@ -31,7 +34,7 @@ jobs:
./scripts/static-analyser-check.sh
- name: Upload logs
if: ${{ always() }}
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v4
with:
name: logs
path: |

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/builddir
/subprojects/packagecache/
/subprojects/inih-r*

View File

@ -1,4 +1,4 @@
Copyright (c) 2017-2024, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -20,7 +20,7 @@ Issues with GameMode should be reported here in the issues section, and not repo
---
## Requesting GameMode
For games/launchers which integrate GameMode support (see list later on), simply running the game will automatically activate GameMode.
For games/launchers which integrate GameMode support, simply running the game will automatically activate GameMode.
For others, you must manually request GameMode when running the game. This can be done by launching the game through `gamemoderun`:
```bash
@ -58,30 +58,6 @@ You can do this by setting the environment variable `GAMEMODERUNEXEC` to your wr
GameMode will not be injected to the wrapper.
---
## Apps with GameMode integration
### Games
The following games are known to integrate GameMode support (meaning they don't require any additional configuration to activate GameMode while running):
* DiRT 4
* Rise of the Tomb Raider
* Shadow of the Tomb Raider
* Total War Saga: Thrones of Britannia
* Total War: ROME REMASTERED
* Total War: Three Kingdoms
* Total War: WARHAMMER II
* Total War: WARHAMMER III
### Others
Other apps which can integrate with GameMode include:
* [ATLauncher](https://atlauncher.com/downloads) Minecraft launcher
* [Cemu](https://cemu.info/) Wii U emulator
* [GNOME Shell Extension](https://github.com/trsnaqe/gamemode-shell-extension) GameMode status indicator extension for GNOME Shell
* [Lutris](https://lutris.net/) Install and play any video game easily
* Enables GameMode for all games by default if available (must have both 32- and 64-bit GameMode libraries installed), configurable in preferences.
* [Prism Launcher](https://prismlauncher.org/) Minecraft launcher
* [RetroArch](https://www.retroarch.com) Frontend for emulators, game engines and media players
* [Vinegar](https://vinegarhq.org/) Roblox Player/Studio bootstrapper
---
## Development [![Build and test](https://github.com/FeralInteractive/gamemode/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/FeralInteractive/gamemode/actions/workflows/build-and-test.yml)
@ -90,15 +66,16 @@ The design of GameMode has a clear-cut abstraction between the host daemon and l
See repository subdirectories for information on each component.
### Install Dependencies
GameMode depends on `meson` for building and `systemd` for internal communication. This repo contains a `bootstrap.sh` script to allow for quick install to the user bus, but check `meson_options.txt` for custom settings.
#### Ubuntu/Debian (you may also need `dbus-user-session`)
GameMode depends on `meson` for building and `systemd` for internal communication. This repo contains a `bootstrap.sh` script to allow for quick install to the user bus, but check `meson_options.txt` for custom settings. These instructions all assume that you
already have a C development environment (gcc or clang, libc-devel, etc) installed.
#### Ubuntu/Debian
Note: Debian 13 and Ubuntu 25.04 (and later) need to install `systemd-dev` in addition to the dependencies below.
```bash
apt install meson libsystemd-dev pkg-config ninja-build git libdbus-1-dev libinih-dev build-essential
apt update && apt install meson libsystemd-dev pkg-config ninja-build git dbus-user-session libdbus-1-dev libinih-dev build-essential
```
On Ubuntu 18.04, you'll need to install `python3` package and install the latest meson version from `pip`.
On Debian 12 and Ubuntu 22 (and earlier), you'll need to install `python3` and `python3-venv` packages to install the latest meson version from `pip`.
```bash
python3 -m venv .venv
@ -115,12 +92,28 @@ rm -rf .venv
#### Arch
```bash
pacman -S meson systemd git dbus libinih
pacman -S meson systemd git dbus libinih gcc pkgconf
```
#### RHEL 10 and variants
Note: Older versions of RHEL (and variants) cannot build gamemode due to not exposing libdbus-1 to pkg-config.
(also - don't try and play games on RHEL, come on)
You must have [EPEL](https://docs.fedoraproject.org/en-US/epel/getting-started/) enabled to install all dependencies.
```bash
dnf install meson systemd-devel pkg-config git dbus-devel inih-devel
```
#### Fedora
```bash
dnf install meson systemd-devel pkg-config git dbus-devel
dnf install meson systemd-devel pkg-config git dbus-devel inih-devel
```
#### OpenSUSE Leap/Tumbleweed
```bash
zypper install meson systemd-devel git dbus-1-devel libgcc_s1 libstdc++-devel libinih-devel
```
#### Gentoo
Gentoo has an ebuild which builds a stable release from sources. It will also pull in all the dependencies so you can work on the source code.
```bash
@ -131,6 +124,12 @@ You can also install using the latest sources from git:
ACCEPT_KEYWORDS="**" emerge --ask ~games-util/gamemode-9999
```
#### Nix
Similar to Gentoo, nixOS already has a package for gamemode, so we can use that to setup an environment:
```bash
nix-shell -p pkgs.gamemode.buildInputs pkgs.gamemode.nativeBuildInputs
```
### Build and Install GameMode
Then clone, build and install a release version of GameMode at 1.8.2:
@ -165,7 +164,7 @@ See the [contributors](https://github.com/FeralInteractive/gamemode/graphs/contr
---
## License
Copyright © 2017-2024 Feral Interactive
Copyright © 2017-2025 Feral Interactive and the GameMode contributors
GameMode is available under the terms of the BSD 3-Clause License (Revised)

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Red Hat
All rights reserved.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Red Hat
All rights reserved.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Red Hat
All rights reserved.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
Copyright (c) 2019, Intel Corporation
All rights reserved.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

86
common/common-profile.c Normal file
View File

@ -0,0 +1,86 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "common-profile.h"
#include "common-logging.h"
/**
* Path for platform profile
*/
const char *profile_path = "/sys/firmware/acpi/platform_profile";
/**
* Check if platform profile file exists
*/
int profile_exists(void)
{
return !access(profile_path, F_OK);
}
/**
* Return the current platform profile state
*/
const char *get_profile_state(void)
{
/* Persistent profile state */
static char profile[64] = { 0 };
memset(profile, 0, sizeof(profile));
FILE *f = fopen(profile_path, "r");
if (!f) {
LOG_ERROR("Failed to open file for read %s\n", profile_path);
return "none";
}
/* Grab the file length */
fseek(f, 0, SEEK_END);
long length = ftell(f);
fseek(f, 0, SEEK_SET);
if (length == -1) {
LOG_ERROR("Failed to seek file %s\n", profile_path);
} else {
char contents[length + 1];
if (fread(contents, 1, (size_t)length, f) > 0) {
strtok(contents, "\n");
strncpy(profile, contents, sizeof(profile) - 1);
} else {
LOG_ERROR("Failed to read contents of %s\n", profile_path);
}
}
fclose(f);
return profile;
}

50
common/common-profile.h Normal file
View File

@ -0,0 +1,50 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <linux/limits.h>
#include <unistd.h>
/**
* Path for platform profile
*/
extern const char *profile_path;
/**
* Check if platform profile file exists
*/
int profile_exists(void);
/**
* Get the current platform profile state
*/
const char *get_profile_state(void);

64
common/common-splitlock.c Normal file
View File

@ -0,0 +1,64 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "common-splitlock.h"
#include "common-logging.h"
/**
* Path for the split lock mitigation state
*/
const char *splitlock_path = "/proc/sys/kernel/split_lock_mitigate";
/**
* Return the current split lock mitigation state
*/
long get_splitlock_state(void)
{
FILE *f = fopen(splitlock_path, "r");
if (!f) {
LOG_ERROR("Failed to open file for read %s\n", splitlock_path);
return -1;
}
char contents[41] = { 0 };
long value = -1;
if (fread(contents, 1, sizeof contents - 1, f) > 0) {
value = strtol(contents, NULL, 10);
} else {
LOG_ERROR("Failed to read contents of %s\n", splitlock_path);
}
fclose(f);
return value;
}

44
common/common-splitlock.h Normal file
View File

@ -0,0 +1,44 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <linux/limits.h>
/**
* Path for the split lock mitagation state
*/
extern const char *splitlock_path;
/**
* Get the current split lock mitigation state
*/
long get_splitlock_state(void);

View File

@ -2,6 +2,8 @@
common_sources = [
'common-logging.c',
'common-governors.c',
'common-profile.c',
'common-splitlock.c',
'common-external.c',
'common-helpers.c',
'common-gpu.c',

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -88,6 +88,9 @@ struct GameModeConfig {
char defaultgov[CONFIG_VALUE_MAX];
char desiredgov[CONFIG_VALUE_MAX];
char defaultprof[CONFIG_VALUE_MAX];
char desiredprof[CONFIG_VALUE_MAX];
char igpu_desiredgov[CONFIG_VALUE_MAX];
float igpu_power_threshold;
@ -265,6 +268,10 @@ static int inih_handler(void *user, const char *section, const char *name, const
valid = get_string_value(value, self->values.defaultgov);
} else if (strcmp(name, "desiredgov") == 0) {
valid = get_string_value(value, self->values.desiredgov);
} else if (strcmp(name, "defaultprof") == 0) {
valid = get_string_value(value, self->values.defaultprof);
} else if (strcmp(name, "desiredprof") == 0) {
valid = get_string_value(value, self->values.desiredprof);
} else if (strcmp(name, "igpu_desiredgov") == 0) {
valid = get_string_value(value, self->values.igpu_desiredgov);
} else if (strcmp(name, "igpu_power_threshold") == 0) {
@ -691,13 +698,29 @@ void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALU
}
/*
* Get the chosen desired governor
* Get the chosen desired platform profile
*/
void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self, governor, self->values.desiredgov, sizeof(self->values.desiredgov));
}
/*
* Get the chosen default platform profile
*/
void config_get_default_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self, profile, self->values.defaultprof, sizeof(self->values.defaultprof));
}
/*
* Get the chosen desired governor
*/
void config_get_desired_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX])
{
memcpy_locked_config(self, profile, self->values.desiredprof, sizeof(self->values.desiredprof));
}
/*
* Get the chosen iGPU desired governor
*/

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -102,6 +102,8 @@ bool config_get_inhibit_screensaver(GameModeConfig *self);
long config_get_script_timeout(GameModeConfig *self);
void config_get_default_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
void config_get_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
void config_get_default_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX]);
void config_get_desired_profile(GameModeConfig *self, char profile[CONFIG_VALUE_MAX]);
void config_get_igpu_desired_governor(GameModeConfig *self, char governor[CONFIG_VALUE_MAX]);
float config_get_igpu_power_threshold(GameModeConfig *self);
void config_get_soft_realtime(GameModeConfig *self, char softrealtime[CONFIG_VALUE_MAX]);

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -36,6 +36,8 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-helpers.h"
#include "common-logging.h"
#include "common-power.h"
#include "common-profile.h"
#include "common-splitlock.h"
#include "gamemode.h"
#include "gamemode-config.h"
@ -71,6 +73,11 @@ enum GameModeGovernor {
GAME_MODE_GOVERNOR_IGPU_DESIRED,
};
enum GameModeProfile {
GAME_MODE_PROFILE_DEFAULT,
GAME_MODE_PROFILE_DESIRED,
};
struct GameModeContext {
pthread_rwlock_t rwlock; /**<Guard access to the client list */
_Atomic int refcount; /**<Allow cycling the game mode */
@ -82,6 +89,9 @@ struct GameModeContext {
enum GameModeGovernor current_govenor;
char initial_profile[64];
enum GameModeProfile current_profile;
struct GameModeGPUInfo *stored_gpu; /**<Stored GPU info for the current GPU */
struct GameModeGPUInfo *target_gpu; /**<Target GPU info for the current GPU */
@ -209,6 +219,13 @@ void game_mode_context_destroy(GameModeContext *self)
pthread_rwlock_destroy(&self->rwlock);
}
static void game_mode_store_splitlock(GameModeContext *self)
{
long initial_state = get_splitlock_state();
self->initial_split_lock_mitigate = initial_state;
LOG_MSG("split lock mitigation was initially set to [%ld]\n", initial_state);
}
static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
{
if (!config_get_disable_splitlock(self->config))
@ -217,30 +234,8 @@ static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
long value_num = self->initial_split_lock_mitigate;
char value_str[40];
if (disable) {
FILE *f = fopen("/proc/sys/kernel/split_lock_mitigate", "r");
if (f == NULL) {
if (errno == ENOENT)
return 0;
LOG_ERROR("Couldn't open /proc/sys/kernel/split_lock_mitigate : %s\n", strerror(errno));
return 1;
}
if (fgets(value_str, sizeof value_str, f) == NULL) {
LOG_ERROR("Couldn't read from /proc/sys/kernel/split_lock_mitigate : %s\n",
strerror(errno));
fclose(f);
return 1;
}
self->initial_split_lock_mitigate = strtol(value_str, NULL, 10);
fclose(f);
value_num = 0;
if (self->initial_split_lock_mitigate == value_num)
return 0;
}
if (disable && value_num == 0)
return 0;
if (value_num == -1)
return 0;
@ -261,25 +256,26 @@ static int game_mode_disable_splitlock(GameModeContext *self, bool disable)
return 0;
}
static void game_mode_store_governor(GameModeContext *self)
{
if (self->current_govenor != GAME_MODE_GOVERNOR_DEFAULT)
return;
const char *initial_state = get_gov_state();
if (initial_state == NULL)
return;
strncpy(self->initial_cpu_mode, initial_state, sizeof(self->initial_cpu_mode) - 1);
self->initial_cpu_mode[sizeof(self->initial_cpu_mode) - 1] = '\0';
LOG_MSG("governor was initially set to [%s]\n", initial_state);
}
static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor gov)
{
if (self->current_govenor == gov) {
return 0;
}
if (self->current_govenor == GAME_MODE_GOVERNOR_DEFAULT) {
/* Read the initial governor state so we can revert it correctly */
const char *initial_state = get_gov_state();
if (initial_state == NULL) {
return 0;
}
/* store the initial cpu governor mode */
strncpy(self->initial_cpu_mode, initial_state, sizeof(self->initial_cpu_mode) - 1);
self->initial_cpu_mode[sizeof(self->initial_cpu_mode) - 1] = '\0';
LOG_MSG("governor was initially set to [%s]\n", initial_state);
}
char *gov_str = NULL;
char gov_config_str[CONFIG_VALUE_MAX] = { 0 };
switch (gov) {
@ -319,6 +315,65 @@ static int game_mode_set_governor(GameModeContext *self, enum GameModeGovernor g
return 0;
}
static void game_mode_store_profile(GameModeContext *self)
{
if (!profile_exists() || self->current_profile != GAME_MODE_PROFILE_DEFAULT)
return;
const char *initial_state = get_profile_state();
if (initial_state == NULL)
return;
strncpy(self->initial_profile, initial_state, sizeof(self->initial_profile) - 1);
self->initial_profile[sizeof(self->initial_profile) - 1] = '\0';
LOG_MSG("platform profile was initially set to [%s]\n", initial_state);
}
static int game_mode_set_profile(GameModeContext *self, enum GameModeProfile prof)
{
if (self->current_profile == prof) {
return 0;
}
if (!profile_exists()) {
LOG_MSG("Setting platform profile unsupported; skipping\n");
return 0;
}
const char *prof_str = NULL;
char prof_config_str[CONFIG_VALUE_MAX] = { 0 };
switch (prof) {
case GAME_MODE_PROFILE_DEFAULT:
config_get_default_profile(self->config, prof_config_str);
prof_str = prof_config_str[0] != '\0' ? prof_config_str : self->initial_profile;
break;
case GAME_MODE_PROFILE_DESIRED:
config_get_desired_profile(self->config, prof_config_str);
prof_str = prof_config_str[0] != '\0' ? prof_config_str : "performance";
break;
default:
assert(!"Invalid platform profile requested");
}
const char *const exec_args[] = {
"pkexec", LIBEXECDIR "/platprofctl", "set", prof_str, NULL,
};
LOG_MSG("Requesting update of platform profile to %s\n", prof_str);
int ret = run_external_process(exec_args, NULL, -1);
if (ret != 0) {
LOG_ERROR("Failed to update platform profile\n");
return ret;
}
/* Update the current govenor only if we succeed at setting govenors. */
self->current_profile = prof;
return 0;
}
static void game_mode_enable_igpu_optimization(GameModeContext *self)
{
float threshold = config_get_igpu_power_threshold(self->config);
@ -406,6 +461,15 @@ unlock:
pthread_rwlock_unlock(&self->rwlock);
}
static void game_mode_context_store_defaults(GameModeContext *self)
{
game_mode_store_profile(self);
game_mode_store_governor(self);
game_mode_store_splitlock(self);
}
/**
* Pivot into game mode.
*
@ -417,6 +481,14 @@ static void game_mode_context_enter(GameModeContext *self)
LOG_MSG("Entering Game Mode...\n");
sd_notifyf(0, "STATUS=%sGameMode is now active.%s\n", "\x1B[1;32m", "\x1B[0m");
/* Store the default value for everything before anything changes. */
game_mode_context_store_defaults(self);
/* Set the profile before anything else since it can restrict things
* like the governor.
*/
game_mode_set_profile(self, GAME_MODE_PROFILE_DESIRED);
if (game_mode_set_governor(self, GAME_MODE_GOVERNOR_DESIRED) == 0) {
/* We just switched to a non-default governor. Enable the iGPU
* optimization.
@ -458,6 +530,11 @@ static void game_mode_context_leave(GameModeContext *self)
LOG_MSG("Leaving Game Mode...\n");
sd_notifyf(0, "STATUS=%sGameMode is currently deactivated.%s\n", "\x1B[1;36m", "\x1B[0m");
/* Restore profile before anything else since it can restrict things
* like the governor.
*/
game_mode_set_profile(self, GAME_MODE_PROFILE_DEFAULT);
/* Remove GPU optimisations */
game_mode_apply_gpu(self->stored_gpu);

View File

@ -1,7 +1,7 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -73,6 +73,38 @@ static int read_small_file(char *path, char **buf, size_t *buflen)
return 1;
}
static void set_online_from_list(char *cpulist, GameModeCPUInfo *info)
{
long from, to;
while ((cpulist = parse_cpulist(cpulist, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online);
}
}
}
static int check_pe_cores(char **buf, size_t *buflen, GameModeCPUInfo *info)
{
if (!read_small_file("/sys/devices/cpu_core/cpus", buf, buflen))
return 0;
LOG_MSG("found kernel support for checking P/E-cores\n");
long from, to;
char *list = *buf;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->to_keep);
}
}
if (CPU_EQUAL_S(CPU_ALLOC_SIZE(info->num_cpu), info->online, info->to_keep) ||
CPU_COUNT_S(CPU_ALLOC_SIZE(info->num_cpu), info->to_keep) == 0)
LOG_MSG("kernel did not indicate that this was an P/E-cores system\n");
return 1;
}
static int walk_sysfs(char *cpulist, char **buf, size_t *buflen, GameModeCPUInfo *info)
{
char path[PATH_MAX];
@ -84,8 +116,6 @@ static int walk_sysfs(char *cpulist, char **buf, size_t *buflen, GameModeCPUInfo
char *list = cpulist;
while ((list = parse_cpulist(list, &from, &to))) {
for (long cpu = from; cpu < to + 1; cpu++) {
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), info->online);
/* check for L3 cache non-uniformity among the cores */
int ret =
snprintf(path, PATH_MAX, "/sys/devices/system/cpu/cpu%ld/cache/index3/size", cpu);
@ -125,7 +155,7 @@ static int walk_sysfs(char *cpulist, char **buf, size_t *buflen, GameModeCPUInfo
if (ret > 0 && ret < PATH_MAX) {
if (read_small_file(path, buf, buflen)) {
unsigned long long freq = strtoull(*buf, NULL, 10);
unsigned long long cutoff = (freq * 5) / 100;
unsigned long long cutoff = (freq * 10) / 100;
if (freq > max_freq) {
if (max_freq < freq - cutoff)
@ -278,8 +308,13 @@ int game_mode_initialise_cpu(GameModeConfig *config, GameModeCPUInfo **info)
} else if (park_or_pin == IS_CPU_PIN && pin_cores[0] != '\0') {
if (!walk_string(buf, pin_cores, new_info))
goto error_exit;
} else if (!walk_sysfs(buf, &buf2, &buf2len, new_info)) {
goto error_exit;
} else {
set_online_from_list(buf, new_info);
if (!check_pe_cores(&buf2, &buf2len, new_info)) {
if (!walk_sysfs(buf, &buf2, &buf2len, new_info))
goto error_exit;
}
}
if (park_or_pin == IS_CPU_PARK &&

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,7 +1,7 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -60,7 +60,7 @@ POSSIBILITY OF SUCH DAMAGE.
#endif
#ifndef IOPRIO_PRIO_DATA
#define IOPRIO_PRIO_DATA(mask) ((mask)&IOPRIO_PRIO_MASK)
#define IOPRIO_PRIO_DATA(mask) ((mask) & IOPRIO_PRIO_MASK)
#endif
#ifndef IOPRIO_PRIO_VALUE

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
@ -36,6 +36,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include "common-gpu.h"
#include "common-helpers.h"
#include "common-logging.h"
#include "common-profile.h"
#include "gamemode.h"
#include "gamemode-config.h"
@ -357,6 +358,62 @@ static int run_cpu_governor_tests(struct GameModeConfig *config)
return 0;
}
/* Check the platform profile setting works */
static int run_platform_profile_tests(struct GameModeConfig *config)
{
if (!profile_exists())
return 1;
/* get the two config parameters we care about */
char desiredprof[CONFIG_VALUE_MAX] = { 0 };
config_get_desired_profile(config, desiredprof);
if (desiredprof[0] == '\0')
strcpy(desiredprof, "performance");
char defaultprof[CONFIG_VALUE_MAX] = { 0 };
config_get_default_profile(config, defaultprof);
if (defaultprof[0] == '\0') {
const char *currentprof = get_profile_state();
if (currentprof) {
strncpy(defaultprof, currentprof, CONFIG_VALUE_MAX - 1);
} else {
LOG_ERROR(
"Could not get current platform profile state, this indicates an error! See rest "
"of log.\n");
return -1;
}
}
/* Start gamemode */
gamemode_request_start();
/* Verify the platform profile is the desired one */
const char *currentprof = get_profile_state();
if (strncmp(currentprof, desiredprof, CONFIG_VALUE_MAX) != 0) {
LOG_ERROR("Platform profile was not set to %s (was actually %s)!\n",
desiredprof,
currentprof);
gamemode_request_end();
return -1;
}
/* End gamemode */
gamemode_request_end();
/* Verify the platform profile has been set back */
currentprof = get_profile_state();
if (strncmp(currentprof, defaultprof, CONFIG_VALUE_MAX) != 0) {
LOG_ERROR("Platform profile was not set back to %s (was actually %s)!\n",
defaultprof,
currentprof);
return -1;
}
return 0;
}
static int run_custom_scripts_tests(struct GameModeConfig *config)
{
int scriptstatus = 0;
@ -801,11 +858,32 @@ static int game_mode_run_feature_tests(struct GameModeConfig *config)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
LOG_MSG(" -- You may need to add your user to the gamemode group:");
LOG_MSG(" -- $ sudo usermod -aG gamemode $(whoami)");
// Consider the CPU governor feature required
status = -1;
}
}
/* Does the platform profile get set properly? */
{
LOG_MSG("::: Verifying platform profile setting\n");
int profstatus = run_platform_profile_tests(config);
if (profstatus == 1)
LOG_MSG("::: Passed (platform profile not supported)\n");
else if (profstatus == 0)
LOG_MSG("::: Passed\n");
else {
LOG_MSG("::: Failed!\n");
LOG_MSG(" -- You may need to add your user to the gamemode group:");
LOG_MSG(" -- $ sudo usermod -aG gamemode $(whoami)");
// If available, setting the platform profile should work
status = -1;
}
}
/* Do custom scripts run? */
{
LOG_MSG("::: Verifying Scripts\n");

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -9,7 +9,7 @@ data_conf.set('GAMEMODE_PRIVILEGED_GROUP', with_privileged_group)
# Pull in the example config
config_example = run_command(
'cat',
join_paths(meson.source_root(), 'example', 'gamemode.ini'),
join_paths(meson.project_source_root(), 'example', 'gamemode.ini'),
check: true,
).stdout().strip()
data_conf.set('GAMEMODE_EXAMPLE_CONFIG', config_example)

View File

@ -5,7 +5,7 @@
<policyconfig>
<!--
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
-->
@ -58,4 +58,16 @@
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/procsysctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
<action id="com.feralinteractive.GameMode.profile-helper">
<description>Modify the platform profile</description>
<message>Authentication is required to modify platform profile</message>
<defaults>
<allow_any>no</allow_any>
<allow_inactive>no</allow_inactive>
<allow_active>no</allow_active>
</defaults>
<annotate key="org.freedesktop.policykit.exec.path">@LIBEXECDIR@/platprofctl</annotate>
<annotate key="org.freedesktop.policykit.exec.allow_gui">true</annotate>
</action>
</policyconfig>

View File

@ -6,7 +6,8 @@ polkit.addRule(function (action, subject) {
if ((action.id == "com.feralinteractive.GameMode.governor-helper" ||
action.id == "com.feralinteractive.GameMode.gpu-helper" ||
action.id == "com.feralinteractive.GameMode.cpu-helper" ||
action.id == "com.feralinteractive.GameMode.procsys-helper") &&
action.id == "com.feralinteractive.GameMode.procsys-helper" ||
action.id == "com.feralinteractive.GameMode.profile-helper") &&
subject.isInGroup("@GAMEMODE_PRIVILEGED_GROUP@"))
{
return polkit.Result.YES;

View File

@ -7,6 +7,11 @@ desiredgov=performance
; The default governor is used when leaving GameMode instead of restoring the original value
;defaultgov=powersave
; The desired platform profile is used when entering GameMode instead of "performance"
desiredprof=performance
; The default platform profile is used when leaving GameMode instead of restoring the original value
;defaultgov=low-power
; The iGPU desired governor is used when the integrated GPU is under heavy load
igpu_desiredgov=powersave
; Threshold to use to decide when the integrated GPU is under heavy load.

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -4,6 +4,7 @@ project(
default_options : ['c_std=c11', 'warning_level=3'],
version: '1.8.2',
license: 'BSD',
meson_version: '>= 1.3.1',
)
am_cflags = [
@ -112,7 +113,7 @@ if sd_bus_provider == 'systemd'
if path_systemd_unit_dir == ''
message('Asking pkg-config for systemd\'s \'systemduserunitdir\' directory')
pkgconfig_systemd = dependency('systemd')
path_systemd_unit_dir = pkgconfig_systemd.get_pkgconfig_variable('systemduserunitdir')
path_systemd_unit_dir = pkgconfig_systemd.get_variable(pkgconfig: 'systemduserunitdir')
endif
endif
if with_privileged_group != ''
@ -122,7 +123,7 @@ if sd_bus_provider == 'systemd'
if path_systemd_group_dir == ''
message('Asking pkg-config for systemd\'s \'sysusersdir\' directory')
pkgconfig_systemd = dependency('systemd')
path_systemd_group_dir = pkgconfig_systemd.get_pkgconfig_variable('sysusersdir')
path_systemd_group_dir = pkgconfig_systemd.get_variable(pkgconfig: 'sysusersdir')
endif
endif
else

View File

@ -1,20 +1,20 @@
# limits.d
option('with-pam-renicing', type: 'boolean', description: 'Install the limits.d configuration file to allow renicing as a user being part of the privileged gamemode group', value: 'true')
option('with-pam-renicing', type: 'boolean', description: 'Install the limits.d configuration file to allow renicing as a user being part of the privileged gamemode group', value: true)
option('with-pam-limits-dir', type: 'string', description: 'Explicitly set the PAM limits.d directory', value: '/etc/security/limits.d')
# sd-bus provider
option('with-sd-bus-provider', type: 'combo', choices: ['systemd', 'elogind', 'no-daemon'], value: 'systemd')
# systemd specific
option('with-systemd-user-unit', type: 'boolean', description: 'Install systemd user unit', value: 'true')
option('with-systemd-user-unit', type: 'boolean', description: 'Install systemd user unit', value: true)
option('with-systemd-user-unit-dir', type: 'string', description: 'Explicitly set the systemd user unit directory')
option('with-systemd-group', type: 'boolean', description: 'Install privileged gamemode group with systemd', value: 'true')
option('with-systemd-group', type: 'boolean', description: 'Install privileged gamemode group with systemd', value: true)
option('with-systemd-group-dir', type: 'string', description: 'Explicitly set the systemd group directory')
# Not using systemd
option('with-dbus-service-dir', type: 'string', description: 'Explicitly set the D-BUS session directory')
# General options
option('with-examples', type: 'boolean', description: 'Build sample programs', value: 'true')
option('with-util', type: 'boolean', description: 'Build the utilities', value: 'true')
option('with-examples', type: 'boolean', description: 'Build sample programs', value: true)
option('with-util', type: 'boolean', description: 'Build the utilities', value: true)
option('with-privileged-group', type: 'string', description: 'Group that has access to privileged gamemode features', value: 'gamemode')

View File

@ -1,10 +1,9 @@
[wrap-file]
directory = inih-r54
source_url = https://github.com/benhoyt/inih/archive/r54.tar.gz
source_filename = inih-r54.tar.gz
source_hash = b5566af5203f8a49fda27f1b864c0c157987678ffbd183280e16124012869869
directory = inih-r60
source_url = https://github.com/benhoyt/inih/archive/r60.tar.gz
source_filename = inih-r60.tar.gz
source_hash = 706aa05c888b53bd170e5d8aa8f8a9d9ccf5449dfed262d5103d1f292af26774
[provide]
inih = inih_dep
inireader = INIReader_dep

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -1,6 +1,6 @@
/*
Copyright (c) 2017-2019, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -58,3 +58,18 @@ procsysctl = executable(
install: true,
install_dir: path_libexecdir,
)
# Small target util to get and set platform profile
platprofctl_sources = [
'platprofctl.c',
]
platprofctl = executable(
'platprofctl',
sources: platprofctl_sources,
dependencies: [
link_daemon_common,
],
install: true,
install_dir: path_libexecdir,
)

84
util/platprofctl.c Normal file
View File

@ -0,0 +1,84 @@
/*
Copyright (c) 2025, the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of Feral Interactive nor the names of its contributors
may be used to endorse or promote products derived from this software
without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include "common-logging.h"
#include "common-profile.h"
#include <unistd.h>
/**
* Sets platform profile to a value
*/
static int set_profile_state(const char *value)
{
int retval = EXIT_SUCCESS;
FILE *f = fopen(profile_path, "w");
if (!f) {
LOG_ERROR("Failed to open file for write %s\n", profile_path);
return EXIT_FAILURE;
}
if (fprintf(f, "%s\n", value) < 0) {
LOG_ERROR("Failed to set platform profile to %s: %s", value, strerror(errno));
retval = EXIT_FAILURE;
}
fclose(f);
return retval;
}
/**
* Main entry point, dispatch to the appropriate helper
*/
int main(int argc, char *argv[])
{
if (argc == 2 && strncmp(argv[1], "get", 3) == 0) {
printf("%s", get_profile_state());
} else if (argc == 3 && strncmp(argv[1], "set", 3) == 0) {
const char *value = argv[2];
/* Must be root to set the state */
if (geteuid() != 0) {
LOG_ERROR("This program must be run as root\n");
return EXIT_FAILURE;
}
return set_profile_state(value);
} else {
fprintf(stderr, "usage: platprofctl [get] [set VALUE]\n");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

View File

@ -1,5 +1,5 @@
/*
Copyright (c) 2017-2023, Feral Interactive
Copyright (c) 2017-2025, Feral Interactive and the GameMode contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
@ -27,8 +27,9 @@ POSSIBILITY OF SUCH DAMAGE.
#define _GNU_SOURCE
#include <unistd.h>
#include "common-logging.h"
#include "common-splitlock.h"
static bool write_value(char *key, char *value)
static bool write_value(const char *key, const char *value)
{
FILE *f = fopen(key, "w");
@ -58,7 +59,7 @@ int main(int argc, char *argv[])
if (argc == 3) {
if (strcmp(argv[1], "split_lock_mitigate") == 0) {
if (!write_value("/proc/sys/kernel/split_lock_mitigate", argv[2]))
if (!write_value(splitlock_path, argv[2]))
return EXIT_FAILURE;
return EXIT_SUCCESS;