Skip to content

Commit 350c8eb

Browse files
committed
feat: add kde wayland support
1 parent 4b281f0 commit 350c8eb

File tree

27 files changed

+1004
-286
lines changed

27 files changed

+1004
-286
lines changed

.github/workflows/tests.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ jobs:
1313
strategy:
1414
matrix:
1515
os: [ubuntu-latest, windows-latest]
16+
17+
env:
18+
# Needed so we don't get errors in CI
19+
XDG_SESSION_TYPE: "x11"
20+
XDG_CURRENT_DESKTOP: "KDE"
21+
1622
steps:
1723
- uses: actions/checkout@v3
1824
- uses: subosito/flutter-action@v2
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// https://unix.stackexchange.com/a/706478/379240
2+
3+
function print(str) {
4+
console.info('Nyrna: ' + str);
5+
}
6+
7+
let windows = workspace.windowList();
8+
print('Found ' + windows.length + ' windows');
9+
10+
function updateWindowsOnDBus(windows) {
11+
let windowsList = [];
12+
13+
for (let window of windows) {
14+
windowsList.push({
15+
caption: window.caption,
16+
pid: window.pid,
17+
internalId: window.internalId,
18+
onCurrentDesktop: isWindowOnCurrentDesktop(window),
19+
});
20+
}
21+
22+
callDBus(
23+
'codes.merritt.Nyrna',
24+
'/',
25+
'codes.merritt.Nyrna',
26+
'updateWindows',
27+
JSON.stringify(windowsList),
28+
(result) => {
29+
if (result) {
30+
print('Successfully updated windows on DBus');
31+
} else {
32+
print('Failed to update windows on DBus');
33+
}
34+
}
35+
);
36+
}
37+
38+
function isWindowOnCurrentDesktop(window) {
39+
let windowDesktops = Object.values(window.desktops);
40+
let windowIsOnCurrentDesktop = window.onAllDesktops;
41+
42+
if (!windowIsOnCurrentDesktop) {
43+
for (let windowDesktop of windowDesktops) {
44+
if (windowDesktop.id === workspace.currentDesktop.id) {
45+
windowIsOnCurrentDesktop = true;
46+
break;
47+
} else {
48+
windowIsOnCurrentDesktop = false;
49+
}
50+
}
51+
}
52+
53+
return windowIsOnCurrentDesktop;
54+
}
55+
56+
function updateCurrentDesktopOnDBus() {
57+
print('Current desktop id: ' + workspace.currentDesktop.id);
58+
59+
callDBus(
60+
'codes.merritt.Nyrna',
61+
'/',
62+
'codes.merritt.Nyrna',
63+
'updateCurrentDesktop',
64+
workspace.currentDesktop,
65+
(result) => {
66+
if (result) {
67+
print('Successfully updated current desktop on DBus');
68+
} else {
69+
print('Failed to update current desktop on DBus');
70+
}
71+
}
72+
);
73+
}
74+
75+
updateCurrentDesktopOnDBus();
76+
updateWindowsOnDBus(windows);
77+
78+
workspace.currentDesktopChanged.connect(() => {
79+
print('Current desktop changed');
80+
updateCurrentDesktopOnDBus();
81+
updateWindowsOnDBus(windows);
82+
});
83+
84+
workspace.windowAdded.connect(window => {
85+
print('Window added: ' + window.caption);
86+
windows.push(window);
87+
updateWindowsOnDBus(windows);
88+
});
89+
90+
workspace.windowRemoved.connect(window => {
91+
print('Window removed: ' + window.caption);
92+
windows = windows.filter(w => w.internalId !== window.internalId);
93+
updateWindowsOnDBus(windows);
94+
});

devtools_options.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
description: This file stores settings for Dart & Flutter DevTools.
2+
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
3+
extensions:

lib/active_window/src/active_window.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ class ActiveWindow {
141141
return false;
142142
}
143143

144-
Future<void> _minimize(int windowId) async {
144+
Future<void> _minimize(String windowId) async {
145145
final shouldMinimize = await _getShouldMinimize();
146146
if (!shouldMinimize) return;
147147

@@ -150,7 +150,7 @@ class ActiveWindow {
150150
if (!minimized) log.e('Failed to minimize window.');
151151
}
152152

153-
Future<void> _restore(int windowId) async {
153+
Future<void> _restore(String windowId) async {
154154
final shouldRestore = await _getShouldMinimize();
155155
if (!shouldRestore) return;
156156

lib/app/cubit/app_cubit.dart

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,9 @@ Unable to determine session type. The XDG_SESSION_TYPE environment variable is s
8484
Please note that Wayland is not currently supported.''';
8585

8686
const waylandNotSupportedMsg = '''
87-
Wayland is not currently supported.
87+
Wayland is currently supported only on KDE Plasma.
8888
89-
Only xwayland apps will be detected.
89+
For other desktop environments, only xwayland apps will be detected.
9090
9191
If Wayland support is important to you, consider voting on the issue:
9292
@@ -106,12 +106,19 @@ env QT_QPA_PLATFORM=xcb <app>
106106
107107
Otherwise, [consider signing in using X11 instead](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/).''';
108108

109-
switch (sessionType) {
110-
case 'wayland':
111-
log.w(waylandNotSupportedMsg);
112-
emit(state.copyWith(linuxSessionMessage: waylandNotSupportedMsg));
113-
return;
114-
case 'x11':
109+
emit(state.copyWith(sessionType: sessionType));
110+
111+
log.i('Session type: $sessionType');
112+
113+
switch (sessionType.displayProtocol) {
114+
case DisplayProtocol.wayland:
115+
if (sessionType.environment == DesktopEnvironment.kde) {
116+
log.i('KDE Wayland session detected and is supported, proceeding.');
117+
} else {
118+
log.w(waylandNotSupportedMsg);
119+
emit(state.copyWith(linuxSessionMessage: waylandNotSupportedMsg));
120+
}
121+
case DisplayProtocol.x11:
115122
break;
116123
default:
117124
log.w(unknownSessionMsg);
@@ -202,4 +209,10 @@ Otherwise, [consider signing in using X11 instead](https://docs.fedoraproject.or
202209
return false;
203210
}
204211
}
212+
213+
@override
214+
Future<void> close() async {
215+
await _nativePlatform.dispose();
216+
await super.close();
217+
}
205218
}

lib/app/cubit/app_state.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ class AppState with _$AppState {
77
/// session type is unknown.
88
String? linuxSessionMessage,
99

10+
/// The type of desktop session the user is running.
11+
///
12+
/// Currently only used on Linux.
13+
SessionType? sessionType,
14+
1015
/// True if this is the first run of the app.
1116
required bool firstRun,
1217
required String runningVersion,

lib/apps_list/models/interaction_error.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import '../enums.dart';
55
class InteractionError {
66
final InteractionType interactionType;
77
final ProcessStatus statusAfterInteraction;
8-
final int windowId;
8+
final String windowId;
99

1010
const InteractionError({
1111
required this.interactionType,

lib/loading/cubit/loading_cubit.dart

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,7 @@ part 'loading_cubit.freezed.dart';
1010
class LoadingCubit extends Cubit<LoadingState> {
1111
final NativePlatform nativePlatform;
1212

13-
LoadingCubit()
14-
: nativePlatform = NativePlatform(),
15-
super(const LoadingInitial()) {
13+
LoadingCubit(this.nativePlatform) : super(const LoadingInitial()) {
1614
checkDependencies();
1715
}
1816

lib/loading/loading_page.dart

Lines changed: 31 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,40 @@ class LoadingPage extends StatelessWidget {
1616

1717
@override
1818
Widget build(BuildContext context) {
19-
return BlocProvider(
20-
create: (context) => LoadingCubit(),
21-
lazy: false,
22-
child: Scaffold(
23-
body: Center(
24-
child: BlocConsumer<LoadingCubit, LoadingState>(
25-
listener: (context, state) {
26-
if (state is LoadingSuccess) {
27-
Navigator.pushReplacementNamed(context, AppsListPage.id);
28-
}
29-
},
30-
builder: (context, state) {
31-
switch (state) {
32-
case LoadingError():
33-
return Padding(
34-
padding: const EdgeInsets.all(16.0),
35-
child: Card(
36-
child: Container(
37-
padding: const EdgeInsets.all(20.0),
38-
child: MarkdownBody(
39-
data: state.errorMsg,
40-
onTapLink: (text, href, title) {
41-
if (href == null) {
42-
log.e('Broken link: $href');
43-
return;
44-
}
19+
return Scaffold(
20+
body: Center(
21+
child: BlocConsumer<LoadingCubit, LoadingState>(
22+
listener: (context, state) {
23+
if (state is LoadingSuccess) {
24+
Navigator.pushReplacementNamed(context, AppsListPage.id);
25+
}
26+
},
27+
builder: (context, state) {
28+
switch (state) {
29+
case LoadingError():
30+
return Padding(
31+
padding: const EdgeInsets.all(16.0),
32+
child: Card(
33+
child: Container(
34+
padding: const EdgeInsets.all(20.0),
35+
child: MarkdownBody(
36+
data: state.errorMsg,
37+
onTapLink: (text, href, title) {
38+
if (href == null) {
39+
log.e('Broken link: $href');
40+
return;
41+
}
4542

46-
AppCubit.instance.launchURL(href);
47-
},
48-
),
43+
AppCubit.instance.launchURL(href);
44+
},
4945
),
5046
),
51-
);
52-
default:
53-
return const CircularProgressIndicator();
54-
}
55-
},
56-
),
47+
),
48+
);
49+
default:
50+
return const CircularProgressIndicator();
51+
}
52+
},
5753
),
5854
),
5955
);

lib/main.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import 'apps_list/apps_list.dart';
1616
import 'argument_parser/argument_parser.dart';
1717
import 'autostart/autostart_service.dart';
1818
import 'hotkey/global/hotkey_service.dart';
19+
import 'loading/loading.dart';
1920
import 'logs/logs.dart';
2021
import 'native_platform/native_platform.dart';
2122
import 'settings/cubit/settings_cubit.dart';
@@ -33,7 +34,6 @@ Future<void> main(List<String> args) async {
3334
..parseArgs(args);
3435

3536
final storage = await StorageRepository.initialize(Hive);
36-
final nativePlatform = NativePlatform();
3737

3838
bool verbose = argParser.verbose;
3939
if (!verbose) {
@@ -52,6 +52,7 @@ Future<void> main(List<String> args) async {
5252
final packageInfo = await PackageInfo.fromPlatform();
5353
log.i('Starting Nyrna v${packageInfo.version}');
5454

55+
final nativePlatform = await NativePlatform.initialize();
5556
final processRepository = ProcessRepository.init();
5657

5758
final appWindow = AppWindow(storage);
@@ -119,6 +120,8 @@ Future<void> main(List<String> args) async {
119120
appVersion: AppVersion(packageInfo),
120121
);
121122

123+
final loadingCubit = LoadingCubit(nativePlatform);
124+
122125
runApp(
123126
MultiRepositoryProvider(
124127
providers: [
@@ -128,6 +131,7 @@ Future<void> main(List<String> args) async {
128131
providers: [
129132
BlocProvider.value(value: appCubit),
130133
BlocProvider.value(value: appsListCubit),
134+
BlocProvider.value(value: loadingCubit),
131135
BlocProvider.value(value: settingsCubit),
132136
BlocProvider.value(value: themeCubit),
133137
],

0 commit comments

Comments
 (0)