Skip to content

Commit

Permalink
Add toggles for enabling MangoHUD and Gamemode for all titles
Browse files Browse the repository at this point in the history
This implementation is very slow and triggers a lot of unnecessary file operations.
Optimization to come in a subsequent commit.
  • Loading branch information
ashuntu committed Sep 20, 2024
1 parent 3e523b0 commit b81dc86
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 8 deletions.
2 changes: 2 additions & 0 deletions packages/game_center/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
"steamGlobalConfigTitle": "Steam Global Config",
"steamUserConfigTitle": "Steam User Configs",
"steamEnableProton": "Enable Steam Play (Proton) for all titles",
"steamEnableMangoHUD": "Enable MangoHUD for all titles",
"steamEnableGameMode": "Enable GameMode for all titles",

"settingsPageLabel": "Settings",

Expand Down
95 changes: 88 additions & 7 deletions packages/game_center/lib/steam/steam_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,36 @@ String steamUserConfig(String installLocation, String userID) {
typedef Config = ({
Map<String, dynamic> globalConfig,
Map<String, Map<String, dynamic>> userConfigs,
SteamUser activeUser,
});
// typedef SteamUser = ({
// String id,
// });

class SteamUser {
SteamUser({
required this.id,
required this.name,
});

factory SteamUser.fromConfig(Map<String, dynamic> config) {
final id = config['UserLocalConfigStore']['friends'].keys.first;
return SteamUser(
id: id,
name: config['UserLocalConfigStore']['friends'][id]['NameHistory']['0'],
);
}

late final String id;
late final String name;
}

@riverpod
class SteamModel extends _$SteamModel {
late String installLocation;
late Map<String, dynamic> globalConfig;
late Map<String, Map<String, dynamic>> userConfigs;
late SteamUser activeUser;

@override
Future<Config> build({String? install}) async {
Expand Down Expand Up @@ -76,9 +99,12 @@ class SteamModel extends _$SteamModel {
});
}

activeUser = SteamUser.fromConfig(userConfigs.values.first);

return (
globalConfig: globalConfig,
userConfigs: userConfigs,
activeUser: activeUser,
);
}

Expand All @@ -96,6 +122,7 @@ class SteamModel extends _$SteamModel {
state = AsyncData((
globalConfig: globalConfig,
userConfigs: userConfigs,
activeUser: activeUser,
));
}

Expand All @@ -105,6 +132,16 @@ class SteamModel extends _$SteamModel {
state = AsyncData((
globalConfig: globalConfig,
userConfigs: userConfigs,
activeUser: activeUser,
));
}

Future<void> setActiveUser(String newActiveUserID) async {
activeUser = SteamUser.fromConfig(userConfigs[newActiveUserID]!);
state = AsyncData((
globalConfig: globalConfig,
userConfigs: userConfigs,
activeUser: activeUser,
));
}

Expand Down Expand Up @@ -165,24 +202,41 @@ class SteamModel extends _$SteamModel {
}

/// Get a map of installed Steam apps for the given user.
Future<Map<String, dynamic>> listApps({required String steamID}) async {
Map<String, dynamic> listApps({required String steamID}) {
Map<String, dynamic> config = Map.from(userConfigs[steamID]!);
var apps = config['UserLocalConfigStore']['Software']['Valve']['Steam']
['apps'] as Map;
return apps.cast<String, dynamic>();
}

Future<String> getGameLaunchOptions({
bool allGamesHaveOption({
required String steamID,
required String option,
}) {
final apps = listApps(steamID: steamID);
for (final app in apps.keys) {
final launchOptions = getGameLaunchOptions(steamID: steamID, appID: app);
List<String> options =
launchOptions.isEmpty ? [] : launchOptions.split(RegExp(r'\s+'));
if (!options.contains(option)) {
return false;
}
}

return true;
}

String getGameLaunchOptions({
required String steamID,
required String appID,
}) async {
}) {
Map<String, dynamic> config = Map.from(userConfigs[steamID]!);
String launchOptions = config['UserLocalConfigStore']['Software']['Valve']
String? launchOptions = config['UserLocalConfigStore']['Software']['Valve']
['Steam']['apps'][appID]['LaunchOptions'];
return launchOptions;
return launchOptions ?? '';
}

/// Set the raw launch options for a specific app.
/// Set the raw launch options for a specific app.checked
Future<void> setGameLaunchOptions({
required String steamID,
required String appID,
Expand All @@ -196,6 +250,34 @@ class SteamModel extends _$SteamModel {
await updateUserConfig(steamID);
}

Future<void> addAllGameLaunchOption({
required String steamID,
required String option,
}) async {
final apps = await listApps(steamID: steamID);
for (final app in apps.keys) {
await addGameLaunchOption(
steamID: steamID,
appID: app,
option: option,
);
}
}

Future<void> removeAllGameLaunchOption({
required String steamID,
required String option,
}) async {
final apps = await listApps(steamID: steamID);
for (final app in apps.keys) {
await removeGameLaunchOption(
steamID: steamID,
appID: app,
option: option,
);
}
}

// Adds an option to a game's launch options. This function does nothing if
// the option already exists for the game.
Future<void> addGameLaunchOption({
Expand Down Expand Up @@ -236,7 +318,6 @@ class SteamModel extends _$SteamModel {
List<String> options =
launchOptions.isEmpty ? [] : launchOptions.split(RegExp(r'\s+'));
options.remove(option);
print(options);
await setGameLaunchOptions(
steamID: steamID,
appID: appID,
Expand Down
45 changes: 44 additions & 1 deletion packages/game_center/lib/steam/steam_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class SteamPage extends ConsumerWidget {
Widget build(BuildContext context, WidgetRef ref) {
final l10n = AppLocalizations.of(context);
final steam = ref.watch(steamModelProvider());
final notifier = ref.watch(steamModelProvider().notifier);

return steam.when(
data: (data) => AppScrollView(
Expand All @@ -27,6 +28,16 @@ class SteamPage extends ConsumerWidget {
style: Theme.of(context).textTheme.headlineMedium,
),
const SizedBox(height: kPagePadding),
YaruPopupMenuButton(
child: Text(data.activeUser.name),
itemBuilder: (context) => data.userConfigs.values.map((id) {
var user = SteamUser.fromConfig(id);
return PopupMenuItem(
child: Text(user.name),
onTap: () => notifier.setActiveUser(user.id),
);
}).toList(),
),
_SteamSimpleSettings(),
const SizedBox(height: kPagePadding),
Text(
Expand All @@ -48,7 +59,7 @@ class SteamPage extends ConsumerWidget {
child: Text(l10n.loadingLabel),
),
error: (error, stackTrace) => Center(
child: Text(error.toString()),
child: Text('${error.toString()}\n${stackTrace.toString()}'),
),
);
}
Expand All @@ -70,6 +81,38 @@ class _SteamSimpleSettings extends ConsumerWidget {
await steam.enableSteamPlay(enable: value);
},
),
YaruSwitchListTile(
title: Text(l10n.steamEnableMangoHUD),
value: steam.allGamesHaveOption(
steamID: steam.activeUser.id, option: 'mangohud'),
onChanged: (value) async {
value
? await steam.addAllGameLaunchOption(
steamID: steam.activeUser.id,
option: 'mangohud',
)
: await steam.removeAllGameLaunchOption(
steamID: steam.activeUser.id,
option: 'mangohud',
);
},
),
YaruSwitchListTile(
title: Text(l10n.steamEnableGameMode),
value: steam.allGamesHaveOption(
steamID: steam.activeUser.id, option: 'gamemode'),
onChanged: (value) async {
value
? await steam.addAllGameLaunchOption(
steamID: steam.activeUser.id,
option: 'gamemode',
)
: await steam.removeAllGameLaunchOption(
steamID: steam.activeUser.id,
option: 'gamemode',
);
},
),
],
);
}
Expand Down

0 comments on commit b81dc86

Please sign in to comment.