Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add kde wayland support #240

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, windows-latest]

env:
# Needed so we don't get errors in CI
XDG_SESSION_TYPE: "x11"
XDG_CURRENT_DESKTOP: "KDE"

steps:
- uses: actions/checkout@v3
- uses: subosito/flutter-action@v2
Expand All @@ -38,7 +44,7 @@ jobs:
run: flutter gen-l10n

- name: Run lint
run: flutter analyze
run: flutter analyze --no-fatal-infos

- name: Run tests
run: flutter test
94 changes: 94 additions & 0 deletions assets/lib/linux/list_windows_kde.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// https://unix.stackexchange.com/a/706478/379240

function print(str) {
console.info('Nyrna: ' + str);
}

let windows = workspace.windowList();
print('Found ' + windows.length + ' windows');

function updateWindowsOnDBus(windows) {
let windowsList = [];

for (let window of windows) {
windowsList.push({
caption: window.caption,
pid: window.pid,
internalId: window.internalId,
onCurrentDesktop: isWindowOnCurrentDesktop(window),
});
}

callDBus(
'codes.merritt.Nyrna',
'/',
'codes.merritt.Nyrna',
'updateWindows',
JSON.stringify(windowsList),
(result) => {
if (result) {
print('Successfully updated windows on DBus');
} else {
print('Failed to update windows on DBus');
}
}
);
}

function isWindowOnCurrentDesktop(window) {
let windowDesktops = Object.values(window.desktops);
let windowIsOnCurrentDesktop = window.onAllDesktops;

if (!windowIsOnCurrentDesktop) {
for (let windowDesktop of windowDesktops) {
if (windowDesktop.id === workspace.currentDesktop.id) {
windowIsOnCurrentDesktop = true;
break;
} else {
windowIsOnCurrentDesktop = false;
}
}
}

return windowIsOnCurrentDesktop;
}

function updateCurrentDesktopOnDBus() {
print('Current desktop id: ' + workspace.currentDesktop.id);

callDBus(
'codes.merritt.Nyrna',
'/',
'codes.merritt.Nyrna',
'updateCurrentDesktop',
workspace.currentDesktop,
(result) => {
if (result) {
print('Successfully updated current desktop on DBus');
} else {
print('Failed to update current desktop on DBus');
}
}
);
}

updateCurrentDesktopOnDBus();
updateWindowsOnDBus(windows);

workspace.currentDesktopChanged.connect(() => {
print('Current desktop changed');
updateCurrentDesktopOnDBus();
updateWindowsOnDBus(windows);
});

workspace.windowAdded.connect(window => {
print('Window added: ' + window.caption);
windows.push(window);
updateWindowsOnDBus(windows);
});

workspace.windowRemoved.connect(window => {
print('Window removed: ' + window.caption);
windows = windows.filter(w => w.internalId !== window.internalId);
updateWindowsOnDBus(windows);
});
3 changes: 3 additions & 0 deletions devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
4 changes: 2 additions & 2 deletions lib/active_window/src/active_window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ class ActiveWindow {
return false;
}

Future<void> _minimize(int windowId) async {
Future<void> _minimize(String windowId) async {
final shouldMinimize = await _getShouldMinimize();
if (!shouldMinimize) return;

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

Future<void> _restore(int windowId) async {
Future<void> _restore(String windowId) async {
final shouldRestore = await _getShouldMinimize();
if (!shouldRestore) return;

Expand Down
29 changes: 21 additions & 8 deletions lib/app/cubit/app_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,9 @@ Unable to determine session type. The XDG_SESSION_TYPE environment variable is s
Please note that Wayland is not currently supported.''';

const waylandNotSupportedMsg = '''
Wayland is not currently supported.
Wayland is currently supported only on KDE Plasma.

Only xwayland apps will be detected.
For other desktop environments, only xwayland apps will be detected.

If Wayland support is important to you, consider voting on the issue:

Expand All @@ -106,12 +106,19 @@ env QT_QPA_PLATFORM=xcb <app>

Otherwise, [consider signing in using X11 instead](https://docs.fedoraproject.org/en-US/quick-docs/configuring-xorg-as-default-gnome-session/).''';

switch (sessionType) {
case 'wayland':
log.w(waylandNotSupportedMsg);
emit(state.copyWith(linuxSessionMessage: waylandNotSupportedMsg));
return;
case 'x11':
emit(state.copyWith(sessionType: sessionType));

log.i('Session type: $sessionType');

switch (sessionType.displayProtocol) {
case DisplayProtocol.wayland:
if (sessionType.environment == DesktopEnvironment.kde) {
log.i('KDE Wayland session detected and is supported, proceeding.');
} else {
log.w(waylandNotSupportedMsg);
emit(state.copyWith(linuxSessionMessage: waylandNotSupportedMsg));
}
case DisplayProtocol.x11:
break;
default:
log.w(unknownSessionMsg);
Expand Down Expand Up @@ -202,4 +209,10 @@ Otherwise, [consider signing in using X11 instead](https://docs.fedoraproject.or
return false;
}
}

@override
Future<void> close() async {
await _nativePlatform.dispose();
await super.close();
}
}
5 changes: 5 additions & 0 deletions lib/app/cubit/app_state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ class AppState with _$AppState {
/// session type is unknown.
String? linuxSessionMessage,

/// The type of desktop session the user is running.
///
/// Currently only used on Linux.
SessionType? sessionType,

/// True if this is the first run of the app.
required bool firstRun,
required String runningVersion,
Expand Down
2 changes: 1 addition & 1 deletion lib/apps_list/models/interaction_error.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import '../enums.dart';
class InteractionError {
final InteractionType interactionType;
final ProcessStatus statusAfterInteraction;
final int windowId;
final String windowId;

const InteractionError({
required this.interactionType,
Expand Down
4 changes: 1 addition & 3 deletions lib/loading/cubit/loading_cubit.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,7 @@ part 'loading_cubit.freezed.dart';
class LoadingCubit extends Cubit<LoadingState> {
final NativePlatform nativePlatform;

LoadingCubit()
: nativePlatform = NativePlatform(),
super(const LoadingInitial()) {
LoadingCubit(this.nativePlatform) : super(const LoadingInitial()) {
checkDependencies();
}

Expand Down
66 changes: 31 additions & 35 deletions lib/loading/loading_page.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,44 +16,40 @@ class LoadingPage extends StatelessWidget {

@override
Widget build(BuildContext context) {
return BlocProvider(
create: (context) => LoadingCubit(),
lazy: false,
child: Scaffold(
body: Center(
child: BlocConsumer<LoadingCubit, LoadingState>(
listener: (context, state) {
if (state is LoadingSuccess) {
Navigator.pushReplacementNamed(context, AppsListPage.id);
}
},
builder: (context, state) {
switch (state) {
case LoadingError():
return Padding(
padding: const EdgeInsets.all(16.0),
child: Card(
child: Container(
padding: const EdgeInsets.all(20.0),
child: MarkdownBody(
data: state.errorMsg,
onTapLink: (text, href, title) {
if (href == null) {
log.e('Broken link: $href');
return;
}
return Scaffold(
body: Center(
child: BlocConsumer<LoadingCubit, LoadingState>(
listener: (context, state) {
if (state is LoadingSuccess) {
Navigator.pushReplacementNamed(context, AppsListPage.id);
}
},
builder: (context, state) {
switch (state) {
case LoadingError():
return Padding(
padding: const EdgeInsets.all(16.0),
child: Card(
child: Container(
padding: const EdgeInsets.all(20.0),
child: MarkdownBody(
data: state.errorMsg,
onTapLink: (text, href, title) {
if (href == null) {
log.e('Broken link: $href');
return;
}

AppCubit.instance.launchURL(href);
},
),
AppCubit.instance.launchURL(href);
},
),
),
);
default:
return const CircularProgressIndicator();
}
},
),
),
);
default:
return const CircularProgressIndicator();
}
},
),
),
);
Expand Down
6 changes: 5 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import 'apps_list/apps_list.dart';
import 'argument_parser/argument_parser.dart';
import 'autostart/autostart_service.dart';
import 'hotkey/global/hotkey_service.dart';
import 'loading/loading.dart';
import 'logs/logs.dart';
import 'native_platform/native_platform.dart';
import 'settings/cubit/settings_cubit.dart';
Expand All @@ -33,7 +34,6 @@ Future<void> main(List<String> args) async {
..parseArgs(args);

final storage = await StorageRepository.initialize(Hive);
final nativePlatform = NativePlatform();

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

final nativePlatform = await NativePlatform.initialize();
final processRepository = ProcessRepository.init();

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

final loadingCubit = LoadingCubit(nativePlatform);

runApp(
MultiRepositoryProvider(
providers: [
Expand All @@ -128,6 +131,7 @@ Future<void> main(List<String> args) async {
providers: [
BlocProvider.value(value: appCubit),
BlocProvider.value(value: appsListCubit),
BlocProvider.value(value: loadingCubit),
BlocProvider.value(value: settingsCubit),
BlocProvider.value(value: themeCubit),
],
Expand Down
10 changes: 10 additions & 0 deletions lib/native_platform/src/linux/dbus/codes.merritt.Nyrna.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">

<node>
<interface name="codes.merritt.Nyrna">
<method name="updateWindows">
<arg type="b" direction="out" />
<arg name="windows" type="s" direction="in" />
</method>
</interface>
</node>
Loading