Compare commits

...

33 Commits
1.8 ... master

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
Gilles Schintgen
c54d6d4243 fix project version in meson.build (1.8.1->1.8.2) 2024-09-03 12:53:21 +01:00
Ahsan Fayaz
715a1389b7 Update version to 1.8.2 2024-08-19 11:18:51 +01:00
Ahsan Fayaz
c0516059ed Update copyright year 2024-08-09 13:14:27 +01:00
Ahsan Fayaz
a875008d8e Standardise formatting of "Other Apps" section 2024-08-09 13:12:16 +01:00
Mahmut Sacit Meydanal
ec10c591ff
README: Add new gamemode extension link (#484)
* README: Add new gamemode extension link

Since the old extension is no longer maintained by the original author and has stopped working in the latest releases of GNOME, I recommend including my extension in the README.
2024-08-09 11:59:49 +01:00
mhmarf
fef538ba92
Update gamemode.ini (#490)
Clarify that user must be added to the gamemode group in order to make Core Parking work.
2024-08-01 14:22:16 +01:00
peelz
a2fe0108b5 Drop duplicate clang-format rule 2024-03-27 16:44:45 +00:00
peelz
c854772369 Fix idle inhibitor closing bus connection too early 2024-03-27 16:44:45 +00:00
Kira Bruneau
7d55ef856b add homepage url to metainfo 2024-03-27 10:22:58 +00:00
Daniel Martinez
1c293d7818 use posix basename to fix build on musl
glibc provides a nonstandard basename implementation,
this can be overriden and posix basename can be used
by includeing libgen.h, however musl only has posix
basename, and must always include libgen.h

In this particular case, it doesn't appear that using
the posix version of basename will cause any issues,
as it is simply being used to match a hardcoded config
file name.
2024-03-27 10:17:17 +00:00
Reilly Brogan
9646f2bd93 gamemodelist: Fix unreadable process maps
Systemd commit [bf1b9ae487b65b1cb1639b222724fab95e508cf5](bf1b9ae487) (present in Systemd v254 and later) effectively broke gamemodelist as the process map for the `systemd --user` process became unreadable. After this change gamemodelist would exit with an error like the following:

```
awk: fatal: cannot open file `/proc/2281/maps' for reading: Permission denied
```

To work around this let's add a hook to the awk statement to skip any files that can't be read.

Closes FeralInteractive/gamemode#456
2024-03-27 10:16:28 +00:00
Jrelvas
8e0a71a0bc README: Add Vinegar to apps list
Vinegar is a bootstrapper for Roblox Player and Roblox Studio. It uses gamemode for both by default.
2024-02-14 10:37:00 +00:00
Trial97
3fa41891cf Fixed crash if dbus is not accesible
Signed-off-by: Trial97 <alexandru.tripon97@gmail.com>
2024-02-14 10:33:22 +00:00
patatahooligan
8cea4c2418 Fix hybrid CPU core pinning
Previously the condition would always evaluate to `false`. With the fix
any core with >=95% of `max_freq` will be added to the set of cores to
keep.
2024-01-03 11:09:13 +00:00
Kostadin Shishmanov
4a82094c98 Fix build with musl
Fixes the following build failure:
In file included from ../common/common-cpu.c:32:
../common/common-cpu.h:44:9: error: unknown type name 'cpu_set_t'
   44 |         cpu_set_t *online;
      |         ^~~~~~~~~
../common/common-cpu.h:45:9: error: unknown type name 'cpu_set_t'
   45 |         cpu_set_t *to_keep;
      |         ^~~~~~~~~

Signed-off-by: Kostadin Shishmanov <kocelfc@tutanota.com>
2024-01-03 10:54:40 +00:00
Ahsan Fayaz
5180d89e66 Update version to 1.8.1 2023-12-12 16:02:49 +00:00
Vamist
e82e9dafd0 [fix] polkit parse error caused by an extra ) 2023-12-11 09:14:05 +00:00
58 changed files with 853 additions and 198 deletions

View File

@ -19,7 +19,6 @@ AlwaysBreakTemplateDeclarations: false
BinPackArguments: false
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Linux
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false

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,3 +1,36 @@
## 1.8.2
### Changes
* Fix idle inhibitor closing bus connection too early (#466)
* Fix hybrid CPU core pinning (#455)
* Fix unreadable process maps in gamemodelist (#463)
* Fixed crash if dbus is not accesible (#458)
* Various bugfixes and improvements to documentation
### Contributors
* @notpeelz
* @patatahooligan
* Reilly Brogan @ReillyBrogan
* Alexandru Ionut Tripon @Trial97
* Kostadin @kostadinsh
* Daniel Martinez @Calandracas606
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2023-12-13&to=2024-08-19&type=c)
## 1.8.1
### Changes
* Fix polkit parse error (#449)
### Contributors
* @Vam-Jam
[View the full list of contributors](https://github.com/FeralInteractive/gamemode/graphs/contributors?from=2022-07-22&to=2023-12-12&type=c)
## 1.8
### Changes

View File

@ -1,4 +1,4 @@
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

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,29 +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 ([via extension](https://github.com/gicmo/gamemode-extension)) - indicates when GameMode is active in the top panel.
* Lutris - 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) - is a frontend for emulators, game engines and media players.
---
## 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)
@ -89,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
@ -114,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
@ -130,13 +124,19 @@ 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:
Then clone, build and install a release version of GameMode at 1.8.2:
```bash
git clone https://github.com/FeralInteractive/gamemode.git
cd gamemode
git checkout 1.8 # omit to build the master branch
git checkout 1.8.2 # omit to build the master branch
./bootstrap.sh
```
To test GameMode installed and will run correctly:
@ -164,7 +164,7 @@ See the [contributors](https://github.com/FeralInteractive/gamemode/graphs/contr
---
## License
Copyright © 2017-2023 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
@ -31,6 +31,8 @@ POSSIBILITY OF SUCH DAMAGE.
#pragma once
#define _GNU_SOURCE
#include <sched.h>
#include <stdlib.h>

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
@ -41,6 +41,7 @@ POSSIBILITY OF SUCH DAMAGE.
#include <ini.h>
#include <dirent.h>
#include <libgen.h>
#include <math.h>
#include <pthread.h>
#include <pwd.h>
@ -87,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;
@ -264,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) {
@ -690,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,11 +89,16 @@ 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 */
struct GameModeCPUInfo *cpu; /**<Stored CPU info for the current CPU */
GameModeIdleInhibitor *idle_inhibitor;
bool igpu_optimization_enabled;
uint32_t last_cpu_energy_uj;
uint32_t last_igpu_energy_uj;
@ -207,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))
@ -215,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;
@ -259,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) {
@ -317,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);
@ -404,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.
*
@ -415,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.
@ -423,8 +497,10 @@ static void game_mode_context_enter(GameModeContext *self)
}
/* Inhibit the screensaver */
if (config_get_inhibit_screensaver(self->config))
game_mode_inhibit_screensaver(true);
if (config_get_inhibit_screensaver(self->config)) {
game_mode_destroy_idle_inhibitor(self->idle_inhibitor);
self->idle_inhibitor = game_mode_create_idle_inhibitor();
}
game_mode_disable_splitlock(self, true);
@ -454,14 +530,21 @@ 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);
game_mode_unpark_cpu(self->cpu);
/* UnInhibit the screensaver */
if (config_get_inhibit_screensaver(self->config))
game_mode_inhibit_screensaver(false);
if (config_get_inhibit_screensaver(self->config)) {
game_mode_destroy_idle_inhibitor(self->idle_inhibitor);
self->idle_inhibitor = NULL;
}
game_mode_disable_splitlock(self, false);

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)
@ -134,7 +164,7 @@ static int walk_sysfs(char *cpulist, char **buf, size_t *buflen, GameModeCPUInfo
max_freq = freq;
}
if (freq - cutoff >= max_freq)
if (freq + cutoff >= max_freq)
CPU_SET_S((size_t)cpu, CPU_ALLOC_SIZE(info->num_cpu), freq_cores);
}
}
@ -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
@ -708,81 +708,106 @@ void game_mode_context_loop(GameModeContext *context)
}
}
struct GameModeIdleInhibitor {
sd_bus *bus;
unsigned int cookie;
};
/**
* Attempts to inhibit the screensaver
* Uses the "org.freedesktop.ScreenSaver" interface
*/
static unsigned int screensaver_inhibit_cookie = 0;
int game_mode_inhibit_screensaver(bool inhibit)
GameModeIdleInhibitor *game_mode_create_idle_inhibitor(void)
{
const char *service = "org.freedesktop.ScreenSaver";
const char *object_path = "/org/freedesktop/ScreenSaver";
const char *interface = "org.freedesktop.ScreenSaver";
const char *function = inhibit ? "Inhibit" : "UnInhibit";
sd_bus_message *msg = NULL;
sd_bus *bus_local = NULL;
sd_bus_error err;
memset(&err, 0, sizeof(sd_bus_error));
int result = -1;
sd_bus_error err = SD_BUS_ERROR_NULL;
// Open the user bus
int ret = sd_bus_open_user(&bus_local);
if (ret < 0) {
LOG_ERROR("Could not connect to user bus: %s\n", strerror(-ret));
return -1;
return NULL;
}
if (inhibit) {
ret = sd_bus_call_method(bus_local,
service,
object_path,
interface,
function,
&err,
&msg,
"ss",
"com.feralinteractive.GameMode",
"GameMode Activated");
} else {
ret = sd_bus_call_method(bus_local,
service,
object_path,
interface,
function,
&err,
&msg,
"u",
screensaver_inhibit_cookie);
}
ret = sd_bus_call_method(bus_local,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"Inhibit",
&err,
&msg,
"ss",
"com.feralinteractive.GameMode",
"GameMode Activated");
if (ret < 0) {
LOG_ERROR(
"Could not call %s on %s: %s\n"
"Failed to call Inhibit on org.freedesktop.ScreenSaver: %s\n"
"\t%s\n"
"\t%s\n",
function,
service,
strerror(-ret),
err.name,
err.message);
} else if (inhibit) {
// Read the reply
ret = sd_bus_message_read(msg, "u", &screensaver_inhibit_cookie);
if (ret < 0) {
LOG_ERROR("Failure to parse response from %s on %s: %s\n",
function,
service,
strerror(-ret));
} else {
result = 0;
}
} else {
result = 0;
sd_bus_close(bus_local);
sd_bus_unrefp(&bus_local);
return NULL;
}
sd_bus_unref(bus_local);
// Read the reply
unsigned int cookie = 0;
ret = sd_bus_message_read(msg, "u", &cookie);
if (ret < 0) {
LOG_ERROR("Invalid response from Inhibit on org.freedesktop.ScreenSaver: %s\n",
strerror(-ret));
sd_bus_close(bus_local);
sd_bus_unrefp(&bus_local);
return NULL;
}
return result;
GameModeIdleInhibitor *inhibitor = malloc(sizeof(GameModeIdleInhibitor));
if (inhibitor == NULL) {
sd_bus_close(bus_local);
sd_bus_unrefp(&bus_local);
return NULL;
}
inhibitor->bus = bus_local;
inhibitor->cookie = cookie;
return inhibitor;
}
void game_mode_destroy_idle_inhibitor(GameModeIdleInhibitor *inhibitor)
{
sd_bus_message *msg = NULL;
sd_bus_error err = SD_BUS_ERROR_NULL;
if (inhibitor == NULL) {
return;
}
int ret = sd_bus_call_method(inhibitor->bus,
"org.freedesktop.ScreenSaver",
"/org/freedesktop/ScreenSaver",
"org.freedesktop.ScreenSaver",
"UnInhibit",
&err,
&msg,
"u",
inhibitor->cookie);
if (ret < 0) {
LOG_ERROR(
"Failed to call UnInhibit on org.freedesktop.ScreenSaver: %s\n"
"\t%s\n"
"\t%s\n",
strerror(-ret),
err.name,
err.message);
}
sd_bus_close(inhibitor->bus);
sd_bus_unrefp(&inhibitor->bus);
free(inhibitor);
}

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
@ -219,7 +219,9 @@ void game_mode_undo_core_pinning(const GameModeCPUInfo *info, const pid_t client
/** gamemode-dbus.c
* Provides an API interface for using dbus
*/
typedef struct GameModeIdleInhibitor GameModeIdleInhibitor;
void game_mode_context_loop(GameModeContext *context) __attribute__((noreturn));
int game_mode_inhibit_screensaver(bool inhibit);
GameModeIdleInhibitor *game_mode_create_idle_inhibitor(void);
void game_mode_destroy_idle_inhibitor(GameModeIdleInhibitor *inhibitor);
void game_mode_client_registered(pid_t);
void game_mode_client_unregistered(pid_t);

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

@ -28,5 +28,5 @@ if [ ! -d /proc ]; then
fi
find /proc -maxdepth 2 -type f -user "${USER}" -readable -name maps -exec \
awk -- '$0 ~ /libgamemodeauto\.so\.0/ {pid=FILENAME; gsub("[^0-9]", "", pid); print pid;nextfile}' {} + \
awk -- 'BEGINFILE { if (ERRNO) nextfile } $0 ~ /libgamemodeauto\.so\.0/ {pid=FILENAME; gsub("[^0-9]", "", pid); print pid;nextfile}' {} + \
| xargs | xargs -I{} -- ps -o pid,ppid,user,ni,psr,comm --pid '{}'

View File

@ -36,4 +36,6 @@
<category>Utility</category>
<category>Game</category>
</categories>
<url type="homepage">https://feralinteractive.github.io/gamemode</url>
</component>

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

@ -5,8 +5,9 @@
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.cpu-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.
@ -87,6 +92,8 @@ disable_splitlock=1
; a range. E.g "park_cores=1,8-15" would park cores 1 and 8 to 15.
; The default is uncommented is to disable parking but enable pinning. If either is enabled the code will
; currently only properly autodetect Ryzen 7900x3d, 7950x3d and Intel CPU:s with E- and P-cores.
; For Core Parking, user must be added to the gamemode group (not required for Core Pinning):
; sudo usermod -aG gamemode $(whoami)
;park_cores=no
;pin_cores=yes

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
@ -150,7 +150,7 @@ static int log_error(const char *fmt, ...)
static void hop_off_the_bus(DBusConnection **bus)
{
if (bus == NULL)
if (bus == NULL || *bus == NULL)
return;
dbus_connection_unref(*bus);

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

@ -2,8 +2,9 @@ project(
'gamemode',
'c',
default_options : ['c_std=c11', 'warning_level=3'],
version: '1.8',
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;