Skip to content

Commit b3f761b

Browse files
authored
Improve Steam Deck support (#850)
1 parent 9f3887b commit b3f761b

7 files changed

Lines changed: 277 additions & 8 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import 'package:commet/config/platform_utils.dart';
2+
import 'package:commet/config/subplatforms/subplatforms.dart';
3+
import 'package:commet/debug/log.dart';
4+
import 'package:commet/utils/system_processes_utils.dart';
5+
import 'package:window_manager/window_manager.dart';
6+
7+
class SteamdeckSubplatform implements Subplatform {
8+
@override
9+
Future<void> init() async {
10+
Log.i(
11+
"Initializing steamdeck subplatform: ${_isSteamDeck} is gamemode: ${_isGameMode}");
12+
13+
if (_isGameMode) {
14+
await windowManager.setFullScreen(true);
15+
ensureFullscreen();
16+
}
17+
}
18+
19+
Future<void> ensureFullscreen() async {
20+
for (int i = 0; i < 10; i++) {
21+
if (await windowManager.isFullScreen()) {
22+
return;
23+
}
24+
25+
Log.i("Window is still not fullscreen, waiting and trying again");
26+
27+
await Future.delayed(Duration(seconds: 1));
28+
await windowManager.setFullScreen(true);
29+
}
30+
}
31+
32+
@override
33+
String get name => _isGameMode ? "steamdeck_gamemode" : "steamdeck_desktop";
34+
35+
static bool _isSteamDeck = false;
36+
37+
static bool _isGameMode = false;
38+
39+
static Future<bool> isSteamdeck() async {
40+
if (!PlatformUtils.isLinux) {
41+
return false;
42+
}
43+
44+
var processes = await SystemProcessesUtils.getProcessList();
45+
46+
var steamProceses = processes.where((i) => i.command.endsWith("/steam"));
47+
48+
_isSteamDeck = steamProceses.any((i) => i.args.contains("-steamdeck"));
49+
50+
_isGameMode =
51+
_isSteamDeck && steamProceses.any((i) => i.args.contains("-gamepadui"));
52+
53+
Log.i(
54+
"Is running on steamdeck: ${_isSteamDeck} is gamemode: ${_isGameMode}");
55+
56+
return _isSteamDeck;
57+
}
58+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'package:commet/config/subplatforms/steamdeck.dart';
2+
import 'package:commet/debug/log.dart';
3+
4+
class Subplatforms {
5+
static Subplatform? subplatform;
6+
7+
static Future<void> init() async {
8+
try {
9+
if (await SteamdeckSubplatform.isSteamdeck()) {
10+
subplatform = SteamdeckSubplatform();
11+
}
12+
13+
if (subplatform != null) {
14+
await subplatform!.init();
15+
}
16+
} catch (e, s) {
17+
Log.onError(e, s);
18+
}
19+
}
20+
}
21+
22+
abstract class Subplatform {
23+
Future<void> init();
24+
25+
String get name;
26+
}

commet/lib/main.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import 'package:commet/config/global_config.dart';
1111
import 'package:commet/config/layout_config.dart';
1212
import 'package:commet/config/platform_utils.dart';
1313
import 'package:commet/config/preferences.dart';
14+
import 'package:commet/config/subplatforms/subplatforms.dart';
1415
import 'package:commet/debug/l10n_debug_lookup.dart';
1516
import 'package:commet/debug/log.dart';
1617
import 'package:commet/diagnostic/diagnostics.dart';
@@ -288,7 +289,9 @@ Future<void> startGui() async {
288289
initialRoom: initialRoomId,
289290
));
290291

291-
WindowManagement.init();
292+
WindowManagement.init().then((_) {
293+
Subplatforms.init();
294+
});
292295
}
293296

294297
void enableEdgeToEdge() async {

commet/lib/ui/pages/settings/categories/about/settings_category_about.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:commet/config/build_config.dart';
22
import 'package:commet/config/platform_utils.dart';
3+
import 'package:commet/config/subplatforms/subplatforms.dart';
34
import 'package:commet/main.dart';
45
import 'package:commet/ui/pages/settings/categories/developer/log_page.dart';
56
import 'package:commet/ui/pages/settings/settings_category.dart';
@@ -170,6 +171,8 @@ class _AppInfoState extends State<_AppInfo> {
170171
PlatformUtils.desktopEnvironment!)
171172
],
172173
),
174+
if (Subplatforms.subplatform != null)
175+
tiamat.Text.labelLow(Subplatforms.subplatform!.name),
173176
if (preferences.developerMode.value)
174177
tiamat.Text.labelLow(getEncryptionInfo()),
175178
if (preferences.developerMode.value)
@@ -231,6 +234,7 @@ ${deviceInfo?.data["version"] is String ? "Version: `${deviceInfo!.data["version
231234
${deviceInfo?.data["product"] is String ? "Product: `${deviceInfo!.data["product"]}`" : ""}
232235
${PlatformUtils.isLinux ? "Display Server: `${PlatformUtils.displayServer}`" : ""}
233236
${PlatformUtils.desktopEnvironment != null ? "Desktop Environment: `${PlatformUtils.desktopEnvironment}`" : ""}
237+
${Subplatforms.subplatform != null ? "Subplatform: `${Subplatforms.subplatform!.name}`" : ""}
234238
</details>
235239
""";
236240

commet/lib/ui/pages/settings/categories/developer/developer_settings_page.dart

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:async';
2+
import 'dart:convert';
13
import 'dart:io';
24
import 'package:commet/client/components/push_notification/notification_content.dart';
35
import 'package:commet/client/components/push_notification/notification_manager.dart';
@@ -6,13 +8,16 @@ import 'package:commet/config/build_config.dart';
68
import 'package:commet/config/platform_utils.dart';
79
import 'package:commet/diagnostic/diagnostics.dart';
810
import 'package:commet/main.dart';
11+
import 'package:commet/ui/atoms/code_block.dart';
12+
import 'package:commet/ui/navigation/adaptive_dialog.dart';
913
import 'package:commet/ui/navigation/navigation_utils.dart';
1014
import 'package:commet/ui/pages/developer/benchmarks/timeline_viewer_benchmark.dart';
1115
import 'package:commet/ui/pages/settings/categories/app/boolean_toggle.dart';
1216
import 'package:commet/ui/pages/settings/categories/app/double_preference_slider.dart';
1317
import 'package:commet/ui/pages/settings/categories/developer/cumulative_diagnostics_widget.dart';
1418
import 'package:commet/utils/background_tasks/background_task_manager.dart';
1519
import 'package:commet/utils/background_tasks/mock_tasks.dart';
20+
import 'package:commet/utils/system_processes_utils.dart';
1621
import 'package:file_picker/file_picker.dart';
1722
import 'package:flutter/material.dart';
1823
import 'package:flutter/rendering.dart';
@@ -42,6 +47,7 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
4247
if (PlatformUtils.isAndroid) shortcuts(),
4348
backgroundTasks(),
4449
dumpDatabases(),
50+
executeShellCommand(),
4551
tiamat.Panel(
4652
header: "Other Settings",
4753
mode: TileType.surfaceContainerLow,
@@ -153,6 +159,10 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
153159
spacing: 8,
154160
runSpacing: 8,
155161
children: [
162+
tiamat.Button(
163+
text: "Maximize",
164+
onTap: () => windowManager.maximize(),
165+
),
156166
tiamat.Button(
157167
text: "1280x720",
158168
onTap: () => windowManager.setSize(const Size(1280, 720)),
@@ -328,6 +338,51 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
328338
);
329339
}
330340

341+
Widget executeShellCommand() {
342+
return ExpansionTile(
343+
title: const tiamat.Text.labelEmphasised("Dangerous"),
344+
backgroundColor: Theme.of(context).colorScheme.surfaceContainerLow,
345+
collapsedBackgroundColor:
346+
Theme.of(context).colorScheme.surfaceContainerLow,
347+
children: [
348+
Wrap(spacing: 8, runSpacing: 8, children: [
349+
tiamat.Button(
350+
text: "Get Process List",
351+
onTap: () async {
352+
var list = await SystemProcessesUtils.getProcessList();
353+
AdaptiveDialog.show(
354+
context,
355+
builder: (context) {
356+
return Codeblock(
357+
text: list
358+
.map((i) =>
359+
"[${i.processId}] = ${i.command} ${i.args}")
360+
.join("\n"));
361+
},
362+
);
363+
},
364+
),
365+
tiamat.Button(
366+
text: "Execute Shell Command",
367+
onTap: () async {
368+
var text = await AdaptiveDialog.textPrompt(context,
369+
title: "Execute Shell Command");
370+
if (text != null) {
371+
var command = text.split(" ");
372+
var exe = command.first;
373+
var args = command.sublist(1);
374+
375+
var process = await Process.start(exe, args);
376+
377+
AdaptiveDialog.show(context,
378+
builder: (context) => ProcessOutputViewer(process));
379+
}
380+
}),
381+
])
382+
],
383+
);
384+
}
385+
331386
Widget dumpDatabases() {
332387
return ExpansionTile(
333388
title: const tiamat.Text.labelEmphasised("Dump Databases"),
@@ -365,3 +420,64 @@ class _DeveloperSettingsPageState extends State<DeveloperSettingsPage> {
365420
);
366421
}
367422
}
423+
424+
class ProcessOutputViewer extends StatefulWidget {
425+
const ProcessOutputViewer(this.process, {super.key});
426+
final Process process;
427+
428+
@override
429+
State<ProcessOutputViewer> createState() => _ProcessOutputViewerState();
430+
}
431+
432+
class _ProcessOutputViewerState extends State<ProcessOutputViewer> {
433+
String stdOut = "";
434+
String stdError = "";
435+
436+
late List<StreamSubscription> subs;
437+
438+
@override
439+
void initState() {
440+
super.initState();
441+
442+
subs = [
443+
widget.process.stdout.transform(utf8.decoder).listen(onStdout),
444+
widget.process.stderr.transform(utf8.decoder).listen(onStderr),
445+
];
446+
}
447+
448+
@override
449+
void dispose() {
450+
for (var sub in subs) {
451+
sub.cancel();
452+
}
453+
454+
widget.process.kill();
455+
super.dispose();
456+
}
457+
458+
@override
459+
Widget build(BuildContext context) {
460+
return SizedBox(
461+
height: 1000,
462+
child: SingleChildScrollView(
463+
child: Codeblock(
464+
text: stdOut,
465+
clipboardText: stdOut,
466+
language: "stdout",
467+
),
468+
),
469+
);
470+
}
471+
472+
void onStderr(String event) {
473+
setState(() {
474+
stdError += event;
475+
});
476+
}
477+
478+
void onStdout(String event) {
479+
setState(() {
480+
stdOut += event;
481+
});
482+
}
483+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import 'dart:io';
2+
3+
import 'package:commet/config/build_config.dart';
4+
import 'package:commet/config/platform_utils.dart';
5+
6+
class SystemProcessesUtils {
7+
static Future<List<ProcessInfo>> getProcessList() async {
8+
if (PlatformUtils.isWeb) {
9+
return [];
10+
}
11+
12+
if (BuildConfig.IS_FLATPAK) {
13+
var result = await Process.run(
14+
"flatpak-spawn", ["--host", "ps", "-e", "-o", "pid,command"]);
15+
return parseLinuxPS(result.stdout);
16+
}
17+
18+
if (BuildConfig.LINUX) {
19+
var result = await Process.run("ps", ["-e", "-o", "pid,command"]);
20+
return parseLinuxPS(result.stdout);
21+
}
22+
23+
return [];
24+
}
25+
26+
static List<ProcessInfo> parseLinuxPS(String output) {
27+
// Skip first line
28+
var lines = output.trim().split("\n").sublist(1);
29+
30+
var result = List<ProcessInfo>.empty(growable: true);
31+
32+
for (var line in lines) {
33+
var split = line.trim().split(" ");
34+
if (split.isEmpty) continue;
35+
36+
print(split);
37+
var pid = int.parse(split[0]);
38+
var command = split[1];
39+
40+
if (command.startsWith("[")) command = command.substring(1);
41+
42+
if (command.endsWith("]"))
43+
command = command.substring(0, command.length - 1);
44+
45+
List<String> args = [];
46+
if (split.length > 2) {
47+
args = split.sublist(2);
48+
}
49+
50+
result.add(ProcessInfo(processId: pid, command: command, args: args));
51+
}
52+
53+
return result;
54+
}
55+
}
56+
57+
class ProcessInfo {
58+
final int processId;
59+
final String command;
60+
final List<String> args;
61+
62+
ProcessInfo(
63+
{required this.processId, required this.command, this.args = const []});
64+
}

commet/lib/utils/window_management.dart

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import 'package:window_manager/window_manager.dart';
1111

1212
class WindowManagement {
1313
static Future<void> init() async {
14-
if (!PlatformUtils.isLinux || PlatformUtils.isWindows) return;
14+
if (!(PlatformUtils.isLinux || PlatformUtils.isWindows)) return;
1515

1616
await windowManager.ensureInitialized();
1717
_WindowListener listener = _WindowListener();
@@ -21,13 +21,11 @@ class WindowManagement {
2121

2222
HardwareKeyboard.instance.addHandler(_onKeyEvent);
2323

24-
if (PlatformUtils.isLinux || PlatformUtils.isWindows) {
25-
EventBus.onSelectedRoomChanged.stream.listen(_onSelectedRoomChanged);
26-
EventBus.onSelectedSpaceChanged.stream.listen(_onSelectedSpaceChanged);
24+
EventBus.onSelectedRoomChanged.stream.listen(_onSelectedRoomChanged);
25+
EventBus.onSelectedSpaceChanged.stream.listen(_onSelectedSpaceChanged);
2726

28-
if (commandLineArgs.contains("--minimize")) {
29-
windowManager.minimize();
30-
}
27+
if (commandLineArgs.contains("--minimize")) {
28+
windowManager.minimize();
3129
}
3230
}
3331

0 commit comments

Comments
 (0)