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

feature: providing a minimal launcher written in flutter for ubuntu frame #191

Merged
merged 20 commits into from
Jul 26, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
9417e15
feature: providing a minimal launcher written in flutter for ubuntu f…
mattkae Jun 24, 2024
1b431ec
feedback: run-client-wait when running the launcher so that we have a…
mattkae Jun 24, 2024
4429074
feedback: plugs.json + remove config_changes check in configure + README
mattkae Jun 24, 2024
0337ef1
rename run-client-wait to wayland-launch + license
mattkae Jun 24, 2024
df4e362
Revert accidental change
mattkae Jun 24, 2024
16dcc95
Checking if interfaces are connected
mattkae Jun 25, 2024
0b4ed7d
update: less verbose is-connected checks
mattkae Jun 25, 2024
fdaf845
disable snap build for flutter launcher on armhf since it does not su…
mattkae Jul 16, 2024
e4cc052
if trying to enable the launcher on an armhf system. ten we throw an …
mattkae Jul 16, 2024
5595548
Enabling launcher only on x86, i32, and amd64 systems
mattkae Jul 16, 2024
ebdb4b1
Cuter check on the architecture conditional for launcher
mattkae Jul 16, 2024
e9069fb
daemon connects to desktop-launch and increased logging
mattkae Jul 17, 2024
7d758df
Merge branch 'feature/multi-app' of github.com:canonical/ubuntu-frame…
mattkae Jul 17, 2024
37a7375
Set XDG_DATA_DIRS for the daemon
mattkae Jul 17, 2024
9dea9dc
Update scripts/bin/wayland-launch
mattkae Jul 18, 2024
b157596
snap: using wayplug instead of wayslot
mattkae Jul 18, 2024
17bd048
configure: fix arm64 -> aarch64
Saviq Jul 25, 2024
e149fa7
launcher: pin to Flutter 3.19.6
Saviq Jul 25, 2024
f29e1a4
configure: require desktop-launch to enable the launcher
Saviq Jul 25, 2024
bc5d16f
snap: add disconnect plug on desktop-launch
Saviq Jul 26, 2024
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 README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ The foundation for many embedded graphical display implementations. `ubuntu-fram

The application you choose (or provide) gets a fullscreen window (or windows) and input from touch, keyboard and mouse without needing to deal with the specific hardware.

## Connections
```sh
snap connect ubuntu-frame:desktop-launch
Saviq marked this conversation as resolved.
Show resolved Hide resolved
```

## Configuration

There are three snap configuration options:
There are four snap configuration options:

* `daemon=[true|false]` enables the daemon (defaults to false on classic systems)
* `config=<options for the shell>`
* `display=<options for display layout>`
* `launcher=[true|false]`

The configuration options are described in detail in [the Ubuntu Frame reference](https://mir-server.io/docs/reference).

Expand Down
29 changes: 29 additions & 0 deletions launcher/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# See https://www.dartlang.org/guides/libraries/private-files

# Files and directories created by pub
.dart_tool/
.packages
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock

# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/

# dotenv environment variables file
.env*

# Avoid committing generated Javascript files:
*.dart.js
*.info.json # Produced by the --dump-info flag.
*.js # When generated by dart2js. Don't specify *.js if your
# project includes source files written in JavaScript.
*.js_
*.js.deps
*.js.map

.flutter-plugins
.flutter-plugins-dependencies

linux/flutter/ephemeral
674 changes: 674 additions & 0 deletions launcher/LICENSE

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions launcher/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# ubuntu-frame-launcher
A flutter-based launcher for Ubuntu Frame.

## Install
```sh
flutter pub get
```

## Development
1 . Run `ubuntu-frame`:

```sh
WAYLAND_DISPLAY=wayland-98 ubuntu-frame --add-wayland-extensions zwlr_layer_shell_v1:zwlr_foreign_toplevel_manager_v1
```

2. Run `launcher`:

```sh
cd launcher
WAYLAND_DISPLAY=wayland-98 flutter run
```
29 changes: 29 additions & 0 deletions launcher/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.

# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml

linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule

# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
84 changes: 84 additions & 0 deletions launcher/lib/controllers/application_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 or 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import 'dart:async';

import 'package:logging/logging.dart';
import 'package:ubuntu_frame_launcher/models/desktop_file.dart';
import 'package:ubuntu_frame_launcher/models/window_event.dart';
import 'package:ubuntu_frame_launcher/services/window_service.dart';

import '../services/desktop_file_manager.dart';

class ApplicationController {
final WindowService windowService;
final DesktopFileManager desktopFileManager;

final _controller = StreamController<List<DesktopFile>>.broadcast();
late Future<List<DesktopFile>> _installedAppFuture;
final _logger = Logger("ApplicationController");

ApplicationController(this.windowService, this.desktopFileManager) {
_installedAppFuture = _loadInstalledApplications();

windowService.watcher.listen((event) async {
if (event.eventType == WindowEventType.created ||
event.eventType == WindowEventType.removed) {
_controller.add(await getOpenApplications());
}
});
}

Future<List<DesktopFile>> _loadInstalledApplications() async {
final applications = await desktopFileManager.getAllDesktopFiles();
applications.sort((a, b) {
final aName = a.name?.toLowerCase() ?? '';
final bName = b.name?.toLowerCase() ?? '';
return aName.compareTo(bName);
});
return applications;
}

/// Returns a list of applications installed on the system. This list
/// is guaranteed to be sorted alphabetically by name.
Future<List<DesktopFile>> getInstalledApplications() async {
// TODO: Watch directories and dynamically reload if they change
return await _installedAppFuture;
}

void focus(DesktopFile file) {
final handle = windowService.getWindowById(file.id);
if (handle != null) {
handle.activate();
return;
}

_logger.shout("Unable to focus desktop file with id = ${file.id}}");
}

Stream<List<DesktopFile>> getOpenedAppsStream() {
return _controller.stream;
}

Future<List<DesktopFile>> getOpenApplications() async {
final List<DesktopFile> result = [];
for (final handle in windowService.getWindowList()) {
result.add(await desktopFileManager.resolveId(handle.getAppId()));
}

return result;
}
}
38 changes: 38 additions & 0 deletions launcher/lib/controllers/window_controller.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 or 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import 'package:ubuntu_frame_launcher/models/desktop_file.dart';
import 'package:ubuntu_frame_launcher/services/window_handle.dart';

import '../services/window_service.dart';

class WindowController {
WindowService service;

WindowController(this.service);

WindowHandle? getWindowById(String appId) {
return service.getWindowById(appId);
}

List<WindowHandle> getWindowList() {
return service.getWindowList();
}

bool isOpen(DesktopFile file) {
return service.getWindowById(file.id) != null;
}
}
87 changes: 87 additions & 0 deletions launcher/lib/main.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright © Canonical Ltd.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 or 3,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import 'package:flutter/material.dart';
import 'package:ubuntu_frame_launcher/services/wayland_window_watcher.dart';
import 'package:ubuntu_frame_launcher/controllers/window_controller.dart';
import 'package:ubuntu_frame_launcher/services/window_service.dart';
import 'services/desktop_file_manager.dart';
import 'package:get_it/get_it.dart';
import 'views/dock.dart';
import 'controllers/application_controller.dart';
import 'package:logging/logging.dart';

void main() async {
Logger.root.level = Level.ALL; // defaults to Level.INFO
Logger.root.onRecord.listen((record) {
print('${record.level.name}: ${record.time}: ${record.message}');
});

final logger = Logger("main");
logger.info("Ubuntu Frame launcher is starting");

final getIt = GetIt.instance;

// Services
getIt.registerLazySingleton<WindowService>(
() => WindowService(WaylandWindowWatcherService()));
getIt.registerLazySingleton<DesktopFileManager>(() => DesktopFileManager());

// Controllers
getIt.registerLazySingleton<ApplicationController>(() =>
ApplicationController(
getIt.get<WindowService>(), getIt.get<DesktopFileManager>()));
getIt.registerLazySingleton<WindowController>(
() => WindowController(getIt.get<WindowService>()));

runApp(const LauncherApp());
}

class LauncherApp extends StatelessWidget {
const LauncherApp({super.key});

@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.black),
primaryColor: Colors.black,
useMaterial3: true,
fontFamily: 'Ubuntu',
iconTheme: const IconThemeData(color: Colors.white, size: 20),
textTheme: const TextTheme(
bodyMedium: TextStyle(
color: Colors.white,
),
)),
home: const Launcher(),
);
}
}

class Launcher extends StatelessWidget {
const Launcher({super.key});

@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[Dock()],
));
}
}
Loading