Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
be0c2e28b2 | |||
af4ac1c167 | |||
ea524fd764 | |||
d829af05f1 | |||
c6434f1a4e | |||
c3a26bb44e | |||
c58b9fbe90 | |||
efb2043ec9 | |||
d0c1cffe8f | |||
6f08777b20 | |||
4d2f4b3bc3 | |||
92a359bb7d | |||
7c50b95d03 | |||
5a628d9dc3 | |||
af5ead2ddc |
145
README.md
Normal file
145
README.md
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# falcond - Advanced Linux Gaming Performance Daemon
|
||||||
|
|
||||||
|
falcond is a powerful system daemon designed to automatically optimize your Linux gaming experience. It intelligently manages system resources and performance settings on a per-game basis, eliminating the need to manually configure settings for each game.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- **Automatic Game Detection**: Automatically detects running games and applies optimized settings
|
||||||
|
- **Per-Game Profiles**: Customizable profiles for different games
|
||||||
|
- **Performance Mode**: Enables system-wide performance optimizations when gaming
|
||||||
|
- **3D vcache Management**: Smart management of AMD 3D vcache settings
|
||||||
|
- **SCX Scheduler Integration**: Dynamically pick a scheduler that is best for the specific game
|
||||||
|
- **Proton Compatibility**: Full support for Steam Proton games, with a global profile for excellent game coverage
|
||||||
|
- **Low Overhead**: Minimal system resource usage
|
||||||
|
- **Different Device Modes**: Profiles for desktops, handhelds, HTPC etc
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
falcond is available on PikaOS via the falcond package or via the Pika Gameutils Metapackage. For other distributions, please check your package manager or build from source.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
The configuration file is located at `/etc/falcond/config.conf`. Here's an example configuration, it will be generated automatically on first run:
|
||||||
|
|
||||||
|
```
|
||||||
|
enable_performance_mode = true
|
||||||
|
scx_sched = none
|
||||||
|
scx_sched_props = default
|
||||||
|
vcache_mode = none
|
||||||
|
profile_mode = none
|
||||||
|
```
|
||||||
|
|
||||||
|
This is global configuration and all options other than profile_mode override individual game profiles.
|
||||||
|
|
||||||
|
There is also a list of proton/wine system processes in `/usr/share/falcond/system.conf`. This list can be updated if for example a crash handler is intercepting your profile and needs to be ignored.
|
||||||
|
|
||||||
|
### Configuration Options
|
||||||
|
|
||||||
|
- `enable_performance_mode`: Enable/disable performance mode (default: true)
|
||||||
|
- `scx_sched`: SCX scheduler (options: none, bpfland, lavd, rusty, flash)
|
||||||
|
- `scx_sched_props`: SCX scheduler mode (options: default, gaming, power, latency, server)
|
||||||
|
- `vcache_mode`: VCache management mode (options: none, cache, freq)
|
||||||
|
- `profile_mode`: What type of device is in use, none means desktop (options: none, handheld, htpc)
|
||||||
|
|
||||||
|
## Profile Modes
|
||||||
|
Falcond now supports different profile modes for different device types:
|
||||||
|
- Default (none): Uses profiles from `/usr/share/falcond/profiles`
|
||||||
|
- Handheld: Uses profiles from `/usr/share/falcond/profiles/handheld`
|
||||||
|
- HTPC: Uses profiles from `/usr/share/falcond/profiles/htpc`
|
||||||
|
|
||||||
|
To set a profile mode, add this to your config file:
|
||||||
|
```
|
||||||
|
profile_mode = "handheld" # or "htpc" or "none"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Game Profiles
|
||||||
|
|
||||||
|
Game profiles are stored in `/usr/share/falcond/profiles/` and define specific optimizations for individual games. You can contribute new profiles via PR at:
|
||||||
|
[Falcond Profiles Repository](https://github.com/PikaOS-Linux/falcond-profiles)
|
||||||
|
|
||||||
|
Example profile:
|
||||||
|
```
|
||||||
|
name = "game.exe"
|
||||||
|
performance_mode = true
|
||||||
|
scx_sched = bpfland
|
||||||
|
scx_sched_props = gaming
|
||||||
|
vcache_mode = cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### Available Options
|
||||||
|
|
||||||
|
- `name`: The exe name (examples: cs2, PathOfExileSteam.exe)
|
||||||
|
- `performance_mode`: Enable/disable performance mode (default: true)
|
||||||
|
- `scx_sched`: SCX scheduler (options: none, bpfland, lavd, rusty, flash)
|
||||||
|
- `scx_sched_props`: SCX scheduler mode (options: none, gaming, power, latency, server)
|
||||||
|
- `vcache_mode`: VCache management mode (options: none, cache, freq)
|
||||||
|
|
||||||
|
## Service Management
|
||||||
|
|
||||||
|
To restart the daemon:
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart falcond
|
||||||
|
```
|
||||||
|
|
||||||
|
To check the status:
|
||||||
|
```
|
||||||
|
sudo systemctl status falcond
|
||||||
|
```
|
||||||
|
|
||||||
|
## Source Code
|
||||||
|
|
||||||
|
The source code for falcond can be found at:
|
||||||
|
[Falcond Source Repository](https://git.pika-os.com/general-packages/falcond)
|
||||||
|
|
||||||
|
## Important Notes
|
||||||
|
|
||||||
|
⚠️ **Compatibility Warning** ⚠️ falcond should not be used alongside Feral GameMode or Falcon GameMode as they may conflict with each other. falcond provides similar functionality with additional features and optimizations.
|
||||||
|
|
||||||
|
## Why falcond?
|
||||||
|
|
||||||
|
Traditional gaming on Linux often requires manual optimization for each game - tweaking CPU governors, scheduling priorities, and cache settings. falcond automates this entire process by:
|
||||||
|
|
||||||
|
1. Automatically detecting when games are running
|
||||||
|
2. Applying optimized settings based on pre-configured profiles
|
||||||
|
3. Reverting settings when games are closed
|
||||||
|
4. Providing a centralized way to manage gaming performance
|
||||||
|
5. Easy switching of options for different device types
|
||||||
|
|
||||||
|
This means you can focus on gaming while falcond handles all the technical optimizations in the background!
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
falcond is released under the [MIT License](http://git.pika-os.com/general-packages/falcond/raw/branch/main/LICENSE).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Please fork the [PikaOS-Linux/falcond](https://github.com/PikaOS-Linux/falcond) repository and submit a pull request.
|
||||||
|
|
||||||
|
## Build Dependencies
|
||||||
|
|
||||||
|
zig 0.14.0+
|
||||||
|
|
||||||
|
## Building from Source
|
||||||
|
|
||||||
|
```
|
||||||
|
git clone https://git.pika-os.com/general-packages/falcond.git
|
||||||
|
cd falcond
|
||||||
|
cd falcond
|
||||||
|
zig build -Doptimize=ReleaseFast
|
||||||
|
```
|
||||||
|
|
||||||
|
## Runtime Dependencies
|
||||||
|
|
||||||
|
These should be feature detected by falcond so if not present that specific feature will not be used.
|
||||||
|
|
||||||
|
power-profiles-daemon or tuned + tuned-ppd
|
||||||
|
scx-sched
|
||||||
|
Linux kernel patched with AMD 3D vcache support
|
||||||
|
|
||||||
|
## Packaging
|
||||||
|
|
||||||
|
falcond should be placed in /usr/bin/falcond and run via a service file. There is an example systemd service file in ./falcond/debian/falcond.service
|
||||||
|
|
||||||
|
falcond needs profiles to be useful, these should be placed in /usr/share/falcond/profiles alongside the system.conf in /usr/share/falcond/system.conf. Upto date profiles can be found in the [PikaOS-Linux/falcond-profiles](https://github.com/PikaOS-Linux/falcond-profiles) repository. We currently pull the latest profiles from there on building of this package but you could also package seperately and depend on that package.
|
||||||
|
|
||||||
|
There is a config file in /etc/falcond/config.conf which is generated automatically on first run. You could also package that if you need different default settings.
|
@ -1,3 +1,39 @@
|
|||||||
|
falcond (1.0.8-101pika2) pika; urgency=low
|
||||||
|
|
||||||
|
* Fix scx scheduler mode selection order
|
||||||
|
|
||||||
|
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
|
||||||
|
|
||||||
|
falcond (1.0.7-101pika2) pika; urgency=low
|
||||||
|
|
||||||
|
* Don't load profiles in subfolders
|
||||||
|
|
||||||
|
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
|
||||||
|
|
||||||
|
falcond (1.0.6-101pika1) pika; urgency=low
|
||||||
|
|
||||||
|
* Add handheld and htpc profiles, split system processes config to live in the profiles repo for easier updates
|
||||||
|
|
||||||
|
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
|
||||||
|
|
||||||
|
falcond (1.0.5-101pika1) pika; urgency=low
|
||||||
|
|
||||||
|
* Fix scx scheduler reactivation + improve info logging - actual fix it this time
|
||||||
|
|
||||||
|
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
|
||||||
|
|
||||||
|
falcond (1.0.4-101pika1) pika; urgency=low
|
||||||
|
|
||||||
|
* Fix scx scheduler reactivation + improve info logging
|
||||||
|
|
||||||
|
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
|
||||||
|
|
||||||
|
falcond (1.0.3-101pika1) pika; urgency=low
|
||||||
|
|
||||||
|
* Disable ananicy-cpp when scx sched is used, reneable when one is disabled
|
||||||
|
|
||||||
|
-- ferreo <ferreo@pika-os.com> Sun, 12 Jan 2025 13:48:00 +0300
|
||||||
|
|
||||||
falcond (1.0.2-101pika1) pika; urgency=low
|
falcond (1.0.2-101pika1) pika; urgency=low
|
||||||
|
|
||||||
* Minor fix for race condition during profile reloadings
|
* Minor fix for race condition during profile reloadings
|
||||||
|
@ -24,7 +24,7 @@ override_dh_install:
|
|||||||
chmod 755 debian/falcond/usr/bin/falcond
|
chmod 755 debian/falcond/usr/bin/falcond
|
||||||
chmod +x debian/falcond/usr/bin/falcond
|
chmod +x debian/falcond/usr/bin/falcond
|
||||||
# Copy profiles
|
# Copy profiles
|
||||||
cp -r falcond-profiles/usr/share/falcond/profiles debian/falcond/usr/share/falcond/
|
cp -r falcond-profiles/usr/share/falcond debian/falcond/usr/share/
|
||||||
|
|
||||||
override_dh_installsystemd:
|
override_dh_installsystemd:
|
||||||
dh_installsystemd --name=falcond --restart-after-upgrade
|
dh_installsystemd --name=falcond --restart-after-upgrade
|
||||||
|
@ -75,7 +75,11 @@ pub fn init(alloc: std.mem.Allocator) !void {
|
|||||||
allocator = alloc;
|
allocator = alloc;
|
||||||
std.log.info("Initializing scheduler state", .{});
|
std.log.info("Initializing scheduler state", .{});
|
||||||
|
|
||||||
const sched_list = try getSupportedSchedulers(alloc);
|
const sched_list = getSupportedSchedulers(alloc) catch |err| {
|
||||||
|
std.log.warn("Failed to get supported schedulers: {}, scx_loader may not be available", .{err});
|
||||||
|
supported_schedulers = &[_]ScxScheduler{};
|
||||||
|
return;
|
||||||
|
};
|
||||||
defer alloc.free(sched_list);
|
defer alloc.free(sched_list);
|
||||||
|
|
||||||
std.log.info("Supported schedulers:", .{});
|
std.log.info("Supported schedulers:", .{});
|
||||||
@ -102,8 +106,8 @@ const SCX_IFACE = "org.scx.Loader";
|
|||||||
fn modeToInt(mode: ScxSchedModes) u32 {
|
fn modeToInt(mode: ScxSchedModes) u32 {
|
||||||
return switch (mode) {
|
return switch (mode) {
|
||||||
.default => 0,
|
.default => 0,
|
||||||
.power => 1,
|
.gaming => 1,
|
||||||
.gaming => 2,
|
.power => 2,
|
||||||
.latency => 3,
|
.latency => 3,
|
||||||
.server => 4,
|
.server => 4,
|
||||||
};
|
};
|
||||||
@ -112,8 +116,8 @@ fn modeToInt(mode: ScxSchedModes) u32 {
|
|||||||
fn intToMode(value: u32) ScxError!ScxSchedModes {
|
fn intToMode(value: u32) ScxError!ScxSchedModes {
|
||||||
return switch (value) {
|
return switch (value) {
|
||||||
0 => .default,
|
0 => .default,
|
||||||
1 => .power,
|
1 => .gaming,
|
||||||
2 => .gaming,
|
2 => .power,
|
||||||
3 => .latency,
|
3 => .latency,
|
||||||
4 => .server,
|
4 => .server,
|
||||||
else => error.InvalidValue,
|
else => error.InvalidValue,
|
||||||
@ -123,23 +127,38 @@ fn intToMode(value: u32) ScxError!ScxSchedModes {
|
|||||||
pub fn getCurrentScheduler(alloc: std.mem.Allocator) !?ScxScheduler {
|
pub fn getCurrentScheduler(alloc: std.mem.Allocator) !?ScxScheduler {
|
||||||
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
||||||
|
|
||||||
const current = try dbus_conn.getProperty("CurrentScheduler");
|
const current = dbus_conn.getProperty("CurrentScheduler") catch |err| {
|
||||||
|
std.log.warn("Failed to get current scheduler: {}", .{err});
|
||||||
|
return .none;
|
||||||
|
};
|
||||||
defer alloc.free(current);
|
defer alloc.free(current);
|
||||||
|
|
||||||
if (current.len == 0) return null;
|
if (current.len == 0) return .none;
|
||||||
return try ScxScheduler.fromString(current);
|
return ScxScheduler.fromString(current) catch |err| {
|
||||||
|
std.log.warn("Invalid scheduler value: {}", .{err});
|
||||||
|
return .none;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getCurrentMode(alloc: std.mem.Allocator) !ScxSchedModes {
|
pub fn getCurrentMode(alloc: std.mem.Allocator) !ScxSchedModes {
|
||||||
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
||||||
|
|
||||||
const mode_str = try dbus_conn.getProperty("SchedulerMode");
|
const mode_str = dbus_conn.getProperty("SchedulerMode") catch |err| {
|
||||||
|
std.log.warn("Failed to get scheduler mode: {}", .{err});
|
||||||
|
return .default;
|
||||||
|
};
|
||||||
defer alloc.free(mode_str);
|
defer alloc.free(mode_str);
|
||||||
|
|
||||||
if (mode_str.len == 0) return .default;
|
if (mode_str.len == 0) return .default;
|
||||||
|
|
||||||
const mode = try std.fmt.parseInt(u32, mode_str, 10);
|
const mode = std.fmt.parseInt(u32, mode_str, 10) catch |err| {
|
||||||
return intToMode(mode);
|
std.log.warn("Invalid scheduler mode value: {}", .{err});
|
||||||
|
return .default;
|
||||||
|
};
|
||||||
|
return intToMode(mode) catch |err| {
|
||||||
|
std.log.warn("Invalid mode value: {}", .{err});
|
||||||
|
return .default;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn getSupportedSchedulers(alloc: std.mem.Allocator) ![]ScxScheduler {
|
pub fn getSupportedSchedulers(alloc: std.mem.Allocator) ![]ScxScheduler {
|
||||||
@ -193,6 +212,10 @@ fn isSchedulerSupported(scheduler: ScxScheduler) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn activateScheduler(alloc: std.mem.Allocator, scheduler: ScxScheduler, mode: ?ScxSchedModes) ScxError!void {
|
pub fn activateScheduler(alloc: std.mem.Allocator, scheduler: ScxScheduler, mode: ?ScxSchedModes) ScxError!void {
|
||||||
|
if (scheduler != .none) {
|
||||||
|
runSystemCtl(alloc, "stop", "ananicy-cpp");
|
||||||
|
}
|
||||||
|
|
||||||
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
||||||
|
|
||||||
const mode_str = try std.fmt.allocPrint(alloc, "{d}", .{modeToInt(mode orelse .default)});
|
const mode_str = try std.fmt.allocPrint(alloc, "{d}", .{modeToInt(mode orelse .default)});
|
||||||
@ -236,7 +259,26 @@ pub fn applyScheduler(alloc: std.mem.Allocator, scheduler: ScxScheduler, mode: ?
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn runSystemCtl(alloc: std.mem.Allocator, command: []const u8, service: []const u8) void {
|
||||||
|
const argv = [_][]const u8{ "systemctl", command, service };
|
||||||
|
const max_output_size = 1024;
|
||||||
|
const result = std.process.Child.run(.{
|
||||||
|
.allocator = alloc,
|
||||||
|
.argv = &argv,
|
||||||
|
.max_output_bytes = max_output_size,
|
||||||
|
}) catch {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
defer alloc.free(result.stderr);
|
||||||
|
defer alloc.free(result.stdout);
|
||||||
|
|
||||||
|
if (result.term.Exited != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn deactivateScheduler(alloc: std.mem.Allocator) ScxError!void {
|
pub fn deactivateScheduler(alloc: std.mem.Allocator) ScxError!void {
|
||||||
|
runSystemCtl(alloc, "start", "ananicy-cpp");
|
||||||
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
var dbus_conn = dbus.DBus.init(alloc, SCX_NAME, SCX_PATH, SCX_IFACE);
|
||||||
try dbus_conn.callMethod("StopScheduler", &[_][]const u8{});
|
try dbus_conn.callMethod("StopScheduler", &[_][]const u8{});
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,9 @@ var previous_mode_buffer: [10]u8 = undefined;
|
|||||||
pub fn applyVCacheMode(vcache_mode: VCacheMode) void {
|
pub fn applyVCacheMode(vcache_mode: VCacheMode) void {
|
||||||
const file = fs.openFileAbsolute(vcache_path, .{ .mode = .read_write }) catch |err| switch (err) {
|
const file = fs.openFileAbsolute(vcache_path, .{ .mode = .read_write }) catch |err| switch (err) {
|
||||||
error.FileNotFound => {
|
error.FileNotFound => {
|
||||||
std.log.info("AMD 3D vcache support not detected", .{});
|
if (vcache_mode != .none) {
|
||||||
|
std.log.info("AMD dual CCD 3D vcache support not detected", .{});
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
},
|
},
|
||||||
else => {
|
else => {
|
||||||
|
@ -4,32 +4,14 @@ const confloader = @import("confloader.zig");
|
|||||||
const vcache_setting = @import("../clients/vcache_setting.zig");
|
const vcache_setting = @import("../clients/vcache_setting.zig");
|
||||||
const scx_scheds = @import("../clients/scx_scheds.zig");
|
const scx_scheds = @import("../clients/scx_scheds.zig");
|
||||||
|
|
||||||
const default_system_processes = [_][]const u8{
|
pub const ProfileMode = enum {
|
||||||
"steam.exe",
|
none,
|
||||||
"services.exe",
|
handheld,
|
||||||
"winedevice.exe",
|
htpc,
|
||||||
"plugplay.exe",
|
};
|
||||||
"svchost.exe",
|
|
||||||
"explorer.exe",
|
pub const SystemConfig = struct {
|
||||||
"rpcss.exe",
|
system_processes: []const []const u8 = &[_][]const u8{},
|
||||||
"tabtip.exe",
|
|
||||||
"wineboot.exe",
|
|
||||||
"rundll32.exe",
|
|
||||||
"iexplore.exe",
|
|
||||||
"conhost.exe",
|
|
||||||
"crashpad_handler.exe",
|
|
||||||
"iscriptevaluator.exe",
|
|
||||||
"VC_redist.x86.exe",
|
|
||||||
"VC_redist.x64.exe",
|
|
||||||
"cmd.exe",
|
|
||||||
"REDEngineErrorReporter.exe",
|
|
||||||
"REDprelauncher.exe",
|
|
||||||
"SteamService.exe",
|
|
||||||
"UnityCrashHandler64.exe",
|
|
||||||
"start.exe",
|
|
||||||
"CrashReportClient.exe",
|
|
||||||
"Battle.net.exe",
|
|
||||||
"Agent.exe",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const Config = struct {
|
pub const Config = struct {
|
||||||
@ -37,10 +19,11 @@ pub const Config = struct {
|
|||||||
scx_sched: scx_scheds.ScxScheduler = .none,
|
scx_sched: scx_scheds.ScxScheduler = .none,
|
||||||
scx_sched_props: ?scx_scheds.ScxSchedModes = null,
|
scx_sched_props: ?scx_scheds.ScxSchedModes = null,
|
||||||
vcache_mode: vcache_setting.VCacheMode = .none,
|
vcache_mode: vcache_setting.VCacheMode = .none,
|
||||||
system_processes: []const []const u8 = &default_system_processes,
|
system_processes: []const []const u8 = &[_][]const u8{},
|
||||||
|
profile_mode: ProfileMode = .none,
|
||||||
arena: ?std.heap.ArenaAllocator = null,
|
arena: ?std.heap.ArenaAllocator = null,
|
||||||
|
|
||||||
pub fn load(allocator: std.mem.Allocator, path: []const u8) !Config {
|
pub fn load(allocator: std.mem.Allocator, path: []const u8, system_conf_path: ?[]const u8) !Config {
|
||||||
var config = Config{};
|
var config = Config{};
|
||||||
const file = fs.openFileAbsolute(path, .{}) catch |err| switch (err) {
|
const file = fs.openFileAbsolute(path, .{}) catch |err| switch (err) {
|
||||||
error.FileNotFound => {
|
error.FileNotFound => {
|
||||||
@ -55,6 +38,18 @@ pub const Config = struct {
|
|||||||
errdefer arena.deinit();
|
errdefer arena.deinit();
|
||||||
|
|
||||||
config = try confloader.loadConf(Config, arena.allocator(), path);
|
config = try confloader.loadConf(Config, arena.allocator(), path);
|
||||||
|
if (system_conf_path) |sys_path| {
|
||||||
|
const system_file = fs.openFileAbsolute(sys_path, .{}) catch |err| switch (err) {
|
||||||
|
error.FileNotFound => null,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
if (system_file) |sf| {
|
||||||
|
defer sf.close();
|
||||||
|
const system_config = try confloader.loadConf(SystemConfig, arena.allocator(), sys_path);
|
||||||
|
config.system_processes = system_config.system_processes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
config.arena = arena;
|
config.arena = arena;
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@ -85,11 +80,6 @@ pub const Config = struct {
|
|||||||
try file.writer().print("enable_performance_mode = {}\n", .{self.enable_performance_mode});
|
try file.writer().print("enable_performance_mode = {}\n", .{self.enable_performance_mode});
|
||||||
try file.writer().print("scx_sched = {s}\n", .{@tagName(self.scx_sched)});
|
try file.writer().print("scx_sched = {s}\n", .{@tagName(self.scx_sched)});
|
||||||
try file.writer().print("vcache_mode = {s}\n", .{@tagName(self.vcache_mode)});
|
try file.writer().print("vcache_mode = {s}\n", .{@tagName(self.vcache_mode)});
|
||||||
|
try file.writer().print("profile_mode = {s}\n", .{@tagName(self.profile_mode)});
|
||||||
try file.writer().print("system_processes = [\n", .{});
|
|
||||||
for (self.system_processes) |proc| {
|
|
||||||
try file.writer().print(" \"{s}\",\n", .{proc});
|
|
||||||
}
|
|
||||||
try file.writer().print("]\n", .{});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -30,12 +30,10 @@ pub fn loadConfDir(comptime T: type, allocator: std.mem.Allocator, dir_path: []c
|
|||||||
var dir = try std.fs.openDirAbsolute(dir_path, .{ .iterate = true });
|
var dir = try std.fs.openDirAbsolute(dir_path, .{ .iterate = true });
|
||||||
defer dir.close();
|
defer dir.close();
|
||||||
|
|
||||||
var walker = try dir.walk(allocator);
|
var iter = dir.iterate();
|
||||||
defer walker.deinit();
|
while (try iter.next()) |entry| {
|
||||||
|
if (entry.kind == .file and std.mem.endsWith(u8, entry.name, ".conf")) {
|
||||||
while (try walker.next()) |entry| {
|
const path = try std.fs.path.join(allocator, &.{ dir_path, entry.name });
|
||||||
if (entry.kind == .file and std.mem.endsWith(u8, entry.path, ".conf")) {
|
|
||||||
const path = try std.fs.path.join(allocator, &.{ dir_path, entry.path });
|
|
||||||
defer allocator.free(path);
|
defer allocator.free(path);
|
||||||
|
|
||||||
const file = try std.fs.openFileAbsolute(path, .{});
|
const file = try std.fs.openFileAbsolute(path, .{});
|
||||||
|
@ -288,7 +288,7 @@ pub fn Parser(comptime T: type) type {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
.pointer => |ptr_info| {
|
.pointer => |ptr_info| {
|
||||||
if (ptr_info.size != .Slice) return error.InvalidSyntax;
|
if (ptr_info.size != .many) return error.InvalidSyntax;
|
||||||
switch (ptr_info.child) {
|
switch (ptr_info.child) {
|
||||||
u8 => {
|
u8 => {
|
||||||
@field(result, field.name) = try self.parseString();
|
@field(result, field.name) = try self.parseString();
|
||||||
|
@ -11,6 +11,7 @@ const scx_scheds = @import("clients/scx_scheds.zig");
|
|||||||
pub const Daemon = struct {
|
pub const Daemon = struct {
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
config_path: []const u8,
|
config_path: []const u8,
|
||||||
|
system_conf_path: []const u8,
|
||||||
profile_manager: ProfileManager,
|
profile_manager: ProfileManager,
|
||||||
oneshot: bool,
|
oneshot: bool,
|
||||||
known_pids: ?std.AutoHashMap(u32, *const Profile),
|
known_pids: ?std.AutoHashMap(u32, *const Profile),
|
||||||
@ -22,11 +23,14 @@ pub const Daemon = struct {
|
|||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator, config_path: []const u8, oneshot: bool) !*Self {
|
pub fn init(allocator: std.mem.Allocator, config_path: []const u8, system_conf_path: []const u8, oneshot: bool) !*Self {
|
||||||
const config_path_owned = try allocator.dupe(u8, config_path);
|
const config_path_owned = try allocator.dupe(u8, config_path);
|
||||||
errdefer allocator.free(config_path_owned);
|
errdefer allocator.free(config_path_owned);
|
||||||
|
|
||||||
const config = try Config.load(allocator, config_path);
|
const system_conf_path_owned = try allocator.dupe(u8, system_conf_path);
|
||||||
|
errdefer allocator.free(system_conf_path_owned);
|
||||||
|
|
||||||
|
const config = try Config.load(allocator, config_path, system_conf_path);
|
||||||
const power_profiles = try PowerProfiles.init(allocator, config);
|
const power_profiles = try PowerProfiles.init(allocator, config);
|
||||||
errdefer power_profiles.deinit();
|
errdefer power_profiles.deinit();
|
||||||
|
|
||||||
@ -36,7 +40,7 @@ pub const Daemon = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var profile_manager = ProfileManager.init(allocator, power_profiles, config);
|
var profile_manager = ProfileManager.init(allocator, power_profiles, config);
|
||||||
try ProfileLoader.loadProfiles(allocator, &profile_manager.profiles, &profile_manager.proton_profile, oneshot);
|
try ProfileLoader.loadProfiles(allocator, &profile_manager.profiles, &profile_manager.proton_profile, oneshot, config.profile_mode);
|
||||||
|
|
||||||
const current_time = std.time.nanoTimestamp();
|
const current_time = std.time.nanoTimestamp();
|
||||||
try scx_scheds.init(allocator);
|
try scx_scheds.init(allocator);
|
||||||
@ -44,15 +48,16 @@ pub const Daemon = struct {
|
|||||||
const self = try allocator.create(Self);
|
const self = try allocator.create(Self);
|
||||||
self.* = .{
|
self.* = .{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
|
.config_path = config_path_owned,
|
||||||
|
.system_conf_path = system_conf_path_owned,
|
||||||
.profile_manager = profile_manager,
|
.profile_manager = profile_manager,
|
||||||
.oneshot = oneshot,
|
.oneshot = oneshot,
|
||||||
.known_pids = if (!oneshot) std.AutoHashMap(u32, *const Profile).init(allocator) else null,
|
.known_pids = if (!oneshot) std.AutoHashMap(u32, *const Profile).init(allocator) else null,
|
||||||
.power_profiles = power_profiles,
|
.power_profiles = power_profiles,
|
||||||
.last_profiles_check = current_time,
|
.last_profiles_check = current_time,
|
||||||
.last_config_check = current_time,
|
.last_config_check = current_time,
|
||||||
.config = config,
|
|
||||||
.config_path = config_path_owned,
|
|
||||||
.performance_mode = performance_mode,
|
.performance_mode = performance_mode,
|
||||||
|
.config = config,
|
||||||
};
|
};
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -67,6 +72,7 @@ pub const Daemon = struct {
|
|||||||
}
|
}
|
||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
self.allocator.free(self.config_path);
|
self.allocator.free(self.config_path);
|
||||||
|
self.allocator.free(self.system_conf_path);
|
||||||
self.allocator.destroy(self);
|
self.allocator.destroy(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +89,11 @@ pub const Daemon = struct {
|
|||||||
self.power_profiles.deinit();
|
self.power_profiles.deinit();
|
||||||
self.config.deinit();
|
self.config.deinit();
|
||||||
|
|
||||||
const config = try Config.load(self.allocator, self.config_path);
|
const config = try Config.load(self.allocator, self.config_path, self.system_conf_path);
|
||||||
const power_profiles = try PowerProfiles.init(self.allocator, config);
|
const power_profiles = try PowerProfiles.init(self.allocator, config);
|
||||||
var profile_manager = ProfileManager.init(self.allocator, power_profiles, config);
|
var profile_manager = ProfileManager.init(self.allocator, power_profiles, config);
|
||||||
try ProfileLoader.loadProfiles(self.allocator, &profile_manager.profiles, &profile_manager.proton_profile, self.oneshot);
|
try ProfileLoader.loadProfiles(self.allocator, &profile_manager.profiles, &profile_manager.proton_profile, self.oneshot, config.profile_mode);
|
||||||
|
|
||||||
self.config = config;
|
self.config = config;
|
||||||
self.power_profiles = power_profiles;
|
self.power_profiles = power_profiles;
|
||||||
self.performance_mode = self.power_profiles.isPerformanceAvailable();
|
self.performance_mode = self.power_profiles.isPerformanceAvailable();
|
||||||
|
@ -26,6 +26,9 @@ const AllocTracker = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const config_path: []const u8 = "/etc/falcond/config.conf";
|
||||||
|
const system_conf_path: []const u8 = "/usr/share/falcond/system.conf";
|
||||||
|
|
||||||
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
|
fn alloc(ctx: *anyopaque, len: usize, ptr_align: u8, ret_addr: usize) ?[*]u8 {
|
||||||
var t: *AllocTracker = @ptrCast(@alignCast(ctx));
|
var t: *AllocTracker = @ptrCast(@alignCast(ctx));
|
||||||
t.trackAlloc();
|
t.trackAlloc();
|
||||||
@ -46,7 +49,6 @@ fn free(ctx: *anyopaque, buf: []u8, log2_buf_align: u8, ret_addr: usize) void {
|
|||||||
|
|
||||||
var gpa_vtable: *const std.mem.Allocator.VTable = undefined;
|
var gpa_vtable: *const std.mem.Allocator.VTable = undefined;
|
||||||
var gpa_ptr: *anyopaque = undefined;
|
var gpa_ptr: *anyopaque = undefined;
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
std.log.info("Starting falcond...", .{});
|
std.log.info("Starting falcond...", .{});
|
||||||
|
|
||||||
@ -85,8 +87,27 @@ pub fn main() !void {
|
|||||||
if (std.mem.eql(u8, arg, "--oneshot")) break true;
|
if (std.mem.eql(u8, arg, "--oneshot")) break true;
|
||||||
} else false;
|
} else false;
|
||||||
|
|
||||||
var daemon = try Daemon.init(allocator, "/etc/falcond/config.conf", oneshot);
|
try checkAndUpgradeConfig(allocator, config_path);
|
||||||
|
|
||||||
|
var daemon = try Daemon.init(allocator, config_path, system_conf_path, oneshot);
|
||||||
defer daemon.deinit();
|
defer daemon.deinit();
|
||||||
|
|
||||||
try daemon.run();
|
try daemon.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn checkAndUpgradeConfig(allocator: std.mem.Allocator, conf_path: []const u8) !void {
|
||||||
|
const file = std.fs.openFileAbsolute(conf_path, .{}) catch |err| switch (err) {
|
||||||
|
error.FileNotFound => return,
|
||||||
|
else => return err,
|
||||||
|
};
|
||||||
|
defer file.close();
|
||||||
|
|
||||||
|
const content = try file.readToEndAlloc(allocator, std.math.maxInt(usize));
|
||||||
|
defer allocator.free(content);
|
||||||
|
|
||||||
|
if (std.mem.indexOf(u8, content, "system_processes = ")) |_| {
|
||||||
|
std.log.info("Upgrading config file to new format", .{});
|
||||||
|
file.close();
|
||||||
|
try std.fs.deleteFileAbsolute(config_path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -2,8 +2,9 @@ const std = @import("std");
|
|||||||
const confloader = @import("../config/confloader.zig");
|
const confloader = @import("../config/confloader.zig");
|
||||||
const types = @import("types.zig");
|
const types = @import("types.zig");
|
||||||
const Profile = types.Profile;
|
const Profile = types.Profile;
|
||||||
|
const ProfileMode = @import("../config/config.zig").ProfileMode;
|
||||||
|
|
||||||
pub fn loadProfiles(allocator: std.mem.Allocator, profiles: *std.ArrayList(Profile), proton_profile: *?*const Profile, oneshot: bool) !void {
|
pub fn loadProfiles(allocator: std.mem.Allocator, profiles: *std.ArrayList(Profile), proton_profile: *?*const Profile, oneshot: bool, mode: ProfileMode) !void {
|
||||||
if (oneshot) {
|
if (oneshot) {
|
||||||
try profiles.append(Profile{
|
try profiles.append(Profile{
|
||||||
.name = try allocator.dupe(u8, "Hades3.exe"),
|
.name = try allocator.dupe(u8, "Hades3.exe"),
|
||||||
@ -19,7 +20,14 @@ pub fn loadProfiles(allocator: std.mem.Allocator, profiles: *std.ArrayList(Profi
|
|||||||
|
|
||||||
proton_profile.* = &profiles.items[1];
|
proton_profile.* = &profiles.items[1];
|
||||||
} else {
|
} else {
|
||||||
var loaded_profiles = try confloader.loadConfDir(Profile, allocator, "/usr/share/falcond/profiles");
|
const base_path = "/usr/share/falcond/profiles";
|
||||||
|
const profiles_path = if (mode != .none)
|
||||||
|
try std.fmt.allocPrint(allocator, "{s}/{s}", .{ base_path, @tagName(mode) })
|
||||||
|
else
|
||||||
|
base_path;
|
||||||
|
defer if (mode != .none) allocator.free(profiles_path);
|
||||||
|
|
||||||
|
var loaded_profiles = try confloader.loadConfDir(Profile, allocator, profiles_path);
|
||||||
defer loaded_profiles.deinit();
|
defer loaded_profiles.deinit();
|
||||||
|
|
||||||
try profiles.appendSlice(loaded_profiles.items);
|
try profiles.appendSlice(loaded_profiles.items);
|
||||||
@ -27,11 +35,10 @@ pub fn loadProfiles(allocator: std.mem.Allocator, profiles: *std.ArrayList(Profi
|
|||||||
for (profiles.items) |*profile| {
|
for (profiles.items) |*profile| {
|
||||||
if (std.mem.eql(u8, profile.name, "Proton")) {
|
if (std.mem.eql(u8, profile.name, "Proton")) {
|
||||||
proton_profile.* = profile;
|
proton_profile.* = profile;
|
||||||
std.log.info("Found Proton profile: {s}", .{profile.name});
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std.log.info("Loaded {d} profiles", .{profiles.items.len});
|
std.log.info("Loaded {d} profiles (mode: {s})", .{ profiles.items.len, @tagName(mode) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ pub const ProfileManager = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vcache_setting.applyVCacheMode(.none);
|
vcache_setting.applyVCacheMode(.none);
|
||||||
try scx_scheds.deactivateScheduler(self.allocator);
|
scx_scheds.restorePreviousState(self.allocator);
|
||||||
self.active_profile = null;
|
self.active_profile = null;
|
||||||
|
|
||||||
if (self.queued_profiles.items.len > 0) {
|
if (self.queued_profiles.items.len > 0) {
|
||||||
@ -108,7 +108,7 @@ pub const ProfileManager = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vcache_setting.applyVCacheMode(.none);
|
vcache_setting.applyVCacheMode(.none);
|
||||||
try scx_scheds.deactivateScheduler(self.allocator);
|
scx_scheds.restorePreviousState(self.allocator);
|
||||||
self.active_profile = null;
|
self.active_profile = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user