├── .gitignore ├── .vscode ├── extensions.json ├── flutter.code-snippets ├── launch.json ├── settings.json └── tasks.json ├── GUIDELINES.md ├── LICENSE ├── LICENSE copy ├── README.md ├── analysis_options.yaml ├── bin ├── commands │ ├── build.dart │ ├── clean.dart │ ├── dependency │ │ ├── check_depencies.dart │ │ └── package_manager │ │ │ ├── apt.dart │ │ │ ├── dnf.dart │ │ │ ├── package_manager_base.dart │ │ │ └── pacman.dart │ ├── dev.dart │ ├── install.dart │ └── run.dart ├── util.dart └── veshell.dart ├── dependencies.md ├── embedder ├── Cargo.lock ├── Cargo.toml ├── build.rs ├── resources │ └── cursor.rgba ├── src │ ├── backend │ │ ├── drm_backend.rs │ │ ├── mod.rs │ │ └── x11_client.rs │ ├── cursor.rs │ ├── flutter_engine │ │ ├── callbacks.rs │ │ ├── embedder.rs │ │ ├── mod.rs │ │ ├── platform_channel_callbacks.rs │ │ ├── platform_channels.rs │ │ ├── platform_channels │ │ │ ├── README.md │ │ │ ├── basic_message_channel.rs │ │ │ ├── binary_messenger.rs │ │ │ ├── binary_messenger_impl.rs │ │ │ ├── byte_buffer_streams.rs │ │ │ ├── byte_streams.rs │ │ │ ├── encodable_value.rs │ │ │ ├── engine_method_result.rs │ │ │ ├── json_message_codec.rs │ │ │ ├── json_method_codec.rs │ │ │ ├── message_codec.rs │ │ │ ├── method_call.rs │ │ │ ├── method_channel.rs │ │ │ ├── method_codec.rs │ │ │ ├── method_result.rs │ │ │ ├── method_result_functions.rs │ │ │ ├── method_result_mpsc_channel.rs │ │ │ ├── standard_codec_serializer.rs │ │ │ ├── standard_message_codec.rs │ │ │ ├── standard_method_codec.rs │ │ │ ├── text_input_model.rs │ │ │ └── text_range.rs │ │ ├── task_runner.rs │ │ ├── text_input.rs │ │ └── wayland_messages.rs │ ├── focus.rs │ ├── gles_framebuffer_importer.rs │ ├── input_handling.rs │ ├── keyboard.rs │ ├── keyboard │ │ ├── glfw_key_codes.rs │ │ └── key_repeater.rs │ ├── main.rs │ ├── mouse_button_tracker.rs │ ├── state.rs │ ├── texture_swap_chain.rs │ ├── wayland │ │ ├── mod.rs │ │ └── xdg.rs │ └── xwayland.rs └── third_party │ ├── flutter_engine │ └── embedder.h │ └── smithay-drm-extras │ ├── Cargo.toml │ ├── README.md │ ├── build.rs │ └── src │ ├── docs │ └── doctest_helpers.rs │ ├── drm_scanner.rs │ ├── drm_scanner │ ├── connector_scanner.rs │ └── crtc_mapper.rs │ ├── edid.rs │ ├── hwdata │ ├── generated │ │ └── pnp_ids.rs │ └── mod.rs │ └── lib.rs ├── pubspec.lock ├── pubspec.yaml └── shell ├── .config ├── custom_devices.json ├── settings └── tool_state ├── .gitignore ├── analysis_options.yaml ├── assets └── ship_vector.svg ├── build.yaml ├── lib ├── application │ ├── model │ │ └── launch_config.serializable.dart │ ├── provider │ │ ├── app_drawer.dart │ │ ├── app_drawer_desktop_entries.dart │ │ ├── app_launch.dart │ │ ├── desktop_entries.dart │ │ ├── file_to_scalable_image.dart │ │ ├── icon_themes.dart │ │ ├── image_from_icon_query.dart │ │ └── localized_desktop_entries.dart │ └── widget │ │ └── app_icon.dart ├── display │ ├── model │ │ └── display.dart │ ├── provider │ │ └── display.dart │ └── widget │ │ └── display.dart ├── enums.dart ├── main.dart ├── monitor │ ├── model │ │ ├── monitor.serializable.dart │ │ ├── monitor_configuration.serializable.dart │ │ └── screen_configuration.serializable.dart │ ├── provider │ │ ├── current_monitor.dart │ │ ├── monitor_configuration_state.dart │ │ └── monitor_list.dart │ └── widget │ │ └── monitor.dart ├── overview │ ├── helm │ │ ├── control_panel │ │ │ ├── bluetooth │ │ │ │ ├── model │ │ │ │ │ ├── bluetooth_device.dart │ │ │ │ │ └── bluetooth_manager_state.dart │ │ │ │ ├── provider │ │ │ │ │ ├── bluetooth_device.dart │ │ │ │ │ └── bluetooth_manager.dart │ │ │ │ └── widget │ │ │ │ │ ├── bluetooth_control.dart │ │ │ │ │ ├── bluetooth_device_icon.dart │ │ │ │ │ └── bluetooth_device_list_tile.dart │ │ │ └── widget │ │ │ │ ├── audio_output.dart │ │ │ │ ├── control_panel.dart │ │ │ │ └── session_controls.dart │ │ ├── monitoring_panel │ │ │ ├── cpu_monitoring │ │ │ │ ├── model │ │ │ │ │ ├── cpu.dart │ │ │ │ │ ├── cpu_line.dart │ │ │ │ │ └── processes_cpu_stats_snapshot.dart │ │ │ │ ├── provider │ │ │ │ │ ├── cpu_stats.dart │ │ │ │ │ ├── process_name.dart │ │ │ │ │ └── processes_cpu_stats.dart │ │ │ │ └── widget │ │ │ │ │ └── cpu_monitoring.dart │ │ │ ├── model │ │ │ │ └── process_stat.dart │ │ │ ├── provider │ │ │ │ └── process_list.dart │ │ │ └── widget │ │ │ │ └── monitoring_panel.dart │ │ ├── notification_panel │ │ │ └── widget │ │ │ │ └── notification_panel.dart │ │ ├── provider │ │ │ └── audio_output.dart │ │ └── widget │ │ │ └── helm.dart │ ├── model │ │ └── overview.dart │ ├── provider │ │ └── overview_state.dart │ ├── search │ │ └── widget │ │ │ ├── application_search_result.dart │ │ │ ├── file_search_result.dart │ │ │ ├── search_engine.dart │ │ │ ├── search_input.dart │ │ │ └── settings_search_result.dart │ └── widget │ │ ├── overview.dart │ │ └── overview_content.dart ├── pointer │ ├── model │ │ └── pointer_focus.serializable.dart │ └── provider │ │ └── pointer_focus.manager.dart ├── polkit │ ├── model │ │ ├── authentication_agent.dart │ │ ├── org.freedesktop.PolicyKit1.AuthenticationAgent.dart │ │ ├── org.freedesktop.PolicyKit1.AuthenticationAgent.xml │ │ └── polkit-agent-helper.dart │ ├── provider │ │ └── authentication_agent.dart │ └── widget │ │ └── polkit_authentication_dialog.dart ├── screen │ ├── model │ │ ├── screen.serializable.dart │ │ ├── screen_shortcuts.dart │ │ └── workspace_drag_data.dart │ ├── provider │ │ ├── current_screen_id.dart │ │ ├── focused_screen.dart │ │ ├── screen_list.dart │ │ ├── screen_state.dart │ │ └── workspace_display_mode.dart │ └── widget │ │ ├── screen.dart │ │ ├── screen_panel.dart │ │ └── workspace_list.dart ├── shared │ ├── bluez │ │ └── provider │ │ │ ├── bluez_client.dart │ │ │ ├── bluez_device.dart │ │ │ ├── bluez_device_added.dart │ │ │ ├── bluez_device_removed.dart │ │ │ └── bluez_devices.dart │ ├── nm │ │ └── provider │ │ │ ├── nm_client.dart │ │ │ ├── nm_device.dart │ │ │ ├── nm_device_added.dart │ │ │ ├── nm_device_removed.dart │ │ │ └── nm_devices.dart │ ├── persistence │ │ ├── persistable_model.mixin.dart │ │ ├── persistable_provider.mixin.dart │ │ └── persistence_manager.dart │ ├── provider │ │ ├── cursor_position.dart │ │ ├── dbus_client.dart │ │ ├── mouse_button_tracker.dart │ │ ├── now_date_time.dart │ │ ├── persistent_json_by_folder.dart │ │ └── root_overlay.dart │ ├── state │ │ └── task_switcher │ │ │ ├── model │ │ │ └── task_switcher.dart │ │ │ └── provider │ │ │ └── task_switcher.dart │ ├── tasks │ │ ├── model │ │ │ └── tasks.dart │ │ └── provider │ │ │ └── tasks.dart │ ├── util │ │ ├── json_converter │ │ │ ├── offset.dart │ │ │ ├── rect.dart │ │ │ └── size.dart │ │ ├── logger.dart │ │ ├── raw_gesture_recognizer.dart │ │ └── rect_overflow_box.dart │ └── widget │ │ ├── clock.dart │ │ ├── cross_reorderable_list.dart │ │ ├── number_picker.dart │ │ └── sliding_container.dart ├── systemd │ └── provider │ │ └── session.dart ├── theme │ └── theme.dart ├── wayland │ ├── model │ │ ├── event │ │ │ ├── app_id_changed │ │ │ │ └── app_id_changed.serializable.dart │ │ │ ├── commit_surface │ │ │ │ └── commit_surface.serializable.dart │ │ │ ├── destroy_popup │ │ │ │ └── destroy_popup.serializable.dart │ │ │ ├── destroy_subsurface │ │ │ │ └── destroy_subsurface.serializable.dart │ │ │ ├── destroy_surface │ │ │ │ └── destroy_surface.serializable.dart │ │ │ ├── destroy_toplevel │ │ │ │ └── destroy_toplevel.serializable.dart │ │ │ ├── destroy_x11_surface │ │ │ │ └── destroy_x11_surface.serializable.dart │ │ │ ├── destroy_xdg_surface │ │ │ │ └── destroy_xdg_surface.serializable.dart │ │ │ ├── interactive_move │ │ │ │ └── interactive_move.serializable.dart │ │ │ ├── interactive_resize │ │ │ │ └── interactive_resize.serializable.dart │ │ │ ├── map_x11_surface │ │ │ │ └── map_x11_surface.serializable.dart │ │ │ ├── monitor_layout_changed │ │ │ │ └── monitor_layout_changed.serializable.dart │ │ │ ├── new_popup │ │ │ │ └── new_popup.serializable.dart │ │ │ ├── new_subsurface │ │ │ │ └── new_subsurface.serializable.dart │ │ │ ├── new_surface │ │ │ │ └── new_surface.serializable.dart │ │ │ ├── new_toplevel │ │ │ │ └── new_toplevel.serializable.dart │ │ │ ├── new_x11_surface │ │ │ │ └── new_x11_surface.serializable.dart │ │ │ ├── set_environment_variables │ │ │ │ └── set_environment_variables.serializable.dart │ │ │ ├── surface_associated │ │ │ │ └── surface_associated.serializable.dart │ │ │ ├── title_changed │ │ │ │ └── title_changed.serializable.dart │ │ │ ├── unmap_x11_surface │ │ │ │ └── unmap_x11_surface.serializable.dart │ │ │ ├── wayland_event.serializable.dart │ │ │ └── x11_properties_changed │ │ │ │ └── x11_properties_changed.serializable.dart │ │ ├── request │ │ │ ├── activate_window │ │ │ │ └── activate_window.serializable.dart │ │ │ ├── close_window │ │ │ │ └── close_window.serializable.dart │ │ │ ├── get_environment_variables │ │ │ │ └── get_environment_variables.serializable.dart │ │ │ ├── get_monitor_layout │ │ │ │ └── get_monitor_layout.serializable.dart │ │ │ ├── maximize_window │ │ │ │ └── maximize_window.serializable.dart │ │ │ ├── mouse_buttons_event │ │ │ │ └── mouse_buttons_event.serializable.dart │ │ │ ├── pointer_focus │ │ │ │ └── pointer_focus.serializable.dart │ │ │ ├── resize_window │ │ │ │ └── resize_window.serializable.dart │ │ │ ├── shell_ready │ │ │ │ └── shell_ready.serializable.dart │ │ │ ├── touch │ │ │ │ └── touch.serializable.dart │ │ │ ├── unregister_view_texture │ │ │ │ └── unregister_view_texture.serializable.dart │ │ │ └── wayland_request.dart │ │ ├── subsurface.dart │ │ ├── surface_manager_state.dart │ │ ├── wl_surface.dart │ │ ├── x11_surface.dart │ │ ├── xdg_popup.dart │ │ ├── xdg_surface.dart │ │ └── xdg_toplevel.dart │ ├── provider │ │ ├── environment_variables.dart │ │ ├── subsurface_state.dart │ │ ├── surface.manager.dart │ │ ├── wayland.manager.dart │ │ ├── wl_surface_state.dart │ │ ├── x11_surface_state.dart │ │ ├── xdg_popup_state.dart │ │ ├── xdg_surface_state.dart │ │ └── xdg_toplevel_state.dart │ └── widget │ │ ├── activate_and_raise.dart │ │ ├── subsurface.dart │ │ ├── surface.dart │ │ ├── surface │ │ ├── contain_to_input_region.dart │ │ ├── pointer_listener.dart │ │ ├── surface_focus.dart │ │ ├── view_input_listener.dart │ │ └── xdg_popup │ │ │ └── popup.dart │ │ ├── surface_size.dart │ │ ├── x11_surface.dart │ │ └── xdg_toplevel_surface.dart ├── window │ ├── model │ │ ├── dialog_window.dart │ │ ├── ephemeral_window.dart │ │ ├── matching_info.serializable.dart │ │ ├── persistent_window.serializable.dart │ │ ├── window_base.dart │ │ ├── window_id.dart │ │ ├── window_move.dart │ │ ├── window_properties.serializable.dart │ │ ├── window_resize.dart │ │ └── window_stack.dart │ ├── provider │ │ ├── dialog_list_for_window.dart │ │ ├── dialog_window_state.dart │ │ ├── ephemeral_window_state.dart │ │ ├── persistent_window_state.dart │ │ ├── surface_window_map.dart │ │ ├── window_manager │ │ │ ├── matching_engine.dart │ │ │ ├── matching_utils.dart │ │ │ ├── weighted_matching.dart │ │ │ └── window_manager.dart │ │ ├── window_move.dart │ │ ├── window_position.dart │ │ ├── window_properties.dart │ │ ├── window_provider.mixin.dart │ │ ├── window_resize.dart │ │ └── window_stack.dart │ └── widget │ │ └── ephemeral_window.dart └── workspace │ ├── model │ ├── workspace.serializable.dart │ └── workspace_shortcuts.dart │ ├── provider │ ├── current_workspace_id.dart │ ├── window_workspace_map.dart │ └── workspace_state.dart │ └── widget │ ├── layout │ └── horizontal.dart │ ├── tileable │ ├── persistent_application_launcher │ │ ├── app_drawer │ │ │ ├── app_drawer.dart │ │ │ └── app_grid.dart │ │ └── persistent_application_launcher.dart │ ├── persistent_window │ │ ├── persistent_window.dart │ │ └── window_placeholder.dart │ └── tileable.dart │ ├── tileable_list.dart │ ├── workspace.dart │ └── workspace_panel.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── README.md ├── flutter │ ├── CMakeLists.txt │ ├── generated_plugin_registrant.cc │ ├── generated_plugin_registrant.h │ └── generated_plugins.cmake ├── main.cc ├── my_application.cc └── my_application.h ├── pubspec.lock └── pubspec.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | embedder/target 3 | embedder/third_party/flutter_engine/debug/*.so 4 | embedder/third_party/flutter_engine/profile/*.so 5 | embedder/third_party/flutter_engine/release/*.so 6 | embedder/third_party/flutter_engine/.flutter_engine_revision 7 | .dart_tool/ 8 | .cargo/config.toml 9 | process.pid 10 | shell/.config 11 | !shell/.config/custom_devices.json 12 | .continuerc.json 13 | .temp 14 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "dart-code.flutter", 4 | "robert-brunhage.flutter-riverpod-snippets", 5 | "localizely.flutter-intl", 6 | "vadimcn.vscode-lldb", 7 | "rust-lang.rust-analyzer", 8 | "serayuzgur.crates" 9 | ] 10 | } -------------------------------------------------------------------------------- /.vscode/flutter.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Part statement": { 3 | "prefix": "pts", 4 | "body": [ 5 | "part '${TM_FILENAME_BASE}.g.dart';", 6 | ], 7 | "description": "Creates a filled-in part statement" 8 | }, 9 | "Part 'Freezed' statement": { 10 | "prefix": "ptf", 11 | "body": [ 12 | "part '${TM_FILENAME_BASE}.freezed.dart';", 13 | ], 14 | "description": "Creates a filled-in freezed part statement" 15 | }, 16 | "Freezed Data Class": { 17 | "prefix": "fdataclass", 18 | "body": [ 19 | "@freezed", 20 | "abstract class ${1:DataClass} with _$${1:DataClass}{", 21 | " const factory ${1:DataClass}(${2}) = _${1:DataClass};", 22 | "}" 23 | ], 24 | "description": "Freezed Data Class" 25 | }, 26 | "Freezed Union": { 27 | "prefix": "funion", 28 | "body": [ 29 | "@freezed", 30 | "abstract class ${1:Union} with _$${1:Union}{", 31 | " const factory ${1:Union}.${2}(${4}) = ${3};", 32 | "}" 33 | ], 34 | "description": "Freezed Union" 35 | }, 36 | "Freezed Union Case": { 37 | "prefix": "funioncase", 38 | "body": [ 39 | "const factory ${1:Union}.${2}(${4}) = ${3};" 40 | ], 41 | "description": "Freezed Union Case" 42 | }, 43 | "From JSON": { 44 | "prefix": "fromJson", 45 | "body": [ 46 | "factory ${1}.fromJson(Map json) => _$${1}FromJson(json);" 47 | ], 48 | "description": "From JSON" 49 | }, 50 | 51 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "editor.codeActionsOnSave": { 3 | "source.organizeImports": "always", 4 | "source.fixAll": "always" 5 | }, 6 | "editor.formatOnSave": true, 7 | "lldb.displayFormat": "auto", 8 | "lldb.showDisassembly": "auto", 9 | "lldb.dereferencePointers": true, 10 | "lldb.consoleMode": "evaluate", 11 | "files.exclude": { 12 | "**/*.freezed.dart": true, 13 | "**/*.g.dart": true 14 | } 15 | } -------------------------------------------------------------------------------- /GUIDELINES.md: -------------------------------------------------------------------------------- 1 | ### Naming convention 2 | 3 | The new naming convention is the following: 4 | 5 | **File naming convention** 6 | 7 | In order to speed-up the build process of the generated code by `build_runner` I specified those 3 extensions: 8 | 9 | - `.model.dart` for Freezed class models 10 | - `.provider.dart` for Riverpod related files 11 | - `.model.serializable.dart` for Freezed class with JSON serialization enabled (which need both generation) 12 | - Bonus `.widget.dart` for the flutter widget files for consistency with the others 13 | 14 | **Variable naming convention** 15 | 16 | For variable related to models always use **singular** and add verbose descriptor for collection of items. 17 | 18 | ```dart 19 | final widget = Widget(); 20 | final List widgetList = []; 21 | final Map widgetMap = {}; 22 | final widgetSet = {}; 23 | ``` 24 | 25 | Boolean variable should start by **is** 26 | 27 | ```dart 28 | final bool isReady = false; 29 | final bool isDrawn = false; 30 | final bool isMapped = isReady && isDrawn; 31 | final bool isParent = true; 32 | ``` 33 | 34 | Number and String should combine **subject** and **descriptor** 35 | 36 | ```dart 37 | final int widgetListLength = 12; 38 | final int taskCount = 5; 39 | final String menuTitle = 'Variables'; 40 | final String categoryDescription = 'The naming convention'; 41 | ``` 42 | 43 | ### Consistent Formating 44 | 45 | For pull-request reviews it's mandatory to have a consistent code formatting. 46 | 47 | That's why I enforce a systematic formatting on file saves. 48 | 49 | *For vscode* 50 | ```json 51 | { 52 | "editor.codeActionsOnSave": { 53 | "source.organizeImports": "always", 54 | "source.fixAll": "always" 55 | }, 56 | "editor.formatOnSave": true 57 | } 58 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # veshell-prototype 2 | 3 | This is the current Proof of concept of the [Veshell](https://github.com/free-explorers/veshell) project. 4 | 5 | It's a custom Flutter Embedder used to provide a Wayland compositor for linux. 6 | 7 | The project responsibility will be splitted into parts: 8 | - the front-end will be handled by the Flutter renderer and it will be responsible of displaying all the UI, organizing the Spatial Layout and render the applications windows. 9 | - the back-end which will be the Wayland Server will be made using Smithay in Rust and will be responsible to handle the relation between the renderer and the wayland windows. 10 | 11 | # Veshell CLI 12 | 13 | You can activate our internal CLI 14 | 15 | ```shell 16 | dart pub global activate --source path . 17 | ``` 18 | 19 | then get access to `veshell` command-line 20 | ```shell 21 | This CLI help install and develop Veshell 22 | 23 | Usage: veshell [arguments] 24 | 25 | Global options: 26 | -h, --help Print this usage information. 27 | --[no-]verbose Noisy logging, including all shell commands executed. 28 | -t, --target Specify the build target 29 | [debug (default), profile, release] 30 | 31 | Available commands: 32 | build Build and package Veshell 33 | clean Clean the project and restore it to a fresh state 34 | dev Start a build_runner watch and run flutter shell 35 | install Build and install Veshell localy 36 | run run veshell 37 | 38 | Run "veshell help " for more information about a command. 39 | ``` 40 | 41 | # Special thanks 42 | 43 | Special thanks to [**roscale**](https://github.com/roscale) for his work on [Zenith](https://github.com/roscale/zenith) and [Wayvern](https://github.com/roscale/wayvern) which are the foundation of this prototype 44 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:very_good_analysis/analysis_options.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /bin/commands/clean.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:args/command_runner.dart'; 5 | import 'package:mason_logger/mason_logger.dart'; 6 | 7 | class CleanCommand extends Command { 8 | CleanCommand({required this.logger}); 9 | final Logger logger; 10 | @override 11 | final name = 'clean'; 12 | @override 13 | final description = 'Clean the project and restore it to a fresh state'; 14 | 15 | @override 16 | Future run() async { 17 | final dirsToDelete = [ 18 | Directory('build'), 19 | Directory('shell/build'), 20 | Directory('embedder/target'), 21 | Directory('shell/.dart_tool'), 22 | ]; 23 | for (final dir in dirsToDelete) { 24 | if (dir.existsSync()) { 25 | dir.deleteSync(recursive: true); 26 | } 27 | } 28 | 29 | final dir = Directory('shell'); 30 | final files = dir.listSync(recursive: true); 31 | for (final file in files) { 32 | if (file is File && 33 | (file.path.endsWith('.g.dart') || 34 | file.path.endsWith('.freezed.dart'))) { 35 | file.deleteSync(); 36 | } 37 | } 38 | 39 | return ExitCode.success.code; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /bin/commands/dependency/check_depencies.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:mason_logger/mason_logger.dart'; 4 | 5 | import '../../util.dart'; 6 | import 'package_manager/apt.dart'; 7 | import 'package_manager/dnf.dart'; 8 | import 'package_manager/package_manager_base.dart'; 9 | import 'package_manager/pacman.dart'; 10 | 11 | const packageManagerSet = {Pacman(), Apt(), Dnf()}; 12 | 13 | Future check(Logger logger) async { 14 | logger.info('Checking dependencies...\n'); 15 | if (!await isCommandAvailable('cargo')) { 16 | logger 17 | ..info( 18 | 'cargo is not installed. Install it using https://rustup.rs installer', 19 | ) 20 | ..info( 21 | "curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh", 22 | style: commandStyle, 23 | ); 24 | } 25 | 26 | PackageManagerBase? packageManager; 27 | 28 | for (final manager in packageManagerSet) { 29 | if (packageManager != null) { 30 | break; 31 | } 32 | 33 | if (await manager.isInstalled() == 0) { 34 | packageManager = manager; 35 | } 36 | } 37 | 38 | if (packageManager == null) { 39 | logger.err( 40 | "No supported package manager found. Can't check dependencies.", 41 | ); 42 | exit(1); 43 | } 44 | 45 | final missingDependencyList = await packageManager.missingDependencyList(); 46 | 47 | if (missingDependencyList.isNotEmpty) { 48 | logger 49 | ..err( 50 | 'Missing dependencies: ${missingDependencyList.join(', ')}', 51 | ) 52 | ..info('You can install them with') 53 | ..info( 54 | packageManager.printInstallDependenciesCommand(missingDependencyList), 55 | style: commandStyle, 56 | ); 57 | exit(1); 58 | } 59 | 60 | logger.success('All dependencies are installed.\n'); 61 | } 62 | -------------------------------------------------------------------------------- /bin/commands/dependency/package_manager/apt.dart: -------------------------------------------------------------------------------- 1 | import '../../../util.dart'; 2 | import 'package_manager_base.dart'; 3 | 4 | class Apt extends PackageManagerBase { 5 | const Apt(); 6 | @override 7 | String get make => 'make'; 8 | 9 | @override 10 | String get cmake => 'cmake'; 11 | 12 | @override 13 | String get clang => 'clang'; 14 | 15 | @override 16 | String get ninja => 'ninja-build'; 17 | 18 | @override 19 | String get gtk3 => 'libgtk-3-dev'; 20 | 21 | @override 22 | String get seat => 'libseat-dev'; 23 | 24 | @override 25 | String get libinput => 'libinput-dev'; 26 | 27 | @override 28 | String get gbm => 'libgbm-dev'; 29 | 30 | @override 31 | String get openssl => 'openssl'; 32 | 33 | @override 34 | String printInstallDependenciesCommand(List dependencyList) { 35 | return 'sudo apt install -y ${dependencyList.join(" ")}'; 36 | } 37 | 38 | @override 39 | Future isPackageInstalled(String packageName) async { 40 | return runProcess('dpkg', ['-s', packageName], verbose: false); 41 | } 42 | 43 | @override 44 | Future isInstalled() async { 45 | return runProcess('which', ['apt'], verbose: false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bin/commands/dependency/package_manager/dnf.dart: -------------------------------------------------------------------------------- 1 | import '../../../util.dart'; 2 | import 'package_manager_base.dart'; 3 | 4 | class Dnf extends PackageManagerBase { 5 | const Dnf(); 6 | @override 7 | String get make => 'make'; 8 | 9 | @override 10 | String get cmake => 'cmake'; 11 | 12 | @override 13 | String get clang => 'clang'; 14 | 15 | @override 16 | String get ninja => 'ninja-build'; 17 | 18 | @override 19 | String get gtk3 => 'gtk3-devel'; 20 | 21 | @override 22 | String get seat => 'libseat-devel'; 23 | 24 | @override 25 | String get libinput => 'libinput-devel'; 26 | 27 | @override 28 | String get gbm => 'mesa-libgbm-devel'; 29 | 30 | @override 31 | String get openssl => 'openssl-devel'; 32 | 33 | @override 34 | String printInstallDependenciesCommand(List dependencyList) { 35 | return 'sudo dnf install -y ${dependencyList.join(" ")}'; 36 | } 37 | 38 | @override 39 | Future isPackageInstalled(String packageName) async { 40 | return runProcess( 41 | 'dnf', 42 | ['list', 'installed', packageName], 43 | verbose: false, 44 | ); 45 | } 46 | 47 | @override 48 | Future isInstalled() async { 49 | return runProcess('which', ['dnf'], verbose: false); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /bin/commands/dependency/package_manager/package_manager_base.dart: -------------------------------------------------------------------------------- 1 | abstract class PackageManagerBase { 2 | const PackageManagerBase(); 3 | String get make; 4 | String get cmake; 5 | String get clang; 6 | String get ninja; 7 | String get gtk3; 8 | String get seat; 9 | String get libinput; 10 | String get gbm; 11 | String get openssl; 12 | 13 | List get dependencyList { 14 | return [ 15 | cmake, 16 | clang, 17 | ninja, 18 | gtk3, 19 | seat, 20 | libinput, 21 | gbm, 22 | openssl, 23 | ]; 24 | } 25 | 26 | Future isInstalled(); 27 | Future isPackageInstalled(String packageName); 28 | String printInstallDependenciesCommand(List dependencyList); 29 | 30 | Future> missingDependencyList() async { 31 | final missingDependencies = []; 32 | for (final dependency in dependencyList) { 33 | if (await isPackageInstalled(dependency) != 0) { 34 | missingDependencies.add(dependency); 35 | } 36 | } 37 | return missingDependencies; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /bin/commands/dependency/package_manager/pacman.dart: -------------------------------------------------------------------------------- 1 | import '../../../util.dart'; 2 | import 'package_manager_base.dart'; 3 | 4 | class Pacman extends PackageManagerBase { 5 | const Pacman(); 6 | @override 7 | String get make => 'make'; 8 | 9 | @override 10 | String get cmake => 'cmake'; 11 | 12 | @override 13 | String get clang => 'clang'; 14 | 15 | @override 16 | String get ninja => 'ninja'; 17 | 18 | @override 19 | String get gtk3 => 'gtk3'; 20 | 21 | @override 22 | String get seat => 'seatd'; 23 | 24 | @override 25 | String get libinput => 'libinput'; 26 | 27 | @override 28 | String get gbm => 'mesa'; 29 | 30 | @override 31 | String get openssl => 'openssl'; 32 | 33 | @override 34 | String printInstallDependenciesCommand(List dependencyList) { 35 | return 'sudo pacman -S --needed ${dependencyList.join(" ")}'; 36 | } 37 | 38 | @override 39 | Future isPackageInstalled(String packageName) async { 40 | return runProcess('pacman', ['-Q', packageName], verbose: false); 41 | } 42 | 43 | @override 44 | Future isInstalled() async { 45 | return runProcess('which', ['pacman'], verbose: false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /bin/commands/dev.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:args/command_runner.dart'; 6 | import 'package:mason_logger/mason_logger.dart'; 7 | 8 | import '../util.dart'; 9 | 10 | class DevCommand extends Command { 11 | DevCommand({required this.logger}); 12 | final Logger logger; 13 | @override 14 | final name = 'dev'; 15 | @override 16 | final description = 'Start a build_runner watch and run flutter shell'; 17 | 18 | final subProcessList = []; 19 | 20 | @override 21 | Future run() async { 22 | await startBuildRunner(); 23 | await startFlutter(); 24 | return ExitCode.success.code; 25 | } 26 | 27 | Future startBuildRunner() async { 28 | final readyFuture = Completer(); 29 | final buildRunnerProcess = await Process.start( 30 | 'dart', 31 | ['run', 'build_runner', 'watch'], 32 | workingDirectory: 'shell', 33 | ); 34 | await buildRunnerProcess.stdin.close(); 35 | 36 | processSet.add(buildRunnerProcess); 37 | 38 | buildRunnerProcess.stdout.transform(utf8.decoder).listen((line) { 39 | logger.info(line); 40 | if (line.contains('Succeeded after') == true) { 41 | readyFuture.complete(true); 42 | } 43 | }); 44 | 45 | // If the process ends and readyFuture has not been completed, complete it with false 46 | unawaited( 47 | buildRunnerProcess.exitCode.then((_) { 48 | if (!readyFuture.isCompleted) { 49 | readyFuture.complete(false); 50 | } 51 | }), 52 | ); 53 | 54 | return readyFuture.future; 55 | } 56 | 57 | Future startFlutter() async { 58 | final flutterProcess = await Process.start( 59 | 'flutter', 60 | ['run', '-d', 'veshell'], 61 | workingDirectory: 'shell', 62 | environment: { 63 | 'XDG_CONFIG_HOME': '${Directory.current.path}/shell/.config', 64 | }, 65 | ); 66 | await flutterProcess.stdin.close(); 67 | 68 | processSet.add(flutterProcess); 69 | 70 | flutterProcess.stdout.transform(utf8.decoder).listen(logger.info); 71 | 72 | return flutterProcess.exitCode; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /bin/commands/run.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:args/command_runner.dart'; 5 | import 'package:mason_logger/mason_logger.dart'; 6 | 7 | import '../util.dart'; 8 | import '../veshell.dart'; 9 | import 'build.dart'; 10 | 11 | class RunCommand extends Command { 12 | RunCommand({required this.logger}); 13 | final Logger logger; 14 | @override 15 | final name = 'run'; 16 | @override 17 | final description = 'run veshell'; 18 | 19 | @override 20 | Future run() async { 21 | final target = 22 | BuildTarget.values.byName(globalResults?['target'] as String); 23 | 24 | final binaryPath = 'embedder/target/${target.name}/$targetExec'; 25 | final binary = File('${Directory.current.path}/$binaryPath'); 26 | 27 | if (!binary.existsSync()) { 28 | await runner!.run(['build', ...argResults!.arguments]); 29 | } 30 | 31 | Process process; 32 | if (Platform.environment['container'] != null) { 33 | process = await Process.start( 34 | 'flatpak-spawn', 35 | ['--host', binary.absolute.path], 36 | mode: ProcessStartMode.inheritStdio, 37 | workingDirectory: './embedder', 38 | ); 39 | } else { 40 | logger.alert('Running ${binary.absolute.path}'); 41 | process = await Process.start( 42 | binary.path, 43 | [], 44 | mode: ProcessStartMode.inheritStdio, 45 | workingDirectory: './embedder', 46 | ); 47 | } 48 | 49 | processSet.add(process); 50 | 51 | // Write the PID to a file 52 | final pidFile = File('process.pid'); 53 | await pidFile.writeAsString(process.pid.toString()); 54 | 55 | return process.exitCode; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /bin/util.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | import 'dart:io'; 3 | 4 | import 'package:mason_logger/mason_logger.dart'; 5 | 6 | String? importantStyle(String? m) => styleBold.wrap(blue.wrap(m)); 7 | 8 | String? commandStyle(String? m) => 9 | '${backgroundBlack.wrap("\n \n > ")}${backgroundBlack.wrap(importantStyle(m))}${backgroundBlack.wrap("\n")}'; 10 | 11 | final processSet = {}; 12 | 13 | Future runProcess( 14 | String executable, 15 | List arguments, { 16 | String? workingDirectory, 17 | Map? environment, 18 | bool includeParentEnvironment = true, 19 | bool runInShell = false, 20 | Encoding? stdoutEncoding = systemEncoding, 21 | Encoding? stderrEncoding = systemEncoding, 22 | bool verbose = true, 23 | }) async { 24 | if (verbose) { 25 | print("$executable ${arguments.join(" ")}\n"); 26 | } 27 | final process = await Process.start( 28 | executable, 29 | arguments, 30 | workingDirectory: workingDirectory, 31 | environment: environment, 32 | includeParentEnvironment: includeParentEnvironment, 33 | runInShell: runInShell, 34 | ); 35 | 36 | processSet.add(process); 37 | process.stdout.transform(utf8.decoder).listen((data) { 38 | if (verbose) { 39 | print(data); 40 | } 41 | }); 42 | 43 | process.stderr.transform(utf8.decoder).listen((data) { 44 | if (verbose) { 45 | print(data); 46 | } 47 | }); 48 | 49 | await process.stdin.close(); 50 | processSet.remove(process); 51 | final exitCode = await process.exitCode; 52 | 53 | return exitCode; 54 | } 55 | 56 | Future isCommandAvailable(String command) async { 57 | final result = await Process.run('which', [command]); 58 | return result.exitCode == 0; 59 | } 60 | -------------------------------------------------------------------------------- /dependencies.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
dependenciesArchFedoraDebian
rusthttps://rustup.rs/
make
cmake
clang
ninja-buildninjaninja-buildninja-build
gtk3gtk3gtk3-develgtk3-dev
udevbase-devellibudev-devellibudev-dev
seatseatdlibseat-devellibseat-dev
libinputlibinputlibinput-devellibinput-dev
gbmlibgbmlibgm-devellibgbm-dev
opensslopensslopenssl-developenssl-dev
69 | -------------------------------------------------------------------------------- /embedder/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "veshell" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [workspace] 7 | members = ["third_party/smithay-drm-extras"] 8 | 9 | [dependencies] 10 | smithay = { git = "https://github.com/Smithay/smithay", rev = "4171247", features = [ 11 | "default", 12 | "wayland_frontend", 13 | "backend_egl", 14 | "use_system_lib", 15 | "xwayland", 16 | ] } 17 | smithay-drm-extras = { path = "third_party/smithay-drm-extras" } 18 | rustix = "0.38.30" 19 | input-linux = "0.6.0" 20 | tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } 21 | log = "0.4.20" 22 | tracing = { version = "0.1.37", features = [] } 23 | profiling = "1.0.11" 24 | thiserror = "1.0.49" 25 | xcursor = "0.3.4" 26 | serde = { version = "1.0.189", features = ["derive"] } 27 | serde_json = "1.0.107" 28 | lazy_static = { version = "1.4.0", features = [] } 29 | rlimit = "0.10.1" 30 | 31 | [build-dependencies] 32 | bindgen = "0.69.1" 33 | bytes = "1.5.0" 34 | reqwest = { version = "0.11.22", features = ["blocking"] } 35 | zip = "0.6.6" 36 | lazy_static = { version = "1.4.0", features = [] } 37 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 38 | -------------------------------------------------------------------------------- /embedder/resources/cursor.rgba: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/free-explorers/veshell-prototype/9b7cdd7f0c057bf51ea04138c1dd670488ffc9c9/embedder/resources/cursor.rgba -------------------------------------------------------------------------------- /embedder/src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | use smithay::backend::session::libseat::LibSeatSession; 2 | 3 | pub mod drm_backend; 4 | pub mod x11_client; 5 | 6 | pub trait Backend { 7 | const HAS_RELATIVE_MOTION: bool = false; 8 | 9 | fn seat_name(&self) -> String; 10 | 11 | fn get_session(&self) -> LibSeatSession; 12 | } 13 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/embedder.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![allow(non_upper_case_globals)] 3 | #![allow(non_camel_case_types)] 4 | #![allow(non_snake_case)] 5 | 6 | include!(concat!(env!("OUT_DIR"), "/embedder.rs")); 7 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels.rs: -------------------------------------------------------------------------------- 1 | pub mod basic_message_channel; 2 | pub mod binary_messenger; 3 | pub mod binary_messenger_impl; 4 | pub mod byte_buffer_streams; 5 | pub mod byte_streams; 6 | pub mod encodable_value; 7 | pub mod engine_method_result; 8 | pub mod json_message_codec; 9 | pub mod json_method_codec; 10 | pub mod message_codec; 11 | pub mod method_call; 12 | pub mod method_channel; 13 | pub mod method_codec; 14 | pub mod method_result; 15 | pub mod method_result_functions; 16 | pub mod method_result_mpsc_channel; 17 | pub mod standard_codec_serializer; 18 | pub mod standard_message_codec; 19 | pub mod standard_method_codec; 20 | pub mod text_input_model; 21 | pub mod text_range; 22 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/README.md: -------------------------------------------------------------------------------- 1 | All these files were translated from the Flutter Engine repository. 2 | Documentation for all this code is also available there. 3 | 4 | https://github.com/flutter/engine/tree/90be25d8aac3d339663e2f9f860939648d9a0cc8/shell/platform/common 5 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/binary_messenger.rs: -------------------------------------------------------------------------------- 1 | use crate::flutter_engine::embedder::FlutterPlatformMessage; 2 | 3 | pub type BinaryReply = Option)>>; 4 | 5 | pub type BinaryMessageHandler = Option>; 6 | 7 | pub trait BinaryMessenger { 8 | fn handle_message(&mut self, message: &FlutterPlatformMessage); 9 | fn send(&mut self, channel: &str, message: &[u8], reply: BinaryReply); 10 | fn set_message_handler(&mut self, channel: &str, handler: BinaryMessageHandler); 11 | } 12 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/byte_streams.rs: -------------------------------------------------------------------------------- 1 | pub trait ByteStreamReader { 2 | fn read_byte(&mut self) -> u8; 3 | fn read_bytes(&mut self, buffer: &mut [u8], count: usize); 4 | fn read_alignment(&mut self, alignment: u8); 5 | 6 | fn read_i32(&mut self) -> i32 { 7 | let mut buffer = [0u8; 4]; 8 | self.read_bytes(buffer.as_mut(), 4); 9 | i32::from_ne_bytes(buffer) 10 | } 11 | 12 | fn read_i64(&mut self) -> i64 { 13 | let mut buffer = [0u8; 8]; 14 | self.read_bytes(buffer.as_mut(), 8); 15 | i64::from_ne_bytes(buffer) 16 | } 17 | 18 | fn read_double(&mut self) -> f64 { 19 | let mut buffer = [0u8; 8]; 20 | self.read_bytes(buffer.as_mut(), 8); 21 | f64::from_ne_bytes(buffer) 22 | } 23 | } 24 | 25 | pub trait ByteStreamWriter { 26 | fn write_byte(&mut self, value: u8); 27 | fn write_bytes(&mut self, buffer: &[u8], count: usize); 28 | fn write_alignment(&mut self, alignment: u8); 29 | 30 | fn write_i32(&mut self, value: i32) { 31 | self.write_bytes(&value.to_ne_bytes(), 4); 32 | } 33 | 34 | fn write_i64(&mut self, value: i64) { 35 | self.write_bytes(&value.to_ne_bytes(), 8); 36 | } 37 | 38 | fn write_double(&mut self, value: f64) { 39 | self.write_bytes(&value.to_ne_bytes(), 8); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/encodable_value.rs: -------------------------------------------------------------------------------- 1 | #[repr(u8)] 2 | #[derive(Debug)] 3 | pub enum EncodableValue { 4 | Null = 0, 5 | Bool(bool), 6 | Int32(i32), 7 | Int64(i64), 8 | Double(f64), 9 | String(String), 10 | ByteList(Vec), 11 | Int32List(Vec), 12 | Int64List(Vec), 13 | DoubleList(Vec), 14 | List(Vec), 15 | Map(Vec<(EncodableValue, EncodableValue)>), 16 | Custom(CustomEncodableValue), 17 | FloatList(Vec), 18 | } 19 | 20 | impl EncodableValue { 21 | pub fn is_null(&self) -> bool { 22 | matches!(self, EncodableValue::Null) 23 | } 24 | 25 | pub fn long_value(&self) -> Option { 26 | match self { 27 | EncodableValue::Int32(v) => Some(*v as i64), 28 | EncodableValue::Int64(v) => Some(*v), 29 | _ => None, 30 | } 31 | } 32 | } 33 | 34 | // TODO: It should be generic over T, but the codec doesn't support custom encodable values, 35 | // so I don't think it's worth implementing this because it's more work than it's worth. 36 | #[derive(Eq, PartialEq, Debug)] 37 | pub struct CustomEncodableValue {} 38 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/json_message_codec.rs: -------------------------------------------------------------------------------- 1 | use crate::flutter_engine::platform_channels::message_codec::MessageCodec; 2 | 3 | #[derive(Default)] 4 | pub struct JsonMessageCodec {} 5 | 6 | impl JsonMessageCodec { 7 | pub fn new() -> Self { 8 | Default::default() 9 | } 10 | } 11 | 12 | impl MessageCodec for JsonMessageCodec { 13 | fn decode_message_internal(&self, message: &[u8]) -> Option { 14 | serde_json::from_slice(message).ok() 15 | } 16 | 17 | fn encode_message_internal(&self, message: &serde_json::Value) -> Vec { 18 | serde_json::to_vec(message).unwrap() 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/message_codec.rs: -------------------------------------------------------------------------------- 1 | pub trait MessageCodec { 2 | fn decode_message(&self, message: &[u8]) -> Option { 3 | self.decode_message_internal(message) 4 | } 5 | 6 | fn decode_message_vec(&self, message: Vec) -> Option { 7 | self.decode_message_internal(&message) 8 | } 9 | fn encode_message(&self, message: &T) -> Vec { 10 | self.encode_message_internal(message) 11 | } 12 | fn decode_message_internal(&self, message: &[u8]) -> Option; 13 | fn encode_message_internal(&self, message: &T) -> Vec; 14 | } 15 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/method_call.rs: -------------------------------------------------------------------------------- 1 | use crate::flutter_engine::platform_channels::encodable_value::EncodableValue; 2 | 3 | #[derive(Debug)] 4 | pub struct MethodCall { 5 | method: String, 6 | arguments: Option>, 7 | } 8 | 9 | impl MethodCall { 10 | pub(crate) fn new(method: String, arguments: Option>) -> Self { 11 | Self { method, arguments } 12 | } 13 | 14 | pub fn method(&self) -> &str { 15 | &self.method 16 | } 17 | 18 | pub fn arguments(&self) -> Option<&T> { 19 | self.arguments.as_deref() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/method_codec.rs: -------------------------------------------------------------------------------- 1 | use crate::flutter_engine::platform_channels::method_call::MethodCall; 2 | use crate::flutter_engine::platform_channels::method_result::MethodResult; 3 | 4 | pub trait MethodCodec { 5 | fn decode_method_call(&self, message: &[u8]) -> Option> { 6 | self.decode_method_call_internal(message) 7 | } 8 | 9 | fn decode_method_call_vec(&self, message: Vec) -> Option> { 10 | self.decode_method_call_internal(&message) 11 | } 12 | 13 | fn encode_method_call(&self, method_call: &MethodCall) -> Vec { 14 | self.encode_method_call_internal(method_call) 15 | } 16 | 17 | fn encode_success_envelope(&self, result: Option<&T>) -> Vec { 18 | self.encode_success_envelope_internal(result) 19 | } 20 | 21 | fn encode_error_envelope(&self, code: &str, message: &str, details: Option<&T>) -> Vec { 22 | self.encode_error_envelope_internal(code, message, details) 23 | } 24 | 25 | fn decode_and_process_response_envelope( 26 | &self, 27 | response: &[u8], 28 | result: &mut dyn MethodResult, 29 | ) -> bool { 30 | self.decode_and_process_response_envelope_internal(response, result) 31 | } 32 | 33 | fn decode_method_call_internal(&self, message: &[u8]) -> Option>; 34 | 35 | fn encode_method_call_internal(&self, method_call: &MethodCall) -> Vec; 36 | 37 | fn encode_success_envelope_internal(&self, result: Option<&T>) -> Vec; 38 | 39 | fn encode_error_envelope_internal( 40 | &self, 41 | code: &str, 42 | message: &str, 43 | details: Option<&T>, 44 | ) -> Vec; 45 | 46 | fn decode_and_process_response_envelope_internal( 47 | &self, 48 | response: &[u8], 49 | result: &mut dyn MethodResult, 50 | ) -> bool; 51 | } 52 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/method_result.rs: -------------------------------------------------------------------------------- 1 | use crate::flutter_engine::platform_channels::encodable_value::EncodableValue; 2 | 3 | pub trait MethodResult { 4 | fn success(&mut self, result: Option) { 5 | self.success_internal(result); 6 | } 7 | 8 | fn error(&mut self, code: String, message: String, details: Option) { 9 | self.error_internal(code, message, details); 10 | } 11 | 12 | fn not_implemented(&mut self) { 13 | self.not_implemented_internal(); 14 | } 15 | 16 | fn success_internal(&mut self, result: Option); 17 | 18 | fn error_internal(&mut self, code: String, message: String, details: Option); 19 | 20 | fn not_implemented_internal(&mut self); 21 | } 22 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/method_result_functions.rs: -------------------------------------------------------------------------------- 1 | use crate::flutter_engine::platform_channels::encodable_value::EncodableValue; 2 | use crate::flutter_engine::platform_channels::method_result::MethodResult; 3 | 4 | type ResultHandlerSuccess = Option)>>; 5 | 6 | type ResultHandlerError = Option)>>; 7 | 8 | type ResultHandlerNotImplemented = Option>; 9 | 10 | pub struct MethodResultFunctions { 11 | on_success: ResultHandlerSuccess, 12 | on_error: ResultHandlerError, 13 | on_not_implemented: ResultHandlerNotImplemented, 14 | } 15 | 16 | impl MethodResultFunctions { 17 | pub fn new( 18 | on_success: ResultHandlerSuccess, 19 | on_error: ResultHandlerError, 20 | on_not_implemented: ResultHandlerNotImplemented, 21 | ) -> Self { 22 | Self { 23 | on_success, 24 | on_error, 25 | on_not_implemented, 26 | } 27 | } 28 | } 29 | 30 | impl MethodResult for MethodResultFunctions { 31 | fn success_internal(&mut self, result: Option) { 32 | if let Some(on_success) = self.on_success.as_mut() { 33 | on_success(result.as_ref()); 34 | } 35 | } 36 | 37 | fn error_internal(&mut self, code: String, message: String, details: Option) { 38 | if let Some(on_error) = self.on_error.as_mut() { 39 | on_error(code.as_str(), message.as_str(), details.as_ref()); 40 | } 41 | } 42 | 43 | fn not_implemented_internal(&mut self) { 44 | if let Some(on_not_implemented) = self.on_not_implemented.as_mut() { 45 | on_not_implemented(); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/method_result_mpsc_channel.rs: -------------------------------------------------------------------------------- 1 | //! This file was not translated from C++ from the Flutter engine repo. 2 | //! It's an alternative for MethodResultFunctions that adds the result into an mpsc channel 3 | //! instead of storing closures. 4 | //! This way, it can be used with the Calloop event loop and access external state without having 5 | //! to capture the state inside closures. 6 | 7 | use smithay::reexports::calloop::channel; 8 | 9 | use crate::flutter_engine::platform_channels::encodable_value::EncodableValue; 10 | use crate::flutter_engine::platform_channels::method_result::MethodResult; 11 | 12 | pub enum MethodResultEnum { 13 | Success(Option), 14 | Error { 15 | code: String, 16 | message: String, 17 | details: Option, 18 | }, 19 | NotImplemented, 20 | } 21 | 22 | pub struct MethodResultMpscChannel { 23 | tx_channel: channel::Sender>, 24 | } 25 | 26 | impl MethodResultMpscChannel { 27 | pub fn new(tx_channel: channel::Sender>) -> Self { 28 | Self { tx_channel } 29 | } 30 | } 31 | 32 | impl MethodResult for MethodResultMpscChannel { 33 | fn success_internal(&mut self, result: Option) { 34 | let _ = self.tx_channel.send(MethodResultEnum::Success(result)); 35 | } 36 | 37 | fn error_internal(&mut self, code: String, message: String, details: Option) { 38 | let _ = self.tx_channel.send(MethodResultEnum::Error { 39 | code, 40 | message, 41 | details, 42 | }); 43 | } 44 | 45 | fn not_implemented_internal(&mut self) { 46 | let _ = self.tx_channel.send(MethodResultEnum::NotImplemented); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/standard_message_codec.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use crate::flutter_engine::platform_channels::byte_buffer_streams::{ 4 | ByteBufferStreamReader, ByteBufferStreamWriter, 5 | }; 6 | use crate::flutter_engine::platform_channels::encodable_value::EncodableValue; 7 | use crate::flutter_engine::platform_channels::message_codec::MessageCodec; 8 | use crate::flutter_engine::platform_channels::standard_codec_serializer::StandardCodecSerializer; 9 | 10 | #[derive(Default)] 11 | pub struct StandardMessageCodec { 12 | serializer: Rc, 13 | } 14 | 15 | impl StandardMessageCodec { 16 | pub fn new() -> Self { 17 | Default::default() 18 | } 19 | } 20 | 21 | impl MessageCodec for StandardMessageCodec { 22 | fn decode_message_internal(&self, message: &[u8]) -> Option { 23 | if message.is_empty() { 24 | return Some(EncodableValue::Null); 25 | } 26 | let mut stream = ByteBufferStreamReader::new(message); 27 | let value = self.serializer.read_value(&mut stream); 28 | Some(value) 29 | } 30 | 31 | fn encode_message_internal(&self, message: &EncodableValue) -> Vec { 32 | let mut buffer = Vec::new(); 33 | let mut stream = ByteBufferStreamWriter::new(&mut buffer); 34 | self.serializer.write_value(message, &mut stream); 35 | buffer 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /embedder/src/flutter_engine/platform_channels/text_range.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use tracing::error; 4 | 5 | #[derive(Copy, Clone, Eq, PartialEq, Default)] 6 | pub struct TextRange { 7 | base: usize, 8 | extent: usize, 9 | } 10 | 11 | impl TextRange { 12 | pub fn new(base: usize, extent: usize) -> Self { 13 | Self { base, extent } 14 | } 15 | 16 | pub fn new_position(position: usize) -> Self { 17 | Self { 18 | base: position, 19 | extent: position, 20 | } 21 | } 22 | 23 | pub fn base(&self) -> usize { 24 | self.base 25 | } 26 | 27 | pub fn set_base(&mut self, base: usize) { 28 | self.base = base; 29 | } 30 | 31 | pub fn extent(&self) -> usize { 32 | self.extent 33 | } 34 | 35 | pub fn set_extent(&mut self, extent: usize) { 36 | self.extent = extent; 37 | } 38 | 39 | pub fn start(&self) -> usize { 40 | self.base.min(self.extent) 41 | } 42 | 43 | pub fn set_start(&mut self, start: usize) { 44 | if self.base <= self.extent { 45 | self.base = start; 46 | } else { 47 | self.extent = start; 48 | } 49 | } 50 | 51 | pub fn end(&self) -> usize { 52 | self.base.max(self.extent) 53 | } 54 | 55 | pub fn set_end(&mut self, end: usize) { 56 | if self.base <= self.extent { 57 | self.extent = end; 58 | } else { 59 | self.base = end; 60 | } 61 | } 62 | 63 | pub fn position(&self) -> usize { 64 | if self.base != self.extent { 65 | error!("base != extent"); 66 | } 67 | self.extent 68 | } 69 | 70 | pub fn length(&self) -> usize { 71 | self.end() - self.start() 72 | } 73 | 74 | pub fn collapsed(&self) -> bool { 75 | self.base == self.extent 76 | } 77 | 78 | pub fn reversed(&self) -> bool { 79 | self.base > self.extent 80 | } 81 | 82 | pub fn contains_position(&self, position: usize) -> bool { 83 | position >= self.start() && position <= self.end() 84 | } 85 | 86 | pub fn contains_range(&self, other: &TextRange) -> bool { 87 | other.start() >= self.start() && other.end() <= self.end() 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /embedder/src/keyboard.rs: -------------------------------------------------------------------------------- 1 | use smithay::backend::input::KeyState; 2 | use smithay::input::keyboard::ModifiersState; 3 | 4 | pub mod glfw_key_codes; 5 | pub mod key_repeater; 6 | 7 | #[derive(Copy, Clone)] 8 | pub struct KeyEvent { 9 | pub key_code: u32, 10 | pub specifiedLogicalKey: Option, 11 | pub codepoint: Option, 12 | pub state: KeyState, 13 | pub time: u32, 14 | pub mods: ModifiersState, 15 | pub mods_changed: bool, 16 | } 17 | -------------------------------------------------------------------------------- /embedder/src/texture_swap_chain.rs: -------------------------------------------------------------------------------- 1 | use smithay::backend::renderer::gles::GlesTexture; 2 | 3 | #[derive(Default, Debug)] 4 | pub struct TextureSwapChain { 5 | pub newest: Option, 6 | in_use: Option, 7 | } 8 | 9 | impl TextureSwapChain { 10 | pub fn new() -> Self { 11 | Default::default() 12 | } 13 | 14 | pub fn commit(&mut self, texture: GlesTexture) { 15 | self.newest = Some(texture); 16 | } 17 | 18 | pub fn start_read(&mut self) -> GlesTexture { 19 | self.in_use = self.newest.clone(); 20 | self.in_use.clone().unwrap() 21 | } 22 | 23 | pub fn end_read(&mut self) { 24 | self.in_use = None; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /embedder/third_party/smithay-drm-extras/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "smithay-drm-extras" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | edid-rs = "0.1.0" 8 | drm = { version = "0.12.0" } 9 | 10 | [features] 11 | default = [] 12 | generate-hwdata = ["pkg-config"] 13 | 14 | #[dev-dependencies.smithay] 15 | #path = "../" 16 | 17 | [build-dependencies] 18 | pkg-config = { version = "0.3.26", optional = true } 19 | -------------------------------------------------------------------------------- /embedder/third_party/smithay-drm-extras/README.md: -------------------------------------------------------------------------------- 1 | # Smithay DRM Extras 2 | 3 | This crate contains some extra abstractions and helpers over DRM 4 | 5 | - `edid` module is responsible for extraction of information from DRM connectors (`model` and `manufacturer`) 6 | - `drm_scanner` module contains helpers for detecting connector connected and disconnected events as well as mapping 7 | crtc to them. 8 | - `ConnectorScanner` is responsible for tracking connected/disconnected events. 9 | - `CrtcMapper` trait and `SimpleCrtcMapper` are meant for mapping crtc to connector. 10 | - `DrmScanner` combines two above into single abstraction. If it does not fit your needs you can always 11 | drop down to using `ConnectoScanner` alone. -------------------------------------------------------------------------------- /embedder/third_party/smithay-drm-extras/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | #[cfg(feature = "generate-hwdata")] 3 | generate_hwdata(); 4 | } 5 | 6 | #[cfg(feature = "generate-hwdata")] 7 | fn generate_hwdata() { 8 | use std::{ 9 | fs, 10 | path::{Path, PathBuf}, 11 | }; 12 | 13 | let pkg_path = pkg_config::get_variable("hwdata", "pkgdatadir"); 14 | // Old versions of hwdata don't have .pc file, so let's guess 15 | let pkg_path = pkg_path.as_deref().unwrap_or("/usr/share/hwdata"); 16 | 17 | let pnp_ids_path = PathBuf::from(pkg_path).join("pnp.ids"); 18 | 19 | println!( 20 | "cargo:rerun-if-changed={}", 21 | pnp_ids_path.as_os_str().to_string_lossy() 22 | ); 23 | 24 | if let Ok(file) = fs::read_to_string(pnp_ids_path) { 25 | let out_dir = PathBuf::from("../../src").join("hwdata").join("generated"); 26 | let dest_path = Path::new(&out_dir).join("pnp_ids.rs"); 27 | 28 | let i1 = " ".repeat(4); 29 | let i2 = " ".repeat(8); 30 | 31 | let mut output = Vec::new(); 32 | 33 | output.push("#[rustfmt::skip]".into()); 34 | output.push("pub fn pnp_id_to_name(vendor: &[char; 3]) -> Option<&'static str> {".into()); 35 | output.push(i1.clone() + "match vendor {"); 36 | 37 | for line in file.lines() { 38 | let mut segment = line.split('\t'); 39 | 40 | let mut code = segment.next().unwrap().chars(); 41 | let n1 = code.next().unwrap(); 42 | let n2 = code.next().unwrap(); 43 | let n3 = code.next().unwrap(); 44 | 45 | let name = segment.next().unwrap(); 46 | 47 | output.push(format!("{i2}['{n1}', '{n2}', '{n3}'] => Some(\"{name}\"),")); 48 | } 49 | 50 | output.push(i2 + "_ => None,"); 51 | 52 | output.push(i1 + "}"); 53 | output.push("}".into()); 54 | 55 | let output = output.join("\n"); 56 | 57 | fs::write(dest_path, output).unwrap(); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /embedder/third_party/smithay-drm-extras/src/docs/doctest_helpers.rs: -------------------------------------------------------------------------------- 1 | pub struct FakeDevice; 2 | 3 | impl std::os::unix::prelude::AsFd for FakeDevice { 4 | fn as_fd(&self) -> std::os::unix::prelude::BorrowedFd<'_> { 5 | unimplemented!() 6 | } 7 | } 8 | 9 | impl drm::Device for FakeDevice {} 10 | 11 | impl drm::control::Device for FakeDevice {} 12 | -------------------------------------------------------------------------------- /embedder/third_party/smithay-drm-extras/src/hwdata/mod.rs: -------------------------------------------------------------------------------- 1 | mod generated { 2 | include!("generated/pnp_ids.rs"); 3 | } 4 | 5 | pub fn pnp_id_to_name(vendor: &[char; 3]) -> Option<&'static str> { 6 | generated::pnp_id_to_name(vendor) 7 | } 8 | -------------------------------------------------------------------------------- /embedder/third_party/smithay-drm-extras/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Smithay DRM Extras 2 | //! 3 | //! This crate contains some extra abstractions and helpers over DRM 4 | //! 5 | //! - [`edid`] is responsible for extraction of information from DRM connectors 6 | //! - [`drm_scanner`] is responsible for detecting connector connected and 7 | //! disconnected events, as well as mapping CRTC to them. 8 | //! 9 | //! ### Features 10 | //! - `generate-hwdata` - If enabled [hwdata](https://github.com/vcrhonek/hwdata) code will be regenerated using `hwdata` system package 11 | 12 | #![warn(missing_docs, missing_debug_implementations)] 13 | 14 | pub mod drm_scanner; 15 | pub mod edid; 16 | mod hwdata; 17 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: veshell 2 | description: A command-line application that help dev veshell . 3 | version: 1.0.0 4 | repository: https://github.com/free-explorers/veshell 5 | 6 | executables: 7 | veshell: veshell 8 | 9 | environment: 10 | sdk: ^3.2.0 11 | 12 | # Add regular dependencies here. 13 | dependencies: 14 | ansicolor: ^2.0.2 15 | args: ^2.5.0 16 | cli_completion: ^0.4.0 17 | mason_logger: ^0.2.10 18 | path: ^1.8.3 19 | 20 | dev_dependencies: 21 | lints: ^3.0.0 22 | test: ^1.24.0 23 | very_good_analysis: ^5.1.0 24 | -------------------------------------------------------------------------------- /shell/.config/custom_devices.json: -------------------------------------------------------------------------------- 1 | { 2 | "custom-devices": [ 3 | { 4 | "id": "veshell", 5 | "label": "Veshell", 6 | "sdkNameAndVersion": "Veshell", 7 | "platform": "linux-x64", 8 | "enabled": true, 9 | "ping": [ 10 | "ping", 11 | "-c", 12 | "1", 13 | "-w", 14 | "1", 15 | "localhost" 16 | ], 17 | "pingSuccessRegex": null, 18 | "postBuild": null, 19 | "install": [ 20 | "pwd" 21 | ], 22 | "uninstall": [ 23 | "pwd" 24 | ], 25 | "runDebug": [ 26 | "dart", 27 | "run", 28 | "../bin/veshell.dart", 29 | "run" 30 | ], 31 | "forwardPort": null, 32 | "forwardPortSuccessRegex": null, 33 | "screenshot": null 34 | } 35 | ], 36 | "$schema": "file:///home/papyelgringo/workspace/flutter/packages/flutter_tools/static/custom-devices.schema.json" 37 | } -------------------------------------------------------------------------------- /shell/.config/settings: -------------------------------------------------------------------------------- 1 | { 2 | "enable-custom-devices": true 3 | } 4 | -------------------------------------------------------------------------------- /shell/.config/tool_state: -------------------------------------------------------------------------------- 1 | { 2 | "is-bot": false, 3 | "license-hash": "595d1dceffbb08773627ff5766d17ddf" 4 | } 5 | -------------------------------------------------------------------------------- /shell/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | /deps/ 48 | 49 | # Generated files 50 | **/**.freezed.dart 51 | **/**.g.dart -------------------------------------------------------------------------------- /shell/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:very_good_analysis/analysis_options.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | 31 | analyzer: 32 | exclude: 33 | - "**/*.g.dart" 34 | - "**/*.freezed.dart" 35 | plugins: 36 | - custom_lint -------------------------------------------------------------------------------- /shell/build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | builders: 4 | source_gen|combining_builder: 5 | generate_for: 6 | - lib/**/provider/**.dart 7 | - lib/**/model/**.serializable.dart 8 | 9 | freezed|freezed: 10 | enabled: true 11 | generate_for: 12 | - lib/**/model/**.dart 13 | 14 | json_serializable: 15 | options: 16 | any_map: true 17 | explicit_to_json: true 18 | -------------------------------------------------------------------------------- /shell/lib/application/model/launch_config.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'launch_config.serializable.freezed.dart'; 5 | part 'launch_config.serializable.g.dart'; 6 | 7 | @freezed 8 | abstract class LaunchConfig with _$LaunchConfig { 9 | const factory LaunchConfig({ 10 | required String command, 11 | @Default([]) List arguments, 12 | @Default(false) bool useDedicatedGpu, 13 | }) = _LaunchConfig; 14 | 15 | factory LaunchConfig.fromJson(Map json) => 16 | _$LaunchConfigFromJson(json); 17 | 18 | factory LaunchConfig.fromDesktopEntry(DesktopEntry desktopEntry) { 19 | var command = 20 | desktopEntry.entries[DesktopEntryKey.exec.string]?.value ?? ''; 21 | command = command.replaceAll(RegExp('( %.?)'), ''); 22 | return LaunchConfig( 23 | command: command, 24 | arguments: [], 25 | ); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /shell/lib/application/provider/app_drawer.dart: -------------------------------------------------------------------------------- 1 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/application/provider/app_drawer_desktop_entries.dart'; 4 | 5 | part 'app_drawer.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | Future> appDrawerFilteredDesktopEntries( 9 | AppDrawerFilteredDesktopEntriesRef ref, 10 | String filter, 11 | ) async { 12 | final desktopEntries = 13 | await ref.watch(appDrawerDesktopEntriesProvider.future); 14 | 15 | final filtered = []; 16 | final desktopEntriesSet = Set.from(desktopEntries); 17 | 18 | int byNames(LocalizedDesktopEntry a, LocalizedDesktopEntry b) { 19 | final aName = a.entries[DesktopEntryKey.name.string]!; 20 | final bName = b.entries[DesktopEntryKey.name.string]!; 21 | return aName.toLowerCase().compareTo(bName.toLowerCase()); 22 | } 23 | 24 | final nameMatched = desktopEntriesSet.where((d) { 25 | final name = d.entries[DesktopEntryKey.name.string]!.toLowerCase(); 26 | return name.startsWith(filter); 27 | }).toList() 28 | ..sort(byNames); 29 | 30 | filtered.addAll(nameMatched); 31 | desktopEntriesSet.removeAll(nameMatched); 32 | 33 | final namePartsMatched = desktopEntriesSet.where((d) { 34 | final name = d.entries[DesktopEntryKey.name.string]!.toLowerCase(); 35 | final nameParts = name.split(' '); 36 | return nameParts.any((part) => part.startsWith(filter)); 37 | }).toList() 38 | ..sort(byNames); 39 | 40 | filtered.addAll(namePartsMatched); 41 | desktopEntriesSet.removeAll(namePartsMatched); 42 | 43 | final keywordsMatched = desktopEntriesSet.where((d) { 44 | final keywords = 45 | d.entries[DesktopEntryKey.keywords.string]?.getStringList(); 46 | if (keywords == null) { 47 | return false; 48 | } 49 | for (final kw in keywords) { 50 | kw.toLowerCase(); 51 | } 52 | return keywords.any((kw) => kw.startsWith(filter)); 53 | }).toList() 54 | ..sort(byNames); 55 | 56 | filtered.addAll(keywordsMatched); 57 | 58 | return filtered; 59 | } 60 | -------------------------------------------------------------------------------- /shell/lib/application/provider/app_drawer_desktop_entries.dart: -------------------------------------------------------------------------------- 1 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/application/provider/localized_desktop_entries.dart'; 4 | 5 | part 'app_drawer_desktop_entries.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | Future> appDrawerDesktopEntries( 9 | AppDrawerDesktopEntriesRef ref, 10 | ) async { 11 | final localizedDesktopEntries = 12 | await ref.watch(localizedDesktopEntriesProvider.future); 13 | return localizedDesktopEntries.values 14 | .where((element) => !element.desktopEntry.isHidden()); 15 | } 16 | -------------------------------------------------------------------------------- /shell/lib/application/provider/app_launch.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:shell/application/model/launch_config.serializable.dart'; 5 | import 'package:shell/wayland/provider/environment_variables.dart'; 6 | 7 | part 'app_launch.g.dart'; 8 | 9 | @Riverpod(keepAlive: true) 10 | class AppLaunch extends _$AppLaunch { 11 | @override 12 | void build() {} 13 | 14 | Future launchApplication(LaunchConfig config) async { 15 | final environment = ref.read(environmentVariablesProvider); 16 | print(config); 17 | final process = await Process.start( 18 | '/bin/sh', 19 | ['-c', config.command], 20 | environment: environment.unlockLazy, 21 | mode: ProcessStartMode.detachedWithStdio, 22 | ); 23 | return process; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /shell/lib/application/provider/desktop_entries.dart: -------------------------------------------------------------------------------- 1 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'desktop_entries.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | Future> installedDesktopEntries( 8 | InstalledDesktopEntriesRef ref, 9 | ) async { 10 | final entries = await parseAllInstalledDesktopFiles(); 11 | return entries; 12 | } 13 | -------------------------------------------------------------------------------- /shell/lib/application/provider/file_to_scalable_image.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:jovial_svg/jovial_svg.dart'; 4 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 5 | 6 | part 'file_to_scalable_image.g.dart'; 7 | 8 | @Riverpod(keepAlive: true) 9 | class FileToScalableImage extends _$FileToScalableImage { 10 | @override 11 | Future build( 12 | String path, 13 | ) async { 14 | final svg = await File(path).readAsString(); 15 | return ScalableImage.fromSvgString(svg); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /shell/lib/application/provider/icon_themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'icon_themes.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | Future iconThemes(IconThemesRef ref) async { 8 | return FreedesktopIconTheme.loadTheme(theme: 'Adwaita'); 9 | } 10 | -------------------------------------------------------------------------------- /shell/lib/application/provider/image_from_icon_query.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter_svg/svg.dart'; 4 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 5 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 6 | import 'package:shell/application/provider/icon_themes.dart'; 7 | 8 | part 'image_from_icon_query.g.dart'; 9 | 10 | @Riverpod(keepAlive: true) 11 | class ImageFromIconQuery extends _$ImageFromIconQuery { 12 | @override 13 | Future build(IconQuery query, Size size) async { 14 | final themes = await ref.watch(iconThemesProvider.future); 15 | final file = await themes.findIcon(query); 16 | if (file == null) return null; 17 | if (file.path.endsWith('.svg')) { 18 | final pictureInfo = await vg.loadPicture(SvgFileLoader(file), null); 19 | final pictureRecorder = PictureRecorder(); 20 | final canvas = Canvas(pictureRecorder); 21 | // Calculate the scale factor 22 | final scaleFactor = size.width / pictureInfo.size.width; 23 | // Apply the scale factor to the canvas 24 | if (scaleFactor != 0) { 25 | canvas.scale(scaleFactor); 26 | } 27 | canvas.drawPicture(pictureInfo.picture); 28 | final picture = pictureRecorder.endRecording(); 29 | 30 | final image = await picture.toImage( 31 | size.width.toInt(), 32 | size.height.toInt(), 33 | ); 34 | pictureInfo.picture.dispose(); 35 | return image; 36 | } else { 37 | final bytes = await file.readAsBytes(); 38 | 39 | // Decode the image 40 | final codec = 41 | await instantiateImageCodec(bytes, targetWidth: size.width.toInt()); 42 | final frameInfo = await codec.getNextFrame(); 43 | return frameInfo.image; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /shell/lib/application/provider/localized_desktop_entries.dart: -------------------------------------------------------------------------------- 1 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 2 | import 'package:path/path.dart' as path; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:shell/application/provider/desktop_entries.dart'; 5 | 6 | part 'localized_desktop_entries.g.dart'; 7 | 8 | @Riverpod(keepAlive: true) 9 | Future> localizedDesktopEntries( 10 | LocalizedDesktopEntriesRef ref, 11 | ) async { 12 | final desktopEntries = 13 | await ref.watch(installedDesktopEntriesProvider.future); 14 | return desktopEntries 15 | .map((key, value) => MapEntry(key, value.localize(lang: 'en'))); 16 | } 17 | 18 | @riverpod 19 | class LocalizedDesktopEntryForId extends _$LocalizedDesktopEntryForId { 20 | @override 21 | FutureOr build(String appId) async { 22 | final binaryToAppId = await ref.watch(binaryToAppIdProvider.future); 23 | return ref.watch( 24 | localizedDesktopEntriesProvider.selectAsync( 25 | (data) => data[appId] ?? data[binaryToAppId[appId]], 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | @riverpod 32 | class binaryToAppId extends _$binaryToAppId { 33 | @override 34 | FutureOr> build() async { 35 | final desktopEntries = 36 | await ref.watch(localizedDesktopEntriesProvider.future); 37 | // extract the binary name of and exec string 38 | return desktopEntries.map( 39 | (key, value) => MapEntry( 40 | path.basenameWithoutExtension( 41 | (value.entries[DesktopEntryKey.exec.string] ?? 42 | value.entries[DesktopEntryKey.tryExec.string]) 43 | ?.split(' ') 44 | .first ?? 45 | key, 46 | ), 47 | key, 48 | ), 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /shell/lib/display/model/display.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'display.freezed.dart'; 5 | 6 | /// DISPLAY 7 | /// The digital world of Veshell 8 | /// The root component that contain everything else 9 | /// It's span accross all monitor. 10 | 11 | @freezed 12 | abstract class Display with _$Display { 13 | /// Factory 14 | const factory Display({ 15 | required Size size, 16 | }) = _Display; 17 | } 18 | -------------------------------------------------------------------------------- /shell/lib/display/provider/display.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/display/model/display.dart'; 4 | 5 | part 'display.g.dart'; 6 | 7 | /// Provide the only Display to all his childrens 8 | @riverpod 9 | Display currentDisplay(CurrentDisplayRef ref) { 10 | final viewSize = 11 | WidgetsBinding.instance.platformDispatcher.views.first.physicalSize; 12 | return Display(size: viewSize); 13 | } 14 | -------------------------------------------------------------------------------- /shell/lib/display/widget/display.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:shell/monitor/provider/current_monitor.dart'; 4 | import 'package:shell/monitor/provider/monitor_list.dart'; 5 | import 'package:shell/monitor/widget/monitor.dart'; 6 | 7 | /// Widget that represent the Display in the widget tree 8 | class DisplayWidget extends HookConsumerWidget { 9 | /// Const constructor 10 | const DisplayWidget({super.key}); 11 | 12 | @override 13 | Widget build(BuildContext context, WidgetRef ref) { 14 | final monitorList = ref 15 | .watch(monitorListProvider) 16 | // Ignore monitors that are connected but not yet configured 17 | // with a resolution. 18 | .where((element) => element.currentMode != null); 19 | 20 | return Stack( 21 | children: [ 22 | for (final monitor in monitorList) 23 | Positioned.fromRect( 24 | rect: monitor.location & monitor.currentMode!.size, 25 | child: ProviderScope( 26 | overrides: [ 27 | currentMonitorProvider.overrideWith((ref) => monitor.name), 28 | ], 29 | child: MonitorWidget( 30 | key: Key(monitor.name), 31 | ), 32 | ), 33 | ), 34 | ], 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /shell/lib/enums.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | enum Edges { 6 | none, 7 | top, 8 | bottom, 9 | left, 10 | right, 11 | } 12 | 13 | extension EdgesExt on Edges { 14 | int get id { 15 | switch (this) { 16 | case Edges.none: 17 | return 0; 18 | case Edges.top: 19 | return 1 << 0; 20 | case Edges.bottom: 21 | return 1 << 1; 22 | case Edges.left: 23 | return 1 << 2; 24 | case Edges.right: 25 | return 1 << 3; 26 | } 27 | } 28 | 29 | bool operator &(int bitmap) { 30 | return bitmap & id != 0; 31 | } 32 | } 33 | 34 | extension ValueNotifierFutureExt on ValueNotifier { 35 | Future future() { 36 | final completer = Completer(); 37 | 38 | void valueChanged() { 39 | removeListener(valueChanged); 40 | completer.complete(value); 41 | } 42 | 43 | addListener(valueChanged); 44 | return completer.future; 45 | } 46 | 47 | Future waitUntil(bool Function(T) until) async { 48 | if (until(value)) { 49 | return; 50 | } 51 | while (!until(await future())) {} 52 | } 53 | } 54 | 55 | extension InterleaveExt on Iterable { 56 | Iterable interleave(T element) sync* { 57 | final it = iterator; 58 | if (it.moveNext()) { 59 | yield it.current; 60 | } 61 | while (it.moveNext()) { 62 | yield element; 63 | yield it.current; 64 | } 65 | } 66 | } 67 | 68 | extension FloorToDouble on Size { 69 | Size floorToDouble() => Size(width.floorToDouble(), height.floorToDouble()); 70 | } 71 | -------------------------------------------------------------------------------- /shell/lib/monitor/model/monitor.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/foundation.dart'; 4 | import 'package:freezed_annotation/freezed_annotation.dart'; 5 | import 'package:shell/shared/util/json_converter/offset.dart'; 6 | import 'package:shell/shared/util/json_converter/size.dart'; 7 | 8 | part 'monitor.serializable.freezed.dart'; 9 | part 'monitor.serializable.g.dart'; 10 | 11 | typedef MonitorId = String; 12 | 13 | /// a Monitor represent a physical display device used to render Veshell 14 | @freezed 15 | class Monitor with _$Monitor { 16 | /// Factory 17 | factory Monitor({ 18 | required MonitorId name, 19 | required String description, 20 | required PhysicalProperties physicalProperties, 21 | required double scale, 22 | @OffsetConverter() required Offset location, 23 | required Mode? currentMode, 24 | required Mode? preferredMode, 25 | required List modes, 26 | }) = _Monitor; 27 | Monitor._(); 28 | 29 | factory Monitor.fromJson(Map json) => 30 | _$MonitorFromJson(json); 31 | } 32 | 33 | @freezed 34 | class Mode with _$Mode { 35 | const factory Mode({ 36 | @SizeConverter() required Size size, 37 | required int refreshRate, 38 | }) = _Mode; 39 | 40 | factory Mode.fromJson(Map json) => _$ModeFromJson(json); 41 | } 42 | 43 | @freezed 44 | class PhysicalProperties with _$PhysicalProperties { 45 | const factory PhysicalProperties({ 46 | @SizeConverter() required Size size, 47 | required String make, 48 | required String model, 49 | }) = _PhysicalProperties; 50 | 51 | factory PhysicalProperties.fromJson(Map json) => 52 | _$PhysicalPropertiesFromJson(json); 53 | } 54 | -------------------------------------------------------------------------------- /shell/lib/monitor/model/monitor_configuration.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/monitor/model/screen_configuration.serializable.dart'; 3 | import 'package:shell/shared/persistence/persistable_model.mixin.dart'; 4 | 5 | part 'monitor_configuration.serializable.freezed.dart'; 6 | part 'monitor_configuration.serializable.g.dart'; 7 | 8 | /// 9 | @freezed 10 | class MonitorConfiguration 11 | with _$MonitorConfiguration 12 | implements PersistableModel { 13 | /// Factory 14 | factory MonitorConfiguration({ 15 | required int selectedMode, 16 | required ScreenConfiguration screenConfiguration, 17 | }) = _MonitorConfiguration; 18 | MonitorConfiguration._(); 19 | factory MonitorConfiguration.fromJson(Map json) => 20 | _$MonitorConfigurationFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/monitor/model/screen_configuration.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:shell/screen/model/screen.serializable.dart'; 4 | import 'package:shell/shared/persistence/persistable_model.mixin.dart'; 5 | 6 | part 'screen_configuration.serializable.freezed.dart'; 7 | part 'screen_configuration.serializable.g.dart'; 8 | 9 | enum ScreenDisplayMode { 10 | splitHorizontal, 11 | splitVertical, 12 | } 13 | 14 | /// 15 | @freezed 16 | class ScreenConfiguration 17 | with _$ScreenConfiguration 18 | implements PersistableModel { 19 | /// Factory 20 | factory ScreenConfiguration({ 21 | required IList screenList, 22 | required ScreenDisplayMode displayMode, 23 | }) = _ScreenConfiguration; 24 | ScreenConfiguration._(); 25 | factory ScreenConfiguration.fromJson(Map json) => 26 | _$ScreenConfigurationFromJson(json); 27 | } 28 | -------------------------------------------------------------------------------- /shell/lib/monitor/provider/current_monitor.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/monitor/model/monitor.serializable.dart'; 3 | 4 | part 'current_monitor.g.dart'; 5 | 6 | /// Provide the current Monitor to all his childrens 7 | @Riverpod(dependencies: []) 8 | MonitorId currentMonitor(CurrentMonitorRef ref) { 9 | // This provider is instentatied in Children Scope 10 | throw Exception('Provider was not initialized'); 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/monitor/provider/monitor_configuration_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/monitor/model/monitor.serializable.dart'; 4 | import 'package:shell/monitor/model/monitor_configuration.serializable.dart'; 5 | import 'package:shell/monitor/model/screen_configuration.serializable.dart'; 6 | import 'package:shell/monitor/provider/monitor_list.dart'; 7 | import 'package:shell/screen/model/screen.serializable.dart'; 8 | import 'package:shell/shared/persistence/persistable_provider.mixin.dart'; 9 | 10 | part 'monitor_configuration_state.g.dart'; 11 | 12 | /// Monitor provider 13 | @riverpod 14 | class MonitorConfigurationState extends _$MonitorConfigurationState 15 | with 16 | PersistableProvider> { 18 | @override 19 | MonitorConfiguration build(MonitorId monitorId) { 20 | persistChanges(clearOnDispose: false); 21 | 22 | final monitor = ref.watch(monitorListProvider).firstWhere( 23 | (element) => element.name == monitorId, 24 | ); 25 | 26 | return getPersisted(MonitorConfiguration.fromJson) ?? 27 | MonitorConfiguration( 28 | selectedMode: monitor.modes.indexOf(monitor.currentMode!), 29 | screenConfiguration: ScreenConfiguration( 30 | screenList: IList(), 31 | displayMode: ScreenDisplayMode.splitHorizontal, 32 | ), 33 | ); 34 | } 35 | 36 | void setScreenList(IList screenList) { 37 | state = state.copyWith( 38 | screenConfiguration: 39 | state.screenConfiguration.copyWith(screenList: screenList), 40 | ); 41 | } 42 | 43 | @override 44 | String getPersistentFolder() => 'Monitor'; 45 | 46 | @override 47 | String getPersistentId() => monitorId; 48 | } 49 | -------------------------------------------------------------------------------- /shell/lib/monitor/provider/monitor_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/monitor/model/monitor.serializable.dart'; 3 | import 'package:shell/wayland/model/event/wayland_event.serializable.dart'; 4 | import 'package:shell/wayland/provider/wayland.manager.dart'; 5 | 6 | part 'monitor_list.g.dart'; 7 | 8 | /// Provide list of plugged Monitors 9 | @riverpod 10 | class MonitorList extends _$MonitorList { 11 | @override 12 | List build() { 13 | ref.listen( 14 | waylandManagerProvider, 15 | (_, next) { 16 | if (next case AsyncData(value: final MonitorLayoutChangedEvent event)) { 17 | state = event.message.monitors; 18 | } 19 | }, 20 | fireImmediately: true, 21 | ); 22 | return []; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /shell/lib/monitor/widget/monitor.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_hooks/flutter_hooks.dart'; 4 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 5 | import 'package:shell/monitor/provider/current_monitor.dart'; 6 | import 'package:shell/monitor/provider/monitor_configuration_state.dart'; 7 | import 'package:shell/screen/provider/current_screen_id.dart'; 8 | import 'package:shell/screen/provider/screen_list.dart'; 9 | import 'package:shell/screen/widget/screen.dart'; 10 | 11 | /// Widget that represent the Monitor in the widget tree 12 | class MonitorWidget extends HookConsumerWidget { 13 | /// Const constructor 14 | const MonitorWidget({super.key}); 15 | @override 16 | Widget build(BuildContext context, WidgetRef ref) { 17 | final monitorName = ref.watch(currentMonitorProvider); 18 | final screenConfiguration = ref.watch( 19 | monitorConfigurationStateProvider(monitorName) 20 | .select((value) => value.screenConfiguration), 21 | ); 22 | 23 | useEffect( 24 | () { 25 | if (screenConfiguration.screenList.isEmpty) { 26 | WidgetsBinding.instance.addPostFrameCallback((_) { 27 | final newScreenId = 28 | ref.read(screenListProvider.notifier).createNewScreen(); 29 | ref 30 | .read(monitorConfigurationStateProvider(monitorName).notifier) 31 | .setScreenList([newScreenId].lock); 32 | }); 33 | } 34 | return null; 35 | }, 36 | [screenConfiguration.screenList], 37 | ); 38 | 39 | return Column( 40 | children: [ 41 | for (final screenId in screenConfiguration.screenList) 42 | Flexible( 43 | child: ProviderScope( 44 | overrides: [ 45 | currentScreenIdProvider.overrideWith((ref) => screenId), 46 | ], 47 | child: const ScreenWidget(), 48 | ), 49 | ), 50 | ], 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/control_panel/bluetooth/model/bluetooth_device.dart: -------------------------------------------------------------------------------- 1 | import 'package:bluez/bluez.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'bluetooth_device.freezed.dart'; 5 | 6 | @freezed 7 | class BluetoothDevice with _$BluetoothDevice { 8 | factory BluetoothDevice({ 9 | required BlueZDevice bluezDevice, 10 | required bool connecting, 11 | required bool pairing, 12 | }) = _BluetoothDevice; 13 | } 14 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/control_panel/bluetooth/model/bluetooth_manager_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'bluetooth_manager_state.freezed.dart'; 4 | 5 | @freezed 6 | class BluetoothManagerState with _$BluetoothManagerState { 7 | factory BluetoothManagerState({ 8 | required bool powered, 9 | required bool discovering, 10 | }) = _BluetoothManagerState; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/control_panel/bluetooth/provider/bluetooth_device.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/overview/helm/control_panel/bluetooth/model/bluetooth_device.dart'; 3 | import 'package:shell/shared/bluez/provider/bluez_device.dart'; 4 | 5 | part 'bluetooth_device.g.dart'; 6 | 7 | @riverpod 8 | class BluetoothDeviceState extends _$BluetoothDeviceState { 9 | @override 10 | BluetoothDevice build(String address) { 11 | return BluetoothDevice( 12 | bluezDevice: ref.watch(bluezDeviceProvider(address)), 13 | connecting: false, 14 | pairing: false, 15 | ); 16 | } 17 | 18 | Future connect() async { 19 | state = state.copyWith(connecting: true); 20 | try { 21 | await state.bluezDevice.connect(); 22 | } catch (e) { 23 | print('connect error $e'); 24 | } 25 | state = state.copyWith(connecting: false); 26 | } 27 | 28 | Future pair() async { 29 | state = state.copyWith(pairing: true); 30 | try { 31 | await state.bluezDevice.pair(); 32 | } catch (e) { 33 | print('pair error $e'); 34 | } 35 | state = state.copyWith(pairing: false); 36 | await connect(); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/control_panel/bluetooth/provider/bluetooth_manager.dart: -------------------------------------------------------------------------------- 1 | import 'package:async/async.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/overview/helm/control_panel/bluetooth/model/bluetooth_manager_state.dart'; 4 | import 'package:shell/shared/bluez/provider/bluez_client.dart'; 5 | 6 | part 'bluetooth_manager.g.dart'; 7 | 8 | @riverpod 9 | class BluetoothManager extends _$BluetoothManager { 10 | @override 11 | Future build() async { 12 | final client = await ref.watch(bluezClientProvider.future); 13 | final subscription = StreamGroup.merge([ 14 | client.adapterAdded, 15 | client.adapterRemoved, 16 | ...client.adapters.map((adapter) => adapter.propertiesChanged), 17 | ]).listen((_) => ref.invalidateSelf()); 18 | ref.onDispose(subscription.cancel); 19 | return BluetoothManagerState( 20 | powered: client.adapters.any((adapter) => adapter.powered), 21 | discovering: client.adapters.any((adapter) => adapter.discovering), 22 | ); 23 | } 24 | 25 | powerOn() async { 26 | final client = await ref.watch(bluezClientProvider.future); 27 | for (final adapter in client.adapters) { 28 | await adapter.setPowered(true); 29 | } 30 | } 31 | 32 | powerOff() async { 33 | final client = await ref.watch(bluezClientProvider.future); 34 | for (final adapter in client.adapters) { 35 | await adapter.setPowered(false); 36 | } 37 | } 38 | 39 | startDiscovery() async { 40 | final client = await ref.watch(bluezClientProvider.future); 41 | for (final adapter in client.adapters) { 42 | if (!adapter.discovering && adapter.powered) { 43 | await adapter.startDiscovery(); 44 | } 45 | } 46 | } 47 | 48 | stopDiscovery() async { 49 | final client = await ref.watch(bluezClientProvider.future); 50 | for (final adapter in client.adapters) { 51 | if (adapter.discovering && adapter.powered) { 52 | await adapter.stopDiscovery(); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/control_panel/widget/audio_output.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 3 | 4 | class AudioOutputWidget extends StatelessWidget { 5 | const AudioOutputWidget({ 6 | this.isExpanded = false, 7 | super.key, 8 | }); 9 | final bool isExpanded; 10 | 11 | @override 12 | Widget build(BuildContext context) { 13 | return Column( 14 | children: [ 15 | ListTile( 16 | onTap: () {}, 17 | leading: IconButton( 18 | onPressed: () {}, 19 | icon: Icon( 20 | MdiIcons.volumeHigh, 21 | ), 22 | ), 23 | title: Slider( 24 | value: 0.5, 25 | onChanged: (double value) {}, 26 | ), 27 | trailing: Icon(MdiIcons.chevronUp), 28 | ), 29 | ColoredBox( 30 | color: Colors.black12, 31 | child: ListView( 32 | shrinkWrap: true, 33 | children: [ 34 | ListTile( 35 | leading: Icon(MdiIcons.check), 36 | title: const Text('Output 1'), 37 | onTap: () {}, 38 | ), 39 | ], 40 | ), 41 | ), 42 | ], 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/control_panel/widget/session_controls.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 4 | import 'package:shell/systemd/provider/session.dart'; 5 | 6 | class SessionControls extends HookConsumerWidget { 7 | const SessionControls({ 8 | super.key, 9 | }); 10 | 11 | @override 12 | Widget build(BuildContext context, WidgetRef ref) { 13 | return Card( 14 | child: Padding( 15 | padding: const EdgeInsets.all(8), 16 | child: Row( 17 | mainAxisAlignment: MainAxisAlignment.spaceEvenly, 18 | children: [ 19 | IconButton( 20 | onPressed: () { 21 | ref.read(sessionProvider.notifier).lock(); 22 | }, 23 | style: IconButton.styleFrom(padding: const EdgeInsets.all(12)), 24 | icon: Icon(MdiIcons.lock), 25 | ), 26 | IconButton( 27 | onPressed: () { 28 | ref.read(sessionProvider.notifier).logout(); 29 | }, 30 | style: IconButton.styleFrom(padding: const EdgeInsets.all(12)), 31 | icon: Icon(MdiIcons.logout), 32 | ), 33 | IconButton( 34 | onPressed: () {}, 35 | icon: Icon(MdiIcons.powerSleep), 36 | style: IconButton.styleFrom(padding: const EdgeInsets.all(12)), 37 | ), 38 | IconButton( 39 | onPressed: () { 40 | ref.read(sessionProvider.notifier).reboot(); 41 | }, 42 | icon: Icon(MdiIcons.restart), 43 | style: IconButton.styleFrom(padding: const EdgeInsets.all(12)), 44 | ), 45 | IconButton( 46 | onPressed: () { 47 | ref.read(sessionProvider.notifier).shutdown(); 48 | }, 49 | style: IconButton.styleFrom(padding: const EdgeInsets.all(12)), 50 | icon: Icon(MdiIcons.power), 51 | ), 52 | ], 53 | ), 54 | ), 55 | ); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/monitoring_panel/cpu_monitoring/model/cpu.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'cpu.freezed.dart'; 4 | 5 | @freezed 6 | class CpuStats with _$CpuStats { 7 | factory CpuStats({ 8 | required int cpuLoad, 9 | required int loadOnMostUsedCore, 10 | }) = _CpuStats; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/monitoring_panel/cpu_monitoring/model/cpu_line.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'cpu_line.freezed.dart'; 4 | 5 | @freezed 6 | class CpuLine with _$CpuLine { 7 | factory CpuLine({ 8 | required String label, 9 | required int user, 10 | required int nice, 11 | required int system, 12 | required int idle, 13 | required int iowait, 14 | required int irq, 15 | required int softirq, 16 | required int steal, 17 | required int guest, 18 | required int guestNice, 19 | }) = _CpuLine; 20 | } 21 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/monitoring_panel/cpu_monitoring/model/processes_cpu_stats_snapshot.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | 4 | part 'processes_cpu_stats_snapshot.freezed.dart'; 5 | 6 | @freezed 7 | class ProcessesCpuStatsSnapshot with _$ProcessesCpuStatsSnapshot { 8 | factory ProcessesCpuStatsSnapshot({ 9 | required int totalCpu, 10 | required IMap cpuUsagePerProcess, 11 | }) = _ProcessesCpuStatsSnapshot; 12 | } 13 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/monitoring_panel/cpu_monitoring/provider/process_name.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'process_name.g.dart'; 6 | 7 | @riverpod 8 | String ProcessName(ProcessNameRef ref, int pid) { 9 | final file = File('/proc/$pid/comm'); 10 | if (file.existsSync()) { 11 | return file.readAsStringSync().trim(); 12 | } else { 13 | return 'Unknown'; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/monitoring_panel/model/process_stat.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'process_stat.freezed.dart'; 4 | 5 | @freezed 6 | class ProcessStat with _$ProcessStat { 7 | factory ProcessStat({ 8 | required int pid, 9 | required String name, 10 | required int cpuUsage, 11 | required int memoryUsage, 12 | required int networkUsage, 13 | }) = _ProcessStat; 14 | } 15 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/monitoring_panel/provider/process_list.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | 4 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 5 | import 'package:path/path.dart' as p; 6 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 7 | 8 | part 'process_list.g.dart'; 9 | 10 | @riverpod 11 | class ProcessList extends _$ProcessList { 12 | @override 13 | Future> build() async { 14 | final timer = Timer.periodic(const Duration(seconds: 1), (Timer t) async { 15 | ref.invalidateSelf(); 16 | }); 17 | 18 | ref.onDispose(timer.cancel); 19 | 20 | return (await _getProcessList()).lock; 21 | } 22 | 23 | Future> _getProcessList() async { 24 | return Directory('/proc') 25 | .list(followLinks: false) 26 | .where((dir) => dir is Directory) 27 | .cast() 28 | .where((dir) => int.tryParse(p.basename(dir.path)) != null) 29 | .map( 30 | (dir) => int.parse(p.basename(dir.path)), 31 | ) 32 | .toSet(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/notification_panel/widget/notification_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 3 | 4 | class NotificationPanel extends StatelessWidget { 5 | const NotificationPanel({ 6 | super.key, 7 | }); 8 | 9 | @override 10 | Widget build(BuildContext context) { 11 | return Column( 12 | crossAxisAlignment: CrossAxisAlignment.start, 13 | children: [ 14 | Expanded( 15 | child: Card( 16 | clipBehavior: Clip.antiAlias, 17 | child: Column( 18 | children: [ 19 | Expanded( 20 | child: ListView( 21 | children: [ 22 | ListTile( 23 | leading: Icon(MdiIcons.bell), 24 | title: const Text('Notification title'), 25 | subtitle: const Text('Notification body'), 26 | onTap: () {}, 27 | ), 28 | ListTile( 29 | leading: Icon(MdiIcons.bell), 30 | title: const Text('Notification title'), 31 | subtitle: const Text('Notification body'), 32 | onTap: () {}, 33 | ), 34 | ListTile( 35 | leading: Icon(MdiIcons.bell), 36 | title: const Text('Notification title'), 37 | subtitle: const Text('Notification body'), 38 | onTap: () {}, 39 | ), 40 | ], 41 | ), 42 | ), 43 | ], 44 | ), 45 | ), 46 | ), 47 | ], 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/provider/audio_output.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | part 'audio_output.g.dart'; 4 | 5 | @riverpod 6 | class AudioOutput extends _$AudioOutput { 7 | @override 8 | void build() { 9 | return; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/overview/helm/widget/helm.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:shell/overview/helm/control_panel/widget/control_panel.dart'; 4 | import 'package:shell/overview/helm/monitoring_panel/widget/monitoring_panel.dart'; 5 | import 'package:shell/overview/helm/notification_panel/widget/notification_panel.dart'; 6 | 7 | class Helm extends HookConsumerWidget { 8 | const Helm({super.key}); 9 | @override 10 | Widget build(BuildContext context, WidgetRef ref) { 11 | return const Row( 12 | children: [ 13 | Expanded(child: ControlPanel()), 14 | SizedBox(width: 8), 15 | Expanded(child: MonitoringPanel()), 16 | SizedBox(width: 8), 17 | Expanded(child: NotificationPanel()), 18 | ], 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/overview/model/overview.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:shell/screen/model/screen.serializable.dart'; 4 | import 'package:shell/window/model/window_id.dart'; 5 | 6 | part 'overview.freezed.dart'; 7 | 8 | @freezed 9 | class Overview with _$Overview { 10 | factory Overview({ 11 | required ScreenId screenId, 12 | required IList windowList, 13 | required bool isDisplayed, 14 | }) = _Overview; 15 | } 16 | -------------------------------------------------------------------------------- /shell/lib/overview/provider/overview_state.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freedesktop_desktop_entry/freedesktop_desktop_entry.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:shell/overview/model/overview.dart'; 5 | import 'package:shell/screen/model/screen.serializable.dart'; 6 | import 'package:shell/window/model/window_id.dart'; 7 | import 'package:shell/window/provider/ephemeral_window_state.dart'; 8 | import 'package:shell/window/provider/window_manager/window_manager.dart'; 9 | 10 | part 'overview_state.g.dart'; 11 | 12 | @riverpod 13 | class OverviewState extends _$OverviewState { 14 | @override 15 | Overview build(ScreenId screenId) { 16 | return Overview( 17 | screenId: screenId, 18 | windowList: [].lock, 19 | isDisplayed: false, 20 | ); 21 | } 22 | 23 | /// Toggle visibility of the overview 24 | void toggle() { 25 | state = state.copyWith(isDisplayed: !state.isDisplayed); 26 | 27 | /// If the overview is hidden, close all ephemeral applications 28 | if (!state.isDisplayed) { 29 | for (final windowId in state.windowList) { 30 | ref.read(windowManagerProvider.notifier).closeWindow(windowId); 31 | } 32 | } 33 | } 34 | 35 | /// Start an new Ephemeral Application 36 | void startEphemeralApplication( 37 | LocalizedDesktopEntry entry, 38 | ) { 39 | final windowId = ref 40 | .read(windowManagerProvider.notifier) 41 | .createEphemeralWindowForDesktopEntry(entry, state.screenId); 42 | 43 | state = state.copyWith( 44 | windowList: state.windowList.add(windowId), 45 | ); 46 | 47 | ref.read(ephemeralWindowStateProvider(windowId).notifier).launchSelf(); 48 | } 49 | 50 | void removeWindow(EphemeralWindowId windowId) { 51 | state = state.copyWith( 52 | windowList: state.windowList.remove(windowId), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /shell/lib/overview/search/widget/file_search_result.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/free-explorers/veshell-prototype/9b7cdd7f0c057bf51ea04138c1dd670488ffc9c9/shell/lib/overview/search/widget/file_search_result.dart -------------------------------------------------------------------------------- /shell/lib/overview/search/widget/search_input.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:shell/theme/theme.dart'; 3 | 4 | class SearchInput extends StatelessWidget { 5 | const SearchInput({ 6 | required this.searchController, 7 | required this.searchFocusNode, 8 | super.key, 9 | }); 10 | 11 | final TextEditingController searchController; 12 | final FocusNode searchFocusNode; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return TextField( 17 | controller: searchController, 18 | focusNode: searchFocusNode, 19 | autofocus: true, 20 | style: Theme.of(context).textTheme.titleLarge, 21 | decoration: InputDecoration( 22 | prefixIcon: const Padding( 23 | padding: EdgeInsets.fromLTRB(12, 12, 32, 12), 24 | child: Icon( 25 | Icons.search, 26 | size: 28, 27 | ), 28 | ), 29 | hintText: 'Search', 30 | fillColor: Theme.of(context).colorScheme.surface, 31 | filled: true, 32 | border: const OutlineInputBorder( 33 | borderRadius: BorderRadius.all(Radius.circular(surfaceRadius)), 34 | borderSide: BorderSide.none, 35 | ), 36 | focusedBorder: OutlineInputBorder( 37 | borderRadius: const BorderRadius.all(Radius.circular(surfaceRadius)), 38 | borderSide: BorderSide( 39 | color: Theme.of(context).colorScheme.primary, 40 | width: 2, 41 | ), 42 | ), 43 | ), 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /shell/lib/overview/search/widget/settings_search_result.dart: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/free-explorers/veshell-prototype/9b7cdd7f0c057bf51ea04138c1dd670488ffc9c9/shell/lib/overview/search/widget/settings_search_result.dart -------------------------------------------------------------------------------- /shell/lib/pointer/model/pointer_focus.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:shell/shared/util/json_converter/offset.dart'; 5 | import 'package:shell/wayland/model/wl_surface.dart'; 6 | 7 | part 'pointer_focus.serializable.freezed.dart'; 8 | part 'pointer_focus.serializable.g.dart'; 9 | 10 | @freezed 11 | abstract class PointerFocus with _$PointerFocus { 12 | const factory PointerFocus({ 13 | required SurfaceId surfaceId, 14 | @OffsetConverter() required Offset globalOffset, 15 | }) = _PointerFocus; 16 | 17 | factory PointerFocus.fromJson(Map json) => 18 | _$PointerFocusFromJson(json); 19 | } 20 | -------------------------------------------------------------------------------- /shell/lib/polkit/model/polkit-agent-helper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | const helperPath = '/usr/lib/polkit-1/polkit-agent-helper-1'; 6 | 7 | enum Event { 8 | failed, 9 | request, 10 | showError, 11 | showDebug, 12 | complete, 13 | } 14 | 15 | class PolkitAgentHelper { 16 | PolkitAgentHelper._(this.process, this.stdout, this.stdin); 17 | final Process process; 18 | final Stream stdout; 19 | final IOSink stdin; 20 | 21 | static Future start(String userName, String cookie) async { 22 | final process = await Process.start( 23 | helperPath, 24 | [userName], 25 | ); 26 | 27 | final stdout = process.stdout.transform(utf8.decoder).asBroadcastStream(); 28 | final stdin = process.stdin; 29 | 30 | stdin.writeln(cookie); 31 | await stdin.flush(); 32 | 33 | return PolkitAgentHelper._(process, stdout, stdin); 34 | } 35 | 36 | Future nextEvent() async { 37 | final line = await stdout.firstWhere( 38 | (line) => line.isNotEmpty, 39 | orElse: () => '', 40 | ); 41 | 42 | final parts = line.trim().split(' '); 43 | final prefix = parts[0]; 44 | 45 | switch (prefix) { 46 | case 'PAM_PROMPT_ECHO_OFF': 47 | return Event.request; 48 | case 'PAM_PROMPT_ECHO_ON': 49 | return Event.request; 50 | case 'PAM_ERROR_MSG': 51 | return Event.showError; 52 | case 'PAM_TEXT_INFO': 53 | return Event.showDebug; 54 | case 'SUCCESS': 55 | return Event.complete; 56 | case 'FAILURE': 57 | return Event.failed; 58 | default: 59 | print('Unknown line from Polkit agent helper: $line'); 60 | return Event.failed; 61 | } 62 | } 63 | 64 | Future respond(String response) async { 65 | stdin.writeln(response); 66 | await stdin.flush(); 67 | } 68 | 69 | Future close() async { 70 | process.kill(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /shell/lib/polkit/provider/authentication_agent.dart: -------------------------------------------------------------------------------- 1 | import 'package:dbus/dbus.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/polkit/model/authentication_agent.dart'; 4 | import 'package:shell/polkit/model/org.freedesktop.PolicyKit1.AuthenticationAgent.dart'; 5 | import 'package:shell/shared/provider/dbus_client.dart'; 6 | import 'package:shell/systemd/provider/session.dart'; 7 | 8 | part 'authentication_agent.g.dart'; 9 | 10 | const busName = 'org.freedesktop.PolicyKit1.AuthenticationAgent'; 11 | const objectPath = '/org/freedesktop/PolicyKit1/AuthenticationAgent'; 12 | 13 | @Riverpod(keepAlive: true) 14 | class PolkitAuthenticationAgentState extends _$PolkitAuthenticationAgentState { 15 | late PolkitAuthenticationAgent _agent; 16 | late DBusRemoteObject _authority; 17 | 18 | @override 19 | Future build() async { 20 | try { 21 | final client = ref.watch(dbusClientProvider); 22 | final sessionManager = ref.watch(sessionProvider); 23 | _agent = PolkitAuthenticationAgent(client, sessionManager); 24 | await client.registerObject(_agent); 25 | _authority = DBusRemoteObject( 26 | client, 27 | name: 'org.freedesktop.PolicyKit1', 28 | path: DBusObjectPath('/org/freedesktop/PolicyKit1/Authority'), 29 | ); 30 | final session = 31 | await ref.watch(sessionProvider.notifier).getActiveUserSession(); 32 | await _agent.registerAgent(await session!.id); 33 | return _agent; 34 | } catch (e) { 35 | print(e); 36 | rethrow; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /shell/lib/screen/model/screen.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:shell/shared/persistence/persistable_model.mixin.dart'; 4 | import 'package:shell/workspace/provider/workspace_state.dart'; 5 | 6 | part 'screen.serializable.freezed.dart'; 7 | part 'screen.serializable.g.dart'; 8 | 9 | typedef ScreenId = String; 10 | 11 | /// a Screen represent a portion of a Monitor where we want to render Veshell. 12 | /// Monitor usually contain a single Screen but for ultra-wide monitor 13 | /// it could be usefull to be able to split it in several Screen. 14 | @freezed 15 | class Screen with _$Screen implements PersistableModel { 16 | /// Factory 17 | factory Screen({ 18 | required ScreenId screenId, 19 | required IList workspaceList, 20 | required int selectedIndex, 21 | }) = _Screen; 22 | Screen._(); 23 | factory Screen.fromJson(Map json) => _$ScreenFromJson(json); 24 | } 25 | -------------------------------------------------------------------------------- /shell/lib/screen/model/screen_shortcuts.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | /// An intent to focus the workspace above. 4 | class FocusWorkspaceAboveIntent extends Intent { 5 | /// 6 | const FocusWorkspaceAboveIntent(); 7 | } 8 | 9 | /// An intent to focus the workspace below. 10 | class FocusWorkspaceBelowIntent extends Intent { 11 | /// 12 | const FocusWorkspaceBelowIntent(); 13 | } 14 | 15 | /// An intent to toggle the overview. 16 | class ToggleOverviewIntent extends Intent { 17 | /// 18 | const ToggleOverviewIntent(); 19 | } 20 | -------------------------------------------------------------------------------- /shell/lib/screen/model/workspace_drag_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/screen/model/screen.serializable.dart'; 3 | import 'package:shell/workspace/provider/workspace_state.dart'; 4 | 5 | part 'workspace_drag_data.freezed.dart'; 6 | 7 | @freezed 8 | class WorkspaceDragData with _$WorkspaceDragData { 9 | /// Factory 10 | const factory WorkspaceDragData({ 11 | required WorkspaceId workspaceId, 12 | required ScreenId screenId, 13 | }) = _WorkspaceDragData; 14 | } 15 | -------------------------------------------------------------------------------- /shell/lib/screen/provider/current_screen_id.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/screen/model/screen.serializable.dart'; 3 | 4 | part 'current_screen_id.g.dart'; 5 | 6 | /// Provide the current Screen to all his childrens 7 | @Riverpod(dependencies: []) 8 | ScreenId currentScreenId(CurrentScreenIdRef ref) { 9 | // This provider is instentatied in Children Scope 10 | throw Exception('Provider was not initialized'); 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/screen/provider/focused_screen.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/screen/model/screen.serializable.dart'; 3 | import 'package:shell/screen/provider/screen_list.dart'; 4 | 5 | part 'focused_screen.g.dart'; 6 | 7 | /// Provide the current Focused screen 8 | @riverpod 9 | class FocusedScreen extends _$FocusedScreen { 10 | @override 11 | ScreenId build() { 12 | return ref.read(screenListProvider).first; 13 | } 14 | 15 | setFocusedScreen(ScreenId screenId) { 16 | state = screenId; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /shell/lib/screen/provider/screen_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/screen/model/screen.serializable.dart'; 4 | import 'package:shell/shared/provider/persistent_json_by_folder.dart'; 5 | import 'package:uuid/uuid.dart'; 6 | 7 | part 'screen_list.g.dart'; 8 | 9 | /// ScreenList provider 10 | @Riverpod(keepAlive: true) 11 | class ScreenList extends _$ScreenList { 12 | final _uuidGenerator = const Uuid(); 13 | 14 | @override 15 | ISet build() { 16 | final intialSet = ref 17 | .read(persistentJsonByFolderProvider) 18 | .requireValue['Screen'] 19 | ?.keys 20 | .toISet() ?? 21 | {}.lock; 22 | return intialSet; 23 | } 24 | 25 | ScreenId createNewScreen() { 26 | final screenId = _uuidGenerator.v4(); 27 | state = state.add(screenId); 28 | return screenId; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /shell/lib/screen/provider/workspace_display_mode.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'workspace_display_mode.g.dart'; 5 | 6 | enum WorkspaceDisplayMode { hybrid, category, application } 7 | 8 | @riverpod 9 | WorkspaceDisplayMode CurrentWorkspaceDisplayMode(Ref ref) { 10 | return WorkspaceDisplayMode.hybrid; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/screen/widget/screen_panel.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 3 | import 'package:material_design_icons_flutter/material_design_icons_flutter.dart'; 4 | import 'package:shell/overview/provider/overview_state.dart'; 5 | import 'package:shell/screen/provider/current_screen_id.dart'; 6 | import 'package:shell/screen/widget/workspace_list.dart'; 7 | import 'package:shell/theme/theme.dart'; 8 | 9 | class ScreenPanel extends HookConsumerWidget implements PreferredSizeWidget { 10 | const ScreenPanel({ 11 | super.key, 12 | }); 13 | 14 | @override 15 | Widget build(BuildContext context, WidgetRef ref) { 16 | final theme = Theme.of(context); 17 | return Material( 18 | color: theme.colorScheme.surface, 19 | textStyle: TextStyle(color: theme.colorScheme.onSurface), 20 | child: SizedBox( 21 | width: panelSize, 22 | child: Column( 23 | children: [ 24 | IconButton.filled( 25 | constraints: const BoxConstraints( 26 | minWidth: panelSize, 27 | minHeight: panelSize, 28 | ), 29 | style: IconButton.styleFrom( 30 | backgroundColor: theme.colorScheme.primaryContainer, 31 | foregroundColor: theme.colorScheme.onPrimaryContainer, 32 | shape: const RoundedRectangleBorder(), 33 | iconSize: 24, 34 | ), 35 | onPressed: () { 36 | ref 37 | .read( 38 | overviewStateProvider( 39 | ref.read(currentScreenIdProvider), 40 | ).notifier, 41 | ) 42 | .toggle(); 43 | }, 44 | icon: Icon(MdiIcons.shipWheel), 45 | ), 46 | const Expanded(child: WorkspaceListView()), 47 | ], 48 | ), 49 | ), 50 | ); 51 | } 52 | 53 | @override 54 | // TODO: implement preferredSize to expand height 55 | Size get preferredSize => const Size.fromWidth(panelSize); 56 | } 57 | -------------------------------------------------------------------------------- /shell/lib/shared/bluez/provider/bluez_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:bluez/bluez.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'bluez_client.g.dart'; 5 | 6 | @riverpod 7 | Future BluezClient(BluezClientRef ref) async { 8 | final client = BlueZClient(); 9 | 10 | await client.connect(); 11 | return client; 12 | } 13 | -------------------------------------------------------------------------------- /shell/lib/shared/bluez/provider/bluez_device.dart: -------------------------------------------------------------------------------- 1 | import 'package:bluez/bluez.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/shared/bluez/provider/bluez_devices.dart'; 4 | 5 | part 'bluez_device.g.dart'; 6 | 7 | @riverpod 8 | class BluezDevice extends _$BluezDevice { 9 | @override 10 | BlueZDevice build(String address) { 11 | final device = ref.read(bluezDevicesProvider.notifier).getDevice(address)!; 12 | final subscription = device.propertiesChanged.listen((changes) { 13 | print('propertiesChanged $changes'); 14 | ref.notifyListeners(); 15 | }); 16 | 17 | ref.onDispose(() { 18 | print('dispose BlueZDevice '); 19 | subscription.cancel(); 20 | }); 21 | return device; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shell/lib/shared/bluez/provider/bluez_device_added.dart: -------------------------------------------------------------------------------- 1 | import 'package:bluez/bluez.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/shared/bluez/provider/bluez_client.dart'; 4 | 5 | part 'bluez_device_added.g.dart'; 6 | 7 | @riverpod 8 | Stream BluezDeviceAdded(BluezDeviceAddedRef ref) async* { 9 | final client = await ref.watch(bluezClientProvider.future); 10 | yield* client.deviceAdded; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/shared/bluez/provider/bluez_device_removed.dart: -------------------------------------------------------------------------------- 1 | import 'package:bluez/bluez.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/shared/bluez/provider/bluez_client.dart'; 4 | 5 | part 'bluez_device_removed.g.dart'; 6 | 7 | @riverpod 8 | Stream BluezDeviceRemoved(BluezDeviceRemovedRef ref) async* { 9 | final client = await ref.watch(bluezClientProvider.future); 10 | yield* client.deviceRemoved; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/shared/bluez/provider/bluez_devices.dart: -------------------------------------------------------------------------------- 1 | import 'package:bluez/bluez.dart'; 2 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:shell/shared/bluez/provider/bluez_client.dart'; 5 | import 'package:shell/shared/bluez/provider/bluez_device_added.dart'; 6 | import 'package:shell/shared/bluez/provider/bluez_device_removed.dart'; 7 | 8 | part 'bluez_devices.g.dart'; 9 | 10 | @riverpod 11 | class BluezDevices extends _$BluezDevices { 12 | IMap _addressDeviceMap = {}.lock; 13 | @override 14 | Future> build() async { 15 | final client = await ref.watch(bluezClientProvider.future); 16 | 17 | ref 18 | ..listen(bluezDeviceAddedProvider, (_, device) { 19 | print('deviced added $device'); 20 | ref.invalidateSelf(); 21 | }) 22 | ..listen(bluezDeviceRemovedProvider, (_, device) { 23 | print('deviced removed $device'); 24 | ref.invalidateSelf(); 25 | }); 26 | 27 | _addressDeviceMap = 28 | {for (final device in client.devices) device.address: device}.lock; 29 | 30 | return _addressDeviceMap.keys.toISet(); 31 | } 32 | 33 | BlueZDevice? getDevice(String address) => _addressDeviceMap[address]; 34 | } 35 | -------------------------------------------------------------------------------- /shell/lib/shared/nm/provider/nm_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:nm/nm.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'nm_client.g.dart'; 5 | 6 | @riverpod 7 | Future NmClient( 8 | NmClientRef ref, 9 | ) async { 10 | final client = NetworkManagerClient(); 11 | 12 | await client.connect(); 13 | return client; 14 | } 15 | -------------------------------------------------------------------------------- /shell/lib/shared/nm/provider/nm_device.dart: -------------------------------------------------------------------------------- 1 | import 'package:nm/nm.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/shared/nm/provider/nm_devices.dart'; 4 | 5 | part 'nm_device.g.dart'; 6 | 7 | @riverpod 8 | class NmDevice extends _$NmDevice { 9 | @override 10 | NetworkManagerDevice build(String hwAddress) { 11 | final device = ref.read(nmDevicesProvider.notifier).getDevice(hwAddress)!; 12 | final subscription = device.propertiesChanged.listen((changes) { 13 | print('propertiesChanged $changes'); 14 | ref.notifyListeners(); 15 | }); 16 | 17 | ref.onDispose(() { 18 | print('dispose nmDevice '); 19 | subscription.cancel(); 20 | }); 21 | return device; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /shell/lib/shared/nm/provider/nm_device_added.dart: -------------------------------------------------------------------------------- 1 | import 'package:nm/nm.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/shared/nm/provider/nm_client.dart'; 4 | 5 | part 'nm_device_added.g.dart'; 6 | 7 | @riverpod 8 | Stream NmDeviceAdded(NmDeviceAddedRef ref) async* { 9 | final client = await ref.watch(nmClientProvider.future); 10 | yield* client.deviceAdded; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/shared/nm/provider/nm_device_removed.dart: -------------------------------------------------------------------------------- 1 | import 'package:nm/nm.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | import 'package:shell/shared/nm/provider/nm_client.dart'; 4 | 5 | part 'nm_device_removed.g.dart'; 6 | 7 | @riverpod 8 | Stream NmDeviceRemoved(NmDeviceRemovedRef ref) async* { 9 | final client = await ref.watch(nmClientProvider.future); 10 | yield* client.deviceRemoved; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/shared/nm/provider/nm_devices.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:nm/nm.dart'; 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | import 'package:shell/shared/nm/provider/nm_client.dart'; 5 | import 'package:shell/shared/nm/provider/nm_device_added.dart'; 6 | import 'package:shell/shared/nm/provider/nm_device_removed.dart'; 7 | 8 | part 'nm_devices.g.dart'; 9 | 10 | @riverpod 11 | class NmDevices extends _$NmDevices { 12 | IMap _addressDeviceMap = 13 | {}.lock; 14 | @override 15 | Future> build() async { 16 | final client = await ref.watch(nmClientProvider.future); 17 | 18 | ref 19 | ..listen(nmDeviceAddedProvider, (_, device) { 20 | print('deviced added $device'); 21 | ref.invalidateSelf(); 22 | }) 23 | ..listen(nmDeviceRemovedProvider, (_, device) { 24 | print('deviced removed $device'); 25 | ref.invalidateSelf(); 26 | }); 27 | 28 | _addressDeviceMap = 29 | {for (final device in client.devices) device.hwAddress: device}.lock; 30 | 31 | return _addressDeviceMap.keys.toISet(); 32 | } 33 | 34 | NetworkManagerDevice? getDevice(String address) => _addressDeviceMap[address]; 35 | } 36 | -------------------------------------------------------------------------------- /shell/lib/shared/persistence/persistable_model.mixin.dart: -------------------------------------------------------------------------------- 1 | abstract class PersistableModel { 2 | factory PersistableModel.fromJson() { 3 | throw UnimplementedError(); 4 | } 5 | Map toJson(); 6 | } 7 | -------------------------------------------------------------------------------- /shell/lib/shared/persistence/persistable_provider.mixin.dart: -------------------------------------------------------------------------------- 1 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 2 | import 'package:shell/shared/persistence/persistable_model.mixin.dart'; 3 | import 'package:shell/shared/persistence/persistence_manager.dart'; 4 | import 'package:shell/shared/provider/persistent_json_by_folder.dart'; 5 | 6 | mixin PersistableProvider> { 7 | RefT get ref; 8 | 9 | String getPersistentFolder(); 10 | String getPersistentId(); 11 | 12 | bool _isInitialized = false; 13 | 14 | T? getPersisted(T Function(Map) fromJsonConstructor) { 15 | final persistedJson = ref 16 | .read(persistentJsonByFolderProvider) 17 | .requireValue[getPersistentFolder()]?[getPersistentId()]; 18 | 19 | if (persistedJson == null) return null; 20 | 21 | try { 22 | return fromJsonConstructor(persistedJson); 23 | } catch (e) { 24 | print( 25 | 'Error parsing persisted json for ${getPersistentFolder()}-${getPersistentId()}: $e', 26 | ); 27 | PersistenceManager.deleteModelJson( 28 | getPersistentFolder(), 29 | getPersistentId(), 30 | ); 31 | return null; 32 | } 33 | } 34 | 35 | void persistChanges({bool clearOnDispose = true}) { 36 | if (_isInitialized) return; 37 | _isInitialized = true; 38 | ref.listenSelf((previous, next) { 39 | if (previous != next) { 40 | print( 41 | 'Persisting changes for ${getPersistentFolder()}-${getPersistentId()}', 42 | ); 43 | PersistenceManager.queueStoreModelJson( 44 | getPersistentFolder(), 45 | getPersistentId(), 46 | next.toJson(), 47 | ); 48 | } 49 | }); 50 | if (clearOnDispose == false) return; 51 | ref.onDispose(() { 52 | print( 53 | 'Deleting persisted model for ${getPersistentFolder()}-${getPersistentId()}', 54 | ); 55 | PersistenceManager.deleteModelJson( 56 | getPersistentFolder(), 57 | getPersistentId(), 58 | ); 59 | }); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /shell/lib/shared/provider/cursor_position.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'cursor_position.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | class CursorPosition extends _$CursorPosition { 8 | @override 9 | Offset build() { 10 | return Offset.zero; 11 | } 12 | 13 | // Make these public. 14 | 15 | @override 16 | Offset get state => super.state; 17 | 18 | @override 19 | set state(Offset value) { 20 | super.state = value; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /shell/lib/shared/provider/dbus_client.dart: -------------------------------------------------------------------------------- 1 | import 'package:dbus/dbus.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'dbus_client.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | DBusClient dbusClient(DbusClientRef ref) { 8 | final client = DBusClient.system(); 9 | ref.onDispose(client.close); 10 | return client; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/shared/provider/mouse_button_tracker.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | 3 | part 'mouse_button_tracker.g.dart'; 4 | 5 | @Riverpod(keepAlive: true) 6 | MouseButtonTracker mouseButtonTracker(MouseButtonTrackerRef ref) => 7 | MouseButtonTracker(); 8 | 9 | /// When receiving a pointer event in the Listener widget, we can only have a bitmap of all pressed mouse buttons, and 10 | /// not the button that has been pressed or released. This class tracks changes between such bitmaps and returns the 11 | /// difference in the form of a MouseButtonEvent object. 12 | class MouseButtonTracker { 13 | int buttons = 0; 14 | 15 | List trackButtonState(int newButtons) { 16 | if (buttons == newButtons) { 17 | return []; 18 | } 19 | 20 | final delta = []; 21 | 22 | for (var i = 0; i < 8; i++) { 23 | final mask = 1 << i; 24 | if ((buttons & mask) == 0 && (newButtons & mask) != 0) { 25 | delta.add(MouseButtonEvent(mask, MouseButtonState.pressed)); 26 | } else if ((buttons & mask) != 0 && (newButtons & mask) == 0) { 27 | delta.add(MouseButtonEvent(mask, MouseButtonState.released)); 28 | } 29 | } 30 | 31 | buttons = newButtons; 32 | return delta; 33 | } 34 | } 35 | 36 | enum MouseButtonState { 37 | pressed, 38 | released, 39 | } 40 | 41 | class MouseButtonEvent { 42 | MouseButtonEvent(this.button, this.state); 43 | 44 | int button; 45 | MouseButtonState state; 46 | 47 | @override 48 | String toString() { 49 | return 'MouseButtonEvent{button: $button, state: $state}'; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /shell/lib/shared/provider/now_date_time.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 4 | 5 | part 'now_date_time.g.dart'; 6 | 7 | @riverpod 8 | DateTime NowDateTime(NowDateTimeRef ref) { 9 | Timer.periodic(const Duration(seconds: 1), (_) { 10 | ref.invalidateSelf(); 11 | }); 12 | return DateTime.now(); 13 | } 14 | -------------------------------------------------------------------------------- /shell/lib/shared/provider/persistent_json_by_folder.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/shared/persistence/persistence_manager.dart'; 3 | 4 | part 'persistent_json_by_folder.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | class PersistentJsonByFolder extends _$PersistentJsonByFolder { 8 | @override 9 | FutureOr>>> build() { 10 | return PersistenceManager.loadAllModelsJson(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /shell/lib/shared/provider/root_overlay.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 3 | 4 | part 'root_overlay.g.dart'; 5 | 6 | @Riverpod(keepAlive: true) 7 | GlobalKey rootOverlayKey(RootOverlayKeyRef ref) => GlobalKey(); 8 | -------------------------------------------------------------------------------- /shell/lib/shared/state/task_switcher/model/task_switcher.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | 3 | part 'task_switcher.freezed.dart'; 4 | 5 | @freezed 6 | class TaskSwitcherState with _$TaskSwitcherState { 7 | const factory TaskSwitcherState({ 8 | required bool shown, 9 | required int index, 10 | }) = _TaskSwitcherState; 11 | } 12 | -------------------------------------------------------------------------------- /shell/lib/shared/state/task_switcher/provider/task_switcher.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/shared/state/task_switcher/model/task_switcher.dart'; 3 | import 'package:shell/shared/tasks/provider/tasks.dart'; 4 | 5 | part 'task_switcher.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | class TaskSwitcher extends _$TaskSwitcher { 9 | @override 10 | TaskSwitcherState build() { 11 | ref.listen(tasksProvider.select((value) => value.tasks.length), 12 | (_, length) { 13 | if (length == 0) { 14 | return; 15 | } 16 | state = state.copyWith(index: state.index % length); 17 | }); 18 | 19 | return const TaskSwitcherState( 20 | shown: false, 21 | index: 0, 22 | ); 23 | } 24 | 25 | void show(bool right) { 26 | final length = ref.read(tasksProvider).tasks.length; 27 | state = state.copyWith( 28 | shown: true, 29 | index: length < 2 30 | ? 0 31 | : right 32 | ? 1 33 | : length - 1, 34 | ); 35 | } 36 | 37 | void hide() { 38 | state = state.copyWith( 39 | shown: false, 40 | ); 41 | } 42 | 43 | void next() { 44 | final length = ref.read(tasksProvider).tasks.length; 45 | if (length == 0) { 46 | return; 47 | } 48 | state = state.copyWith( 49 | index: (state.index + 1) % ref.read(tasksProvider).tasks.length, 50 | ); 51 | } 52 | 53 | void previous() { 54 | final length = ref.read(tasksProvider).tasks.length; 55 | if (length == 0) { 56 | return; 57 | } 58 | state = state.copyWith( 59 | index: (state.index - 1) % ref.read(tasksProvider).tasks.length, 60 | ); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /shell/lib/shared/tasks/model/tasks.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:shell/shared/tasks/provider/tasks.dart'; 4 | 5 | part 'tasks.freezed.dart'; 6 | 7 | @freezed 8 | class TasksState with _$TasksState { 9 | const factory TasksState({ 10 | required IList tasks, 11 | 12 | /// The list of operations applied to the previous tasks list to obtain current one. 13 | /// The desktop window stack will use this diff to delay the removal of windows in order to 14 | /// animate their closing instead of synchronizing with the tasks list immediately. 15 | required IList> diff, 16 | }) = _TasksState; 17 | } 18 | -------------------------------------------------------------------------------- /shell/lib/shared/util/json_converter/offset.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | class OffsetConverter extends JsonConverter> { 6 | /// Serialize [Offset] to [Map]. 7 | const OffsetConverter(); 8 | 9 | @override 10 | Offset fromJson(Map json) => 11 | Offset((json['x']! as num).toDouble(), (json['y']! as num).toDouble()); 12 | 13 | @override 14 | Map toJson(Offset object) => 15 | {'x': object.dx, 'y': object.dy}; 16 | } 17 | -------------------------------------------------------------------------------- /shell/lib/shared/util/json_converter/rect.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | class RectConverter extends JsonConverter> { 6 | /// Serialize [Rect] to [Map]. 7 | const RectConverter(); 8 | 9 | @override 10 | Rect fromJson(Map json) => Rect.fromLTWH( 11 | (json['x']! as num).toDouble(), 12 | (json['y']! as num).toDouble(), 13 | (json['width']! as num).toDouble(), 14 | (json['height']! as num).toDouble(), 15 | ); 16 | 17 | @override 18 | Map toJson(Rect object) => { 19 | 'x': object.left, 20 | 'y': object.top, 21 | 'width': object.width, 22 | 'height': object.height, 23 | }; 24 | } 25 | -------------------------------------------------------------------------------- /shell/lib/shared/util/json_converter/size.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:json_annotation/json_annotation.dart'; 4 | 5 | class SizeConverter extends JsonConverter> { 6 | /// Serialize [Size] to [List]. 7 | const SizeConverter(); 8 | 9 | @override 10 | Size fromJson(Map json) => Size( 11 | (json['width']! as num).toDouble(), 12 | (json['height']! as num).toDouble(), 13 | ); 14 | 15 | @override 16 | Map toJson(Size object) => 17 | {'width': object.width, 'height': object.height}; 18 | } 19 | -------------------------------------------------------------------------------- /shell/lib/shared/util/logger.dart: -------------------------------------------------------------------------------- 1 | import 'package:logger/logger.dart'; 2 | import 'package:logging/logging.dart' as Logging; 3 | 4 | final _simpleLogger = Logger( 5 | printer: HybridPrinter( 6 | PrettyPrinter( 7 | methodCount: 0, 8 | noBoxingByDefault: true, 9 | ), 10 | info: PrettyPrinter( 11 | methodCount: 0, 12 | noBoxingByDefault: true, 13 | printEmojis: false, 14 | ), 15 | ), 16 | ); 17 | 18 | final _stacktraceLogger = Logger( 19 | printer: PrettyPrinter(), 20 | ); 21 | 22 | /// Configures the logging system to print logs to the console. 23 | void configureLogs() { 24 | Logging.Logger.root.level = Logging.Level.ALL; // defaults to Level.INFO 25 | Logging.Logger.root.onRecord.listen((record) { 26 | final logger = 27 | record.stackTrace != null ? _stacktraceLogger : _simpleLogger; 28 | 29 | final loggerFunc = switch (record.level) { 30 | Logging.Level.INFO => logger.i, 31 | Logging.Level.WARNING => logger.w, 32 | Logging.Level.SEVERE => logger.e, 33 | _ => logger.d, 34 | }; 35 | 36 | loggerFunc( 37 | '[${record.loggerName}] ${record.message}', 38 | stackTrace: record.stackTrace, 39 | ); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /shell/lib/shared/util/rect_overflow_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | // Crops the child to a rectangle inside it. 4 | // The size of this widget will be the size of the rectangle. 5 | // The child can still visually overflow even though hit-testing doesn't work outside the rectangle. 6 | class CropOverflowBox extends StatelessWidget { 7 | 8 | const CropOverflowBox({ 9 | required this.rect, required this.child, super.key, 10 | this.resetTopLeft = false, 11 | }); 12 | final Widget child; 13 | final Rect rect; 14 | final bool resetTopLeft; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Transform.translate( 19 | offset: resetTopLeft ? Offset.zero : rect.topLeft, 20 | child: SizedOverflowBox( 21 | alignment: Alignment.topLeft, 22 | size: rect.size + rect.topLeft, 23 | child: Transform.translate( 24 | offset: -rect.topLeft, 25 | child: child, 26 | ), 27 | ), 28 | ); 29 | } 30 | } 31 | 32 | class TransformBox extends StatelessWidget { 33 | 34 | const TransformBox({ 35 | required this.from, required this.to, required this.child, super.key, 36 | }); 37 | final Rect from; 38 | final Rect to; 39 | final Widget child; 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return Transform.scale( 44 | alignment: Alignment.topLeft, 45 | scaleX: to.width / from.width, 46 | scaleY: to.height / from.height, 47 | child: child, 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /shell/lib/shared/widget/clock.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_hooks/flutter_hooks.dart'; 3 | import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 | import 'package:intl/intl.dart'; 5 | import 'package:shell/shared/provider/now_date_time.dart'; 6 | 7 | class ClockWidget extends HookConsumerWidget { 8 | const ClockWidget({super.key}); 9 | 10 | @override 11 | Widget build(BuildContext context, WidgetRef ref) { 12 | final now = useState(DateTime.now()); 13 | ref.listen(nowDateTimeProvider, (prev, next) { 14 | if (prev?.minute != next.minute) now.value = next; 15 | }); 16 | final currentTime = DateFormat('hh:mm a').format(now.value); 17 | final currentDate = DateFormat('MMMM dd, yyyy').format(now.value); 18 | 19 | return Row( 20 | mainAxisAlignment: MainAxisAlignment.center, 21 | children: [ 22 | Text( 23 | currentTime, 24 | style: Theme.of(context).textTheme.titleMedium!.copyWith( 25 | fontWeight: FontWeight.w500, 26 | ), 27 | ), 28 | const SizedBox(width: 16), 29 | Text( 30 | currentDate, 31 | style: Theme.of(context).textTheme.titleMedium!.copyWith( 32 | fontWeight: FontWeight.w500, 33 | ), 34 | ), 35 | ], 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /shell/lib/systemd/provider/session.dart: -------------------------------------------------------------------------------- 1 | import 'package:riverpod_annotation/riverpod_annotation.dart'; 2 | import 'package:shell/shared/provider/dbus_client.dart'; 3 | import 'package:ubuntu_session/ubuntu_session.dart'; 4 | 5 | part 'session.g.dart'; 6 | 7 | @Riverpod(keepAlive: true) 8 | class Session extends _$Session { 9 | @override 10 | SystemdSessionManager build() { 11 | final client = ref.watch(dbusClientProvider); 12 | 13 | return SystemdSessionManager(bus: client); 14 | } 15 | 16 | /// Returns a list of all user sessions. 17 | Future> getUserSessions() async => 18 | Stream.fromIterable(await state.listSessions()) 19 | .asyncMap((session) async { 20 | final classStr = await session.classString; 21 | return classStr == 'user' ? session : null; 22 | }) 23 | .where((session) => session != null) 24 | .cast() 25 | .toList(); 26 | 27 | /// Returns the active user session. 28 | Future getActiveUserSession() async { 29 | final sessions = await getUserSessions(); 30 | for (final session in sessions) { 31 | if (await session.active) { 32 | return session; 33 | } 34 | } 35 | return null; 36 | } 37 | 38 | Future shutdown() => state.powerOff(true); 39 | 40 | Future reboot() => state.reboot(true); 41 | 42 | Future sleep() => state.reboot(true); 43 | 44 | Future logout() async { 45 | final session = await getActiveUserSession(); 46 | if (session != null) { 47 | await session.terminate(interactive: true); 48 | } 49 | } 50 | 51 | Future lock() async { 52 | final session = await getActiveUserSession(); 53 | if (session != null) { 54 | await session.lock(interactive: true); 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /shell/lib/theme/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | const veshellColor = Color.fromARGB(255, 67, 76, 94); 4 | const surfaceRadius = 24.0; 5 | const panelSize = 48.0; 6 | 7 | class VeshellTheme { 8 | static ThemeData get light => _buildTheme( 9 | ThemeData.light( 10 | useMaterial3: true, 11 | ), 12 | ); 13 | static ThemeData get dark => _buildTheme( 14 | ThemeData.dark( 15 | useMaterial3: true, 16 | ), 17 | ); 18 | 19 | static ThemeData _buildTheme(ThemeData defaultTheme) { 20 | final colorScheme = ColorScheme.fromSeed( 21 | seedColor: veshellColor, 22 | brightness: defaultTheme.brightness, 23 | ); 24 | 25 | final lighterSurface = Color.lerp( 26 | colorScheme.surface, 27 | Colors.white, 28 | 0.4, 29 | )!; 30 | 31 | var theme = defaultTheme.copyWith( 32 | visualDensity: VisualDensity.standard, 33 | brightness: colorScheme.brightness, 34 | colorScheme: colorScheme, 35 | highlightColor: lighterSurface.withOpacity(0.12), 36 | focusColor: lighterSurface.withOpacity(0.24), 37 | hoverColor: lighterSurface.withOpacity(0.08), 38 | ); 39 | 40 | theme = _applyCardTheme(theme); 41 | return theme; 42 | } 43 | 44 | static ThemeData _applyCardTheme(ThemeData theme) { 45 | return theme.copyWith( 46 | cardTheme: theme.cardTheme.copyWith( 47 | color: theme.colorScheme.surface, 48 | elevation: 0, 49 | shape: RoundedRectangleBorder( 50 | borderRadius: BorderRadius.circular(surfaceRadius), 51 | ), 52 | ), 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/app_id_changed/app_id_changed.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'app_id_changed.serializable.freezed.dart'; 6 | 7 | part 'app_id_changed.serializable.g.dart'; 8 | 9 | /// Model for AppIdChangedMessage 10 | @freezed 11 | class AppIdChangedMessage 12 | with _$AppIdChangedMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory AppIdChangedMessage({ 16 | required SurfaceId surfaceId, 17 | required String? appId, 18 | }) = _AppIdChangedMessage; 19 | 20 | factory AppIdChangedMessage.fromJson(Map json) => 21 | _$AppIdChangedMessageFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/destroy_popup/destroy_popup.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'destroy_popup.serializable.freezed.dart'; 6 | part 'destroy_popup.serializable.g.dart'; 7 | 8 | /// Model for DestroyPopupMessage 9 | @freezed 10 | class DestroyPopupMessage with _$DestroyPopupMessage implements WaylandMessage { 11 | /// Factory 12 | factory DestroyPopupMessage({ 13 | required SurfaceId surfaceId, 14 | }) = _DestroyPopupMessage; 15 | 16 | factory DestroyPopupMessage.fromJson(Map json) => 17 | _$DestroyPopupMessageFromJson(json); 18 | } 19 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/destroy_subsurface/destroy_subsurface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'destroy_subsurface.serializable.freezed.dart'; 6 | part 'destroy_subsurface.serializable.g.dart'; 7 | 8 | /// Model for DestroySubsurfaceMessage 9 | @freezed 10 | class DestroySubsurfaceMessage 11 | with _$DestroySubsurfaceMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory DestroySubsurfaceMessage({ 15 | required SurfaceId surfaceId, 16 | }) = _DestroySubsurfaceMessage; 17 | 18 | factory DestroySubsurfaceMessage.fromJson(Map json) => 19 | _$DestroySubsurfaceMessageFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/destroy_surface/destroy_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'destroy_surface.serializable.freezed.dart'; 6 | part 'destroy_surface.serializable.g.dart'; 7 | 8 | /// Model for DestroySurfaceMessage 9 | @freezed 10 | class DestroySurfaceMessage 11 | with _$DestroySurfaceMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory DestroySurfaceMessage({ 15 | required SurfaceId surfaceId, 16 | }) = _DestroySurfaceMessage; 17 | 18 | factory DestroySurfaceMessage.fromJson(Map json) => 19 | _$DestroySurfaceMessageFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/destroy_toplevel/destroy_toplevel.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'destroy_toplevel.serializable.freezed.dart'; 6 | 7 | part 'destroy_toplevel.serializable.g.dart'; 8 | 9 | /// Model for DestroyToplevelMessage 10 | @freezed 11 | class DestroyToplevelMessage 12 | with _$DestroyToplevelMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory DestroyToplevelMessage({ 16 | required SurfaceId surfaceId, 17 | }) = _DestroyToplevelMessage; 18 | 19 | factory DestroyToplevelMessage.fromJson(Map json) => 20 | _$DestroyToplevelMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/destroy_x11_surface/destroy_x11_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/x11_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'destroy_x11_surface.serializable.freezed.dart'; 6 | 7 | part 'destroy_x11_surface.serializable.g.dart'; 8 | 9 | /// Model for DestroyX11SurfaceMessage 10 | @freezed 11 | class DestroyX11SurfaceMessage 12 | with _$DestroyX11SurfaceMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory DestroyX11SurfaceMessage({ 16 | required X11SurfaceId x11SurfaceId, 17 | }) = _DestroyX11SurfaceMessage; 18 | 19 | factory DestroyX11SurfaceMessage.fromJson(Map json) => 20 | _$DestroyX11SurfaceMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/destroy_xdg_surface/destroy_xdg_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'destroy_xdg_surface.serializable.freezed.dart'; 6 | 7 | part 'destroy_xdg_surface.serializable.g.dart'; 8 | 9 | /// Model for DestroyXdgSurfaceMessage 10 | @freezed 11 | class DestroyXdgSurfaceMessage 12 | with _$DestroyXdgSurfaceMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory DestroyXdgSurfaceMessage({ 16 | required SurfaceId surfaceId, 17 | }) = _DestroyXdgSurfaceMessage; 18 | 19 | factory DestroyXdgSurfaceMessage.fromJson(Map json) => 20 | _$DestroyXdgSurfaceMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/interactive_move/interactive_move.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'interactive_move.serializable.freezed.dart'; 6 | part 'interactive_move.serializable.g.dart'; 7 | 8 | /// Model for InteractiveMoveMessage 9 | @freezed 10 | class InteractiveMoveMessage 11 | with _$InteractiveMoveMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory InteractiveMoveMessage({ 15 | required SurfaceId surfaceId, 16 | }) = _InteractiveMoveMessage; 17 | 18 | factory InteractiveMoveMessage.fromJson(Map json) => 19 | _$InteractiveMoveMessageFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/interactive_resize/interactive_resize.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'interactive_resize.serializable.freezed.dart'; 6 | part 'interactive_resize.serializable.g.dart'; 7 | 8 | /// Model for InteractiveResizeMessage 9 | @freezed 10 | class InteractiveResizeMessage 11 | with _$InteractiveResizeMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory InteractiveResizeMessage({ 15 | required SurfaceId surfaceId, 16 | required int edge, 17 | }) = _InteractiveResizeMessage; 18 | 19 | factory InteractiveResizeMessage.fromJson(Map json) => 20 | _$InteractiveResizeMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/map_x11_surface/map_x11_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:shell/shared/util/json_converter/rect.dart'; 5 | import 'package:shell/wayland/model/x11_surface.dart'; 6 | import 'package:shell/wayland/provider/wayland.manager.dart'; 7 | 8 | part 'map_x11_surface.serializable.freezed.dart'; 9 | part 'map_x11_surface.serializable.g.dart'; 10 | 11 | /// Model for MapX11SurfaceMessage 12 | @freezed 13 | sealed class MapX11SurfaceMessage 14 | with _$MapX11SurfaceMessage 15 | implements WaylandMessage { 16 | /// Factory 17 | factory MapX11SurfaceMessage({ 18 | required X11SurfaceId x11SurfaceId, 19 | @RectConverter() required Rect geometry, 20 | required X11SurfaceId? parent, 21 | }) = _MapX11SurfaceMessage; 22 | 23 | factory MapX11SurfaceMessage.fromJson(Map json) => 24 | _$MapX11SurfaceMessageFromJson(json); 25 | } 26 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/monitor_layout_changed/monitor_layout_changed.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/monitor/model/monitor.serializable.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'monitor_layout_changed.serializable.freezed.dart'; 6 | part 'monitor_layout_changed.serializable.g.dart'; 7 | 8 | /// Model for MonitorLayoutChangedMessage 9 | @freezed 10 | class MonitorLayoutChangedMessage 11 | with _$MonitorLayoutChangedMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory MonitorLayoutChangedMessage({ 15 | required List monitors, 16 | }) = _MonitorLayoutChangedMessage; 17 | 18 | factory MonitorLayoutChangedMessage.fromJson(Map json) => 19 | _$MonitorLayoutChangedMessageFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/new_popup/new_popup.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:freezed_annotation/freezed_annotation.dart'; 4 | import 'package:shell/shared/util/json_converter/offset.dart'; 5 | import 'package:shell/wayland/model/wl_surface.dart'; 6 | import 'package:shell/wayland/provider/wayland.manager.dart'; 7 | 8 | part 'new_popup.serializable.freezed.dart'; 9 | 10 | part 'new_popup.serializable.g.dart'; 11 | 12 | /// Model for NewPopupMessage 13 | @freezed 14 | sealed class NewPopupMessage with _$NewPopupMessage implements WaylandMessage { 15 | /// Factory 16 | factory NewPopupMessage({ 17 | required SurfaceId surfaceId, 18 | required SurfaceId parent, 19 | @OffsetConverter() required Offset position, 20 | }) = _NewPopupMessage; 21 | 22 | factory NewPopupMessage.fromJson(Map json) => 23 | _$NewPopupMessageFromJson(json); 24 | } 25 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/new_subsurface/new_subsurface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'new_subsurface.serializable.freezed.dart'; 6 | 7 | part 'new_subsurface.serializable.g.dart'; 8 | 9 | /// Model for NewSubsurfaceMessage 10 | @freezed 11 | sealed class NewSubsurfaceMessage 12 | with _$NewSubsurfaceMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory NewSubsurfaceMessage({ 16 | required SurfaceId surfaceId, 17 | required SurfaceId parent, 18 | }) = _NewSubsurfaceMessage; 19 | 20 | factory NewSubsurfaceMessage.fromJson(Map json) => 21 | _$NewSubsurfaceMessageFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/new_surface/new_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'new_surface.serializable.freezed.dart'; 6 | 7 | part 'new_surface.serializable.g.dart'; 8 | 9 | /// Model for NewSurfaceMessage 10 | @freezed 11 | sealed class NewSurfaceMessage 12 | with _$NewSurfaceMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory NewSurfaceMessage({ 16 | required SurfaceId surfaceId, 17 | }) = _NewSurfaceMessage; 18 | 19 | factory NewSurfaceMessage.fromJson(Map json) => 20 | _$NewSurfaceMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/new_toplevel/new_toplevel.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'new_toplevel.serializable.freezed.dart'; 6 | part 'new_toplevel.serializable.g.dart'; 7 | 8 | /// Model for NewToplevelMessage 9 | @freezed 10 | sealed class NewToplevelMessage 11 | with _$NewToplevelMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory NewToplevelMessage({ 15 | required SurfaceId surfaceId, 16 | required int pid, 17 | }) = _NewToplevelMessage; 18 | 19 | factory NewToplevelMessage.fromJson(Map json) => 20 | _$NewToplevelMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/new_x11_surface/new_x11_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/x11_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'new_x11_surface.serializable.freezed.dart'; 6 | part 'new_x11_surface.serializable.g.dart'; 7 | 8 | /// Model for NewX11SurfaceMessage 9 | @freezed 10 | sealed class NewX11SurfaceMessage 11 | with _$NewX11SurfaceMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory NewX11SurfaceMessage({ 15 | required X11SurfaceId x11SurfaceId, 16 | required bool overrideRedirect, 17 | }) = _NewX11SurfaceMessage; 18 | 19 | factory NewX11SurfaceMessage.fromJson(Map json) => 20 | _$NewX11SurfaceMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/set_environment_variables/set_environment_variables.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'set_environment_variables.serializable.freezed.dart'; 6 | part 'set_environment_variables.serializable.g.dart'; 7 | 8 | /// Model for SetEnvironmentVariablesMessage 9 | @freezed 10 | sealed class SetEnvironmentVariablesMessage 11 | with _$SetEnvironmentVariablesMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory SetEnvironmentVariablesMessage({ 15 | /// A nullable value means that the variable should be unset. 16 | required IMap environmentVariables, 17 | }) = _SetEnvironmentVariablesMessage; 18 | 19 | factory SetEnvironmentVariablesMessage.fromJson(Map json) => 20 | _$SetEnvironmentVariablesMessageFromJson(json); 21 | } 22 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/surface_associated/surface_associated.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/model/x11_surface.dart'; 4 | import 'package:shell/wayland/provider/wayland.manager.dart'; 5 | 6 | part 'surface_associated.serializable.freezed.dart'; 7 | part 'surface_associated.serializable.g.dart'; 8 | 9 | /// Model for SurfaceAssociatedMessage 10 | @freezed 11 | sealed class SurfaceAssociatedMessage 12 | with _$SurfaceAssociatedMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory SurfaceAssociatedMessage({ 16 | required X11SurfaceId x11SurfaceId, 17 | required SurfaceId surfaceId, 18 | }) = _SurfaceAssociatedMessage; 19 | 20 | factory SurfaceAssociatedMessage.fromJson(Map json) => 21 | _$SurfaceAssociatedMessageFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/title_changed/title_changed.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/wl_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'title_changed.serializable.freezed.dart'; 6 | 7 | part 'title_changed.serializable.g.dart'; 8 | 9 | /// Model for TitleChangedMessage 10 | @freezed 11 | class TitleChangedMessage 12 | with _$TitleChangedMessage 13 | implements WaylandMessage { 14 | /// Factory 15 | factory TitleChangedMessage({ 16 | required SurfaceId surfaceId, 17 | required String? title, 18 | }) = _TitleChangedMessage; 19 | 20 | factory TitleChangedMessage.fromJson(Map json) => 21 | _$TitleChangedMessageFromJson(json); 22 | } 23 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/unmap_x11_surface/unmap_x11_surface.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/x11_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'unmap_x11_surface.serializable.freezed.dart'; 6 | part 'unmap_x11_surface.serializable.g.dart'; 7 | 8 | /// Model for UnmapX11SurfaceMessage 9 | @freezed 10 | sealed class UnmapX11SurfaceMessage 11 | with _$UnmapX11SurfaceMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory UnmapX11SurfaceMessage({ 15 | required X11SurfaceId x11SurfaceId, 16 | }) = _UnmapX11SurfaceMessage; 17 | 18 | factory UnmapX11SurfaceMessage.fromJson(Map json) => 19 | _$UnmapX11SurfaceMessageFromJson(json); 20 | } 21 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/event/x11_properties_changed/x11_properties_changed.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/x11_surface.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'x11_properties_changed.serializable.freezed.dart'; 6 | part 'x11_properties_changed.serializable.g.dart'; 7 | 8 | /// Model for X11PropertiesChangedMessage 9 | @freezed 10 | sealed class X11PropertiesChangedMessage 11 | with _$X11PropertiesChangedMessage 12 | implements WaylandMessage { 13 | /// Factory 14 | factory X11PropertiesChangedMessage({ 15 | required X11SurfaceId x11SurfaceId, 16 | required String? title, 17 | required String? windowClass, 18 | required String? instance, 19 | required String? startupId, 20 | required int? pid, 21 | }) = _X11PropertiesChangedMessage; 22 | 23 | factory X11PropertiesChangedMessage.fromJson(Map json) => 24 | _$X11PropertiesChangedMessageFromJson(json); 25 | } 26 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/request/activate_window/activate_window.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/request/wayland_request.dart'; 3 | import 'package:shell/wayland/model/wl_surface.dart'; 4 | import 'package:shell/wayland/provider/wayland.manager.dart'; 5 | 6 | part 'activate_window.serializable.freezed.dart'; 7 | part 'activate_window.serializable.g.dart'; 8 | 9 | /// [ActivateWindowRequest] 10 | class ActivateWindowRequest extends WaylandRequest { 11 | /// constructor 12 | const ActivateWindowRequest({ 13 | required ActivateWindowMessage super.message, 14 | super.method = 'activate_window', 15 | }); 16 | } 17 | 18 | /// Model for [ActivateWindowMessage] 19 | @freezed 20 | class ActivateWindowMessage 21 | with _$ActivateWindowMessage 22 | implements WaylandMessage { 23 | /// Factory 24 | factory ActivateWindowMessage({ 25 | required SurfaceId surfaceId, 26 | required bool activate, 27 | }) = _ActivateWindowMessage; 28 | 29 | /// Creates a new [ActivateWindowMessage] instance from a map. 30 | /// 31 | /// This constructor is used by the `json_serializable` package to 32 | /// deserialize JSON data into a [ActivateWindowMessage] instance. 33 | factory ActivateWindowMessage.fromJson(Map json) => 34 | _$ActivateWindowMessageFromJson(json); 35 | } 36 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/request/close_window/close_window.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/request/wayland_request.dart'; 3 | import 'package:shell/wayland/model/wl_surface.dart'; 4 | import 'package:shell/wayland/provider/wayland.manager.dart'; 5 | 6 | part 'close_window.serializable.freezed.dart'; 7 | part 'close_window.serializable.g.dart'; 8 | 9 | /// [CloseWindowRequest] 10 | class CloseWindowRequest extends WaylandRequest { 11 | /// constructor 12 | const CloseWindowRequest({ 13 | required CloseWindowMessage super.message, 14 | super.method = 'close_window', 15 | }); 16 | } 17 | 18 | /// Model for [CloseWindowMessage] 19 | @freezed 20 | class CloseWindowMessage with _$CloseWindowMessage implements WaylandMessage { 21 | /// Factory 22 | factory CloseWindowMessage({ 23 | required SurfaceId surfaceId, 24 | }) = _CloseWindowMessage; 25 | 26 | /// Creates a new [CloseWindowMessage] instance from a map. 27 | /// 28 | /// This constructor is used by the `json_serializable` package to 29 | /// deserialize JSON data into a [CloseWindowMessage] instance. 30 | factory CloseWindowMessage.fromJson(Map json) => 31 | _$CloseWindowMessageFromJson(json); 32 | } 33 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/request/get_environment_variables/get_environment_variables.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/request/wayland_request.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'get_environment_variables.serializable.freezed.dart'; 6 | 7 | part 'get_environment_variables.serializable.g.dart'; 8 | 9 | /// [GetEnvironmentVariablesRequest] 10 | class GetEnvironmentVariablesRequest extends WaylandRequest { 11 | /// constructor 12 | const GetEnvironmentVariablesRequest({ 13 | required GetEnvironmentVariablesMessage super.message, 14 | super.method = 'get_environment_variables', 15 | }); 16 | } 17 | 18 | /// Model for [GetEnvironmentVariablesMessage] 19 | @freezed 20 | class GetEnvironmentVariablesMessage 21 | with _$GetEnvironmentVariablesMessage 22 | implements WaylandMessage { 23 | /// Factory 24 | factory GetEnvironmentVariablesMessage() = _GetEnvironmentVariablesMessage; 25 | 26 | /// Creates a new [GetEnvironmentVariablesMessage] instance from a map. 27 | /// 28 | /// This constructor is used by the `json_serializable` package to 29 | /// deserialize JSON data into a [GetEnvironmentVariablesMessage] instance. 30 | factory GetEnvironmentVariablesMessage.fromJson(Map json) => 31 | _$GetEnvironmentVariablesMessageFromJson(json); 32 | } 33 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/request/get_monitor_layout/get_monitor_layout.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/request/wayland_request.dart'; 3 | import 'package:shell/wayland/provider/wayland.manager.dart'; 4 | 5 | part 'get_monitor_layout.serializable.freezed.dart'; 6 | part 'get_monitor_layout.serializable.g.dart'; 7 | 8 | /// [GetMonitorLayoutRequest] 9 | class GetMonitorLayoutRequest extends WaylandRequest { 10 | /// constructor 11 | const GetMonitorLayoutRequest({ 12 | required GetMonitorLayoutMessage super.message, 13 | super.method = 'get_monitor_layout', 14 | }); 15 | } 16 | 17 | /// Model for [GetMonitorLayoutMessage] 18 | @freezed 19 | class GetMonitorLayoutMessage 20 | with _$GetMonitorLayoutMessage 21 | implements WaylandMessage { 22 | /// Factory 23 | factory GetMonitorLayoutMessage() = _GetMonitorLayoutMessage; 24 | 25 | /// Creates a new [GetMonitorLayoutMessage] instance from a map. 26 | /// 27 | /// This constructor is used by the `json_serializable` package to 28 | /// deserialize JSON data into a [GetMonitorLayoutMessage] instance. 29 | factory GetMonitorLayoutMessage.fromJson(Map json) => 30 | _$GetMonitorLayoutMessageFromJson(json); 31 | } 32 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/request/maximize_window/maximize_window.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:freezed_annotation/freezed_annotation.dart'; 2 | import 'package:shell/wayland/model/request/wayland_request.dart'; 3 | import 'package:shell/wayland/model/wl_surface.dart'; 4 | import 'package:shell/wayland/provider/wayland.manager.dart'; 5 | 6 | part 'maximize_window.serializable.freezed.dart'; 7 | part 'maximize_window.serializable.g.dart'; 8 | 9 | /// [MaximizeWindowRequest] 10 | class MaximizeWindowRequest extends WaylandRequest { 11 | /// constructor 12 | const MaximizeWindowRequest({ 13 | required MaximizeWindowMessage super.message, 14 | super.method = 'maximize_window', 15 | }); 16 | } 17 | 18 | /// Model for [MaximizeWindowMessage] 19 | @freezed 20 | class MaximizeWindowMessage 21 | with _$MaximizeWindowMessage 22 | implements WaylandMessage { 23 | /// Factory 24 | factory MaximizeWindowMessage({ 25 | required SurfaceId surfaceId, 26 | required bool isMaximized, 27 | }) = _MaximizeWindowMessage; 28 | 29 | /// Creates a new [MaximizeWindowMessage] instance from a map. 30 | /// 31 | /// This constructor is used by the `json_serializable` package to 32 | /// deserialize JSON data into a [MaximizeWindowMessage] instance. 33 | factory MaximizeWindowMessage.fromJson(Map json) => 34 | _$MaximizeWindowMessageFromJson(json); 35 | } 36 | -------------------------------------------------------------------------------- /shell/lib/wayland/model/request/mouse_buttons_event/mouse_buttons_event.serializable.dart: -------------------------------------------------------------------------------- 1 | import 'package:fast_immutable_collections/fast_immutable_collections.dart'; 2 | import 'package:freezed_annotation/freezed_annotation.dart'; 3 | import 'package:shell/wayland/model/request/wayland_request.dart'; 4 | import 'package:shell/wayland/model/wl_surface.dart'; 5 | import 'package:shell/wayland/provider/wayland.manager.dart'; 6 | 7 | part 'mouse_buttons_event.serializable.freezed.dart'; 8 | 9 | part 'mouse_buttons_event.serializable.g.dart'; 10 | 11 | /// [MouseButtonsEventRequest] 12 | class MouseButtonsEventRequest extends WaylandRequest { 13 | /// constructor 14 | const MouseButtonsEventRequest({ 15 | required MouseButtonsEventMessage super.message, 16 | super.method = 'mouse_buttons_event', 17 | }); 18 | } 19 | 20 | /// Model for [MouseButtonsEventMessage] 21 | @freezed 22 | class MouseButtonsEventMessage 23 | with _$MouseButtonsEventMessage 24 | implements WaylandMessage { 25 | /// Factory 26 | factory MouseButtonsEventMessage({ 27 | required SurfaceId surfaceId, 28 | required IList