commit 965a9439c2a348343433ee3468dda1d2edcf42e6 Author: baldeau Date: Sat Mar 9 14:29:48 2024 +0100 complete project diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3faab82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +**/doc/api/ +**/ios/Flutter/.last_build_id +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +/build/ + +# Symbolication related +app.*.symbols + +# Obfuscation related +app.*.map.json + +# Android Studio will place build artifacts here +/android/app/debug +/android/app/profile +/android/app/release + +# Rust related +.cargo/ +target/ + +# Generated messages +*/**/messages/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..b9b6155 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "packages/real_time_chart"] + path = packages/real_time_chart + url = https://avoid.sh/bachelor/real_time_chart.git +[submodule "packages/virtual_keyboard_multi_language"] + path = packages/virtual_keyboard_multi_language + url = https://avoid.sh/bachelor/virtual_keyboard_multi_language.git diff --git a/.metadata b/.metadata new file mode 100644 index 0000000..52b37db --- /dev/null +++ b/.metadata @@ -0,0 +1,33 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "7482962148e8d758338d8a28f589f317e1e42ba4" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4 + base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4 + - platform: linux + create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4 + base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4 + - platform: macos + create_revision: 7482962148e8d758338d8a28f589f317e1e42ba4 + base_revision: 7482962148e8d758338d8a28f589f317e1e42ba4 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/.puro.json b/.puro.json new file mode 100644 index 0000000..ea2575b --- /dev/null +++ b/.puro.json @@ -0,0 +1,3 @@ +{ + "env": "bachelor_elinux" +} \ No newline at end of file diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..834113c --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,19 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "${workspaceFolder}/**" + ], + "defines": [], + "macFrameworkPath": [ + "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks" + ], + "compilerPath": "/usr/bin/clang", + "cStandard": "c17", + "cppStandard": "c++17", + "intelliSenseMode": "macos-clang-arm64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..0406c79 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,4 @@ +{ + "dart.flutterSdkPath": "/Users/fabian/.puro/envs/bachelor_elinux/flutter", + "dart.sdkPath": "/Users/fabian/.puro/envs/bachelor_elinux/flutter/bin/cache/dart-sdk" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..fc892dd --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,16 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "label": "make", + "type": "shell", + "command": "dart run build_runner build --delete-conflicting-outputs", + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9b33dac --- /dev/null +++ b/LICENSE @@ -0,0 +1,29 @@ +BSD 3-Clause License + +Copyright (c) 2023, Fabian Baldeau +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d6f83b2 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +release: + flutter pub global run flutterpi_tool build --arch=arm64 --cpu=pi4 --release + +profile: + flutter pub global run flutterpi_tool build --arch=arm64 --cpu=pi4 --profile + +sync-release: + rsync -a ./build/flutter_assets/ fabian@raspberrypi:/home/fabian/release + +sync-profile: + rsync -a ./build/flutter_assets/ fabian@raspberrypi:/home/fabian/profile + +stop: + ssh -o BatchMode=no fabian@raspberrypi pkill flutter-pi + +dep-graph: + layerlens \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b1a5150 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# flutter_elinux + +A demo Flutter project for embedded Linux systems. + +## Getting Started + +This project is a starting point for a Flutter application. + +A few resources to get you started if this is your first Flutter project: + +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) + +For help getting started with Flutter development, view the +[online documentation](https://docs.flutter.dev/), which offers tutorials, +samples, guidance on mobile development, and a full API reference. diff --git a/analysis_options.yaml b/analysis_options.yaml new file mode 100644 index 0000000..9132fb3 --- /dev/null +++ b/analysis_options.yaml @@ -0,0 +1,32 @@ +# This file configures the analyzer, which statically analyzes Dart code to +# check for errors, warnings, and lints. +# +# The issues identified by the analyzer are surfaced in the UI of Dart-enabled +# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be +# invoked from the command line by running `flutter analyze`. + +# The following line activates a set of recommended lints for Flutter apps, +# packages, and plugins designed to encourage good coding practices. +include: package:flutter_lints/flutter.yaml + +linter: + # The lint rules applied to this project can be customized in the + # section below to disable rules from the `package:flutter_lints/flutter.yaml` + # included above or to enable additional rules. A list of all available lints + # and their documentation is published at https://dart.dev/lints. + # + # Instead of disabling a lint rule for the entire project in the + # section below, it can also be suppressed for a single line of code + # or a specific dart file by using the `// ignore: name_of_lint` and + # `// ignore_for_file: name_of_lint` syntax on the line or in the file + # producing the lint. + rules: + # avoid_print: false # Uncomment to disable the `avoid_print` rule + # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + +# Additional information about this file can be found at +# https://dart.dev/guides/language/analysis-options + +analyzer: + plugins: + - custom_lint diff --git a/assets/images/flame_benchmark/rogue_shooter/bullet.png b/assets/images/flame_benchmark/rogue_shooter/bullet.png new file mode 100644 index 0000000..42db32b Binary files /dev/null and b/assets/images/flame_benchmark/rogue_shooter/bullet.png differ diff --git a/assets/images/flame_benchmark/rogue_shooter/enemy.png b/assets/images/flame_benchmark/rogue_shooter/enemy.png new file mode 100644 index 0000000..3e95f7c Binary files /dev/null and b/assets/images/flame_benchmark/rogue_shooter/enemy.png differ diff --git a/assets/images/flame_benchmark/rogue_shooter/explosion.png b/assets/images/flame_benchmark/rogue_shooter/explosion.png new file mode 100644 index 0000000..c5cfd2d Binary files /dev/null and b/assets/images/flame_benchmark/rogue_shooter/explosion.png differ diff --git a/assets/images/flame_benchmark/rogue_shooter/player.png b/assets/images/flame_benchmark/rogue_shooter/player.png new file mode 100644 index 0000000..75e045e Binary files /dev/null and b/assets/images/flame_benchmark/rogue_shooter/player.png differ diff --git a/assets/images/flame_benchmark/rogue_shooter/stars.png b/assets/images/flame_benchmark/rogue_shooter/stars.png new file mode 100644 index 0000000..cf75dcb Binary files /dev/null and b/assets/images/flame_benchmark/rogue_shooter/stars.png differ diff --git a/assets/images/flame_benchmark/sprite_benchmark/ember.png b/assets/images/flame_benchmark/sprite_benchmark/ember.png new file mode 100644 index 0000000..ded814a Binary files /dev/null and b/assets/images/flame_benchmark/sprite_benchmark/ember.png differ diff --git a/assets/img/image_placeholder.png b/assets/img/image_placeholder.png new file mode 100644 index 0000000..a908dab Binary files /dev/null and b/assets/img/image_placeholder.png differ diff --git a/assets/img/raspi_logo.png b/assets/img/raspi_logo.png new file mode 100644 index 0000000..d68656d Binary files /dev/null and b/assets/img/raspi_logo.png differ diff --git a/assets/img/tagesschau_logo.jpg b/assets/img/tagesschau_logo.jpg new file mode 100644 index 0000000..4783c01 Binary files /dev/null and b/assets/img/tagesschau_logo.jpg differ diff --git a/assets/rive/dark_light_switch.riv b/assets/rive/dark_light_switch.riv new file mode 100644 index 0000000..326ff9a Binary files /dev/null and b/assets/rive/dark_light_switch.riv differ diff --git a/assets/rive/walk_cycle_blend.riv b/assets/rive/walk_cycle_blend.riv new file mode 100644 index 0000000..733caeb Binary files /dev/null and b/assets/rive/walk_cycle_blend.riv differ diff --git a/assets/rive/weather_icon_animation.riv b/assets/rive/weather_icon_animation.riv new file mode 100644 index 0000000..4d4fbea Binary files /dev/null and b/assets/rive/weather_icon_animation.riv differ diff --git a/assets/video/video_1080p.mp4 b/assets/video/video_1080p.mp4 new file mode 100644 index 0000000..f8dc5fd Binary files /dev/null and b/assets/video/video_1080p.mp4 differ diff --git a/assets/video/video_2160p.mp4 b/assets/video/video_2160p.mp4 new file mode 100644 index 0000000..726f0c0 Binary files /dev/null and b/assets/video/video_2160p.mp4 differ diff --git a/assets/video/video_720p.mp4 b/assets/video/video_720p.mp4 new file mode 100644 index 0000000..c8796de Binary files /dev/null and b/assets/video/video_720p.mp4 differ diff --git a/lib/app.dart b/lib/app.dart new file mode 100644 index 0000000..ed9cece --- /dev/null +++ b/lib/app.dart @@ -0,0 +1,36 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import 'common/settings/app_settings.dart'; +import 'constants/colors.dart'; +import 'common/routing/router.dart'; + +class App extends ConsumerWidget { + const App({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool useMaterial3 = ref.watch(useMaterial3Provider); + bool useDarkMode = ref.watch(useDarkModeProvider); + bool showPerformanceOverlay = ref.watch(showPerformanceOverlayProvider); + ColorSeed colorSeedSelected = ref.watch(colorSeedSelectedProvider); + + return MaterialApp.router( + showPerformanceOverlay: showPerformanceOverlay, + routerConfig: goRouter, + debugShowCheckedModeBanner: false, + title: 'eLinux', + theme: ThemeData( + colorSchemeSeed: colorSeedSelected.color, + useMaterial3: useMaterial3, + brightness: Brightness.light, + ), + darkTheme: ThemeData( + colorSchemeSeed: colorSeedSelected.color, + useMaterial3: useMaterial3, + brightness: Brightness.dark, + ), + themeMode: useDarkMode ? ThemeMode.dark : ThemeMode.light, + ); + } +} diff --git a/lib/common/extension/getCharCodes.dart b/lib/common/extension/getCharCodes.dart new file mode 100644 index 0000000..bfe50fa --- /dev/null +++ b/lib/common/extension/getCharCodes.dart @@ -0,0 +1,3 @@ +extension StringExtension on String { + Iterable getCharCodes() => codeUnits; +} diff --git a/lib/common/extension/toFormatedNewsDate.dart b/lib/common/extension/toFormatedNewsDate.dart new file mode 100644 index 0000000..fbb269d --- /dev/null +++ b/lib/common/extension/toFormatedNewsDate.dart @@ -0,0 +1,11 @@ +import 'package:intl/intl.dart'; + +extension StringExtension on String { + String toFormattedNewsDate() { + final parsedDate = DateTime.parse(this); + + // The intl package is used here for more robust formatting options + final DateFormat formatter = DateFormat('dd.MM.yyyy HH:mm', 'de_DE'); + return "Stand: ${formatter.format(parsedDate)} Uhr"; + } +} diff --git a/lib/common/routing/router.dart b/lib/common/routing/router.dart new file mode 100644 index 0000000..0f237c2 --- /dev/null +++ b/lib/common/routing/router.dart @@ -0,0 +1,203 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:nm/nm.dart'; + +import '../../features/benchmark/presentation/benchmark_screen.dart'; +import '../../features/benchmark/presentation/map_benchmark_screen.dart'; +import '../../features/benchmark/presentation/rogue_shooter_screen.dart'; +import '../../features/benchmark/presentation/sprite_benchmark_screen.dart'; +import '../../features/benchmark/presentation/video_benchmark_screen.dart'; +import '../../features/benchmark/presentation/vsync_benchmark_screen.dart'; +import '../../features/home/presentation/home_screen.dart'; +import '../../features/material_demo/presentation/material_demo_screen.dart'; +import '../../features/matrix_rgb/presentation/matrix_screen.dart'; +import '../../features/morse_led/presentation/led_screen.dart'; +import '../../features/news_api/presentation/news_screen.dart'; +import '../../features/settings/presentation/settings_screen/settings_screen.dart'; +import '../../features/settings/presentation/wifi_settings_screen/accesspoint_screen.dart'; +import '../../features/settings/presentation/wifi_settings_screen/wifi_settings_screen.dart'; +import '../../features/system_resources/presentation/system_resources_screen.dart'; +import '../widgets/home_scaffold.dart'; +import 'routes.dart'; + +const ValueKey _scaffoldKey = ValueKey('App scaffold'); + +final goRouter = GoRouter( + initialLocation: '/', + debugLogDiagnostics: false, + routes: [ + GoRoute( + path: '/', + redirect: (_, __) => '/home', + ), + GoRoute( + path: '/home', + name: RoutesHome.home.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.home, + child: HomeScreen(), + ), + ), + ), + GoRoute( + path: '/material-demo', + name: RoutesHome.materialDemo.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.materialDemo, + child: MaterialDemoScreen(), + ), + ), + ), + GoRoute( + path: '/news', + name: RoutesHome.news.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.news, + child: NewsScreen(), + ), + ), + ), + GoRoute( + path: '/matrix', + name: RoutesHome.matrix.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.matrix, + child: MatrixScreen(), + ), + ), + ), + GoRoute( + path: '/led', + name: RoutesHome.led.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.led, + child: LedScreen(), + ), + ), + ), + GoRoute( + path: '/system', + name: RoutesHome.system.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.system, + child: SystemResourcesScreen(), + ), + ), + ), + GoRoute( + path: '/benchmark', + name: RoutesHome.benchmark.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.benchmark, + child: BenchmarkScreen(), + ), + ), + routes: [ + GoRoute( + path: RoutesBenchmark.sprites.name, + name: RoutesBenchmark.sprites.name, + builder: (context, state) => const SpriteBenchmarkScreen(), + ), + GoRoute( + path: RoutesBenchmark.rogueShooter.name, + name: RoutesBenchmark.rogueShooter.name, + builder: (context, state) => const RogueShooterScreen(), + ), + GoRoute( + path: RoutesBenchmark.shader.name, + name: RoutesBenchmark.shader.name, + builder: (context, state) => const VsyncBenchmarkScreen(), + ), + GoRoute( + path: RoutesBenchmark.video.name, + name: RoutesBenchmark.video.name, + builder: (context, state) => const VideoBenchmarkScreen(), + ), + GoRoute( + path: RoutesBenchmark.map.name, + name: RoutesBenchmark.map.name, + builder: (context, state) => const MapBenchmarkScreen(), + ), + ], + ), + GoRoute( + path: '/settings', + name: RoutesHome.settings.name, + pageBuilder: (BuildContext context, GoRouterState state) => + FadeTransitionPage( + key: _scaffoldKey, + child: const HomeScaffold( + selectedTab: RoutesHome.settings, + child: SettingsScreen(), + ), + ), + routes: [ + GoRoute( + path: RoutesSettings.wifi.name, + name: RoutesSettings.wifi.name, + builder: (context, state) => const WifiSettingsScreen(), + routes: [ + GoRoute( + path: 'accesspoint', + name: RoutesSettings.accesspoint.name, + builder: (context, state) { + if (state.extra == null) { + return const AccessPointScreen(); + } + final accessPoint = state.extra as NetworkManagerAccessPoint; + return AccessPointScreen( + accessPoint: accessPoint, + ); + }, + ), + ], + ), + ], + ), + ], +); + +/// A page that fades in and out. +/// From the official Go Router Example: +/// https://github.com/flutter/packages/blob/6701c9e618041574cf4cc53cb028a38de8322b53/packages/go_router/example/lib/books/main.dart#L154 +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +class FadeTransitionPage extends CustomTransitionPage { + /// Creates a [FadeTransitionPage]. + FadeTransitionPage({ + required LocalKey super.key, + required super.child, + }) : super( + transitionsBuilder: (BuildContext context, + Animation animation, + Animation secondaryAnimation, + Widget child) => + FadeTransition( + opacity: animation.drive(_curveTween), + child: child, + )); + + static final CurveTween _curveTween = CurveTween(curve: Curves.easeIn); +} diff --git a/lib/common/routing/routes.dart b/lib/common/routing/routes.dart new file mode 100644 index 0000000..1db71a5 --- /dev/null +++ b/lib/common/routing/routes.dart @@ -0,0 +1,29 @@ +enum RoutesHome { + home, + materialDemo, + benchmark, + news, + led, + matrix, + system, + settings, +} + +enum RoutesSettings { + wifi, + accesspoint, +} + +enum RoutesBenchmark { + sprites, + rogueShooter, + shader, + video, + map, +} + +enum RoutesEndlessRunner { + play, + session, + settings, +} diff --git a/lib/common/settings/app_settings.dart b/lib/common/settings/app_settings.dart new file mode 100644 index 0000000..349cd03 --- /dev/null +++ b/lib/common/settings/app_settings.dart @@ -0,0 +1,61 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../../constants/colors.dart'; +import 'sharedPrefs.dart'; + +part 'app_settings.g.dart'; + +@riverpod +class UseMaterial3 extends _$UseMaterial3 { + @override + bool build() { + final bool material3 = SharedPrefs().getValue('material3') ?? true; + return material3; + } + + void toggle() { + SharedPrefs().setValue('material3', !state); + state = !state; + } +} + +@riverpod +class UseDarkMode extends _$UseDarkMode { + @override + bool build() { + final bool darkMode = SharedPrefs().getValue('darkMode') ?? false; + return darkMode; + } + + void toggle() { + SharedPrefs().setValue('darkMode', !state); + state = !state; + } +} + +@riverpod +class ColorSeedSelected extends _$ColorSeedSelected { + @override + ColorSeed build() { + final String colorSeed = + SharedPrefs().getValue('colorSeed') ?? ColorSeed.baseColor.label; + return ColorSeed.values.firstWhere((element) => element.label == colorSeed); + } + + void setColorSeed(ColorSeed colorSeed) { + SharedPrefs().setValue('colorSeed', colorSeed.label); + state = colorSeed; + } +} + +@riverpod +class ShowPerformanceOverlay extends _$ShowPerformanceOverlay { + @override + bool build() { + return false; + } + + void toggle() { + state = !state; + } +} diff --git a/lib/common/settings/app_settings.g.dart b/lib/common/settings/app_settings.g.dart new file mode 100644 index 0000000..2fcca68 --- /dev/null +++ b/lib/common/settings/app_settings.g.dart @@ -0,0 +1,73 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'app_settings.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$useMaterial3Hash() => r'38dbfd0b19379fa8ee73c1b0ec03dfe914dc23bc'; + +/// See also [UseMaterial3]. +@ProviderFor(UseMaterial3) +final useMaterial3Provider = + AutoDisposeNotifierProvider.internal( + UseMaterial3.new, + name: r'useMaterial3Provider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$useMaterial3Hash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$UseMaterial3 = AutoDisposeNotifier; +String _$useDarkModeHash() => r'd9f1ffc5d34dc2c9a59b99e0393b8f5ccc0a7040'; + +/// See also [UseDarkMode]. +@ProviderFor(UseDarkMode) +final useDarkModeProvider = + AutoDisposeNotifierProvider.internal( + UseDarkMode.new, + name: r'useDarkModeProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') ? null : _$useDarkModeHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$UseDarkMode = AutoDisposeNotifier; +String _$colorSeedSelectedHash() => r'13745dbe8aee72eb6d43f49cb0d155164a96eb8c'; + +/// See also [ColorSeedSelected]. +@ProviderFor(ColorSeedSelected) +final colorSeedSelectedProvider = + AutoDisposeNotifierProvider.internal( + ColorSeedSelected.new, + name: r'colorSeedSelectedProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$colorSeedSelectedHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ColorSeedSelected = AutoDisposeNotifier; +String _$showPerformanceOverlayHash() => + r'8fb9b73ed0c32730b2b83cf0e6db78f066603ac4'; + +/// See also [ShowPerformanceOverlay]. +@ProviderFor(ShowPerformanceOverlay) +final showPerformanceOverlayProvider = + AutoDisposeNotifierProvider.internal( + ShowPerformanceOverlay.new, + name: r'showPerformanceOverlayProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$showPerformanceOverlayHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ShowPerformanceOverlay = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/common/settings/custom_layout.dart b/lib/common/settings/custom_layout.dart new file mode 100644 index 0000000..ae0b745 --- /dev/null +++ b/lib/common/settings/custom_layout.dart @@ -0,0 +1,88 @@ +import 'package:virtual_keyboard_multi_language/virtual_keyboard_multi_language.dart'; + +// example custom layout file (example from the virtual_keyboard_multi_language package) +// here different layouts for different languages could be defined: + +class CustomLayoutKeys extends VirtualKeyboardLayoutKeys { + @override + int getLanguagesCount() => 2; + + @override + List getLanguage(int index) { + switch (index) { + case 1: + return _arabicLayout; + default: + return defaultEnglishLayout; + } + } +} + +const List _arabicLayout = [ + // Row 1 + [ + '1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + '0', + ], + // Row 2 + [ + 'ض', + 'ص', + 'ث', + 'ق', + 'ف', + 'غ', + 'ع', + 'ه', + 'خ', + 'ح', + 'د', + VirtualKeyboardKeyAction.Backspace + ], + // Row 3 + [ + 'ش', + 'س', + 'ي', + 'ب', + 'ل', + 'ا', + 'ت', + 'ن', + 'م', + 'ك', + 'ط', + VirtualKeyboardKeyAction.Return + ], + // Row 4 + [ + 'ذ', + 'ئ', + 'ء', + 'ؤ', + 'ر', + 'لا', + 'ى', + 'ة', + 'و', + '.', + 'ظ', + VirtualKeyboardKeyAction.Shift + ], + // Row 5 + [ + VirtualKeyboardKeyAction.SwithLanguage, + '@', + VirtualKeyboardKeyAction.Space, + '-', + '_', + ] +]; diff --git a/lib/common/settings/sharedPrefs.dart b/lib/common/settings/sharedPrefs.dart new file mode 100644 index 0000000..89ffd1c --- /dev/null +++ b/lib/common/settings/sharedPrefs.dart @@ -0,0 +1,31 @@ +import 'package:shared_preferences/shared_preferences.dart'; + +/// A class to handle shared preferences +class SharedPrefs { + late final SharedPreferences _sharedPrefs; + + static final SharedPrefs _instance = SharedPrefs._internal(); + factory SharedPrefs() => _instance; + SharedPrefs._internal(); + Future init() async { + _sharedPrefs = await SharedPreferences.getInstance(); + } + + setValue(String key, dynamic value) { + if (value is bool) { + _sharedPrefs.setBool(key, value); + } else if (value is int) { + _sharedPrefs.setInt(key, value); + } else if (value is double) { + _sharedPrefs.setDouble(key, value); + } else if (value is String) { + _sharedPrefs.setString(key, value); + } else if (value is List) { + _sharedPrefs.setStringList(key, value); + } + } + + dynamic getValue(String key) { + return _sharedPrefs.get(key); + } +} diff --git a/lib/common/utils/linux_shutdown.dart b/lib/common/utils/linux_shutdown.dart new file mode 100644 index 0000000..cdfed71 --- /dev/null +++ b/lib/common/utils/linux_shutdown.dart @@ -0,0 +1,13 @@ +import 'dart:io'; + +Future linuxShutdown({bool restart = false, int delay = 0}) { + final params = []; + + if (restart) { + params.add('-r'); + } + + params.addAll(['-t', delay.toString(), 'now']); + + return Process.run('sudo', ['shutdown', ...params]); +} diff --git a/lib/common/utils/time_stream.dart b/lib/common/utils/time_stream.dart new file mode 100644 index 0000000..4f56830 --- /dev/null +++ b/lib/common/utils/time_stream.dart @@ -0,0 +1,6 @@ +Stream timeStream() async* { + while (true) { + await Future.delayed(const Duration(milliseconds: 100)); + yield DateTime.now(); + } +} diff --git a/lib/common/widgets/clock.dart b/lib/common/widgets/clock.dart new file mode 100644 index 0000000..fb472ed --- /dev/null +++ b/lib/common/widgets/clock.dart @@ -0,0 +1,30 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:intl/intl.dart'; + +import '../utils/time_stream.dart'; + +class ClockWidget extends ConsumerWidget { + const ClockWidget({this.textStyle, super.key}); + + final TextStyle? textStyle; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return StreamBuilder( + stream: timeStream(), + builder: (context, snapshot) { + if (snapshot.hasData) { + DateTime time = snapshot.data ?? DateTime.now(); + DateFormat format = DateFormat('HH:mm:ss'); + return Text( + format.format(time), + style: textStyle, + ); + } else { + return const CircularProgressIndicator(); + } + }, + ); + } +} diff --git a/lib/common/widgets/floating_menu_button.dart b/lib/common/widgets/floating_menu_button.dart new file mode 100644 index 0000000..e0cbbe2 --- /dev/null +++ b/lib/common/widgets/floating_menu_button.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +class FloatingMenuButton extends StatelessWidget { + const FloatingMenuButton({this.popupMenuButton, super.key}); + + final PopupMenuButton? popupMenuButton; + + @override + Widget build(BuildContext context) { + return PositionedDirectional( + start: 16, + top: 16, + child: SafeArea( + child: Container( + decoration: BoxDecoration( + color: Theme.of(context).colorScheme.background, + borderRadius: BorderRadius.circular(999), + boxShadow: [ + BoxShadow( + // check if dark mode + color: (Theme.of(context).brightness == Brightness.light) + ? Colors.grey.withOpacity(0.5) + : Colors.black.withOpacity(0.5), + spreadRadius: 1, + blurRadius: 7, + offset: const Offset(0, 3), + ), + ], + ), + padding: const EdgeInsets.all(8), + child: Row( + children: [ + IconButton( + onPressed: () => context.pop(), + icon: const Icon(Icons.arrow_back), + ), + if (popupMenuButton != null) + Row( + children: [ + const SizedBox(width: 8), + popupMenuButton!, + const SizedBox(width: 8), + ], + ) + ], + ), + ), + ), + ); + } +} diff --git a/lib/common/widgets/home_scaffold.dart b/lib/common/widgets/home_scaffold.dart new file mode 100644 index 0000000..13549a6 --- /dev/null +++ b/lib/common/widgets/home_scaffold.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_adaptive_scaffold/flutter_adaptive_scaffold.dart'; +import 'package:go_router/go_router.dart'; + +import '../routing/routes.dart'; +import 'scaffold_with_statusbar.dart'; + +/// The Scaffold that is used for the home screen. +class HomeScaffold extends StatelessWidget { + const HomeScaffold({ + required this.selectedTab, + required this.child, + this.appBar, + super.key, + }); + + /// Which tab of the scaffold to display. + final RoutesHome selectedTab; + + /// The scaffold body. + final Widget child; + + /// The scaffold AppBar. + final AppBar? appBar; + + @override + Widget build(BuildContext context) => ScaffoldWithStatusbar( + body: AdaptiveScaffold( + appBar: appBar, + smallBreakpoint: const WidthPlatformBreakpoint(end: 700), + mediumBreakpoint: + const WidthPlatformBreakpoint(begin: 700, end: 1400), + largeBreakpoint: const WidthPlatformBreakpoint(begin: 1400), + body: (_) => child, + selectedIndex: selectedTab.index, + destinations: const [ + NavigationDestination( + label: 'Home', + icon: Icon(Icons.home_outlined), + selectedIcon: Icon(Icons.home), + ), + NavigationDestination( + label: 'Material Demo', + icon: Icon(Icons.widgets_outlined), + selectedIcon: Icon(Icons.widgets), + ), + NavigationDestination( + label: 'Benchmark', + icon: Icon(Icons.fireplace_outlined), + selectedIcon: Icon(Icons.fireplace), + ), + NavigationDestination( + label: 'News', + icon: Icon(Icons.newspaper_outlined), + selectedIcon: Icon(Icons.newspaper), + ), + NavigationDestination( + label: 'Led', + icon: Icon(Icons.fluorescent_outlined), + selectedIcon: Icon(Icons.fluorescent), + ), + NavigationDestination( + label: 'Matrix', + icon: Icon(Icons.grid_off), + selectedIcon: Icon(Icons.grid_on), + ), + NavigationDestination( + label: 'System', + icon: Icon(Icons.insert_chart_outlined_outlined), + selectedIcon: Icon(Icons.insert_chart), + ), + NavigationDestination( + label: 'Settings', + icon: Icon(Icons.settings_outlined), + selectedIcon: Icon(Icons.settings), + ), + ], + onSelectedIndexChange: (int idx) { + switch (RoutesHome.values[idx]) { + case RoutesHome.home: + context.goNamed(RoutesHome.home.name); + case RoutesHome.materialDemo: + context.goNamed(RoutesHome.materialDemo.name); + case RoutesHome.news: + context.goNamed(RoutesHome.news.name); + case RoutesHome.benchmark: + context.goNamed(RoutesHome.benchmark.name); + case RoutesHome.led: + context.goNamed(RoutesHome.led.name); + case RoutesHome.matrix: + context.goNamed(RoutesHome.matrix.name); + case RoutesHome.system: + context.goNamed(RoutesHome.system.name); + case RoutesHome.settings: + context.goNamed(RoutesHome.settings.name); + } + }, + ), + ); +} diff --git a/lib/common/widgets/menu_drawer.dart b/lib/common/widgets/menu_drawer.dart new file mode 100644 index 0000000..08770db --- /dev/null +++ b/lib/common/widgets/menu_drawer.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../routing/routes.dart'; + +class MenuDrawer extends StatelessWidget { + const MenuDrawer({super.key}); + + @override + Widget build(BuildContext context) { + return Drawer( + child: ListView( + children: [ + const DrawerHeader( + child: Column( + children: [], + )), + ListTile( + selected: GoRouterState.of(context).uri.toString() == + GoRouter.of(context).namedLocation(RoutesHome.home.name), + title: const Text('Home'), + leading: const Icon(Icons.home), + onTap: () { + context.replaceNamed(RoutesHome.home.name); + }, + ), + ListTile( + selected: GoRouterState.of(context).uri.toString() == + GoRouter.of(context).namedLocation(RoutesHome.news.name), + title: const Text('News'), + leading: const Icon(Icons.newspaper), + onTap: () { + context.replaceNamed(RoutesHome.news.name); + }, + ), + ListTile( + selected: GoRouterState.of(context).uri.toString() == + GoRouter.of(context).namedLocation(RoutesHome.news.name), + title: const Text('Led'), + leading: const Icon(Icons.newspaper), + onTap: () { + context.replaceNamed(RoutesHome.led.name); + }, + ), + ListTile( + selected: GoRouterState.of(context).uri.toString() == + GoRouter.of(context).namedLocation(RoutesHome.news.name), + title: const Text('Matrix'), + leading: const Icon(Icons.newspaper), + onTap: () { + context.replaceNamed(RoutesHome.matrix.name); + }, + ), + ListTile( + selected: GoRouterState.of(context).uri.toString() == + GoRouter.of(context).namedLocation(RoutesHome.settings.name), + title: const Text('Settings'), + leading: const Icon(Icons.settings), + onTap: () { + context.replaceNamed(RoutesHome.settings.name); + }, + ), + ], + ), + ); + } +} diff --git a/lib/common/widgets/scaffold_with_statusbar.dart b/lib/common/widgets/scaffold_with_statusbar.dart new file mode 100644 index 0000000..351488a --- /dev/null +++ b/lib/common/widgets/scaffold_with_statusbar.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +import 'statusbar.dart'; + +class ScaffoldWithStatusbar extends StatelessWidget { + const ScaffoldWithStatusbar({ + this.appBar, + this.body, + this.floatingActionButton, + super.key, + }); + + final AppBar? appBar; + final Widget? body; + final FloatingActionButton? floatingActionButton; + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Column( + children: [ + const StatusBar(), + Expanded( + child: Scaffold( + appBar: appBar, + body: body, + floatingActionButton: floatingActionButton, + ), + ), + ], + ), + ); + } +} diff --git a/lib/common/widgets/statusbar.dart b/lib/common/widgets/statusbar.dart new file mode 100644 index 0000000..2d5cc54 --- /dev/null +++ b/lib/common/widgets/statusbar.dart @@ -0,0 +1,113 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:nm/nm.dart'; + +import '../../features/settings/business/wifi_controller.dart'; +import '../../features/settings/presentation/wifi_settings_screen/widgets/wifi_bar_icon.dart'; +import 'clock.dart'; + +const horizontalSpace = SizedBox( + width: 8, +); + +class StatusBar extends ConsumerWidget { + const StatusBar({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final wifiStream = (Platform.isLinux) + ? ref.watch(wifiControllerProvider.notifier).wifiStream() + : const Stream.empty(); + + return Container( + width: double.infinity, + height: 24, + color: Colors.black, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Row( + children: [ + horizontalSpace, + kDebugMode + ? Text( + 'DEBUG MODE', + style: TextStyle(color: Colors.white), + ) + : SizedBox(), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + StreamBuilder( + stream: wifiStream, + builder: (context, snapshot) { + if (!Platform.isLinux) { + return const Row( + children: [ + Text( + 'DemoConnection', + style: TextStyle(color: Colors.white), + ), + horizontalSpace, + WifiBarIcon( + strength: 1, + size: 16, + color: Colors.white, + ), + ], + ); + } + if (snapshot.hasData) { + final data = snapshot.data?.$1 as NetworkManagerDevice; + return Row( + children: [ + Text( + data.activeConnection?.id ?? 'No connection', + style: const TextStyle(color: Colors.white), + ), + horizontalSpace, + WifiBarIcon( + strength: + data.wireless?.activeAccessPoint?.strength ?? 0, + size: 16, + color: Colors.white, + ), + ], + ); + } + if (snapshot.hasError) { + // error can be caught here + // display error in statusbar: + // return Text( + // snapshot.error.toString(), + // style: const TextStyle(color: Colors.white), + // ); + // show nothing (hide error message from user): + // the error that can happen here is mostly related + // to not finding any usable wifi device + return const SizedBox(); + } + return const Text( + "No connection", + style: TextStyle(color: Colors.white), + ); + }, + ), + horizontalSpace, + const ClockWidget( + textStyle: TextStyle(color: Colors.white), + ), + horizontalSpace, + ], + ), + ], + ), + ); + } +} diff --git a/lib/common/widgets/text_input_dialog.dart b/lib/common/widgets/text_input_dialog.dart new file mode 100644 index 0000000..4d9aa9a --- /dev/null +++ b/lib/common/widgets/text_input_dialog.dart @@ -0,0 +1,97 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:virtual_keyboard_multi_language/virtual_keyboard_multi_language.dart'; + +import '../settings/custom_layout.dart'; + +class TextInputDialog extends ConsumerWidget { + const TextInputDialog({ + required this.controller, + this.title = 'Text Input', + this.onReturnPress, + this.hintText, + super.key, + }); + + final TextEditingController controller; + final String title; + final Function? onReturnPress; + final String? hintText; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return TextField( + controller: controller, + decoration: InputDecoration(hintText: hintText), + onTap: () { + // always move the cursor to the end position by default + controller.value = TextEditingValue( + text: controller.text, + selection: TextSelection.collapsed( + offset: controller.text.length, + ), + ); + showDialog( + context: context, + builder: (context) => Dialog.fullscreen( + // backgroundColor: Colors.transparent, + child: ClipRect( + // TODO: Blur background causes lag on the raspberry pi + // BackdropFilter() with: + // filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), + child: Scaffold( + backgroundColor: + Theme.of(context).colorScheme.background.withOpacity(0.5), + appBar: AppBar( + // backgroundColor: Colors.transparent, + title: Text(title), + centerTitle: false, + leading: IconButton( + onPressed: () { + FocusScope.of(context).unfocus(); + context.pop(); + }, + icon: const Icon(Icons.close), + ), + ), + body: Stack( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Align( + alignment: Alignment.topCenter, + child: Container( + constraints: const BoxConstraints(maxWidth: 400), + child: TextField( + controller: controller, + autofocus: true, + ), + ), + ), + ), + Align( + alignment: Alignment.bottomCenter, + child: TextFieldTapRegion( + child: VirtualKeyboard( + textController: controller, + textColor: + Theme.of(context).textTheme.headlineLarge!.color!, + type: VirtualKeyboardType.Alphanumeric, + // onKeyPress: (key) => onKeyPress(key, context), + customLayoutKeys: CustomLayoutKeys(), + onReturnKey: onReturnPress, + unfocus: true, + ), + ), + ), + ], + ), + ), + ), + ), + ); + }, + ); + } +} diff --git a/lib/constants/api_urls.dart b/lib/constants/api_urls.dart new file mode 100644 index 0000000..5e2c39b --- /dev/null +++ b/lib/constants/api_urls.dart @@ -0,0 +1,8 @@ +class TagesschauApiUrls { + static const main = 'https://www.tagesschau.de/api2/'; + static const news = '$main/news/'; + static const placeholderImage = + 'https://upload.wikimedia.org/wikipedia/commons/thumb/3/3f/Placeholder_view_vector.svg/1362px-Placeholder_view_vector.svg.png'; + static const logoImage = + 'https://images.tagesschau.de/image/89045d82-5cd5-46ad-8f91-73911add30ee/AAABh3YLLz0/AAABjcWen7M/16x9-1280/tagesschau-logo-100.jpg'; +} diff --git a/lib/constants/colors.dart b/lib/constants/colors.dart new file mode 100644 index 0000000..d3d2f93 --- /dev/null +++ b/lib/constants/colors.dart @@ -0,0 +1,17 @@ +import 'package:flutter/material.dart'; + +enum ColorSeed { + baseColor('M3 Baseline', Color(0xff6750a4)), + indigo('Indigo', Colors.indigo), + blue('Blue', Colors.blue), + teal('Teal', Colors.teal), + green('Green', Colors.green), + yellow('Yellow', Colors.yellow), + orange('Orange', Colors.orange), + deepOrange('Deep Orange', Colors.deepOrange), + pink('Pink', Colors.pink); + + const ColorSeed(this.label, this.color); + final String label; + final Color color; +} diff --git a/lib/features/benchmark/business/flame/README.md b/lib/features/benchmark/business/flame/README.md new file mode 100644 index 0000000..20e4b75 --- /dev/null +++ b/lib/features/benchmark/business/flame/README.md @@ -0,0 +1,3 @@ +# Flame Business Layer + +Custom Layer for the benchmarks that use the Flame engine. diff --git a/lib/features/benchmark/business/flame/rogue_shooter/LICENSE b/lib/features/benchmark/business/flame/rogue_shooter/LICENSE new file mode 100644 index 0000000..e9b5ed4 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Blue Fire + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/lib/features/benchmark/business/flame/rogue_shooter/README.md b/lib/features/benchmark/business/flame/rogue_shooter/README.md new file mode 100644 index 0000000..78c9c19 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/README.md @@ -0,0 +1,10 @@ +# Flame Performance Test Game + +This is a simple scrolling shooter game which we use for testing the performance of Flame, +since it uses a lot of components and hitboxes. When it reaches a certain amount of +components (counted in the lower right corner) you can expect it to drop a bit in FPS, +depending on what platform you are on. + +# Source + +https://github.com/flame-engine/flame/tree/main/examples/games/rogue_shooter diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/bullet_component.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/bullet_component.dart new file mode 100644 index 0000000..66dab22 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/bullet_component.dart @@ -0,0 +1,55 @@ +import 'package:flame/collisions.dart'; +import 'package:flame/components.dart'; + +import 'enemy_component.dart'; + +class BulletComponent extends SpriteAnimationComponent + with HasGameRef, CollisionCallbacks { + static const speed = 500.0; + late final Vector2 velocity; + final Vector2 deltaPosition = Vector2.zero(); + + BulletComponent({required super.position, super.angle}) + : super(size: Vector2(10, 20), anchor: Anchor.center); + + @override + Future onLoad() async { + add(CircleHitbox()); + animation = await game.loadSpriteAnimation( + 'flame_benchmark/rogue_shooter/bullet.png', + SpriteAnimationData.sequenced( + stepTime: 0.2, + amount: 4, + textureSize: Vector2(8, 16), + ), + ); + velocity = Vector2(0, -1) + ..rotate(angle) + ..scale(speed); + } + + @override + void onCollisionStart( + Set intersectionPoints, + PositionComponent other, + ) { + super.onCollisionStart(intersectionPoints, other); + if (other is EnemyComponent) { + other.takeHit(); + removeFromParent(); + } + } + + @override + void update(double dt) { + super.update(dt); + deltaPosition + ..setFrom(velocity) + ..scale(dt); + position += deltaPosition; + + if (position.y < 0 || position.x > game.size.x || position.x + size.x < 0) { + removeFromParent(); + } + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/enemy_component.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/enemy_component.dart new file mode 100644 index 0000000..6d7d7d2 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/enemy_component.dart @@ -0,0 +1,43 @@ +import 'package:flame/collisions.dart'; +import 'package:flame/components.dart'; + +import '../rogue_shooter_game.dart'; +import 'explosion_component.dart'; + +class EnemyComponent extends SpriteAnimationComponent + with HasGameReference, CollisionCallbacks { + static const speed = 150; + static final Vector2 initialSize = Vector2.all(25); + + EnemyComponent({required super.position}) + : super(size: initialSize, anchor: Anchor.center); + + @override + Future onLoad() async { + animation = await game.loadSpriteAnimation( + 'flame_benchmark/rogue_shooter/enemy.png', + SpriteAnimationData.sequenced( + stepTime: 0.2, + amount: 4, + textureSize: Vector2.all(16), + ), + ); + add(CircleHitbox(collisionType: CollisionType.passive)); + } + + @override + void update(double dt) { + super.update(dt); + y += speed * dt; + if (y >= game.size.y) { + removeFromParent(); + } + } + + void takeHit() { + removeFromParent(); + + game.add(ExplosionComponent(position: position)); + game.increaseScore(); + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/enemy_creator.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/enemy_creator.dart new file mode 100644 index 0000000..11447ec --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/enemy_creator.dart @@ -0,0 +1,27 @@ +import 'dart:math'; + +import 'package:flame/components.dart'; + +import 'enemy_component.dart'; + +class EnemyCreator extends TimerComponent with HasGameRef { + final Random random = Random(); + final _halfWidth = EnemyComponent.initialSize.x / 2; + + EnemyCreator() : super(period: 0.05, repeat: true); + + @override + void onTick() { + game.addAll( + List.generate( + 5, + (index) => EnemyComponent( + position: Vector2( + _halfWidth + (game.size.x - _halfWidth) * random.nextDouble(), + 0, + ), + ), + ), + ); + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/explosion_component.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/explosion_component.dart new file mode 100644 index 0000000..bb2deb7 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/explosion_component.dart @@ -0,0 +1,23 @@ +import 'package:flame/components.dart'; + +class ExplosionComponent extends SpriteAnimationComponent with HasGameRef { + ExplosionComponent({super.position}) + : super( + size: Vector2.all(50), + anchor: Anchor.center, + removeOnFinish: true, + ); + + @override + Future onLoad() async { + animation = await game.loadSpriteAnimation( + 'flame_benchmark/rogue_shooter/explosion.png', + SpriteAnimationData.sequenced( + stepTime: 0.1, + amount: 6, + textureSize: Vector2.all(32), + loop: false, + ), + ); + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/player_component.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/player_component.dart new file mode 100644 index 0000000..e3231a3 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/player_component.dart @@ -0,0 +1,70 @@ +import 'package:flame/collisions.dart'; +import 'package:flame/components.dart'; + +import 'bullet_component.dart'; +import 'enemy_component.dart'; +import 'explosion_component.dart'; + +class PlayerComponent extends SpriteAnimationComponent + with HasGameRef, CollisionCallbacks { + late TimerComponent bulletCreator; + + PlayerComponent() : super(size: Vector2(50, 75), anchor: Anchor.center); + + @override + Future onLoad() async { + position = game.size / 2; + add(CircleHitbox()); + add( + bulletCreator = TimerComponent( + period: 0.05, + repeat: true, + autoStart: false, + onTick: _createBullet, + ), + ); + animation = await game.loadSpriteAnimation( + 'flame_benchmark/rogue_shooter/player.png', + SpriteAnimationData.sequenced( + stepTime: 0.2, + amount: 4, + textureSize: Vector2(32, 39), + ), + ); + } + + final _bulletAngles = [0.5, 0.3, 0.0, -0.5, -0.3]; + void _createBullet() { + game.addAll( + _bulletAngles.map( + (angle) => BulletComponent( + position: position + Vector2(0, -size.y / 2), + angle: angle, + ), + ), + ); + } + + void beginFire() { + bulletCreator.timer.start(); + } + + void stopFire() { + bulletCreator.timer.pause(); + } + + void takeHit() { + game.add(ExplosionComponent(position: position)); + } + + @override + void onCollisionStart( + Set intersectionPoints, + PositionComponent other, + ) { + super.onCollisionStart(intersectionPoints, other); + if (other is EnemyComponent) { + other.takeHit(); + } + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/star_background_creator.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/star_background_creator.dart new file mode 100644 index 0000000..e0163e6 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/star_background_creator.dart @@ -0,0 +1,66 @@ +import 'dart:math'; + +import 'package:flame/components.dart'; +import 'package:flame/sprite.dart'; + +import 'star_component.dart'; + +class StarBackGroundCreator extends Component with HasGameRef { + final gapSize = 12; + + late final SpriteSheet spriteSheet; + Random random = Random(); + + StarBackGroundCreator(); + + @override + Future onLoad() async { + spriteSheet = SpriteSheet.fromColumnsAndRows( + image: await game.images.load('flame_benchmark/rogue_shooter/stars.png'), + rows: 4, + columns: 4, + ); + + final starGapTime = (game.size.y / gapSize) / StarComponent.speed; + + add( + TimerComponent( + period: starGapTime, + repeat: true, + onTick: () => _createRowOfStars(0), + ), + ); + + _createInitialStars(); + } + + void _createStarAt(double x, double y) { + final animation = spriteSheet.createAnimation( + row: random.nextInt(3), + to: 4, + stepTime: 0.1, + )..variableStepTimes = [max(20, 100 * random.nextDouble()), 0.1, 0.1, 0.1]; + + game.add(StarComponent(animation: animation, position: Vector2(x, y))); + } + + void _createRowOfStars(double y) { + const gapSize = 6; + final starGap = game.size.x / gapSize; + + for (var i = 0; i < gapSize; i++) { + _createStarAt( + starGap * i + (random.nextDouble() * starGap), + y + (random.nextDouble() * 20), + ); + } + } + + void _createInitialStars() { + final rows = game.size.y / gapSize; + + for (var i = 0; i < gapSize; i++) { + _createRowOfStars(i * rows); + } + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/components/star_component.dart b/lib/features/benchmark/business/flame/rogue_shooter/components/star_component.dart new file mode 100644 index 0000000..df5eb1b --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/components/star_component.dart @@ -0,0 +1,17 @@ +import 'package:flame/components.dart'; + +class StarComponent extends SpriteAnimationComponent with HasGameRef { + static const speed = 10; + + StarComponent({super.animation, super.position}) + : super(size: Vector2.all(20)); + + @override + void update(double dt) { + super.update(dt); + y += dt * speed; + if (y >= game.size.y) { + removeFromParent(); + } + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/main.dart b/lib/features/benchmark/business/flame/rogue_shooter/main.dart new file mode 100644 index 0000000..e4b6cf2 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/main.dart @@ -0,0 +1,7 @@ +// import 'package:flame/game.dart'; +// import 'package:flutter/widgets.dart'; +// import 'package:rogue_shooter/rogue_shooter_game.dart'; + +// void main() { +// runApp(GameWidget(game: RogueShooterGame())); +// } diff --git a/lib/features/benchmark/business/flame/rogue_shooter/mixins/has_performance_tracker.dart b/lib/features/benchmark/business/flame/rogue_shooter/mixins/has_performance_tracker.dart new file mode 100644 index 0000000..88bb078 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/mixins/has_performance_tracker.dart @@ -0,0 +1,36 @@ +import 'dart:ui'; + +// Source: https://github.com/flame-engine/flame/blob/main/packages/flame/lib/src/game/mixins/has_performance_tracker.dart#L6 + +import 'package:flame/game.dart'; + +/// A mixin that adds performance tracking to a game. +mixin HasPerformanceTracker on Game { + int _updateTime = 0; + int _renderTime = 0; + final _stopwatch = Stopwatch(); + + /// The time it took to update the game in milliseconds. + int get updateTime => _updateTime; + + /// The time it took to render the game in milliseconds. + int get renderTime => _renderTime; + + @override + void update(double dt) { + _stopwatch.reset(); + _stopwatch.start(); + super.update(dt); + _stopwatch.stop(); + _updateTime = _stopwatch.elapsedMilliseconds; + } + + @override + void render(Canvas canvas) { + _stopwatch.reset(); + _stopwatch.start(); + super.render(canvas); + _stopwatch.stop(); + _renderTime = _stopwatch.elapsedMilliseconds; + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/rogue_shooter_game.dart b/lib/features/benchmark/business/flame/rogue_shooter/rogue_shooter_game.dart new file mode 100644 index 0000000..960cccb --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/rogue_shooter_game.dart @@ -0,0 +1,93 @@ +import 'package:flame/components.dart'; +import 'package:flame/events.dart'; +import 'package:flame/game.dart'; + +import 'components/enemy_creator.dart'; +import 'components/player_component.dart'; +import 'components/star_background_creator.dart'; +import 'mixins/has_performance_tracker.dart'; + +class RogueShooterGame extends FlameGame + with PanDetector, HasCollisionDetection, HasPerformanceTracker { + static const String description = ''' + A simple space shooter game used for testing performance of the collision + detection system in Flame. + '''; + + late final PlayerComponent _player; + late final TextComponent _componentCounter; + late final TextComponent _scoreText; + + final _updateTime = TextComponent( + text: 'Update time: 0ms', + position: Vector2(90, 20), + priority: 1, + ); + + final TextComponent _renderTime = TextComponent( + text: 'Render time: 0ms', + position: Vector2(90, 45), + priority: 1, + ); + + int _score = 0; + + @override + Future onLoad() async { + add(_player = PlayerComponent()); + addAll([ + FpsTextComponent( + position: size - Vector2(0, 50), + anchor: Anchor.bottomRight, + ), + _scoreText = TextComponent( + position: size - Vector2(0, 25), + anchor: Anchor.bottomRight, + priority: 1, + ), + _componentCounter = TextComponent( + position: size, + anchor: Anchor.bottomRight, + priority: 1, + ), + ]); + + add(EnemyCreator()); + add(StarBackGroundCreator()); + + addAll([_updateTime, _renderTime]); + } + + @override + void update(double dt) { + super.update(dt); + _scoreText.text = 'Score: $_score'; + _componentCounter.text = 'Components: ${children.length}'; + _updateTime.text = 'Update time: $updateTime ms'; + _renderTime.text = 'Render time: $renderTime ms'; + } + + @override + void onPanStart(_) { + _player.beginFire(); + } + + @override + void onPanEnd(_) { + _player.stopFire(); + } + + @override + void onPanCancel() { + _player.stopFire(); + } + + @override + void onPanUpdate(DragUpdateInfo info) { + _player.position += info.delta.global; + } + + void increaseScore() { + _score++; + } +} diff --git a/lib/features/benchmark/business/flame/rogue_shooter/rogue_shooter_widget.dart b/lib/features/benchmark/business/flame/rogue_shooter/rogue_shooter_widget.dart new file mode 100644 index 0000000..60140c4 --- /dev/null +++ b/lib/features/benchmark/business/flame/rogue_shooter/rogue_shooter_widget.dart @@ -0,0 +1,18 @@ +import 'package:flame/game.dart'; +import 'package:flutter/widgets.dart'; + +import 'rogue_shooter_game.dart'; + +class RogueShooterWidget extends StatelessWidget { + const RogueShooterWidget({super.key}); + + @override + Widget build(BuildContext context) { + return GameWidget( + game: RogueShooterGame(), + loadingBuilder: (_) => const Center( + child: Text('Loading'), + ), + ); + } +} diff --git a/lib/features/benchmark/business/flame/sprite_renderer/LICENSE.md b/lib/features/benchmark/business/flame/sprite_renderer/LICENSE.md new file mode 100644 index 0000000..fe85c6d --- /dev/null +++ b/lib/features/benchmark/business/flame/sprite_renderer/LICENSE.md @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Blue Fire + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/lib/features/benchmark/business/flame/sprite_renderer/README.md b/lib/features/benchmark/business/flame/sprite_renderer/README.md new file mode 100644 index 0000000..8478a47 --- /dev/null +++ b/lib/features/benchmark/business/flame/sprite_renderer/README.md @@ -0,0 +1,12 @@ +# Flame Engine Sprite Renderer Benchmark + +This is a simple benchmark to test the render +performance of the Flutter Render Engine. + +It was modified from: https://github.com/flame-engine/flame/blob/main/examples/lib/stories/animations/benchmark_example.dart + +## Modifications + +- added framerate display +- add sprites automatically for 30 minutes and only if the framerate is above 59 +- timer and benchmark test starts automatically and displays the result after 30 minutes diff --git a/lib/features/benchmark/business/flame/sprite_renderer/benchmark_world.dart b/lib/features/benchmark/business/flame/sprite_renderer/benchmark_world.dart new file mode 100644 index 0000000..a6d21df --- /dev/null +++ b/lib/features/benchmark/business/flame/sprite_renderer/benchmark_world.dart @@ -0,0 +1,14 @@ +import 'dart:math'; + +import 'package:flame/components.dart'; +import 'package:flame/events.dart'; + +import 'sprite_benchmark.dart'; + +class BenchmarkWorld extends World + with TapCallbacks, HasGameReference { + final Random random = Random(); + + @override + void onTapDown(TapDownEvent event) {} +} diff --git a/lib/features/benchmark/business/flame/sprite_renderer/sprite_benchmark.dart b/lib/features/benchmark/business/flame/sprite_renderer/sprite_benchmark.dart new file mode 100644 index 0000000..3704e2b --- /dev/null +++ b/lib/features/benchmark/business/flame/sprite_renderer/sprite_benchmark.dart @@ -0,0 +1,144 @@ +import 'dart:collection'; +import 'dart:io'; +import 'dart:math'; + +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:flame/text.dart'; +import 'package:flutter/material.dart'; + +import '../../../data/models/ember.dart'; +import 'benchmark_world.dart'; + +class SpriteBenchmark extends FlameGame with HasGameRef { + static const description = ''' +See how many SpriteAnimationComponent's your platform can handle before it +starts to drop in FPS, this is without any sprite batching and such. + '''; + + SpriteBenchmark() : super(world: BenchmarkWorld()); + + // 30 minutes countdown + final countdown = Timer(1800); + + final emberSize = Vector2.all(20); + late final TextComponent emberCounter; + final counterPrefix = 'Animations: '; + double posX = 0; + double posY = 0; + + final Queue window = Queue(); + + @override + Color backgroundColor() => Colors.black; + + double _sum = 0; + int spriteCount = 0; + + final int windowSize = 60; + + TextPaint textPaint = TextPaint( + style: TextStyle( + fontSize: 20.0, + fontFamily: 'Awesome Font', + background: Paint() + ..color = Colors.indigo + ..strokeJoin = StrokeJoin.round + ..strokeCap = StrokeCap.round + ..style = PaintingStyle.stroke + ..strokeWidth = 25.0, + ), + ); + + @override + Future onLoad() async { + await camera.viewport.addAll([ + FpsTextComponent( + position: size - Vector2(20, 80), + anchor: Anchor.bottomRight, + textRenderer: textPaint, + ), + emberCounter = TextComponent( + position: size - Vector2(20, 25), + anchor: Anchor.bottomRight, + priority: 1, + textRenderer: textPaint, + ), + ]); + // world.add(Ember(size: emberSize)); + children.register(); + } + + double get fps { + return window.isEmpty ? 0 : window.length / _sum; + } + + @override + void update(double dt) async { + // add text to the screen only once + if (countdown.finished) { + showDialog( + context: gameRef.buildContext!, + builder: (context) { + return AlertDialog( + title: const Text('Benchmark finished'), + content: Text( + 'After 30 minutes, the benchmark finished with $spriteCount sprite animations.', + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + Navigator.of(context).pop(); + }, + child: const Text('Close'), + ), + ], + ); + }, + ); + countdown.reset(); + } + if (!countdown.finished && countdown.isRunning()) { + countdown.update(dt); + + window.addLast(dt); + _sum += dt; + if (window.length > windowSize) { + _sum -= window.removeFirst(); + } + if (fps > 59) { + if (posY > size.y) { + final Random random = Random(); + add( + Ember( + size: emberSize, + position: Vector2( + (size.x) * random.nextDouble() * (random.nextBool() ? 1 : -1), + (size.y) * random.nextDouble() * (random.nextBool() ? 1 : -1), + ), + ), + ); + sleep(const Duration(milliseconds: 50)); + } else { + for (int i = 0; i < 1; i++) { + add(Ember( + size: emberSize, + position: Vector2(10 + posX, 10 + posY), + )); + if (posX < size.x) { + posX += 20; + } else { + posY += 20; + posX = 0; + } + } + sleep(const Duration(milliseconds: 50)); + } + spriteCount += 1; + } + super.update(dt); + emberCounter.text = '$counterPrefix $spriteCount'; + } + } +} diff --git a/lib/features/benchmark/data/benchmarks_list.dart b/lib/features/benchmark/data/benchmarks_list.dart new file mode 100644 index 0000000..773843d --- /dev/null +++ b/lib/features/benchmark/data/benchmarks_list.dart @@ -0,0 +1,31 @@ +import '../../../common/routing/routes.dart'; +import 'models/benchmark.dart'; + +final List benchmarksList = [ + Benchmark( + title: 'Flame Sprite Rendering', + desc: 'Tests how many sprites can be renderer without a frame drop.', + route: RoutesBenchmark.sprites, + ), + Benchmark( + title: 'Rogue Shooter Benchmark', + desc: + 'A simple scrolling shooter game which is used by the Flame Engines team for testing the performance of Flame.', + route: RoutesBenchmark.rogueShooter, + ), + Benchmark( + title: 'VSync Test', + desc: 'Tests if the VSync is working properly with a fragment shader.', + route: RoutesBenchmark.shader, + ), + Benchmark( + title: 'Video Benchmark', + desc: 'Video Player demo.', + route: RoutesBenchmark.video, + ), + Benchmark( + title: 'Simple Tiles Map Benchmark', + desc: 'Map demo.', + route: RoutesBenchmark.map, + ), +]; diff --git a/lib/features/benchmark/data/models/benchmark.dart b/lib/features/benchmark/data/models/benchmark.dart new file mode 100644 index 0000000..bed9cfa --- /dev/null +++ b/lib/features/benchmark/data/models/benchmark.dart @@ -0,0 +1,15 @@ +import '../../../../common/routing/routes.dart'; + +class Benchmark { + final String title; + final String desc; + final String buttonText; + final RoutesBenchmark route; + + Benchmark({ + required this.title, + required this.desc, + this.buttonText = 'Go to benchmark', + required this.route, + }); +} diff --git a/lib/features/benchmark/data/models/ember.dart b/lib/features/benchmark/data/models/ember.dart new file mode 100644 index 0000000..792ef1a --- /dev/null +++ b/lib/features/benchmark/data/models/ember.dart @@ -0,0 +1,25 @@ +import 'package:flame/components.dart'; +import 'package:flame/game.dart'; +import 'package:meta/meta.dart'; + +class Ember extends SpriteAnimationComponent + with HasGameReference { + Ember({super.position, Vector2? size, super.priority, super.key}) + : super( + size: size ?? Vector2.all(50), + anchor: Anchor.center, + ); + + @mustCallSuper + @override + Future onLoad() async { + animation = await game.loadSpriteAnimation( + 'flame_benchmark/sprite_benchmark/ember.png', + SpriteAnimationData.sequenced( + amount: 3, + textureSize: Vector2.all(16), + stepTime: 0.15, + ), + ); + } +} diff --git a/lib/features/benchmark/presentation/benchmark_screen.dart b/lib/features/benchmark/presentation/benchmark_screen.dart new file mode 100644 index 0000000..6375949 --- /dev/null +++ b/lib/features/benchmark/presentation/benchmark_screen.dart @@ -0,0 +1,27 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; + +import '../data/benchmarks_list.dart'; +import 'widgets/benchmark_card.dart'; + +class BenchmarkScreen extends StatelessWidget { + const BenchmarkScreen({super.key}); + + @override + Widget build(BuildContext context) { + return GridView.builder( + padding: const EdgeInsets.all(12), + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: MediaQuery.of(context).size.shortestSide < 600 ? 2 : 3, + crossAxisSpacing: 12.0, + mainAxisSpacing: 12, + ), + itemCount: benchmarksList.length, + itemBuilder: (context, index) { + return BenchmarkCard( + benchmark: benchmarksList[index], + ); + }, + ); + } +} diff --git a/lib/features/benchmark/presentation/map_benchmark_screen.dart b/lib/features/benchmark/presentation/map_benchmark_screen.dart new file mode 100644 index 0000000..df5f889 --- /dev/null +++ b/lib/features/benchmark/presentation/map_benchmark_screen.dart @@ -0,0 +1,94 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_elinux/common/widgets/floating_menu_button.dart'; +import 'package:flutter_elinux/common/widgets/scaffold_with_statusbar.dart'; +import 'package:flutter_map/flutter_map.dart'; +import 'package:simple_tiles_map/simple_tiles_map.dart'; + +class MapBenchmarkScreen extends StatefulWidget { + const MapBenchmarkScreen({super.key}); + + @override + State createState() => _MapBenchmarkScreenState(); +} + +class _MapBenchmarkScreenState extends State { + TypeMap _typeMap = TypeMap.osm; + + @override + Widget build(BuildContext context) { + MapOptions mapOptions = MapOptions( + maxZoom: 19, + minZoom: 5, + ); + return ScaffoldWithStatusbar( + body: Stack(children: [ + Column( + children: [ + SimpleTilesMap( + typeMap: _typeMap, + mapOptions: mapOptions, + ), + ], + ), + FloatingMenuButton( + popupMenuButton: PopupMenuButton( + itemBuilder: (BuildContext context) { + return [ + PopupMenuItem( + value: TypeMap.google, + enabled: (_typeMap != TypeMap.google), + child: const Text('Google'), + ), + PopupMenuItem( + value: TypeMap.googleHybrid, + enabled: (_typeMap != TypeMap.googleHybrid), + child: const Text('Google Hybrid'), + ), + PopupMenuItem( + value: TypeMap.googleSatellite, + enabled: (_typeMap != TypeMap.googleSatellite), + child: const Text('Google Satellite'), + ), + PopupMenuItem( + value: TypeMap.osm, + enabled: (_typeMap != TypeMap.osm), + child: const Text('Open Street Map'), + ), + PopupMenuItem( + value: TypeMap.cartoMapPositron, + enabled: (_typeMap != TypeMap.cartoMapPositron), + child: const Text('Carto Map Positron'), + ), + PopupMenuItem( + value: TypeMap.cartoMapDark, + enabled: (_typeMap != TypeMap.cartoMapDark), + child: const Text('Carto Map Dark'), + ), + PopupMenuItem( + value: TypeMap.esriSatellite, + enabled: (_typeMap != TypeMap.esriSatellite), + child: const Text('Esri Satellite'), + ), + PopupMenuItem( + value: TypeMap.esriStreets, + enabled: (_typeMap != TypeMap.esriStreets), + child: const Text('Esri Streets'), + ), + PopupMenuItem( + value: TypeMap.esriTopo, + enabled: (_typeMap != TypeMap.esriTopo), + child: const Text('Esri Topo'), + ), + ]; + }, + onSelected: (dynamic value) { + setState(() { + _typeMap = value; + }); + }, + ), + ), + ]), + ); + } +} diff --git a/lib/features/benchmark/presentation/rogue_shooter_screen.dart b/lib/features/benchmark/presentation/rogue_shooter_screen.dart new file mode 100644 index 0000000..e2b2b66 --- /dev/null +++ b/lib/features/benchmark/presentation/rogue_shooter_screen.dart @@ -0,0 +1,24 @@ +import 'package:flame/game.dart'; +import 'package:flutter/material.dart'; + +import '../../../common/widgets/floating_menu_button.dart'; +import '../business/flame/rogue_shooter/rogue_shooter_game.dart'; + +class RogueShooterScreen extends StatefulWidget { + const RogueShooterScreen({super.key}); + + @override + State createState() => _RogueShooterScreenState(); +} + +class _RogueShooterScreenState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack(children: [ + GameWidget(game: RogueShooterGame()), + const FloatingMenuButton(), + ]), + ); + } +} diff --git a/lib/features/benchmark/presentation/sprite_benchmark_screen.dart b/lib/features/benchmark/presentation/sprite_benchmark_screen.dart new file mode 100644 index 0000000..0bc2a48 --- /dev/null +++ b/lib/features/benchmark/presentation/sprite_benchmark_screen.dart @@ -0,0 +1,21 @@ +import 'package:flame/game.dart'; +import 'package:flutter/material.dart'; + +import '../../../common/widgets/floating_menu_button.dart'; +import '../business/flame/sprite_renderer/sprite_benchmark.dart'; + +class SpriteBenchmarkScreen extends StatelessWidget { + const SpriteBenchmarkScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack(children: [ + GameWidget( + game: SpriteBenchmark(), + ), + const FloatingMenuButton(), + ]), + ); + } +} diff --git a/lib/features/benchmark/presentation/video_benchmark_screen.dart b/lib/features/benchmark/presentation/video_benchmark_screen.dart new file mode 100644 index 0000000..2233bcd --- /dev/null +++ b/lib/features/benchmark/presentation/video_benchmark_screen.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:video_player/video_player.dart'; + +class VideoBenchmarkScreen extends StatefulWidget { + const VideoBenchmarkScreen({super.key}); + + @override + State createState() => _VideoBenchmarkScreenState(); +} + +class _VideoBenchmarkScreenState extends State { + late VideoPlayerController _controller; + String selectedMode = '720p'; + + @override + void initState() { + super.initState(); + _controller = VideoPlayerController.asset( + 'assets/video/video_720p.mp4', + ) + ..initialize().then((_) { + setState(() {}); + }) + ..setLooping(true); + } + + @override + void dispose() { + super.dispose(); + _controller.dispose(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text('Video Player Demo ($selectedMode)'), + actions: [ + ElevatedButton( + onPressed: () { + _controller = VideoPlayerController.asset( + 'assets/video/video_720p.mp4', + ) + ..initialize().then((_) { + setState(() { + selectedMode = '720p'; + }); + }) + ..setLooping(true); + }, + child: const Text('load 720p')), + const SizedBox( + width: 8, + ), + ElevatedButton( + onPressed: () { + _controller = VideoPlayerController.asset( + 'assets/video/video_1080p.mp4', + ) + ..initialize().then((_) { + setState(() { + selectedMode = '1080p'; + }); + }) + ..setLooping(true); + }, + child: const Text('load 1080p')), + const SizedBox( + width: 8, + ), + ElevatedButton( + onPressed: () { + _controller = VideoPlayerController.asset( + 'assets/video/video_2160p.mp4', + ) + ..initialize().then((_) { + setState(() { + selectedMode = '2160p'; + }); + }) + ..setLooping(true); + }, + child: const Text('load 2160p')), + const SizedBox( + width: 8, + ), + ], + ), + body: Center( + child: _controller.value.isInitialized + ? AspectRatio( + aspectRatio: _controller.value.aspectRatio, + child: VideoPlayer(_controller), + ) + : Container(), + ), + floatingActionButton: FloatingActionButton( + onPressed: () { + setState(() { + _controller.value.isPlaying + ? _controller.pause() + : _controller.play(); + }); + }, + child: Icon( + _controller.value.isPlaying ? Icons.pause : Icons.play_arrow, + ), + ), + ); + } +} diff --git a/lib/features/benchmark/presentation/vsync_benchmark_screen.dart b/lib/features/benchmark/presentation/vsync_benchmark_screen.dart new file mode 100644 index 0000000..9b30e53 --- /dev/null +++ b/lib/features/benchmark/presentation/vsync_benchmark_screen.dart @@ -0,0 +1,96 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; + +import '../../../common/widgets/floating_menu_button.dart'; + +class VsyncBenchmarkScreen extends StatelessWidget { + const VsyncBenchmarkScreen({super.key}); + + @override + Widget build(BuildContext context) { + return const Scaffold( + body: Stack( + children: [ + SizedBox( + width: double.infinity, + height: double.infinity, + child: ShaderHomePage(), + ), + FloatingMenuButton(), + ], + ), + ); + } +} + +class ShaderHomePage extends StatefulWidget { + const ShaderHomePage({super.key}); + + @override + State createState() => _ShaderHomePageState(); +} + +class _ShaderHomePageState extends State + with SingleTickerProviderStateMixin { + double delta = 0; + FragmentShader? shader; + + late final Ticker _ticker; + + void loadMyShader() async { + var program = await FragmentProgram.fromAsset('shaders/vsync.frag'); + shader = program.fragmentShader(); + } + + @override + void initState() { + super.initState(); + loadMyShader(); + _ticker = createTicker((elapsed) { + setState(() { + delta += 1 / 60; + }); + }); + _ticker.start(); + } + + @override + void dispose() { + _ticker.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + if (shader == null) { + return const Center(child: CircularProgressIndicator()); + } else { + return Container( + color: Colors.white, + child: CustomPaint(painter: ShaderPainter(shader!, delta))); + } + } +} + +class ShaderPainter extends CustomPainter { + final FragmentShader shader; + final double time; + + ShaderPainter(FragmentShader fragmentShader, this.time) + : shader = fragmentShader; + + @override + void paint(Canvas canvas, Size size) { + final paint = Paint(); + shader.setFloat(0, size.width); + shader.setFloat(1, size.height); + shader.setFloat(2, time); + paint.shader = shader; + canvas.drawRect(Offset.zero & size, paint); + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} diff --git a/lib/features/benchmark/presentation/widgets/benchmark_card.dart b/lib/features/benchmark/presentation/widgets/benchmark_card.dart new file mode 100644 index 0000000..b9636b7 --- /dev/null +++ b/lib/features/benchmark/presentation/widgets/benchmark_card.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; + +import '../../data/models/benchmark.dart'; + +class BenchmarkCard extends StatelessWidget { + const BenchmarkCard({ + required this.benchmark, + super.key, + }); + + final Benchmark benchmark; + + @override + Widget build(BuildContext context) { + return Card( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Text( + benchmark.title, + textAlign: TextAlign.center, + style: const TextStyle(fontSize: 18), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20.0), + child: Text( + benchmark.desc, + textAlign: TextAlign.center, + ), + ), + TextButton( + onPressed: () { + context.pushNamed(benchmark.route.name); + }, + child: Text(benchmark.buttonText), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/home/presentation/home_screen.dart b/lib/features/home/presentation/home_screen.dart new file mode 100644 index 0000000..33d7b97 --- /dev/null +++ b/lib/features/home/presentation/home_screen.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; + +class HomeScreen extends StatefulWidget { + const HomeScreen({super.key}); + + @override + State createState() => _HomeScreenState(); +} + +class _HomeScreenState extends State { + @override + void initState() { + super.initState(); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + body: SingleChildScrollView( + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + children: [ + Text( + 'Flutter on Embedded Linux', + style: Theme.of(context).textTheme.headlineLarge, + ), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/material_demo/presentation/material_demo_screen.dart b/lib/features/material_demo/presentation/material_demo_screen.dart new file mode 100644 index 0000000..ee2c04e --- /dev/null +++ b/lib/features/material_demo/presentation/material_demo_screen.dart @@ -0,0 +1,365 @@ +import 'package:flutter/material.dart'; + +import 'widgets/component_decoration.dart'; + +// https://github.com/flutter/samples/blob/main/material_3_demo/lib/component_screen.dart + +const rowDivider = SizedBox(width: 20); +const colDivider = SizedBox(height: 10); + +const tinySpacing = 3.0; +const smallSpacing = 10.0; +const double cardWidth = 115; +const double widthConstraint = 450; + +class MaterialDemoScreen extends StatefulWidget { + const MaterialDemoScreen({super.key}); + + @override + State createState() => _MaterialDemoScreenState(); +} + +class _MaterialDemoScreenState extends State { + bool switch1 = false; + bool switch2 = true; + + double sliderValue1 = 0.2; + double sliderValue2 = 0.5; + + int navBarIndex = 0; + + bool indicatorPlayAnimation = false; + + @override + Widget build(BuildContext context) { + return SingleChildScrollView( + child: Column( + children: [ + const SizedBox( + height: 84, + ), + ComponentDecoration( + title: 'Common Buttons', + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () {}, + child: const Text('Elevated'), + ), + rowDivider, + ElevatedButton.icon( + onPressed: () {}, + icon: const Icon(Icons.add), + label: const Text('Icon'), + ), + rowDivider, + const ElevatedButton( + style: ButtonStyle(), + onPressed: null, + child: Text('Elevated'), + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + FilledButton( + onPressed: () {}, + child: const Text('Filled'), + ), + rowDivider, + FilledButton.icon( + onPressed: () {}, + icon: const Icon(Icons.add), + label: const Text('Icon'), + ), + rowDivider, + const FilledButton( + style: ButtonStyle(), + onPressed: null, + child: Text('Filled'), + ), + ], + ), + const SizedBox(height: 16), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + TextButton( + onPressed: () {}, + child: const Text('Text'), + ), + rowDivider, + TextButton.icon( + onPressed: () {}, + icon: const Icon(Icons.add), + label: const Text('Icon'), + ), + rowDivider, + const TextButton( + style: ButtonStyle(), + onPressed: null, + child: Text('Text'), + ), + ], + ), + ], + ), + ), + ComponentDecoration( + title: 'Switches', + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Switch( + value: switch1, + onChanged: (bool value) { + setState(() { + switch1 = value; + }); + }, + ), + Switch( + value: switch2, + thumbIcon: MaterialStateProperty.resolveWith( + (Set states) { + if (states.contains(MaterialState.selected)) { + return const Icon(Icons.check); + } + return null; // All other states will use the default thumbIcon. + }), + onChanged: (bool value) { + setState(() { + switch2 = value; + }); + }, + ), + ], + ), + ), + ComponentDecoration( + title: 'Floating Action Buttons', + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + FloatingActionButton.small( + onPressed: () {}, + tooltip: 'Small', + child: const Icon(Icons.add), + ), + FloatingActionButton( + onPressed: () {}, + tooltip: 'Standard', + child: const Icon(Icons.add), + ), + FloatingActionButton.extended( + onPressed: () {}, + tooltip: 'Extended', + icon: const Icon(Icons.add), + label: const Text('Create'), + ), + FloatingActionButton.large( + onPressed: () {}, + tooltip: 'Large', + child: const Icon(Icons.add), + ), + ], + ), + ), + ComponentDecoration( + title: 'Sliders', + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Slider( + value: sliderValue1, + onChanged: (double value) { + setState(() { + sliderValue1 = value; + }); + }, + ), + Slider( + value: sliderValue2, + onChanged: (double value) { + setState(() { + sliderValue2 = value; + }); + }, + divisions: 5, + ), + ], + ), + ), + ComponentDecoration( + title: 'Loading Indicators', + child: Column( + children: [ + IconButton( + onPressed: () { + setState(() { + indicatorPlayAnimation = !indicatorPlayAnimation; + }); + }, + icon: indicatorPlayAnimation + ? const Icon(Icons.pause) + : const Icon(Icons.play_arrow), + ), + colDivider, + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + rowDivider, + CircularProgressIndicator( + value: indicatorPlayAnimation ? null : 0.5, + ), + rowDivider, + Expanded( + child: LinearProgressIndicator( + value: indicatorPlayAnimation ? null : 0.5, + ), + ), + rowDivider, + ], + ), + ], + ), + ), + ComponentDecoration( + title: 'Dialogs and BottomSheets', + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + ElevatedButton( + onPressed: () { + showModalBottomSheet( + context: context, + builder: (BuildContext context) { + // https://api.flutter.dev/flutter/material/showModalBottomSheet.html + return SizedBox( + height: 200, + child: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Modal BottomSheet'), + ElevatedButton( + child: const Text('Close BottomSheet'), + onPressed: () => Navigator.pop(context), + ), + ], + ), + ), + ); + }, + ); + }, + child: const Text('show ModalBottomSheet'), + ), + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return AlertDialog( + title: const Text('Dialog Title'), + content: const Text('Dialog description'), + actions: [ + TextButton( + onPressed: () => Navigator.pop(context), + child: const Text('Close'), + ), + ], + ); + }, + ); + }, + child: const Text('show Dialog'), + ), + ElevatedButton( + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return Dialog.fullscreen( + child: Scaffold( + appBar: AppBar( + title: const Text('Fullscreen Dialog'), + ), + body: Center( + child: ElevatedButton( + child: const Text('Close Dialog'), + onPressed: () => Navigator.pop(context), + ), + ), + ), + ); + }, + ); + }, + child: const Text('show Fullscreen Dialog'), + ), + ], + ), + ), + ComponentDecoration( + title: 'Navigation Bar', + child: NavigationBar( + selectedIndex: navBarIndex, + onDestinationSelected: (value) { + setState(() { + navBarIndex = value; + }); + }, + destinations: const [ + NavigationDestination( + icon: Icon(Icons.home), + label: 'Home', + ), + NavigationDestination( + icon: Icon(Icons.favorite), + label: 'Favorites', + ), + NavigationDestination( + icon: Icon(Icons.search), + label: 'Search', + ), + NavigationDestination( + icon: Icon(Icons.settings), + label: 'Settings', + ), + ], + ), + ), + ComponentDecoration( + title: 'Bottom App Bar', + child: BottomAppBar( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + IconButton( + onPressed: () {}, + icon: const Icon(Icons.menu), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.search), + ), + IconButton( + onPressed: () {}, + icon: const Icon(Icons.more_vert), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/material_demo/presentation/widgets/component_decoration.dart b/lib/features/material_demo/presentation/widgets/component_decoration.dart new file mode 100644 index 0000000..14cdfbc --- /dev/null +++ b/lib/features/material_demo/presentation/widgets/component_decoration.dart @@ -0,0 +1,52 @@ +import 'package:flutter/material.dart'; + +class ComponentDecoration extends StatelessWidget { + const ComponentDecoration( + {required this.child, required this.title, super.key}); + + final Widget child; + final String title; + + @override + Widget build(BuildContext context) { + return Column( + children: [ + Padding( + padding: const EdgeInsets.symmetric(vertical: 8.0), + child: Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(title), + const SizedBox(width: 8), + const Icon( + Icons.info_outline, + size: 16, + ), + ], + ), + ), + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: Card( + elevation: 0, + shape: RoundedRectangleBorder( + side: BorderSide( + color: Theme.of(context).colorScheme.outlineVariant, + ), + borderRadius: const BorderRadius.all(Radius.circular(12)), + ), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 5.0, + vertical: 20.0, + ), + child: child, + ), + ), + ), + ], + ); + } +} diff --git a/lib/features/matrix_rgb/business/service/matrix_service.dart b/lib/features/matrix_rgb/business/service/matrix_service.dart new file mode 100644 index 0000000..92f881c --- /dev/null +++ b/lib/features/matrix_rgb/business/service/matrix_service.dart @@ -0,0 +1,28 @@ +import 'package:dart_periphery/dart_periphery.dart'; +import 'package:flutter/foundation.dart'; + +// linux_spidev was tried out here beforehand, +// but it didn't work and was replaced by dart_periphery +// import 'package:linux_spidev/linux_spidev.dart'; + +class MatrixService { + final spi = SPI(0, 0, SPImode.mode0, 35000); + // final spidev = Spidev.fromBusDevNrs(0, 0); + + void setLedsOn(List pixels) async { + // final handle = spidev.open(); + try { + int refresh = 1; + while (refresh != 0) { + spi.transfer(pixels, false); + await Future.delayed(const Duration(milliseconds: 100)); + refresh--; + } + } catch (e) { + debugPrint(e.toString()); + } finally { + // clean up the resource + spi.dispose(); + } + } +} diff --git a/lib/features/matrix_rgb/business/utils/find_closest_color_index.dart b/lib/features/matrix_rgb/business/utils/find_closest_color_index.dart new file mode 100644 index 0000000..c497c38 --- /dev/null +++ b/lib/features/matrix_rgb/business/utils/find_closest_color_index.dart @@ -0,0 +1,28 @@ +import 'dart:ui'; + +int findClosestColorIndex(Color sourceColor) { + int bestMatchIndex = 0; + double smallestDistance = double.infinity; // Start with a 'large' distance + + // Iterate through possible controller colors (0-255) + for (int i = 0; i < 256; i++) { + // Example: Assume your controller just uses an even R/G/B scaling + int r = (i >> 5) * 32; // Assuming an even split to get 8 values + int g = ((i >> 2) & 0x7) * 32; + int b = (i & 0x3) * 64; + + // Color controllerColor = Color.fromARGB(255, r, g, b); + + // Calculate distance (you can use different formulas) + int distance = (sourceColor.red - r) * (sourceColor.red - r) + + (sourceColor.green - g) * (sourceColor.green - g) + + (sourceColor.blue - b) * (sourceColor.blue - b); + + if (distance < smallestDistance) { + smallestDistance = distance.toDouble(); + bestMatchIndex = i; + } + } + + return bestMatchIndex; +} diff --git a/lib/features/matrix_rgb/data/models/pixel.dart b/lib/features/matrix_rgb/data/models/pixel.dart new file mode 100644 index 0000000..26ca982 --- /dev/null +++ b/lib/features/matrix_rgb/data/models/pixel.dart @@ -0,0 +1,11 @@ +import 'dart:ui'; + +class Pixel { + final Offset offset; + final Color color; + + Pixel({ + required this.offset, + required this.color, + }); +} diff --git a/lib/features/matrix_rgb/presentation/matrix_screen.dart b/lib/features/matrix_rgb/presentation/matrix_screen.dart new file mode 100644 index 0000000..6cc02e3 --- /dev/null +++ b/lib/features/matrix_rgb/presentation/matrix_screen.dart @@ -0,0 +1,143 @@ +import 'package:flex_color_picker/flex_color_picker.dart'; +import 'package:flutter/material.dart'; + +import '../data/models/pixel.dart'; +import 'widgets/pixel_painter.dart'; + +class MatrixScreen extends StatefulWidget { + const MatrixScreen({super.key}); + + @override + State createState() => _MatrixScreenState(); +} + +class _MatrixScreenState extends State { + List pixels = List.generate( + 64, + (index) => Pixel( + offset: Offset.zero, + color: Colors.transparent, + ), + ); + Color color = Colors.green; + + void _updatePixel(Offset offset) { + const double cellSize = 50; // Size of each grid cell + + // Get the row and column based on touch position + int row = (offset.dy / cellSize).floor(); + int col = (offset.dx / cellSize).floor(); + + // Bound Check (Only proceed if within the grid) + if (row >= 0 && row < 8 && col >= 0 && col < 8) { + // Calculate the index in the flat list + int index = row * 8 + col; + + // Update the Pixel + pixels[index] = Pixel(offset: offset, color: color); + setState(() {}); + } + } + + @override + Widget build(BuildContext context) { + return Center( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Listener( + onPointerDown: (event) { + // Determine offset + Offset offset = + Offset(event.localPosition.dx, event.localPosition.dy); + + // Calculate index and update pixel + _updatePixel(offset); + }, + onPointerMove: (event) { + // Determine offset + Offset offset = + Offset(event.localPosition.dx, event.localPosition.dy); + + // Calculate index and update pixel + _updatePixel(offset); + }, + child: InteractiveViewer( + maxScale: 5, + child: Container( + // color: Colors.white, + decoration: BoxDecoration(border: Border.all()), + child: ClipRRect( + child: GridPaper( + divisions: 2, + interval: 500, + color: Theme.of(context).textTheme.headlineLarge?.color ?? + Colors.red, + child: Transform( + transform: Matrix4.identity()..scale(50.0), + child: ClipRect( + child: CustomPaint( + size: const Size(400, 400), + painter: PixelPainter( + pixels: pixels, + ), + ), + ), + ), + ), + ), + ), + ), + ), + const SizedBox( + width: 24, + ), + ColorIndicator( + width: 40, + height: 40, + borderRadius: 24, + color: color, + elevation: 1, + onSelectFocus: false, + onSelect: () async { + // Wait for the dialog to return color selection result. + final Color newColor = await showColorPickerDialog( + context, + color, + title: Text('ColorPicker', + style: Theme.of(context).textTheme.titleLarge), + width: 40, + height: 40, + spacing: 0, + runSpacing: 0, + borderRadius: 0, + wheelDiameter: 165, + enableOpacity: true, + showColorCode: true, + colorCodeHasColor: true, + pickersEnabled: { + ColorPickerType.wheel: true, + }, + copyPasteBehavior: const ColorPickerCopyPasteBehavior( + copyButton: true, + pasteButton: true, + longPressMenu: true, + ), + actionButtons: const ColorPickerActionButtons( + okButton: true, + closeButton: true, + dialogActionButtons: false, + ), + constraints: const BoxConstraints( + minHeight: 480, minWidth: 320, maxWidth: 320), + ); + // Update the state with the newly selected color from the color picker + setState(() { + color = newColor; + }); + }), + ], + ), + ); + } +} diff --git a/lib/features/matrix_rgb/presentation/widgets/pixel_painter.dart b/lib/features/matrix_rgb/presentation/widgets/pixel_painter.dart new file mode 100644 index 0000000..d773c44 --- /dev/null +++ b/lib/features/matrix_rgb/presentation/widgets/pixel_painter.dart @@ -0,0 +1,63 @@ +import 'dart:io'; +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +import '../../data/models/pixel.dart'; +import '../../business/service/matrix_service.dart'; +import '../../business/utils/find_closest_color_index.dart'; + +class PixelPainter extends CustomPainter { + final List pixels; + + PixelPainter({required this.pixels}); + + @override + void paint(Canvas canvas, Size size) { + const double cellSize = 50; + + if (pixels.isNotEmpty) { + for (final pixel in pixels) { + final Offset roundedOffset = Offset( + (pixel.offset.dx / cellSize).clamp(0, 7).toInt() * cellSize + 25, + (pixel.offset.dy / cellSize).clamp(0, 7).toInt() * cellSize + 25, + ); + + canvas.drawPoints( + PointMode.points, + [roundedOffset / 50], + Paint() + ..color = pixel.color + ..strokeWidth = 1 + ..isAntiAlias = false + ..strokeJoin = StrokeJoin.round + ..style = PaintingStyle.stroke, + ); + } + + // Assuming 'width' represents the width of your matrix (in this case, 8) + const width = 8; + + List colorIntList = pixels.map((pixel) { + if (pixel.color.value != 0) return findClosestColorIndex(pixel.color); + return pixel.color.value; + }).toList(); + + // horizontal mirroring + List mirroredList = List.generate(64, (index) { + int row = index ~/ width; + int reversedCol = width - 1 - (index % width); + int newIndex = row * width + reversedCol; + return colorIntList[newIndex]; + }); + + // dart periphery is only supported on linux + if (Platform.isLinux) { + MatrixService().setLedsOn(mirroredList.reversed.toList()); + } + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) => true; +} diff --git a/lib/features/morse_led/business/service/morse_service.dart b/lib/features/morse_led/business/service/morse_service.dart new file mode 100644 index 0000000..f803394 --- /dev/null +++ b/lib/features/morse_led/business/service/morse_service.dart @@ -0,0 +1,102 @@ +import 'package:flutter_gpiod/flutter_gpiod.dart'; + +class MorseService { + final _chips = FlutterGpiod.instance.chips; + + printMessage(String message) async { + final chip = _getRaspberryGPIO(); + final ledPin = chip.lines[18]; + ledPin.requestOutput(consumer: "morse code led", initialValue: false); + final List chars = message.toUpperCase().split(''); + const int pulseDuration = 100; + + for (var char in chars) { + final List morseCode = _morseAlphabet[char]?.split('') ?? ['*']; + for (var code in morseCode) { + print('CODE: $code from $morseCode'); + if (code == '*') { + await Future.delayed(const Duration(milliseconds: pulseDuration * 7)); + break; + } + ledPin.setValue(true); + print('ON'); + if (code == '.') { + await Future.delayed(const Duration(milliseconds: pulseDuration)); + } else if (code == '-') { + await Future.delayed(const Duration(milliseconds: pulseDuration * 3)); + } + print('OFF'); + ledPin.setValue(false); + // pause between letters + await Future.delayed(const Duration(milliseconds: pulseDuration)); + } + print("SPACE BETWEEN LETTERS"); + // space between letters is 3*pulseDuration + await Future.delayed(const Duration(milliseconds: pulseDuration * 3)); + } + + ledPin.release(); + return true; + } + + GpioChip _getRaspberryGPIO() { + /// Retrieve the line with index 23 of the first chip. + /// This is BCM pin 23 for the Raspberry Pi. + /// + /// I recommend finding the chip you want + /// based on the chip label, as is done here. + /// + /// In this example, we search for the main Raspberry Pi GPIO chip and then + /// retrieve the line with index 23 of it. So [line] is GPIO pin BCM 23. + /// + /// The main GPIO chip is called `pinctrl-bcm2711` on Pi 4 and `pinctrl-bcm2835` + /// on older Raspberry Pis and it was also called that way on Pi 4 with older + /// kernel versions. + final chip = _chips.singleWhere( + (chip) => chip.label == 'pinctrl-bcm2711', + orElse: () => + _chips.singleWhere((chip) => chip.label == 'pinctrl-bcm2835'), + ); + return chip; + } + + // International Morse Code + final Map _morseAlphabet = { + 'A': '.-', + 'B': '-...', + 'C': '-.-.', + 'D': '-..', + 'E': '.', + 'F': '..-.', + 'G': '--.', + 'H': '....', + 'I': '..', + 'J': '.---', + 'K': '-.-', + 'L': '.-..', + 'M': '--', + 'N': '-.', + 'O': '---', + 'P': '.--.', + 'Q': '--.-', + 'R': '.-.', + 'S': '...', + 'T': '-', + 'U': '..-', + 'V': '...-', + 'W': '.--', + 'X': '-..-', + 'Y': '-.--', + 'Z': '--..', + '0': '-----', + '1': '.----', + '2': '..---', + '3': '...--', + '4': '....-', + '5': '.....', + '6': '-....', + '7': '--...', + '8': '---..', + '9': '----.', + }; +} diff --git a/lib/features/morse_led/presentation/led_screen.dart b/lib/features/morse_led/presentation/led_screen.dart new file mode 100644 index 0000000..ce6ae72 --- /dev/null +++ b/lib/features/morse_led/presentation/led_screen.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../common/widgets/text_input_dialog.dart'; +import '../business/service/morse_service.dart'; + +class LedScreen extends ConsumerWidget { + const LedScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final TextEditingController textController = TextEditingController(); + + return Center( + child: SizedBox( + height: 224, + child: Card( + color: Theme.of(context).colorScheme.surfaceVariant.withOpacity(0.3), + elevation: 0, + margin: const EdgeInsets.all(32), + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 20.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Container( + constraints: const BoxConstraints(maxWidth: 400), + child: TextInputDialog( + controller: textController, + title: 'Enter Morse Code Message', + onReturnPress: () { + // FocusScope.of(context).unfocus(); + context.pop(); + }, + ), + ), + ), + const SizedBox( + height: 12, + ), + ElevatedButton( + onPressed: () { + MorseService().printMessage(textController.text); + }, + child: const Text("Morse Code"), + ), + ], + ), + ), + ), + ), + ); + } +} diff --git a/lib/features/news_api/business/news_controller.dart b/lib/features/news_api/business/news_controller.dart new file mode 100644 index 0000000..fd66567 --- /dev/null +++ b/lib/features/news_api/business/news_controller.dart @@ -0,0 +1,45 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +import '../data/repository/news_repository.dart'; +import '../data/models/region_news.dart'; + +part 'news_controller.g.dart'; + +/// The news controller +@riverpod +class NewsController extends _$NewsController { + @override + Future build() async { + try { + final String newsResponse = await NewsRepository().fetchNews(); + final Map jsonData = json.decode(newsResponse); + final RegionNews news = RegionNews.fromJson(jsonData); + return news; + } catch (e) { + rethrow; + } + } + + /// Append the next page of news to the current news list + Future appendPage() async { + try { + final RegionNews currentNews = + state.asData?.value ?? const RegionNews(news: []); + final newsResponse = + await NewsRepository().fetchNewsPage(currentNews.nextPage ?? 'null'); + final Map jsonData = jsonDecode(newsResponse); + final RegionNews nextNews = RegionNews.fromJson(jsonData); + final RegionNews appendNews = + currentNews.copyWith(nextPage: nextNews.nextPage, news: [ + ...currentNews.news, + ...nextNews.news, + ]); + state = AsyncData(appendNews); + } catch (e) { + debugPrint(e.toString()); + } + } +} diff --git a/lib/features/news_api/business/news_controller.g.dart b/lib/features/news_api/business/news_controller.g.dart new file mode 100644 index 0000000..5167e5d --- /dev/null +++ b/lib/features/news_api/business/news_controller.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'news_controller.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$newsControllerHash() => r'd2b544008f72eeee9a71bec7d0fe5b8981536fee'; + +/// The news controller +/// +/// Copied from [NewsController]. +@ProviderFor(NewsController) +final newsControllerProvider = + AutoDisposeAsyncNotifierProvider.internal( + NewsController.new, + name: r'newsControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$newsControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$NewsController = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/news_api/business/scroll_state_controller.dart b/lib/features/news_api/business/scroll_state_controller.dart new file mode 100644 index 0000000..9a3a9ab --- /dev/null +++ b/lib/features/news_api/business/scroll_state_controller.dart @@ -0,0 +1,15 @@ +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'scroll_state_controller.g.dart'; + +@riverpod +class ScrollStateController extends _$ScrollStateController { + @override + bool build() { + return false; + } + + toggle(bool set) { + state = set; + } +} diff --git a/lib/features/news_api/business/scroll_state_controller.g.dart b/lib/features/news_api/business/scroll_state_controller.g.dart new file mode 100644 index 0000000..d9fa4f2 --- /dev/null +++ b/lib/features/news_api/business/scroll_state_controller.g.dart @@ -0,0 +1,27 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'scroll_state_controller.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$scrollStateControllerHash() => + r'0fd7eab4471ba8adac0d631c0580afb88e31901d'; + +/// See also [ScrollStateController]. +@ProviderFor(ScrollStateController) +final scrollStateControllerProvider = + AutoDisposeNotifierProvider.internal( + ScrollStateController.new, + name: r'scrollStateControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$scrollStateControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$ScrollStateController = AutoDisposeNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/news_api/data/models/news.dart b/lib/features/news_api/data/models/news.dart new file mode 100644 index 0000000..6c12118 --- /dev/null +++ b/lib/features/news_api/data/models/news.dart @@ -0,0 +1,134 @@ +import 'package:flutter/foundation.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'teaser_image.dart'; + +part 'news.freezed.dart'; +part 'news.g.dart'; + +@freezed +class News with _$News { + const factory News({ + String? title, + TeaserImage? teaserImage, + TeaserImage? brandingImage, + String? date, + String? type, + }) = _News; + + factory News.fromJson(Map json) => _$NewsFromJson(json); +} + +// old manual implementation: + +// final String sophoraId; +// final String externalId; +// final String title; +// final TeaserImage? teaserImage; +// final TeaserImage? brandingImage; +// final String date; +// final String updateCheckUrl; +// final int regionId; +// final String details; +// final String detailsweb; +// final String shareURL; +// final String topline; +// final String firstSentence; +// final String type; +// News({ +// required this.sophoraId, +// required this.externalId, +// required this.title, +// this.teaserImage, +// this.brandingImage, +// required this.date, +// required this.updateCheckUrl, +// required this.regionId, +// required this.details, +// required this.detailsweb, +// required this.shareURL, +// required this.topline, +// required this.firstSentence, +// required this.type, +// }); + +// News copyWith({ +// String? sophoraId, +// String? externalId, +// String? title, +// TeaserImage? teaserImage, +// TeaserImage? brandingImage, +// String? date, +// String? updateCheckUrl, +// int? regionId, +// String? details, +// String? detailsweb, +// String? shareURL, +// String? topline, +// String? firstSentence, +// String? type, +// }) { +// return News( +// sophoraId: sophoraId ?? this.sophoraId, +// externalId: externalId ?? this.externalId, +// title: title ?? this.title, +// teaserImage: teaserImage ?? this.teaserImage, +// brandingImage: brandingImage ?? this.brandingImage, +// date: date ?? this.date, +// updateCheckUrl: updateCheckUrl ?? this.updateCheckUrl, +// regionId: regionId ?? this.regionId, +// details: details ?? this.details, +// detailsweb: detailsweb ?? this.detailsweb, +// shareURL: shareURL ?? this.shareURL, +// topline: topline ?? this.topline, +// firstSentence: firstSentence ?? this.firstSentence, +// type: type ?? this.type, +// ); +// } + +// Map toMap() { +// return { +// 'sophoraId': sophoraId, +// 'externalId': externalId, +// 'title': title, +// 'teaserImage': teaserImage?.toMap(), +// 'brandingImage': brandingImage?.toMap(), +// 'date': date, +// 'updateCheckUrl': updateCheckUrl, +// 'regionId': regionId, +// 'details': details, +// 'detailsweb': detailsweb, +// 'shareURL': shareURL, +// 'topline': topline, +// 'firstSentence': firstSentence, +// 'type': type, +// }; +// } + +// factory News.fromMap(Map map) { +// return News( +// sophoraId: map['sophoraId'] ?? '', +// externalId: map['externalId'] ?? '', +// title: map['title'] ?? '', +// teaserImage: map['teaserImage'] != null +// ? TeaserImage.fromMap(map['teaserImage']) +// : null, +// brandingImage: map['brandingImage'] != null +// ? TeaserImage.fromMap(map['brandingImage']) +// : null, +// date: map['date'] ?? '', +// updateCheckUrl: map['updateCheckUrl'] ?? '', +// regionId: map['regionId']?.toInt() ?? 0, +// details: map['details'] ?? '', +// detailsweb: map['detailsweb'] ?? '', +// shareURL: map['shareURL'] ?? '', +// topline: map['topline'] ?? '', +// firstSentence: map['firstSentence'] ?? '', +// type: map['type'] ?? '', +// ); +// } + +// String toJson() => json.encode(toMap()); + +// factory News.fromJson(String source) => News.fromMap(json.decode(source)); +// } diff --git a/lib/features/news_api/data/models/news.freezed.dart b/lib/features/news_api/data/models/news.freezed.dart new file mode 100644 index 0000000..1db9b46 --- /dev/null +++ b/lib/features/news_api/data/models/news.freezed.dart @@ -0,0 +1,273 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'news.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +News _$NewsFromJson(Map json) { + return _News.fromJson(json); +} + +/// @nodoc +mixin _$News { + String? get title => throw _privateConstructorUsedError; + TeaserImage? get teaserImage => throw _privateConstructorUsedError; + TeaserImage? get brandingImage => throw _privateConstructorUsedError; + String? get date => throw _privateConstructorUsedError; + String? get type => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $NewsCopyWith get copyWith => throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $NewsCopyWith<$Res> { + factory $NewsCopyWith(News value, $Res Function(News) then) = + _$NewsCopyWithImpl<$Res, News>; + @useResult + $Res call( + {String? title, + TeaserImage? teaserImage, + TeaserImage? brandingImage, + String? date, + String? type}); + + $TeaserImageCopyWith<$Res>? get teaserImage; + $TeaserImageCopyWith<$Res>? get brandingImage; +} + +/// @nodoc +class _$NewsCopyWithImpl<$Res, $Val extends News> + implements $NewsCopyWith<$Res> { + _$NewsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? title = freezed, + Object? teaserImage = freezed, + Object? brandingImage = freezed, + Object? date = freezed, + Object? type = freezed, + }) { + return _then(_value.copyWith( + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + teaserImage: freezed == teaserImage + ? _value.teaserImage + : teaserImage // ignore: cast_nullable_to_non_nullable + as TeaserImage?, + brandingImage: freezed == brandingImage + ? _value.brandingImage + : brandingImage // ignore: cast_nullable_to_non_nullable + as TeaserImage?, + date: freezed == date + ? _value.date + : date // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } + + @override + @pragma('vm:prefer-inline') + $TeaserImageCopyWith<$Res>? get teaserImage { + if (_value.teaserImage == null) { + return null; + } + + return $TeaserImageCopyWith<$Res>(_value.teaserImage!, (value) { + return _then(_value.copyWith(teaserImage: value) as $Val); + }); + } + + @override + @pragma('vm:prefer-inline') + $TeaserImageCopyWith<$Res>? get brandingImage { + if (_value.brandingImage == null) { + return null; + } + + return $TeaserImageCopyWith<$Res>(_value.brandingImage!, (value) { + return _then(_value.copyWith(brandingImage: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$NewsImplCopyWith<$Res> implements $NewsCopyWith<$Res> { + factory _$$NewsImplCopyWith( + _$NewsImpl value, $Res Function(_$NewsImpl) then) = + __$$NewsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String? title, + TeaserImage? teaserImage, + TeaserImage? brandingImage, + String? date, + String? type}); + + @override + $TeaserImageCopyWith<$Res>? get teaserImage; + @override + $TeaserImageCopyWith<$Res>? get brandingImage; +} + +/// @nodoc +class __$$NewsImplCopyWithImpl<$Res> + extends _$NewsCopyWithImpl<$Res, _$NewsImpl> + implements _$$NewsImplCopyWith<$Res> { + __$$NewsImplCopyWithImpl(_$NewsImpl _value, $Res Function(_$NewsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? title = freezed, + Object? teaserImage = freezed, + Object? brandingImage = freezed, + Object? date = freezed, + Object? type = freezed, + }) { + return _then(_$NewsImpl( + title: freezed == title + ? _value.title + : title // ignore: cast_nullable_to_non_nullable + as String?, + teaserImage: freezed == teaserImage + ? _value.teaserImage + : teaserImage // ignore: cast_nullable_to_non_nullable + as TeaserImage?, + brandingImage: freezed == brandingImage + ? _value.brandingImage + : brandingImage // ignore: cast_nullable_to_non_nullable + as TeaserImage?, + date: freezed == date + ? _value.date + : date // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$NewsImpl with DiagnosticableTreeMixin implements _News { + const _$NewsImpl( + {this.title, this.teaserImage, this.brandingImage, this.date, this.type}); + + factory _$NewsImpl.fromJson(Map json) => + _$$NewsImplFromJson(json); + + @override + final String? title; + @override + final TeaserImage? teaserImage; + @override + final TeaserImage? brandingImage; + @override + final String? date; + @override + final String? type; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'News(title: $title, teaserImage: $teaserImage, brandingImage: $brandingImage, date: $date, type: $type)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'News')) + ..add(DiagnosticsProperty('title', title)) + ..add(DiagnosticsProperty('teaserImage', teaserImage)) + ..add(DiagnosticsProperty('brandingImage', brandingImage)) + ..add(DiagnosticsProperty('date', date)) + ..add(DiagnosticsProperty('type', type)); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$NewsImpl && + (identical(other.title, title) || other.title == title) && + (identical(other.teaserImage, teaserImage) || + other.teaserImage == teaserImage) && + (identical(other.brandingImage, brandingImage) || + other.brandingImage == brandingImage) && + (identical(other.date, date) || other.date == date) && + (identical(other.type, type) || other.type == type)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => + Object.hash(runtimeType, title, teaserImage, brandingImage, date, type); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$NewsImplCopyWith<_$NewsImpl> get copyWith => + __$$NewsImplCopyWithImpl<_$NewsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$NewsImplToJson( + this, + ); + } +} + +abstract class _News implements News { + const factory _News( + {final String? title, + final TeaserImage? teaserImage, + final TeaserImage? brandingImage, + final String? date, + final String? type}) = _$NewsImpl; + + factory _News.fromJson(Map json) = _$NewsImpl.fromJson; + + @override + String? get title; + @override + TeaserImage? get teaserImage; + @override + TeaserImage? get brandingImage; + @override + String? get date; + @override + String? get type; + @override + @JsonKey(ignore: true) + _$$NewsImplCopyWith<_$NewsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/news_api/data/models/news.g.dart b/lib/features/news_api/data/models/news.g.dart new file mode 100644 index 0000000..31a0263 --- /dev/null +++ b/lib/features/news_api/data/models/news.g.dart @@ -0,0 +1,28 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'news.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$NewsImpl _$$NewsImplFromJson(Map json) => _$NewsImpl( + title: json['title'] as String?, + teaserImage: json['teaserImage'] == null + ? null + : TeaserImage.fromJson(json['teaserImage'] as Map), + brandingImage: json['brandingImage'] == null + ? null + : TeaserImage.fromJson(json['brandingImage'] as Map), + date: json['date'] as String?, + type: json['type'] as String?, + ); + +Map _$$NewsImplToJson(_$NewsImpl instance) => + { + 'title': instance.title, + 'teaserImage': instance.teaserImage, + 'brandingImage': instance.brandingImage, + 'date': instance.date, + 'type': instance.type, + }; diff --git a/lib/features/news_api/data/models/region_news.dart b/lib/features/news_api/data/models/region_news.dart new file mode 100644 index 0000000..8f7e4a7 --- /dev/null +++ b/lib/features/news_api/data/models/region_news.dart @@ -0,0 +1,94 @@ +import 'package:flutter/foundation.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +import 'news.dart'; + +part 'region_news.freezed.dart'; +part 'region_news.g.dart'; + +@freezed +class RegionNews with _$RegionNews { + const factory RegionNews({ + required List news, + String? nextPage, + }) = _RegionNews; + + factory RegionNews.fromJson(Map json) => + _$RegionNewsFromJson(json); +} + +// old manual implementation: + +// class RegionNews { +// List news; +// String newStoriesCountLink; +// String type; +// String? nextPage; +// RegionNews({ +// required this.news, +// required this.newStoriesCountLink, +// required this.type, +// this.nextPage, +// }); + +// RegionNews copyWith({ +// List? news, +// String? newStoriesCountLink, +// String? type, +// String? nextPage, +// }) { +// return RegionNews( +// news: news ?? this.news, +// newStoriesCountLink: newStoriesCountLink ?? this.newStoriesCountLink, +// type: type ?? this.type, +// nextPage: nextPage ?? this.nextPage, +// ); +// } + +// Map toMap() { +// return { +// 'news': news.map((x) => x.toMap()).toList(), +// 'newStoriesCountLink': newStoriesCountLink, +// 'type': type, +// 'nextPage': nextPage, +// }; +// } + +// factory RegionNews.fromMap(Map map) { +// return RegionNews( +// news: List.from(map['news']?.map((x) => News.fromMap(x))), +// newStoriesCountLink: map['newStoriesCountLink'] ?? '', +// type: map['type'] ?? '', +// nextPage: map['nextPage'], +// ); +// } + +// String toJson() => json.encode(toMap()); + +// factory RegionNews.fromJson(String source) => +// RegionNews.fromMap(json.decode(source)); + +// @override +// String toString() { +// return 'RegionNews(news: $news, newStoriesCountLink: $newStoriesCountLink, type: $type, nextPage: $nextPage)'; +// } + +// @override +// bool operator ==(Object other) { +// if (identical(this, other)) return true; + +// return other is RegionNews && +// listEquals(other.news, news) && +// other.newStoriesCountLink == newStoriesCountLink && +// other.type == type && +// other.nextPage == nextPage; +// } + +// @override +// int get hashCode { +// return news.hashCode ^ +// newStoriesCountLink.hashCode ^ +// type.hashCode ^ +// nextPage.hashCode; +// } +// } diff --git a/lib/features/news_api/data/models/region_news.freezed.dart b/lib/features/news_api/data/models/region_news.freezed.dart new file mode 100644 index 0000000..820af54 --- /dev/null +++ b/lib/features/news_api/data/models/region_news.freezed.dart @@ -0,0 +1,187 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'region_news.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +RegionNews _$RegionNewsFromJson(Map json) { + return _RegionNews.fromJson(json); +} + +/// @nodoc +mixin _$RegionNews { + List get news => throw _privateConstructorUsedError; + String? get nextPage => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $RegionNewsCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $RegionNewsCopyWith<$Res> { + factory $RegionNewsCopyWith( + RegionNews value, $Res Function(RegionNews) then) = + _$RegionNewsCopyWithImpl<$Res, RegionNews>; + @useResult + $Res call({List news, String? nextPage}); +} + +/// @nodoc +class _$RegionNewsCopyWithImpl<$Res, $Val extends RegionNews> + implements $RegionNewsCopyWith<$Res> { + _$RegionNewsCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? news = null, + Object? nextPage = freezed, + }) { + return _then(_value.copyWith( + news: null == news + ? _value.news + : news // ignore: cast_nullable_to_non_nullable + as List, + nextPage: freezed == nextPage + ? _value.nextPage + : nextPage // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$RegionNewsImplCopyWith<$Res> + implements $RegionNewsCopyWith<$Res> { + factory _$$RegionNewsImplCopyWith( + _$RegionNewsImpl value, $Res Function(_$RegionNewsImpl) then) = + __$$RegionNewsImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({List news, String? nextPage}); +} + +/// @nodoc +class __$$RegionNewsImplCopyWithImpl<$Res> + extends _$RegionNewsCopyWithImpl<$Res, _$RegionNewsImpl> + implements _$$RegionNewsImplCopyWith<$Res> { + __$$RegionNewsImplCopyWithImpl( + _$RegionNewsImpl _value, $Res Function(_$RegionNewsImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? news = null, + Object? nextPage = freezed, + }) { + return _then(_$RegionNewsImpl( + news: null == news + ? _value._news + : news // ignore: cast_nullable_to_non_nullable + as List, + nextPage: freezed == nextPage + ? _value.nextPage + : nextPage // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$RegionNewsImpl with DiagnosticableTreeMixin implements _RegionNews { + const _$RegionNewsImpl({required final List news, this.nextPage}) + : _news = news; + + factory _$RegionNewsImpl.fromJson(Map json) => + _$$RegionNewsImplFromJson(json); + + final List _news; + @override + List get news { + if (_news is EqualUnmodifiableListView) return _news; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_news); + } + + @override + final String? nextPage; + + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return 'RegionNews(news: $news, nextPage: $nextPage)'; + } + + @override + void debugFillProperties(DiagnosticPropertiesBuilder properties) { + super.debugFillProperties(properties); + properties + ..add(DiagnosticsProperty('type', 'RegionNews')) + ..add(DiagnosticsProperty('news', news)) + ..add(DiagnosticsProperty('nextPage', nextPage)); + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$RegionNewsImpl && + const DeepCollectionEquality().equals(other._news, _news) && + (identical(other.nextPage, nextPage) || + other.nextPage == nextPage)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_news), nextPage); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$RegionNewsImplCopyWith<_$RegionNewsImpl> get copyWith => + __$$RegionNewsImplCopyWithImpl<_$RegionNewsImpl>(this, _$identity); + + @override + Map toJson() { + return _$$RegionNewsImplToJson( + this, + ); + } +} + +abstract class _RegionNews implements RegionNews { + const factory _RegionNews( + {required final List news, + final String? nextPage}) = _$RegionNewsImpl; + + factory _RegionNews.fromJson(Map json) = + _$RegionNewsImpl.fromJson; + + @override + List get news; + @override + String? get nextPage; + @override + @JsonKey(ignore: true) + _$$RegionNewsImplCopyWith<_$RegionNewsImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/news_api/data/models/region_news.g.dart b/lib/features/news_api/data/models/region_news.g.dart new file mode 100644 index 0000000..de0c7ad --- /dev/null +++ b/lib/features/news_api/data/models/region_news.g.dart @@ -0,0 +1,21 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'region_news.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$RegionNewsImpl _$$RegionNewsImplFromJson(Map json) => + _$RegionNewsImpl( + news: (json['news'] as List) + .map((e) => News.fromJson(e as Map)) + .toList(), + nextPage: json['nextPage'] as String?, + ); + +Map _$$RegionNewsImplToJson(_$RegionNewsImpl instance) => + { + 'news': instance.news, + 'nextPage': instance.nextPage, + }; diff --git a/lib/features/news_api/data/models/teaser_image.dart b/lib/features/news_api/data/models/teaser_image.dart new file mode 100644 index 0000000..1996663 --- /dev/null +++ b/lib/features/news_api/data/models/teaser_image.dart @@ -0,0 +1,62 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'teaser_image.freezed.dart'; +part 'teaser_image.g.dart'; + +@freezed +class TeaserImage with _$TeaserImage { + const factory TeaserImage({ + Map? imageVariants, + }) = _TeaserImage; + + factory TeaserImage.fromJson(Map json) => + _$TeaserImageFromJson(json); +} + +// old manual implementation: + +// class TeaserImage { +// final String? title; +// final String alttext; +// final String? copyright; +// final String type; +// final Map imageVariants; + +// TeaserImage({ +// this.title, +// required this.alttext, +// this.copyright, +// required this.type, +// required this.imageVariants, +// }); + +// Map toMap() { +// return { +// 'title': title, +// 'alttext': alttext, +// 'copyright': copyright, +// 'type': type, +// 'imageVariants': Map.from(imageVariants), +// }; +// } + +// factory TeaserImage.fromMap(Map map) { +// return TeaserImage( +// title: map['title'] ?? '', +// alttext: map['alttext'] ?? '', +// copyright: map['copyright'], +// type: map['type'] ?? '', +// imageVariants: Map.from(map['imageVariants']), +// ); +// } + +// String toJson() => json.encode(toMap()); + +// factory TeaserImage.fromJson(String source) => +// TeaserImage.fromMap(json.decode(source)); + +// @override +// String toString() { +// return 'TeaserImage(title: $title, alttext: $alttext, copyright: $copyright, type: $type)'; +// } +// } diff --git a/lib/features/news_api/data/models/teaser_image.freezed.dart b/lib/features/news_api/data/models/teaser_image.freezed.dart new file mode 100644 index 0000000..c0b5619 --- /dev/null +++ b/lib/features/news_api/data/models/teaser_image.freezed.dart @@ -0,0 +1,162 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'teaser_image.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +TeaserImage _$TeaserImageFromJson(Map json) { + return _TeaserImage.fromJson(json); +} + +/// @nodoc +mixin _$TeaserImage { + Map? get imageVariants => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $TeaserImageCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TeaserImageCopyWith<$Res> { + factory $TeaserImageCopyWith( + TeaserImage value, $Res Function(TeaserImage) then) = + _$TeaserImageCopyWithImpl<$Res, TeaserImage>; + @useResult + $Res call({Map? imageVariants}); +} + +/// @nodoc +class _$TeaserImageCopyWithImpl<$Res, $Val extends TeaserImage> + implements $TeaserImageCopyWith<$Res> { + _$TeaserImageCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? imageVariants = freezed, + }) { + return _then(_value.copyWith( + imageVariants: freezed == imageVariants + ? _value.imageVariants + : imageVariants // ignore: cast_nullable_to_non_nullable + as Map?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TeaserImageImplCopyWith<$Res> + implements $TeaserImageCopyWith<$Res> { + factory _$$TeaserImageImplCopyWith( + _$TeaserImageImpl value, $Res Function(_$TeaserImageImpl) then) = + __$$TeaserImageImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({Map? imageVariants}); +} + +/// @nodoc +class __$$TeaserImageImplCopyWithImpl<$Res> + extends _$TeaserImageCopyWithImpl<$Res, _$TeaserImageImpl> + implements _$$TeaserImageImplCopyWith<$Res> { + __$$TeaserImageImplCopyWithImpl( + _$TeaserImageImpl _value, $Res Function(_$TeaserImageImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? imageVariants = freezed, + }) { + return _then(_$TeaserImageImpl( + imageVariants: freezed == imageVariants + ? _value._imageVariants + : imageVariants // ignore: cast_nullable_to_non_nullable + as Map?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$TeaserImageImpl implements _TeaserImage { + const _$TeaserImageImpl({final Map? imageVariants}) + : _imageVariants = imageVariants; + + factory _$TeaserImageImpl.fromJson(Map json) => + _$$TeaserImageImplFromJson(json); + + final Map? _imageVariants; + @override + Map? get imageVariants { + final value = _imageVariants; + if (value == null) return null; + if (_imageVariants is EqualUnmodifiableMapView) return _imageVariants; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(value); + } + + @override + String toString() { + return 'TeaserImage(imageVariants: $imageVariants)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TeaserImageImpl && + const DeepCollectionEquality() + .equals(other._imageVariants, _imageVariants)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, const DeepCollectionEquality().hash(_imageVariants)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TeaserImageImplCopyWith<_$TeaserImageImpl> get copyWith => + __$$TeaserImageImplCopyWithImpl<_$TeaserImageImpl>(this, _$identity); + + @override + Map toJson() { + return _$$TeaserImageImplToJson( + this, + ); + } +} + +abstract class _TeaserImage implements TeaserImage { + const factory _TeaserImage({final Map? imageVariants}) = + _$TeaserImageImpl; + + factory _TeaserImage.fromJson(Map json) = + _$TeaserImageImpl.fromJson; + + @override + Map? get imageVariants; + @override + @JsonKey(ignore: true) + _$$TeaserImageImplCopyWith<_$TeaserImageImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/news_api/data/models/teaser_image.g.dart b/lib/features/news_api/data/models/teaser_image.g.dart new file mode 100644 index 0000000..2fde00b --- /dev/null +++ b/lib/features/news_api/data/models/teaser_image.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'teaser_image.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$TeaserImageImpl _$$TeaserImageImplFromJson(Map json) => + _$TeaserImageImpl( + imageVariants: (json['imageVariants'] as Map?)?.map( + (k, e) => MapEntry(k, e as String), + ), + ); + +Map _$$TeaserImageImplToJson(_$TeaserImageImpl instance) => + { + 'imageVariants': instance.imageVariants, + }; diff --git a/lib/features/news_api/data/models/teaser_image_url.dart b/lib/features/news_api/data/models/teaser_image_url.dart new file mode 100644 index 0000000..6daa0ef --- /dev/null +++ b/lib/features/news_api/data/models/teaser_image_url.dart @@ -0,0 +1,62 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'teaser_image_url.freezed.dart'; +part 'teaser_image_url.g.dart'; + +@freezed +class TeaserImageUrl with _$TeaserImageUrl { + const factory TeaserImageUrl({ + String? imageurl, + }) = _TeaserImageUrl; + + factory TeaserImageUrl.fromJson(Map json) => + _$TeaserImageUrlFromJson(json); +} + +// old manual implementation: + +// class TeaserImageUrl { +// final String imageurl; + +// TeaserImageUrl({ +// required this.imageurl, +// }); + +// TeaserImageUrl copyWith({ +// String? imageurl, +// }) { +// return TeaserImageUrl( +// imageurl: imageurl ?? this.imageurl, +// ); +// } + +// Map toMap() { +// return { +// 'imageurl': imageurl, +// }; +// } + +// factory TeaserImageUrl.fromMap(Map map) { +// return TeaserImageUrl( +// imageurl: map['imageurl'] ?? '', +// ); +// } + +// String toJson() => json.encode(toMap()); + +// factory TeaserImageUrl.fromJson(String source) => +// TeaserImageUrl.fromMap(json.decode(source)); + +// @override +// String toString() => 'TeaserImageUrl(imageurl: $imageurl)'; + +// @override +// bool operator ==(Object other) { +// if (identical(this, other)) return true; + +// return other is TeaserImageUrl && other.imageurl == imageurl; +// } + +// @override +// int get hashCode => imageurl.hashCode; +// } diff --git a/lib/features/news_api/data/models/teaser_image_url.freezed.dart b/lib/features/news_api/data/models/teaser_image_url.freezed.dart new file mode 100644 index 0000000..05584d1 --- /dev/null +++ b/lib/features/news_api/data/models/teaser_image_url.freezed.dart @@ -0,0 +1,154 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'teaser_image_url.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +TeaserImageUrl _$TeaserImageUrlFromJson(Map json) { + return _TeaserImageUrl.fromJson(json); +} + +/// @nodoc +mixin _$TeaserImageUrl { + String? get imageurl => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $TeaserImageUrlCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TeaserImageUrlCopyWith<$Res> { + factory $TeaserImageUrlCopyWith( + TeaserImageUrl value, $Res Function(TeaserImageUrl) then) = + _$TeaserImageUrlCopyWithImpl<$Res, TeaserImageUrl>; + @useResult + $Res call({String? imageurl}); +} + +/// @nodoc +class _$TeaserImageUrlCopyWithImpl<$Res, $Val extends TeaserImageUrl> + implements $TeaserImageUrlCopyWith<$Res> { + _$TeaserImageUrlCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? imageurl = freezed, + }) { + return _then(_value.copyWith( + imageurl: freezed == imageurl + ? _value.imageurl + : imageurl // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TeaserImageUrlImplCopyWith<$Res> + implements $TeaserImageUrlCopyWith<$Res> { + factory _$$TeaserImageUrlImplCopyWith(_$TeaserImageUrlImpl value, + $Res Function(_$TeaserImageUrlImpl) then) = + __$$TeaserImageUrlImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String? imageurl}); +} + +/// @nodoc +class __$$TeaserImageUrlImplCopyWithImpl<$Res> + extends _$TeaserImageUrlCopyWithImpl<$Res, _$TeaserImageUrlImpl> + implements _$$TeaserImageUrlImplCopyWith<$Res> { + __$$TeaserImageUrlImplCopyWithImpl( + _$TeaserImageUrlImpl _value, $Res Function(_$TeaserImageUrlImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? imageurl = freezed, + }) { + return _then(_$TeaserImageUrlImpl( + imageurl: freezed == imageurl + ? _value.imageurl + : imageurl // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$TeaserImageUrlImpl implements _TeaserImageUrl { + const _$TeaserImageUrlImpl({this.imageurl}); + + factory _$TeaserImageUrlImpl.fromJson(Map json) => + _$$TeaserImageUrlImplFromJson(json); + + @override + final String? imageurl; + + @override + String toString() { + return 'TeaserImageUrl(imageurl: $imageurl)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TeaserImageUrlImpl && + (identical(other.imageurl, imageurl) || + other.imageurl == imageurl)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, imageurl); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TeaserImageUrlImplCopyWith<_$TeaserImageUrlImpl> get copyWith => + __$$TeaserImageUrlImplCopyWithImpl<_$TeaserImageUrlImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$TeaserImageUrlImplToJson( + this, + ); + } +} + +abstract class _TeaserImageUrl implements TeaserImageUrl { + const factory _TeaserImageUrl({final String? imageurl}) = + _$TeaserImageUrlImpl; + + factory _TeaserImageUrl.fromJson(Map json) = + _$TeaserImageUrlImpl.fromJson; + + @override + String? get imageurl; + @override + @JsonKey(ignore: true) + _$$TeaserImageUrlImplCopyWith<_$TeaserImageUrlImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/lib/features/news_api/data/models/teaser_image_url.g.dart b/lib/features/news_api/data/models/teaser_image_url.g.dart new file mode 100644 index 0000000..dfecd58 --- /dev/null +++ b/lib/features/news_api/data/models/teaser_image_url.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'teaser_image_url.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$TeaserImageUrlImpl _$$TeaserImageUrlImplFromJson(Map json) => + _$TeaserImageUrlImpl( + imageurl: json['imageurl'] as String?, + ); + +Map _$$TeaserImageUrlImplToJson( + _$TeaserImageUrlImpl instance) => + { + 'imageurl': instance.imageurl, + }; diff --git a/lib/features/news_api/data/repository/news_repository.dart b/lib/features/news_api/data/repository/news_repository.dart new file mode 100644 index 0000000..9371719 --- /dev/null +++ b/lib/features/news_api/data/repository/news_repository.dart @@ -0,0 +1,49 @@ +import 'package:dio/dio.dart'; +import 'package:flutter/foundation.dart'; + +import '../../../../constants/api_urls.dart'; + +/// The news repository +class NewsRepository { + /// The Dio instance + final Dio _dio = Dio( + BaseOptions( + baseUrl: TagesschauApiUrls.main, + responseType: ResponseType.json, + ), + ); + + /// Fetch the news + Future fetchNews() async { + try { + final Response response = await _dio.get('/news'); + if (response.statusCode == 200) { + if (response.data != null) { + return response.data!; + } else { + throw Exception('Failed to fetch news: response data is null'); + } + } + throw Exception( + 'Failed to fetch news. Status code: ${response.statusCode}.'); + } catch (e) { + debugPrint("Error fetching news: $e"); + rethrow; + } + } + + /// Fetch the next page of news + fetchNewsPage(String url) async { + final date = _extractDate(url); + final Response response = + await _dio.get('/news', queryParameters: {'date': date}); + return response.data; + } + + /// private method to extract the date from the url + String _extractDate(String url) { + final uri = Uri.parse(url); + final queryParameters = uri.queryParameters; + return queryParameters['date']!; + } +} diff --git a/lib/features/news_api/presentation/news_screen.dart b/lib/features/news_api/presentation/news_screen.dart new file mode 100644 index 0000000..a9393a3 --- /dev/null +++ b/lib/features/news_api/presentation/news_screen.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; + +import 'widgets/news_grid.dart'; +import 'widgets/news_list.dart'; + +class NewsScreen extends StatefulWidget { + const NewsScreen({super.key}); + + @override + State createState() => _NewsScreenState(); +} + +class _NewsScreenState extends State { + bool _isGrid = false; + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('Tagesschau News'), + actions: [ + PopupMenuButton(itemBuilder: (context) { + return const [ + PopupMenuItem( + value: false, + child: Text('List'), + ), + PopupMenuItem( + value: true, + child: Text('Grid'), + ), + ]; + }, onSelected: (bool value) { + setState(() { + _isGrid = value; + }); + }), + ], + ), + body: (_isGrid) ? NewsGrid() : NewsList(), + ); + } +} diff --git a/lib/features/news_api/presentation/widgets/news_grid.dart b/lib/features/news_api/presentation/widgets/news_grid.dart new file mode 100644 index 0000000..4abbb5f --- /dev/null +++ b/lib/features/news_api/presentation/widgets/news_grid.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../business/news_controller.dart'; +import '../../business/scroll_state_controller.dart'; +import 'news_grid_item.dart'; + +// scroll edge listener source: +// https://github.com/jezsung/scroll_edge_listener/blob/main/lib/scroll_edge_listener.dart + +class NewsGrid extends ConsumerWidget { + NewsGrid({super.key}); + + final ScrollController scrollController = ScrollController(); + + bool get _isBottom => + scrollController.position.maxScrollExtent == scrollController.offset; + + bool get _hasScrolledDown => scrollController.offset > 500; + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool hasScrolledDown = ref.watch(scrollStateControllerProvider); + final news = ref.watch(newsControllerProvider); + + scrollController.addListener(() { + if (_isBottom) { + // make call to fetch next batch of items + ref.read(newsControllerProvider.notifier).appendPage(); + } + if (_hasScrolledDown) { + ref.read(scrollStateControllerProvider.notifier).toggle(true); + } else { + ref.read(scrollStateControllerProvider.notifier).toggle(false); + } + }); + + return Scaffold( + body: news.when( + data: (data) { + return GridView.builder( + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 4, + childAspectRatio: 1, + ), + padding: const EdgeInsets.only(top: 32, left: 64, right: 64), + controller: scrollController, + itemCount: data.news.length + 1, + itemBuilder: (context, index) { + if (index < data.news.length) { + // only show stories + if (data.news[index].type == 'story' && + data.news[index].teaserImage != null) { + return NewsGridItem(news: data.news[index]); + } else { + // return empty container for other types + return NewsGridItem(news: data.news[index]); + } + } else { + return const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 24.0), + child: CircularProgressIndicator(), + ), + ); + } + }, + ); + }, + error: (err, st) => Text( + 'ERROR: ${err.toString()}', + ), + loading: () => const Center( + child: CircularProgressIndicator(), + ), + ), + floatingActionButton: Visibility( + visible: hasScrolledDown, + child: FloatingActionButton( + child: const Icon(Icons.arrow_upward), + onPressed: () { + scrollController.animateTo( + 0, + duration: const Duration(seconds: 1), + curve: Curves.linear, + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/news_api/presentation/widgets/news_grid_item.dart b/lib/features/news_api/presentation/widgets/news_grid_item.dart new file mode 100644 index 0000000..d36663a --- /dev/null +++ b/lib/features/news_api/presentation/widgets/news_grid_item.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; + +import '../../../../constants/api_urls.dart'; +import '../../data/models/news.dart'; + +class NewsGridItem extends StatelessWidget { + const NewsGridItem({required this.news, super.key}); + + final News news; + + @override + Widget build(BuildContext context) { + return Container( + color: (news.teaserImage == null) ? Colors.black : Colors.transparent, + child: FadeInImage.assetNetwork( + image: news.teaserImage != null + ? news.teaserImage?.imageVariants!['16x9-512'] ?? + TagesschauApiUrls.logoImage + : TagesschauApiUrls.logoImage, + fit: BoxFit.cover, + placeholder: 'assets/img/image_placeholder.png', + ), + ); + } +} diff --git a/lib/features/news_api/presentation/widgets/news_list.dart b/lib/features/news_api/presentation/widgets/news_list.dart new file mode 100644 index 0000000..bfa3fdf --- /dev/null +++ b/lib/features/news_api/presentation/widgets/news_list.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../business/news_controller.dart'; +import '../../business/scroll_state_controller.dart'; +import 'news_list_item.dart'; + +// scroll edge listener source: +// https://github.com/jezsung/scroll_edge_listener/blob/main/lib/scroll_edge_listener.dart + +class NewsList extends ConsumerWidget { + NewsList({super.key}); + + final ScrollController scrollController = ScrollController(); + + bool get _isBottom => + scrollController.position.maxScrollExtent == scrollController.offset; + + bool get _hasScrolledDown => scrollController.offset > 500; + + @override + Widget build(BuildContext context, WidgetRef ref) { + bool hasScrolledDown = ref.watch(scrollStateControllerProvider); + final news = ref.watch(newsControllerProvider); + + scrollController.addListener(() { + if (_isBottom) { + // make call to fetch next batch of items + ref.read(newsControllerProvider.notifier).appendPage(); + } + if (_hasScrolledDown) { + ref.read(scrollStateControllerProvider.notifier).toggle(true); + } else { + ref.read(scrollStateControllerProvider.notifier).toggle(false); + } + }); + + return Scaffold( + body: news.when( + data: (data) { + return ListView.builder( + padding: const EdgeInsets.only(top: 32, left: 64, right: 64), + controller: scrollController, + itemCount: data.news.length + 1, + itemBuilder: (context, index) { + if (index < data.news.length) { + // only show stories + if (data.news[index].type == 'story') { + return NewsListItem(news: data.news[index]); + } else { + // return empty container for other types + return const SizedBox(); + } + } else { + return const Center( + child: Padding( + padding: EdgeInsets.symmetric(vertical: 24.0), + child: CircularProgressIndicator(), + ), + ); + } + }, + ); + }, + error: (err, st) => Text( + 'ERROR: ${err.toString()}', + ), + loading: () => const Center( + child: CircularProgressIndicator(), + ), + ), + floatingActionButton: Visibility( + visible: hasScrolledDown, + child: FloatingActionButton( + child: const Icon(Icons.arrow_upward), + onPressed: () { + scrollController.animateTo( + 0, + duration: const Duration(seconds: 1), + curve: Curves.linear, + ); + }, + ), + ), + ); + } +} diff --git a/lib/features/news_api/presentation/widgets/news_list_item.dart b/lib/features/news_api/presentation/widgets/news_list_item.dart new file mode 100644 index 0000000..2571a4b --- /dev/null +++ b/lib/features/news_api/presentation/widgets/news_list_item.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; + +import '../../../../common/extension/toFormatedNewsDate.dart'; +import '../../../../constants/api_urls.dart'; +import '../../data/models/news.dart'; + +class NewsListItem extends StatelessWidget { + const NewsListItem({required this.news, super.key}); + + final News news; + + @override + Widget build(BuildContext context) { + return Card( + child: ListTile( + title: Text(news.title ?? 'Kein Titel'), + leading: Container( + width: 100, + height: 200, + color: + (news.teaserImage == null) ? Colors.indigo : Colors.transparent, + child: FadeInImage.assetNetwork( + image: news.teaserImage != null + ? news.teaserImage?.imageVariants!['16x9-512'] ?? + TagesschauApiUrls.placeholderImage + : news.brandingImage != null + ? news.brandingImage?.imageVariants!['original'] ?? + TagesschauApiUrls.placeholderImage + : TagesschauApiUrls.placeholderImage, + placeholder: 'assets/img/image_placeholder.png', + ), + ), + subtitle: Text(news.date?.toFormattedNewsDate() ?? 'Kein Datum'), + ), + ); + } +} diff --git a/lib/features/settings/business/wifi_controller.dart b/lib/features/settings/business/wifi_controller.dart new file mode 100644 index 0000000..a359970 --- /dev/null +++ b/lib/features/settings/business/wifi_controller.dart @@ -0,0 +1,71 @@ +import 'dart:async'; + +import 'package:dbus/dbus.dart'; +import 'package:flutter/material.dart'; +import 'package:nm/nm.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'wifi_controller.g.dart'; + +@riverpod +class WifiController extends _$WifiController { + @override + Future build() async { + try { + NetworkManagerClient client = NetworkManagerClient(); + await client.connect(); + + return client; + } catch (e) { + return Future.error(e); + } + } + + NetworkManagerDevice get _device => state.value!.devices + .firstWhere((d) => d.deviceType == NetworkManagerDeviceType.wifi); + + Stream<(NetworkManagerDevice, NetworkManagerClient)> wifiStream() { + return Stream.periodic(const Duration(milliseconds: 100), (_) { + return (_device, state.value!); + }); + } + + void disconnectFromWifiNetwork({ + required NetworkManagerAccessPoint accessPoint, + }) async { + try { + final device = _device; + var active = await state.value!.activateConnection(device: device); + await state.value!.deactivateConnection(active); + } catch (e) { + debugPrint(e.toString()); + } + } + + void connectToWifiNetwork({ + required NetworkManagerAccessPoint accessPoint, + String? psk, + }) async { + try { + final device = _device; + // Has password + if (psk != null) { + await state.value!.addAndActivateConnection( + device: device, + accessPoint: accessPoint, + connection: { + '802-11-wireless-security': { + 'key-mgmt': const DBusString('wpa-psk'), + 'psk': DBusString(psk) + } + }, + ); + } else { + await state.value! + .addAndActivateConnection(device: device, accessPoint: accessPoint); + } + } catch (e) { + debugPrint(e.toString()); + } + } +} diff --git a/lib/features/settings/business/wifi_controller.g.dart b/lib/features/settings/business/wifi_controller.g.dart new file mode 100644 index 0000000..0c08552 --- /dev/null +++ b/lib/features/settings/business/wifi_controller.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'wifi_controller.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$wifiControllerHash() => r'06f8067cb53b0d9db1a576cacafe85c13c7139d3'; + +/// See also [WifiController]. +@ProviderFor(WifiController) +final wifiControllerProvider = AutoDisposeAsyncNotifierProvider.internal( + WifiController.new, + name: r'wifiControllerProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$wifiControllerHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$WifiController = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/settings/presentation/settings_screen/settings_screen.dart b/lib/features/settings/presentation/settings_screen/settings_screen.dart new file mode 100644 index 0000000..b811992 --- /dev/null +++ b/lib/features/settings/presentation/settings_screen/settings_screen.dart @@ -0,0 +1,125 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../common/routing/routes.dart'; +import '../../../../common/settings/app_settings.dart'; +import '../../../../common/utils/linux_shutdown.dart'; +import 'widgets/color_seed_button.dart'; +import '../../business/wifi_controller.dart'; + +// dialog api: +// https://api.flutter.dev/flutter/material/Dialog-class.html + +// inspired from: +// https://github.com/canonical/nm.dart/blob/main/example/wifi.dart + +/// Show Settings Screen +class SettingsScreen extends ConsumerWidget { + const SettingsScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final wifiStream = (Platform.isLinux) + ? ref.watch(wifiControllerProvider.notifier).wifiStream() + : const Stream.empty(); + + final useDarkMode = ref.watch(useDarkModeProvider); + final useMaterial3 = ref.watch(useMaterial3Provider); + final showPerformanceOverlay = ref.watch(showPerformanceOverlayProvider); + + return Scaffold( + appBar: AppBar( + title: const Text('Preferences'), + ), + body: SingleChildScrollView( + child: Column( + children: [ + ListTile( + leading: const Icon(Icons.wifi), + title: const Text('Internet'), + subtitle: StreamBuilder( + stream: wifiStream, + builder: (context, snapshot) { + if (!Platform.isLinux) { + return const Text("Only supported on Linux"); + } + if (snapshot.hasData) { + if (snapshot.data?.$1.activeConnection?.id != null) { + return Text(snapshot.data!.$1.activeConnection!.id); + } + } + if (snapshot.hasError) return Text(snapshot.error.toString()); + return const Text("Not connected"); + }, + ), + onTap: () => context.pushNamed(RoutesSettings.wifi.name), + ), + const Divider(), + ListTile( + onTap: () => ref.read(useDarkModeProvider.notifier).toggle(), + leading: const Icon(Icons.dark_mode), + title: const Text('Dark theme'), + subtitle: const Text('Switch between the light and dark theme.'), + trailing: Switch( + value: useDarkMode, + onChanged: (_) => + ref.read(useDarkModeProvider.notifier).toggle(), + ), + ), + ListTile( + onTap: () => ref.read(useMaterial3Provider.notifier).toggle(), + leading: const Icon(Icons.filter_3), + title: const Text('Use Material 3 theme'), + subtitle: const Text( + 'Switch between the Material 3 and Material 2 theme.'), + trailing: Switch( + value: useMaterial3, + onChanged: (_) => + ref.read(useMaterial3Provider.notifier).toggle(), + ), + ), + ListTile( + onTap: () => + ref.read(showPerformanceOverlayProvider.notifier).toggle(), + leading: const Icon(Icons.speed), + title: const Text('Show Performance Overlay'), + subtitle: const Text( + 'Show the performance overlay at the top of the screen.'), + trailing: Switch( + value: showPerformanceOverlay, + onChanged: (_) => + ref.read(showPerformanceOverlayProvider.notifier).toggle(), + ), + ), + const ColorSeedButton(), + const Divider(), + const SizedBox( + height: 4, + ), + if (Platform.isLinux) + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ElevatedButton( + onPressed: () async => await linuxShutdown(restart: true), + child: const Text('Reboot'), + ), + const SizedBox( + width: 24, + height: 48, + ), + ElevatedButton( + onPressed: () async => await linuxShutdown(), + child: const Text('Shutdown'), + ), + ], + ) + ], + ), + ), + ); + } +} diff --git a/lib/features/settings/presentation/settings_screen/widgets/color_seed_button.dart b/lib/features/settings/presentation/settings_screen/widgets/color_seed_button.dart new file mode 100644 index 0000000..8bc9490 --- /dev/null +++ b/lib/features/settings/presentation/settings_screen/widgets/color_seed_button.dart @@ -0,0 +1,49 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../../../common/settings/app_settings.dart'; +import '../../../../../constants/colors.dart'; + +class ColorSeedButton extends ConsumerWidget { + const ColorSeedButton({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return PopupMenuButton( + tooltip: 'Select a seed color', + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)), + itemBuilder: (context) { + return List.generate(ColorSeed.values.length, (index) { + ColorSeed currentColor = ColorSeed.values[index]; + return PopupMenuItem( + onTap: () { + ref + .read(colorSeedSelectedProvider.notifier) + .setColorSeed(currentColor); + }, + child: TextButton.icon( + onPressed: null, + icon: Icon( + Icons.color_lens, + color: currentColor.color, + ), + label: Text( + currentColor.label, + style: TextStyle( + color: Theme.of(context).textTheme.bodyLarge!.color), + ), + ), + ); + }); + }, + child: ListTile( + leading: Icon( + Icons.palette, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + title: const Text('Set color seed'), + subtitle: const Text('Select the Material theme color seed.'), + ), + ); + } +} diff --git a/lib/features/settings/presentation/wifi_settings_screen/accesspoint_screen.dart b/lib/features/settings/presentation/wifi_settings_screen/accesspoint_screen.dart new file mode 100644 index 0000000..6241637 --- /dev/null +++ b/lib/features/settings/presentation/wifi_settings_screen/accesspoint_screen.dart @@ -0,0 +1,172 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:nm/nm.dart'; + +import '../../../../common/extension/getCharCodes.dart'; +import '../../../../common/widgets/scaffold_with_statusbar.dart'; +import '../../business/wifi_controller.dart'; +import 'widgets/ap_pw_dialog.dart'; +import 'widgets/wifi_bar_icon.dart'; + +class AccessPointScreen extends ConsumerWidget { + const AccessPointScreen({ + this.accessPoint, + super.key, + }); + + final NetworkManagerAccessPoint? accessPoint; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final wifiRepository = (accessPoint != null) + ? ref.watch(wifiControllerProvider.notifier).wifiStream() + : const Stream.empty(); + + return ScaffoldWithStatusbar( + appBar: AppBar( + title: Row( + children: [ + Text( + String.fromCharCodes( + accessPoint?.ssid ?? 'Demo'.getCharCodes(), + ), + ), + if (accessPoint?.rsnFlags.isNotEmpty ?? true) + const Padding( + padding: EdgeInsets.only(left: 8.0), + child: Icon( + Icons.lock, + ), + ) + ], + ), + ), + body: SingleChildScrollView( + child: Center( + child: Container( + // TODO: boxconstraint constant + constraints: const BoxConstraints(maxWidth: 500), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ListTile( + leading: WifiBarIcon(strength: accessPoint?.strength ?? 1), + title: const Text('Signal Strength'), + subtitle: Text(accessPoint?.strength.toString() ?? '1'), + ), + const Divider(), + ListTile( + leading: const Icon(Icons.hardware), + title: const Text('Hardware Address'), + subtitle: Text(accessPoint?.hwAddress ?? '00:00:00:00:00:00'), + ), + const Divider(), + ListTile( + leading: const Icon(Icons.speed), + title: const Text('Max Bitrate in kb/s'), + subtitle: Text(accessPoint?.maxBitrate.toString() ?? '0'), + ), + const Divider(), + ListTile( + leading: const Icon(Icons.radio), + title: const Text('Radio frequency in MHz'), + subtitle: Text(accessPoint?.frequency.toString() ?? '0'), + ), + const Divider(), + StreamBuilder( + stream: wifiRepository, + builder: (context, snapshot) { + if (!Platform.isLinux) { + return ElevatedButton( + onPressed: () { + if (accessPoint?.rsnFlags.isNotEmpty ?? true) { + showDialog( + context: context, + builder: (context) { + return AccessPointPasswordDialog( + accessPoint: accessPoint, + ); + }, + ); + } else { + if (accessPoint != null) { + ref + .read(wifiControllerProvider.notifier) + .connectToWifiNetwork( + accessPoint: accessPoint!, + ); + context.pop(); + } + } + }, + child: const Text('Demo Connect'), + ); + } + if (snapshot.hasData) { + return Padding( + padding: const EdgeInsets.all(12.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + ElevatedButton( + onPressed: () { + if (accessPoint != null) { + ref + .read(wifiControllerProvider.notifier) + .disconnectFromWifiNetwork( + accessPoint: accessPoint!, + ); + } + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.red, + foregroundColor: Colors.white, + ), + child: const Text('Forget Connection'), + ), + ElevatedButton( + onPressed: () { + if (accessPoint?.rsnFlags.isNotEmpty ?? true) { + showDialog( + context: context, + builder: (context) { + return AccessPointPasswordDialog( + accessPoint: accessPoint, + ); + }, + ); + } else { + if (accessPoint != null) { + ref + .read(wifiControllerProvider.notifier) + .connectToWifiNetwork( + accessPoint: accessPoint!, + ); + context.pop(); + } + } + }, + child: const Text('Connect'), + ) + ], + ), + ); + } else if (snapshot.hasError) { + return const Text('Lost connection'); + } else { + return const Padding( + padding: EdgeInsets.only(top: 16.0), + child: CircularProgressIndicator(), + ); + } + }), + ], + ), + ), + )), + ); + } +} diff --git a/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_pw_dialog.dart b/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_pw_dialog.dart new file mode 100644 index 0000000..23858cc --- /dev/null +++ b/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_pw_dialog.dart @@ -0,0 +1,69 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:nm/nm.dart'; + +import '../../../../../common/widgets/text_input_dialog.dart'; +import '../../../business/wifi_controller.dart'; + +class AccessPointPasswordDialog extends ConsumerWidget { + const AccessPointPasswordDialog({this.accessPoint, super.key}); + + final NetworkManagerAccessPoint? accessPoint; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final TextEditingController pskController = TextEditingController(); + + return Dialog( + child: Padding( + padding: const EdgeInsets.all(24.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text( + 'You are trying to connect to a protected network.', + // TODO: constant font sizes + style: TextStyle(fontSize: 20), + textAlign: TextAlign.start, + ), + const SizedBox( + height: 24, + ), + Container( + constraints: const BoxConstraints( + maxWidth: 300, + ), + child: TextInputDialog( + title: 'Enter the WiFi password', + controller: pskController, + onReturnPress: () => context.pop(), + hintText: 'Enter the WiFi password', + ), + ), + const SizedBox( + height: 24, + ), + ElevatedButton( + onPressed: () { + if (accessPoint != null) { + ref + .read(wifiControllerProvider.notifier) + .connectToWifiNetwork( + accessPoint: accessPoint!, + psk: pskController.text, + ); + } + // close Connect AlertDialog + context.pop(); + // close AccessPoint FullscreenDialog + context.pop(); + }, + child: const Text('Connect'), + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_tile.dart b/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_tile.dart new file mode 100644 index 0000000..4eed6a1 --- /dev/null +++ b/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_tile.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; +import 'package:nm/nm.dart'; + +import '../../../../../common/routing/routes.dart'; +import 'wifi_bar_icon.dart'; + +class AccessPointTile extends ConsumerWidget { + const AccessPointTile({ + required this.accessPoint, + required this.client, + required this.device, + super.key, + }); + + final NetworkManagerAccessPoint accessPoint; + final NetworkManagerClient client; + final NetworkManagerDevice device; + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Card( + child: InkWell( + borderRadius: Theme.of(context).useMaterial3 + ? const BorderRadius.all(Radius.circular(12.0)) + : const BorderRadius.all(Radius.circular(4.0)), + onTap: () { + context.pushNamed( + RoutesSettings.accesspoint.name, + extra: accessPoint, + ); + }, + child: ListTile( + leading: Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + WifiBarIcon(strength: accessPoint.strength), + if (accessPoint.rsnFlags.isNotEmpty) + const Icon( + Icons.lock, + size: 14, + ) + ], + ), + title: Text(String.fromCharCodes(accessPoint.ssid)), + trailing: device.activeConnection?.id == + String.fromCharCodes(accessPoint.ssid) + ? const Text('Connected') + : null, + ), + ), + ); + } +} diff --git a/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_tile_demo.dart b/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_tile_demo.dart new file mode 100644 index 0000000..0519c57 --- /dev/null +++ b/lib/features/settings/presentation/wifi_settings_screen/widgets/ap_tile_demo.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:go_router/go_router.dart'; + +import '../../../../../common/routing/routes.dart'; +import '../../../../../common/extension/getCharCodes.dart'; +import 'wifi_bar_icon.dart'; + +class AccessPointTileDemo extends ConsumerWidget { + const AccessPointTileDemo({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Card( + child: InkWell( + borderRadius: Theme.of(context).useMaterial3 + ? const BorderRadius.all(Radius.circular(12.0)) + : const BorderRadius.all(Radius.circular(4.0)), + onTap: () { + context.pushNamed( + RoutesSettings.accesspoint.name, + ); + }, + child: ListTile( + leading: const Row( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + WifiBarIcon(strength: 1), + if (true) + Icon( + Icons.lock, + size: 14, + ) + ], + ), + title: Text(String.fromCharCodes('Demo SSID'.getCharCodes())), + trailing: (false) // connected or not + ? const Text('Connected') + : null, + ), + ), + ); + } +} diff --git a/lib/features/settings/presentation/wifi_settings_screen/widgets/wifi_bar_icon.dart b/lib/features/settings/presentation/wifi_settings_screen/widgets/wifi_bar_icon.dart new file mode 100644 index 0000000..8c060b5 --- /dev/null +++ b/lib/features/settings/presentation/wifi_settings_screen/widgets/wifi_bar_icon.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +class WifiBarIcon extends StatelessWidget { + const WifiBarIcon({required this.strength, this.size, this.color, super.key}); + + final int strength; + final double? size; + final Color? color; + + @override + Widget build(BuildContext context) { + switch (strength) { + case >= 75: + return Icon( + Icons.network_wifi, + size: size, + color: color, + ); + case >= 45: + return Icon( + Icons.network_wifi_3_bar, + size: size, + color: color, + ); + case >= 25: + return Icon( + Icons.network_wifi_2_bar, + size: size, + color: color, + ); + case >= 1: + return Icon( + Icons.network_wifi_1_bar, + size: size, + color: color, + ); + default: + return Icon( + Icons.wifi_off, + size: size, + color: color, + ); + } + } +} diff --git a/lib/features/settings/presentation/wifi_settings_screen/wifi_settings_screen.dart b/lib/features/settings/presentation/wifi_settings_screen/wifi_settings_screen.dart new file mode 100644 index 0000000..d4ae880 --- /dev/null +++ b/lib/features/settings/presentation/wifi_settings_screen/wifi_settings_screen.dart @@ -0,0 +1,98 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../../../common/widgets/scaffold_with_statusbar.dart'; +import '../../business/wifi_controller.dart'; +import 'widgets/ap_tile.dart'; +import 'widgets/ap_tile_demo.dart'; + +// dialog api: +// https://api.flutter.dev/flutter/material/Dialog-class.html + +// inspired from: +// https://github.com/canonical/nm.dart/blob/main/example/wifi.dart + +class WifiSettingsScreen extends ConsumerWidget { + const WifiSettingsScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final wifiRepository = (Platform.isLinux) + ? ref.watch(wifiControllerProvider.notifier).wifiStream() + : const Stream.empty(); + + return ScaffoldWithStatusbar( + appBar: AppBar( + title: const Text('Wifi Settings'), + ), + body: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + StreamBuilder( + stream: wifiRepository, + builder: (BuildContext context, snapshot) { + if (!Platform.isLinux) { + return ListView.builder( + physics: const NeverScrollableScrollPhysics(), + shrinkWrap: true, + itemCount: 5, + itemBuilder: (BuildContext context, int index) { + return const AccessPointTileDemo(); + }); + } + if (snapshot.hasData) { + return Center( + child: Column( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + Padding( + padding: const EdgeInsets.all(24.0), + child: Switch( + value: snapshot.data!.$2.wirelessEnabled, + onChanged: (_) { + snapshot.data!.$2.setWirelessEnabled( + !snapshot.data!.$2.wirelessEnabled, + ); + }, + ), + ), + Container( + constraints: const BoxConstraints(maxWidth: 500), + child: ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + itemCount: snapshot + .data!.$1.wireless?.accessPoints.length ?? + 0, + itemBuilder: (BuildContext context, int index) { + return AccessPointTile( + accessPoint: snapshot + .data!.$1.wireless!.accessPoints + .elementAt(index), + client: snapshot.data!.$2, + device: snapshot.data!.$1, + ); + }, + ), + ), + ], + ), + ); + } + if (snapshot.hasError) return Text(snapshot.error.toString()); + return const Center( + child: Padding( + padding: EdgeInsets.only(top: 16.0), + child: CircularProgressIndicator(), + )); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/features/system_resources/business/service/memory_service.dart b/lib/features/system_resources/business/service/memory_service.dart new file mode 100644 index 0000000..058cb2e --- /dev/null +++ b/lib/features/system_resources/business/service/memory_service.dart @@ -0,0 +1,78 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +import 'package:riverpod_annotation/riverpod_annotation.dart'; + +part 'memory_service.g.dart'; + +// riverpod stream builder docs: +// https://riverpod.dev/docs/providers/stream_provider + +@riverpod +class MemoryService extends _$MemoryService { + @override + Future build() async { + if (Platform.isLinux) { + final processMemory = await Process.start( + 'grep', + [ + 'MemTotal', + '/proc/meminfo', + ], + ); + final awkMemInKB = await Process.start('awk', ['{print \$2}']); + processMemory.stdout.pipe(awkMemInKB.stdin); + + var result = await awkMemInKB.stdout.transform(utf8.decoder).join(); + + return double.parse(result) / 1024; + } else { + return 8192; + } + } + + Stream freeMemory() async* { + while (true) { + if (Platform.isLinux) { + await Future.delayed(const Duration(milliseconds: 100)); + final meminfo = await Process.start( + 'cat', + [ + '/proc/meminfo', + ], + ); + String output = await meminfo.stdout.transform(utf8.decoder).join(); + + Iterable lines = LineSplitter.split(output); + + int memTotal = _getKeyValue(list: lines, key: 'MemTotal'); + int shMem = _getKeyValue(list: lines, key: 'Shmem'); + int memFree = _getKeyValue(list: lines, key: 'MemFree'); + int buffers = _getKeyValue(list: lines, key: 'Buffers'); + int cached = _getKeyValue(list: lines, key: 'Cached'); + int sReclaimable = _getKeyValue(list: lines, key: 'SReclaimable'); + + // Used memory is calculated using the following "formula": + // MemUsed = MemTotal + Shmem - MemFree - Buffers - Cached - SReclaimable + // Source: https://github.com/dylanaraps/pfetch/blob/master/pfetch + // Source: https://github.com/KittyKatt/screenFetch/issues/386 + + final double memoryUsed = + (memTotal + shMem - memFree - buffers - cached - sReclaimable) / + 1024; + + yield memoryUsed; + } else { + await Future.delayed(const Duration(milliseconds: 100)); + yield Random().nextDouble() * 8192; + } + } + } +} + +/// get the key value from a /proc/meminfo line +int _getKeyValue({required Iterable list, required String key}) { + final String keyLine = list.firstWhere((element) => element.startsWith(key)); + return int.parse(keyLine.replaceAll(RegExp(r"\s+"), " ").split(' ')[1]); +} diff --git a/lib/features/system_resources/business/service/memory_service.g.dart b/lib/features/system_resources/business/service/memory_service.g.dart new file mode 100644 index 0000000..4856916 --- /dev/null +++ b/lib/features/system_resources/business/service/memory_service.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'memory_service.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$memoryServiceHash() => r'f6d75a138b6b67bf679d4627206dc4ab5bcfb025'; + +/// See also [MemoryService]. +@ProviderFor(MemoryService) +final memoryServiceProvider = + AutoDisposeAsyncNotifierProvider.internal( + MemoryService.new, + name: r'memoryServiceProvider', + debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product') + ? null + : _$memoryServiceHash, + dependencies: null, + allTransitiveDependencies: null, +); + +typedef _$MemoryService = AutoDisposeAsyncNotifier; +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member diff --git a/lib/features/system_resources/business/utils/get_cpu_usage_percentage.dart b/lib/features/system_resources/business/utils/get_cpu_usage_percentage.dart new file mode 100644 index 0000000..bab69ae --- /dev/null +++ b/lib/features/system_resources/business/utils/get_cpu_usage_percentage.dart @@ -0,0 +1,30 @@ +import 'dart:convert'; +import 'dart:io'; +import 'dart:math'; + +Stream getCpuUsagePercentage() async* { + while (true) { + if (Platform.isLinux) { + await Future.delayed(const Duration(milliseconds: 100)); + final vmstat = await Process.start( + 'vmstat', + [ + '1', + '2', + ], + ); + final tail = await Process.start('tail', ['-1']); + final awk = await Process.start('awk', ['{print \$15}']); + + vmstat.stdout.pipe(tail.stdin); + tail.stdout.pipe(awk.stdin); + + var cpuUsage = await awk.stdout.transform(utf8.decoder).join(); + + yield 100 - double.parse(cpuUsage); + } else { + await Future.delayed(const Duration(milliseconds: 100)); + yield Random().nextDouble() * 100; + } + } +} diff --git a/lib/features/system_resources/presentation/system_resources_screen.dart b/lib/features/system_resources/presentation/system_resources_screen.dart new file mode 100644 index 0000000..3995015 --- /dev/null +++ b/lib/features/system_resources/presentation/system_resources_screen.dart @@ -0,0 +1,84 @@ +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:real_time_chart/real_time_chart.dart'; + +import '../business/utils/get_cpu_usage_percentage.dart'; +import '../business/service/memory_service.dart'; + +class SystemResourcesScreen extends ConsumerWidget { + const SystemResourcesScreen({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final totalMemory = ref.watch(memoryServiceProvider); + final freeMemory = ref.watch(memoryServiceProvider.notifier).freeMemory(); + + return SingleChildScrollView( + child: Center( + child: Column( + children: [ + Padding( + padding: const EdgeInsets.only(top: 128.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + children: [ + const Text('CPU Usage'), + const SizedBox( + height: 4, + ), + RealTimeChart( + stream: getCpuUsagePercentage(), + maxY: 100, + valueDesc: 'Total CPU Usage', + valueType: '%', + ), + ], + ), + const SizedBox( + width: 64, + ), + Column( + children: [ + const Text('RAM Usage'), + const SizedBox( + height: 4, + ), + totalMemory.when( + data: (data) => RealTimeChart( + stream: freeMemory, + maxY: data, + valueDesc: 'Used Memory', + valueType: 'MiB', + maxValueDesc: 'Total Memory', + maxValueType: 'MiB', + ), + error: (error, stackTrace) => Text(error.toString()), + loading: () => const SizedBox( + height: 200, + width: 200, + ), + ), + ], + ), + ], + ), + ), + if (!Platform.isLinux) + const Padding( + padding: EdgeInsets.only(top: 16.0), + child: Text( + 'The live monitoring of the system resources is only available on Linux. \n Using random data for demonstration purposes.', + textAlign: TextAlign.center, + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart new file mode 100644 index 0000000..b7324a8 --- /dev/null +++ b/lib/main.dart @@ -0,0 +1,33 @@ +import 'dart:io'; + +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutterpi_gstreamer_video_player/flutterpi_gstreamer_video_player.dart'; +import 'package:scaled_app/scaled_app.dart'; +import 'package:intl/date_symbol_data_local.dart'; + +import 'app.dart'; +import 'common/settings/sharedPrefs.dart'; + +void main() async { + ScaledWidgetsFlutterBinding.ensureInitialized( + scaleFactor: (deviceSize) { + const double widthOfDesign = 960; + return deviceSize.width / widthOfDesign; + }, + ); + await SharedPrefs().init(); + + initializeDateFormatting('de_DE'); + + // initialize the video player for Linux devices + if (Platform.isLinux) { + FlutterpiVideoPlayer.registerWith(); + } + + runAppScaled( + const ProviderScope( + overrides: [], + child: App(), + ), + ); +} diff --git a/linux/.gitignore b/linux/.gitignore new file mode 100644 index 0000000..d3896c9 --- /dev/null +++ b/linux/.gitignore @@ -0,0 +1 @@ +flutter/ephemeral diff --git a/linux/CMakeLists.txt b/linux/CMakeLists.txt new file mode 100644 index 0000000..078ec68 --- /dev/null +++ b/linux/CMakeLists.txt @@ -0,0 +1,139 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "flutter_elinux") +# The unique GTK application identifier for this application. See: +# https://wiki.gnome.org/HowDoI/ChooseApplicationID +set(APPLICATION_ID "com.example.flutter_elinux") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Load bundled libraries from the lib/ directory relative to the binary. +set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") + +# Root filesystem for cross-building. +if(FLUTTER_TARGET_PLATFORM_SYSROOT) + set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) + set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) + set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) + set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +endif() + +# Define build configuration options. +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") +endif() + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_14) + target_compile_options(${TARGET} PRIVATE -Wall -Werror) + target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") + target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) + +add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") + +# Define the application target. To change its name, change BINARY_NAME above, +# not the value here, or `flutter run` will no longer work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} + "main.cc" + "my_application.cc" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add dependency libraries. Add any application-specific dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter) +target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) + +# Only the install-generated bundle's copy of the executable will launch +# correctly, since the resources must in the right relative locations. To avoid +# people trying to run the unbundled copy, put it in a subdirectory instead of +# the default top-level location. +set_target_properties(${BINARY_NAME} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" +) + + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# By default, "installing" just makes a relocatable bundle in the build +# directory. +set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +# Start with a clean build bundle directory every time. +install(CODE " + file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") + " COMPONENT Runtime) + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) + install(FILES "${bundled_library}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endforeach(bundled_library) + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") + install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() diff --git a/linux/flutter/CMakeLists.txt b/linux/flutter/CMakeLists.txt new file mode 100644 index 0000000..d5bd016 --- /dev/null +++ b/linux/flutter/CMakeLists.txt @@ -0,0 +1,88 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.10) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. + +# Serves the same purpose as list(TRANSFORM ... PREPEND ...), +# which isn't available in 3.10. +function(list_prepend LIST_NAME PREFIX) + set(NEW_LIST "") + foreach(element ${${LIST_NAME}}) + list(APPEND NEW_LIST "${PREFIX}${element}") + endforeach(element) + set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) +endfunction() + +# === Flutter Library === +# System-level dependencies. +find_package(PkgConfig REQUIRED) +pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) +pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) +pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) + +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "fl_basic_message_channel.h" + "fl_binary_codec.h" + "fl_binary_messenger.h" + "fl_dart_project.h" + "fl_engine.h" + "fl_json_message_codec.h" + "fl_json_method_codec.h" + "fl_message_codec.h" + "fl_method_call.h" + "fl_method_channel.h" + "fl_method_codec.h" + "fl_method_response.h" + "fl_plugin_registrar.h" + "fl_plugin_registry.h" + "fl_standard_message_codec.h" + "fl_standard_method_codec.h" + "fl_string_codec.h" + "fl_value.h" + "fl_view.h" + "flutter_linux.h" +) +list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") +target_link_libraries(flutter INTERFACE + PkgConfig::GTK + PkgConfig::GLIB + PkgConfig::GIO +) +add_dependencies(flutter flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/_phony_ + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" + ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} +) diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc new file mode 100644 index 0000000..e71a16d --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.cc @@ -0,0 +1,11 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#include "generated_plugin_registrant.h" + + +void fl_register_plugins(FlPluginRegistry* registry) { +} diff --git a/linux/flutter/generated_plugin_registrant.h b/linux/flutter/generated_plugin_registrant.h new file mode 100644 index 0000000..e0f0a47 --- /dev/null +++ b/linux/flutter/generated_plugin_registrant.h @@ -0,0 +1,15 @@ +// +// Generated file. Do not edit. +// + +// clang-format off + +#ifndef GENERATED_PLUGIN_REGISTRANT_ +#define GENERATED_PLUGIN_REGISTRANT_ + +#include + +// Registers Flutter plugins. +void fl_register_plugins(FlPluginRegistry* registry); + +#endif // GENERATED_PLUGIN_REGISTRANT_ diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake new file mode 100644 index 0000000..2e1de87 --- /dev/null +++ b/linux/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/linux/main.cc b/linux/main.cc new file mode 100644 index 0000000..e7c5c54 --- /dev/null +++ b/linux/main.cc @@ -0,0 +1,6 @@ +#include "my_application.h" + +int main(int argc, char** argv) { + g_autoptr(MyApplication) app = my_application_new(); + return g_application_run(G_APPLICATION(app), argc, argv); +} diff --git a/linux/my_application.cc b/linux/my_application.cc new file mode 100644 index 0000000..b53ebc7 --- /dev/null +++ b/linux/my_application.cc @@ -0,0 +1,104 @@ +#include "my_application.h" + +#include +#ifdef GDK_WINDOWING_X11 +#include +#endif + +#include "flutter/generated_plugin_registrant.h" + +struct _MyApplication { + GtkApplication parent_instance; + char** dart_entrypoint_arguments; +}; + +G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) + +// Implements GApplication::activate. +static void my_application_activate(GApplication* application) { + MyApplication* self = MY_APPLICATION(application); + GtkWindow* window = + GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); + + // Use a header bar when running in GNOME as this is the common style used + // by applications and is the setup most users will be using (e.g. Ubuntu + // desktop). + // If running on X and not using GNOME then just use a traditional title bar + // in case the window manager does more exotic layout, e.g. tiling. + // If running on Wayland assume the header bar will work (may need changing + // if future cases occur). + gboolean use_header_bar = TRUE; +#ifdef GDK_WINDOWING_X11 + GdkScreen* screen = gtk_window_get_screen(window); + if (GDK_IS_X11_SCREEN(screen)) { + const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); + if (g_strcmp0(wm_name, "GNOME Shell") != 0) { + use_header_bar = FALSE; + } + } +#endif + if (use_header_bar) { + GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); + gtk_widget_show(GTK_WIDGET(header_bar)); + gtk_header_bar_set_title(header_bar, "flutter_elinux"); + gtk_header_bar_set_show_close_button(header_bar, TRUE); + gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); + } else { + gtk_window_set_title(window, "flutter_elinux"); + } + + gtk_window_set_default_size(window, 1280, 720); + gtk_widget_show(GTK_WIDGET(window)); + + g_autoptr(FlDartProject) project = fl_dart_project_new(); + fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); + + FlView* view = fl_view_new(project); + gtk_widget_show(GTK_WIDGET(view)); + gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); + + fl_register_plugins(FL_PLUGIN_REGISTRY(view)); + + gtk_widget_grab_focus(GTK_WIDGET(view)); +} + +// Implements GApplication::local_command_line. +static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { + MyApplication* self = MY_APPLICATION(application); + // Strip out the first argument as it is the binary name. + self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); + + g_autoptr(GError) error = nullptr; + if (!g_application_register(application, nullptr, &error)) { + g_warning("Failed to register: %s", error->message); + *exit_status = 1; + return TRUE; + } + + g_application_activate(application); + *exit_status = 0; + + return TRUE; +} + +// Implements GObject::dispose. +static void my_application_dispose(GObject* object) { + MyApplication* self = MY_APPLICATION(object); + g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); + G_OBJECT_CLASS(my_application_parent_class)->dispose(object); +} + +static void my_application_class_init(MyApplicationClass* klass) { + G_APPLICATION_CLASS(klass)->activate = my_application_activate; + G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_OBJECT_CLASS(klass)->dispose = my_application_dispose; +} + +static void my_application_init(MyApplication* self) {} + +MyApplication* my_application_new() { + return MY_APPLICATION(g_object_new(my_application_get_type(), + "application-id", APPLICATION_ID, + "flags", G_APPLICATION_NON_UNIQUE, + nullptr)); +} diff --git a/linux/my_application.h b/linux/my_application.h new file mode 100644 index 0000000..72271d5 --- /dev/null +++ b/linux/my_application.h @@ -0,0 +1,18 @@ +#ifndef FLUTTER_MY_APPLICATION_H_ +#define FLUTTER_MY_APPLICATION_H_ + +#include + +G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, + GtkApplication) + +/** + * my_application_new: + * + * Creates a new Flutter-based application. + * + * Returns: a new #MyApplication. + */ +MyApplication* my_application_new(); + +#endif // FLUTTER_MY_APPLICATION_H_ diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..746adbb --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,7 @@ +# Flutter-related +**/Flutter/ephemeral/ +**/Pods/ + +# Xcode-related +**/dgph +**/xcuserdata/ diff --git a/macos/Flutter/Flutter-Debug.xcconfig b/macos/Flutter/Flutter-Debug.xcconfig new file mode 100644 index 0000000..4b81f9b --- /dev/null +++ b/macos/Flutter/Flutter-Debug.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/Flutter-Release.xcconfig b/macos/Flutter/Flutter-Release.xcconfig new file mode 100644 index 0000000..5caa9d1 --- /dev/null +++ b/macos/Flutter/Flutter-Release.xcconfig @@ -0,0 +1,2 @@ +#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +#include "ephemeral/Flutter-Generated.xcconfig" diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift new file mode 100644 index 0000000..15cedd1 --- /dev/null +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -0,0 +1,14 @@ +// +// Generated file. Do not edit. +// + +import FlutterMacOS +import Foundation + +import shared_preferences_foundation +import video_player_avfoundation + +func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + FVPVideoPlayerPlugin.register(with: registry.registrar(forPlugin: "FVPVideoPlayerPlugin")) +} diff --git a/macos/Podfile b/macos/Podfile new file mode 100644 index 0000000..c795730 --- /dev/null +++ b/macos/Podfile @@ -0,0 +1,43 @@ +platform :osx, '10.14' + +# CocoaPods analytics sends network stats synchronously affecting flutter build latency. +ENV['COCOAPODS_DISABLE_STATS'] = 'true' + +project 'Runner', { + 'Debug' => :debug, + 'Profile' => :release, + 'Release' => :release, +} + +def flutter_root + generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) + unless File.exist?(generated_xcode_build_settings_path) + raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" + end + + File.foreach(generated_xcode_build_settings_path) do |line| + matches = line.match(/FLUTTER_ROOT\=(.*)/) + return matches[1].strip if matches + end + raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" +end + +require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) + +flutter_macos_podfile_setup + +target 'Runner' do + use_frameworks! + use_modular_headers! + + flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) + target 'RunnerTests' do + inherit! :search_paths + end +end + +post_install do |installer| + installer.pods_project.targets.each do |target| + flutter_additional_macos_build_settings(target) + end +end diff --git a/macos/Podfile.lock b/macos/Podfile.lock new file mode 100644 index 0000000..c59b8a9 --- /dev/null +++ b/macos/Podfile.lock @@ -0,0 +1,30 @@ +PODS: + - FlutterMacOS (1.0.0) + - shared_preferences_foundation (0.0.1): + - Flutter + - FlutterMacOS + - video_player_avfoundation (0.0.1): + - Flutter + - FlutterMacOS + +DEPENDENCIES: + - FlutterMacOS (from `Flutter/ephemeral`) + - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - video_player_avfoundation (from `Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin`) + +EXTERNAL SOURCES: + FlutterMacOS: + :path: Flutter/ephemeral + shared_preferences_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + video_player_avfoundation: + :path: Flutter/ephemeral/.symlinks/plugins/video_player_avfoundation/darwin + +SPEC CHECKSUMS: + FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 + shared_preferences_foundation: b4c3b4cddf1c21f02770737f147a3f5da9d39695 + video_player_avfoundation: 02011213dab73ae3687df27ce441fbbcc82b5579 + +PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 + +COCOAPODS: 1.13.0 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj new file mode 100644 index 0000000..6fd41ec --- /dev/null +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -0,0 +1,780 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXAggregateTarget section */ + 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */; + buildPhases = ( + 33CC111E2044C6BF0003C045 /* ShellScript */, + ); + dependencies = ( + ); + name = "Flutter Assemble"; + productName = FLX; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; + 5A8B4A3D88FF5837183F1ADB /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C3163D25B0468613E3672854 /* Pods_Runner.framework */; }; + E73F9DCBBCCB2ED86F5F24FB /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FC37109A455C56B16781E0A4 /* Pods_RunnerTests.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC10EC2044A3C60003C045; + remoteInfo = Runner; + }; + 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 33CC111A2044C6BA0003C045; + remoteInfo = FLX; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 16361B19A232F862BA92F64F /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 1F7E221D2B74D37500A2136A /* libsysres-darwin-arm64.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libsysres-darwin-arm64.dylib"; path = "../assets/library/libsysres-darwin-arm64.dylib"; sourceTree = ""; }; + 1F7E221E2B74D37500A2136A /* libsysres-darwin-x86_64.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = "libsysres-darwin-x86_64.dylib"; path = "../assets/library/libsysres-darwin-x86_64.dylib"; sourceTree = ""; }; + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; + 33CC10ED2044A3C60003C045 /* flutter_elinux.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = flutter_elinux.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; + 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; + 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; }; + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; }; + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; }; + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; }; + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; }; + 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; + 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; + 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 9F1AC85846FAD6249AC72619 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; + A4C23E0A934AFC3FD2E1897C /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + B6D2A871886AA67BA22BA060 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + C02A0AD7BBB3B498BE665B1C /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + C3163D25B0468613E3672854 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + F019F17570D8D4685F08AE5C /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + FC37109A455C56B16781E0A4 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 331C80D2294CF70F00263BE5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E73F9DCBBCCB2ED86F5F24FB /* Pods_RunnerTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EA2044A3C60003C045 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 5A8B4A3D88FF5837183F1ADB /* Pods_Runner.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 331C80D6294CF71000263BE5 /* RunnerTests */ = { + isa = PBXGroup; + children = ( + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, + ); + path = RunnerTests; + sourceTree = ""; + }; + 33BA886A226E78AF003329D5 /* Configs */ = { + isa = PBXGroup; + children = ( + 33E5194F232828860026EE4D /* AppInfo.xcconfig */, + 9740EEB21CF90195004384FC /* Debug.xcconfig */, + 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, + 333000ED22D3DE5D00554162 /* Warnings.xcconfig */, + ); + path = Configs; + sourceTree = ""; + }; + 33CC10E42044A3C60003C045 = { + isa = PBXGroup; + children = ( + 33FAB671232836740065AC1E /* Runner */, + 33CEB47122A05771004F2AC0 /* Flutter */, + 331C80D6294CF71000263BE5 /* RunnerTests */, + 33CC10EE2044A3C60003C045 /* Products */, + D73912EC22F37F3D000D13A0 /* Frameworks */, + 78573628029CA7E21E3F9E3D /* Pods */, + ); + sourceTree = ""; + }; + 33CC10EE2044A3C60003C045 /* Products */ = { + isa = PBXGroup; + children = ( + 33CC10ED2044A3C60003C045 /* flutter_elinux.app */, + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 33CC11242044D66E0003C045 /* Resources */ = { + isa = PBXGroup; + children = ( + 33CC10F22044A3C60003C045 /* Assets.xcassets */, + 33CC10F42044A3C60003C045 /* MainMenu.xib */, + 33CC10F72044A3C60003C045 /* Info.plist */, + ); + name = Resources; + path = ..; + sourceTree = ""; + }; + 33CEB47122A05771004F2AC0 /* Flutter */ = { + isa = PBXGroup; + children = ( + 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, + 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, + 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, + 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */, + ); + path = Flutter; + sourceTree = ""; + }; + 33FAB671232836740065AC1E /* Runner */ = { + isa = PBXGroup; + children = ( + 33CC10F02044A3C60003C045 /* AppDelegate.swift */, + 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */, + 33E51913231747F40026EE4D /* DebugProfile.entitlements */, + 33E51914231749380026EE4D /* Release.entitlements */, + 33CC11242044D66E0003C045 /* Resources */, + 33BA886A226E78AF003329D5 /* Configs */, + ); + path = Runner; + sourceTree = ""; + }; + 78573628029CA7E21E3F9E3D /* Pods */ = { + isa = PBXGroup; + children = ( + 9F1AC85846FAD6249AC72619 /* Pods-Runner.debug.xcconfig */, + 16361B19A232F862BA92F64F /* Pods-Runner.release.xcconfig */, + C02A0AD7BBB3B498BE665B1C /* Pods-Runner.profile.xcconfig */, + B6D2A871886AA67BA22BA060 /* Pods-RunnerTests.debug.xcconfig */, + A4C23E0A934AFC3FD2E1897C /* Pods-RunnerTests.release.xcconfig */, + F019F17570D8D4685F08AE5C /* Pods-RunnerTests.profile.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; + D73912EC22F37F3D000D13A0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1F7E221D2B74D37500A2136A /* libsysres-darwin-arm64.dylib */, + 1F7E221E2B74D37500A2136A /* libsysres-darwin-x86_64.dylib */, + C3163D25B0468613E3672854 /* Pods_Runner.framework */, + FC37109A455C56B16781E0A4 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; + buildPhases = ( + F06F08029A71C0D1A44F2F2E /* [CP] Check Pods Manifest.lock */, + 331C80D1294CF70F00263BE5 /* Sources */, + 331C80D2294CF70F00263BE5 /* Frameworks */, + 331C80D3294CF70F00263BE5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, + ); + name = RunnerTests; + productName = RunnerTests; + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 33CC10EC2044A3C60003C045 /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 063BB9CE5980737869BC2333 /* [CP] Check Pods Manifest.lock */, + 33CC10E92044A3C60003C045 /* Sources */, + 33CC10EA2044A3C60003C045 /* Frameworks */, + 33CC10EB2044A3C60003C045 /* Resources */, + 3399D490228B24CF009A79C7 /* ShellScript */, + B4639AA1CCE3E714F492A6E4 /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + 33CC11202044C79F0003C045 /* PBXTargetDependency */, + ); + name = Runner; + productName = Runner; + productReference = 33CC10ED2044A3C60003C045 /* flutter_elinux.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 33CC10E52044A3C60003C045 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0920; + LastUpgradeCheck = 1510; + ORGANIZATIONNAME = ""; + TargetAttributes = { + 331C80D4294CF70F00263BE5 = { + CreatedOnToolsVersion = 14.0; + TestTargetID = 33CC10EC2044A3C60003C045; + }; + 33CC10EC2044A3C60003C045 = { + CreatedOnToolsVersion = 9.2; + LastSwiftMigration = 1100; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Sandbox = { + enabled = 1; + }; + }; + }; + 33CC111A2044C6BA0003C045 = { + CreatedOnToolsVersion = 9.2; + ProvisioningStyle = Manual; + }; + }; + }; + buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 33CC10E42044A3C60003C045; + productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 33CC10EC2044A3C60003C045 /* Runner */, + 331C80D4294CF70F00263BE5 /* RunnerTests */, + 33CC111A2044C6BA0003C045 /* Flutter Assemble */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 331C80D3294CF70F00263BE5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10EB2044A3C60003C045 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */, + 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 063BB9CE5980737869BC2333 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 3399D490228B24CF009A79C7 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n"; + }; + 33CC111E2044C6BF0003C045 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + Flutter/ephemeral/FlutterInputs.xcfilelist, + ); + inputPaths = ( + Flutter/ephemeral/tripwire, + ); + outputFileListPaths = ( + Flutter/ephemeral/FlutterOutputs.xcfilelist, + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + }; + B4639AA1CCE3E714F492A6E4 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + F06F08029A71C0D1A44F2F2E /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 331C80D1294CF70F00263BE5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33CC10E92044A3C60003C045 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */, + 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */, + 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC10EC2044A3C60003C045 /* Runner */; + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; + }; + 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; + targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33CC10F42044A3C60003C045 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 33CC10F52044A3C60003C045 /* Base */, + ); + name = MainMenu.xib; + path = Runner; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 331C80DB294CF71000263BE5 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B6D2A871886AA67BA22BA060 /* Pods-RunnerTests.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElinux.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_elinux.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_elinux"; + }; + name = Debug; + }; + 331C80DC294CF71000263BE5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = A4C23E0A934AFC3FD2E1897C /* Pods-RunnerTests.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElinux.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_elinux.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_elinux"; + }; + name = Release; + }; + 331C80DD294CF71000263BE5 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F019F17570D8D4685F08AE5C /* Pods-RunnerTests.profile.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CURRENT_PROJECT_VERSION = 1; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElinux.RunnerTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/flutter_elinux.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/flutter_elinux"; + }; + name = Profile; + }; + 338D0CE9231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Profile; + }; + 338D0CEA231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Profile; + }; + 338D0CEB231458BD00FA5F75 /* Profile */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Profile; + }; + 33CC10F92044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 33CC10FA2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + }; + name = Release; + }; + 33CC10FC2044A3C60003C045 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 33CC10FD2044A3C60003C045 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + ); + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; + 33CC111C2044C6BA0003C045 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Manual; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 33CC111D2044C6BA0003C045 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 331C80DB294CF71000263BE5 /* Debug */, + 331C80DC294CF71000263BE5 /* Release */, + 331C80DD294CF71000263BE5 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10F92044A3C60003C045 /* Debug */, + 33CC10FA2044A3C60003C045 /* Release */, + 338D0CE9231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC10FC2044A3C60003C045 /* Debug */, + 33CC10FD2044A3C60003C045 /* Release */, + 338D0CEA231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 33CC111C2044C6BA0003C045 /* Debug */, + 33CC111D2044C6BA0003C045 /* Release */, + 338D0CEB231458BD00FA5F75 /* Profile */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 33CC10E52044A3C60003C045 /* Project object */; +} diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000..530c42f --- /dev/null +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner.xcworkspace/contents.xcworkspacedata b/macos/Runner.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..21a3cc1 --- /dev/null +++ b/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift new file mode 100644 index 0000000..d53ef64 --- /dev/null +++ b/macos/Runner/AppDelegate.swift @@ -0,0 +1,9 @@ +import Cocoa +import FlutterMacOS + +@NSApplicationMain +class AppDelegate: FlutterAppDelegate { + override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + return true + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..a2ec33f --- /dev/null +++ b/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,68 @@ +{ + "images" : [ + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_16.png", + "scale" : "1x" + }, + { + "size" : "16x16", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "2x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_32.png", + "scale" : "1x" + }, + { + "size" : "32x32", + "idiom" : "mac", + "filename" : "app_icon_64.png", + "scale" : "2x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_128.png", + "scale" : "1x" + }, + { + "size" : "128x128", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "2x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_256.png", + "scale" : "1x" + }, + { + "size" : "256x256", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "2x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_512.png", + "scale" : "1x" + }, + { + "size" : "512x512", + "idiom" : "mac", + "filename" : "app_icon_1024.png", + "scale" : "2x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png new file mode 100644 index 0000000..82b6f9d Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png new file mode 100644 index 0000000..13b35eb Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png new file mode 100644 index 0000000..0a3f5fa Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png new file mode 100644 index 0000000..bdb5722 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png new file mode 100644 index 0000000..f083318 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png new file mode 100644 index 0000000..326c0e7 Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png new file mode 100644 index 0000000..2f1632c Binary files /dev/null and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/macos/Runner/Base.lproj/MainMenu.xib b/macos/Runner/Base.lproj/MainMenu.xib new file mode 100644 index 0000000..80e867a --- /dev/null +++ b/macos/Runner/Base.lproj/MainMenu.xib @@ -0,0 +1,343 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig new file mode 100644 index 0000000..a5f43ef --- /dev/null +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -0,0 +1,14 @@ +// Application-level settings for the Runner target. +// +// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the +// future. If not, the values below would default to using the project name when this becomes a +// 'flutter create' template. + +// The application's name. By default this is also the title of the Flutter window. +PRODUCT_NAME = flutter_elinux + +// The application's bundle identifier +PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterElinux + +// The copyright displayed in application information +PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. diff --git a/macos/Runner/Configs/Debug.xcconfig b/macos/Runner/Configs/Debug.xcconfig new file mode 100644 index 0000000..36b0fd9 --- /dev/null +++ b/macos/Runner/Configs/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Debug.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Release.xcconfig b/macos/Runner/Configs/Release.xcconfig new file mode 100644 index 0000000..dff4f49 --- /dev/null +++ b/macos/Runner/Configs/Release.xcconfig @@ -0,0 +1,2 @@ +#include "../../Flutter/Flutter-Release.xcconfig" +#include "Warnings.xcconfig" diff --git a/macos/Runner/Configs/Warnings.xcconfig b/macos/Runner/Configs/Warnings.xcconfig new file mode 100644 index 0000000..42bcbf4 --- /dev/null +++ b/macos/Runner/Configs/Warnings.xcconfig @@ -0,0 +1,13 @@ +WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings +GCC_WARN_UNDECLARED_SELECTOR = YES +CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES +CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE +CLANG_WARN__DUPLICATE_METHOD_MATCH = YES +CLANG_WARN_PRAGMA_PACK = YES +CLANG_WARN_STRICT_PROTOTYPES = YES +CLANG_WARN_COMMA = YES +GCC_WARN_STRICT_SELECTOR_MATCH = YES +CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES +CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES +GCC_WARN_SHADOW = YES +CLANG_WARN_UNREACHABLE_CODE = YES diff --git a/macos/Runner/DebugProfile.entitlements b/macos/Runner/DebugProfile.entitlements new file mode 100644 index 0000000..03fc700 --- /dev/null +++ b/macos/Runner/DebugProfile.entitlements @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + + diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist new file mode 100644 index 0000000..4789daa --- /dev/null +++ b/macos/Runner/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + $(PRODUCT_COPYRIGHT) + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift new file mode 100644 index 0000000..3cc05eb --- /dev/null +++ b/macos/Runner/MainFlutterWindow.swift @@ -0,0 +1,15 @@ +import Cocoa +import FlutterMacOS + +class MainFlutterWindow: NSWindow { + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + RegisterGeneratedPlugins(registry: flutterViewController) + + super.awakeFromNib() + } +} diff --git a/macos/Runner/Release.entitlements b/macos/Runner/Release.entitlements new file mode 100644 index 0000000..f74cd49 --- /dev/null +++ b/macos/Runner/Release.entitlements @@ -0,0 +1,8 @@ + + + + + com.apple.security.network.client + + + diff --git a/macos/RunnerTests/RunnerTests.swift b/macos/RunnerTests/RunnerTests.swift new file mode 100644 index 0000000..5418c9f --- /dev/null +++ b/macos/RunnerTests/RunnerTests.swift @@ -0,0 +1,12 @@ +import FlutterMacOS +import Cocoa +import XCTest + +class RunnerTests: XCTestCase { + + func testExample() { + // If you add code to the Runner application, consider adding tests here. + // See https://developer.apple.com/documentation/xctest for more information about using XCTest. + } + +} diff --git a/packages/real_time_chart b/packages/real_time_chart new file mode 160000 index 0000000..bb73115 --- /dev/null +++ b/packages/real_time_chart @@ -0,0 +1 @@ +Subproject commit bb731151f2102ad3c8dbea4891c45cabfcd27220 diff --git a/packages/virtual_keyboard_multi_language b/packages/virtual_keyboard_multi_language new file mode 160000 index 0000000..2445d16 --- /dev/null +++ b/packages/virtual_keyboard_multi_language @@ -0,0 +1 @@ +Subproject commit 2445d1612156b1d57474c3a4e8770ef75d98439c diff --git a/pubspec.lock b/pubspec.lock new file mode 100644 index 0000000..f7ad414 --- /dev/null +++ b/pubspec.lock @@ -0,0 +1,1152 @@ +# Generated by pub +# See https://dart.dev/tools/pub/glossary#lockfile +packages: + _ardera_common_libc_bindings: + dependency: transitive + description: + name: _ardera_common_libc_bindings + sha256: "80dd1643898c17125e694cdaec2584c5969e770865eda2f391f4e12ea28336ed" + url: "https://pub.dev" + source: hosted + version: "0.3.2" + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + url: "https://pub.dev" + source: hosted + version: "67.0.0" + analyzer: + dependency: transitive + description: + name: analyzer + sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + url: "https://pub.dev" + source: hosted + version: "6.4.1" + analyzer_plugin: + dependency: transitive + description: + name: analyzer_plugin + sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" + url: "https://pub.dev" + source: hosted + version: "0.11.3" + args: + dependency: transitive + description: + name: args + sha256: eef6c46b622e0494a36c5a12d10d77fb4e855501a91c1b9ef9339326e58f0596 + url: "https://pub.dev" + source: hosted + version: "2.4.2" + async: + dependency: transitive + description: + name: async + sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + url: "https://pub.dev" + source: hosted + version: "2.11.0" + boolean_selector: + dependency: transitive + description: + name: boolean_selector + sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + build: + dependency: transitive + description: + name: build + sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + build_config: + dependency: transitive + description: + name: build_config + sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 + url: "https://pub.dev" + source: hosted + version: "1.1.1" + build_daemon: + dependency: transitive + description: + name: build_daemon + sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" + url: "https://pub.dev" + source: hosted + version: "4.0.1" + build_resolvers: + dependency: transitive + description: + name: build_resolvers + sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" + url: "https://pub.dev" + source: hosted + version: "2.4.2" + build_runner: + dependency: "direct dev" + description: + name: build_runner + sha256: "581bacf68f89ec8792f5e5a0b2c4decd1c948e97ce659dc783688c8a88fbec21" + url: "https://pub.dev" + source: hosted + version: "2.4.8" + build_runner_core: + dependency: transitive + description: + name: build_runner_core + sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" + url: "https://pub.dev" + source: hosted + version: "7.3.0" + built_collection: + dependency: transitive + description: + name: built_collection + sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" + url: "https://pub.dev" + source: hosted + version: "5.1.1" + built_value: + dependency: transitive + description: + name: built_value + sha256: fedde275e0a6b798c3296963c5cd224e3e1b55d0e478d5b7e65e6b540f363a0e + url: "https://pub.dev" + source: hosted + version: "8.9.1" + characters: + dependency: transitive + description: + name: characters + sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + url: "https://pub.dev" + source: hosted + version: "1.3.0" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff + url: "https://pub.dev" + source: hosted + version: "2.0.3" + ci: + dependency: transitive + description: + name: ci + sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + url: "https://pub.dev" + source: hosted + version: "0.1.0" + cli_util: + dependency: transitive + description: + name: cli_util + sha256: c05b7406fdabc7a49a3929d4af76bcaccbbffcbcdcf185b082e1ae07da323d19 + url: "https://pub.dev" + source: hosted + version: "0.4.1" + clock: + dependency: transitive + description: + name: clock + sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + url: "https://pub.dev" + source: hosted + version: "1.1.1" + code_builder: + dependency: transitive + description: + name: code_builder + sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 + url: "https://pub.dev" + source: hosted + version: "4.10.0" + collection: + dependency: transitive + description: + name: collection + sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + url: "https://pub.dev" + source: hosted + version: "1.18.0" + convert: + dependency: transitive + description: + name: convert + sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" + url: "https://pub.dev" + source: hosted + version: "3.1.1" + crypto: + dependency: transitive + description: + name: crypto + sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab + url: "https://pub.dev" + source: hosted + version: "3.0.3" + csslib: + dependency: transitive + description: + name: csslib + sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" + url: "https://pub.dev" + source: hosted + version: "1.0.0" + custom_lint: + dependency: "direct dev" + description: + name: custom_lint + sha256: "22bd87a362f433ba6aae127a7bac2838645270737f3721b180916d7c5946cb5d" + url: "https://pub.dev" + source: hosted + version: "0.5.11" + custom_lint_builder: + dependency: transitive + description: + name: custom_lint_builder + sha256: "0d48e002438950f9582e574ef806b2bea5719d8d14c0f9f754fbad729bcf3b19" + url: "https://pub.dev" + source: hosted + version: "0.5.14" + custom_lint_core: + dependency: transitive + description: + name: custom_lint_core + sha256: "2952837953022de610dacb464f045594854ced6506ac7f76af28d4a6490e189b" + url: "https://pub.dev" + source: hosted + version: "0.5.14" + dart_periphery: + dependency: "direct main" + description: + name: dart_periphery + sha256: "537d346bcb02bde2dc6b67efaa2e85e124e1b078088e9502d463e98ef3b93ba0" + url: "https://pub.dev" + source: hosted + version: "0.9.5" + dart_style: + dependency: transitive + description: + name: dart_style + sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + url: "https://pub.dev" + source: hosted + version: "2.3.6" + dbus: + dependency: transitive + description: + name: dbus + sha256: "365c771ac3b0e58845f39ec6deebc76e3276aa9922b0cc60840712094d9047ac" + url: "https://pub.dev" + source: hosted + version: "0.7.10" + dio: + dependency: "direct main" + description: + name: dio + sha256: "49af28382aefc53562459104f64d16b9dfd1e8ef68c862d5af436cc8356ce5a8" + url: "https://pub.dev" + source: hosted + version: "5.4.1" + fake_async: + dependency: transitive + description: + name: fake_async + sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + url: "https://pub.dev" + source: hosted + version: "1.3.1" + ffi: + dependency: transitive + description: + name: ffi + sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" + fixnum: + dependency: transitive + description: + name: fixnum + sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + flame: + dependency: "direct main" + description: + name: flame + sha256: bb42a35fa5dabdea535daebe5b020e21a2fce8192b67b7e55cd30fed86d29f69 + url: "https://pub.dev" + source: hosted + version: "1.16.0" + flex_color_picker: + dependency: "direct main" + description: + name: flex_color_picker + sha256: "904373c7b0531fd4a92d29705a80ab4594b7647da2d93044487aaec4614cb6ed" + url: "https://pub.dev" + source: hosted + version: "3.4.0" + flex_seed_scheme: + dependency: transitive + description: + name: flex_seed_scheme + sha256: "29c12aba221eb8a368a119685371381f8035011d18de5ba277ad11d7dfb8657f" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + flutter: + dependency: "direct main" + description: flutter + source: sdk + version: "0.0.0" + flutter_adaptive_scaffold: + dependency: "direct main" + description: + name: flutter_adaptive_scaffold + sha256: "4257142551ec97761d44f4258b8ad53ac76593dd0992197b876769df19f8a018" + url: "https://pub.dev" + source: hosted + version: "0.1.8" + flutter_gpiod: + dependency: "direct main" + description: + name: flutter_gpiod + sha256: "9c5361f3d4cf953bb85e52ab51faf70ada6d76fe751409c408e34a50a16a2540" + url: "https://pub.dev" + source: hosted + version: "0.5.1+5" + flutter_lints: + dependency: "direct dev" + description: + name: flutter_lints + sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 + url: "https://pub.dev" + source: hosted + version: "2.0.3" + flutter_map: + dependency: "direct main" + description: + name: flutter_map + sha256: "5286f72f87deb132daa1489442d6cc46e986fc105cb727d9ae1b602b35b1d1f3" + url: "https://pub.dev" + source: hosted + version: "5.0.0" + flutter_riverpod: + dependency: "direct main" + description: + name: flutter_riverpod + sha256: "4bce556b7ecbfea26109638d5237684538d4abc509d253e6c5c4c5733b360098" + url: "https://pub.dev" + source: hosted + version: "2.4.10" + flutter_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + flutterpi_gstreamer_video_player: + dependency: "direct main" + description: + name: flutterpi_gstreamer_video_player + sha256: "1f70015783e8a9c4805ee1590d351131bf83853a53fc6bc7b93c3a325804f061" + url: "https://pub.dev" + source: hosted + version: "0.1.1+1" + freezed: + dependency: "direct main" + description: + name: freezed + sha256: "57247f692f35f068cae297549a46a9a097100685c6780fe67177503eea5ed4e5" + url: "https://pub.dev" + source: hosted + version: "2.4.7" + freezed_annotation: + dependency: "direct main" + description: + name: freezed_annotation + sha256: c3fd9336eb55a38cc1bbd79ab17573113a8deccd0ecbbf926cca3c62803b5c2d + url: "https://pub.dev" + source: hosted + version: "2.4.1" + freezed_lint: + dependency: "direct dev" + description: + name: freezed_lint + sha256: f48f9c0f2c5f4a95b0e2b06efecea8accc3ef8213128dac9bb7005c64ad9ecff + url: "https://pub.dev" + source: hosted + version: "0.0.1-dev+1" + frontend_server_client: + dependency: transitive + description: + name: frontend_server_client + sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" + url: "https://pub.dev" + source: hosted + version: "3.2.0" + glob: + dependency: transitive + description: + name: glob + sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + go_router: + dependency: "direct main" + description: + name: go_router + sha256: c5fa45fa502ee880839e3b2152d987c44abae26d064a2376d4aad434cf0f7b15 + url: "https://pub.dev" + source: hosted + version: "12.1.3" + graphs: + dependency: transitive + description: + name: graphs + sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + url: "https://pub.dev" + source: hosted + version: "2.3.1" + hotreloader: + dependency: transitive + description: + name: hotreloader + sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e + url: "https://pub.dev" + source: hosted + version: "4.2.0" + html: + dependency: transitive + description: + name: html + sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" + url: "https://pub.dev" + source: hosted + version: "0.15.4" + http: + dependency: transitive + description: + name: http + sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + url: "https://pub.dev" + source: hosted + version: "1.2.1" + http_multi_server: + dependency: transitive + description: + name: http_multi_server + sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" + url: "https://pub.dev" + source: hosted + version: "3.2.1" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + url: "https://pub.dev" + source: hosted + version: "4.0.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf + url: "https://pub.dev" + source: hosted + version: "0.19.0" + io: + dependency: transitive + description: + name: io + sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + js: + dependency: transitive + description: + name: js + sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf + url: "https://pub.dev" + source: hosted + version: "0.7.1" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + url: "https://pub.dev" + source: hosted + version: "4.8.1" + json_serializable: + dependency: "direct dev" + description: + name: json_serializable + sha256: aa1f5a8912615733e0fdc7a02af03308933c93235bdc8d50d0b0c8a8ccb0b969 + url: "https://pub.dev" + source: hosted + version: "6.7.1" + latlong2: + dependency: transitive + description: + name: latlong2 + sha256: "18712164760cee655bc790122b0fd8f3d5b3c36da2cb7bf94b68a197fbb0811b" + url: "https://pub.dev" + source: hosted + version: "0.9.0" + leak_tracker: + dependency: transitive + description: + name: leak_tracker + sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + leak_tracker_flutter_testing: + dependency: transitive + description: + name: leak_tracker_flutter_testing + sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + leak_tracker_testing: + dependency: transitive + description: + name: leak_tracker_testing + sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + url: "https://pub.dev" + source: hosted + version: "2.0.1" + lints: + dependency: transitive + description: + name: lints + sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" + url: "https://pub.dev" + source: hosted + version: "2.1.1" + lists: + dependency: transitive + description: + name: lists + sha256: "4ca5c19ae4350de036a7e996cdd1ee39c93ac0a2b840f4915459b7d0a7d4ab27" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + logging: + dependency: transitive + description: + name: logging + sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + matcher: + dependency: transitive + description: + name: matcher + sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + url: "https://pub.dev" + source: hosted + version: "0.12.16+1" + material_color_utilities: + dependency: transitive + description: + name: material_color_utilities + sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + url: "https://pub.dev" + source: hosted + version: "0.8.0" + meta: + dependency: transitive + description: + name: meta + sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + url: "https://pub.dev" + source: hosted + version: "1.11.0" + mgrs_dart: + dependency: transitive + description: + name: mgrs_dart + sha256: fb89ae62f05fa0bb90f70c31fc870bcbcfd516c843fb554452ab3396f78586f7 + url: "https://pub.dev" + source: hosted + version: "2.0.0" + mime: + dependency: transitive + description: + name: mime + sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" + url: "https://pub.dev" + source: hosted + version: "1.0.5" + nm: + dependency: "direct main" + description: + name: nm + sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254" + url: "https://pub.dev" + source: hosted + version: "0.5.0" + ordered_set: + dependency: transitive + description: + name: ordered_set + sha256: "3858c7d84619edfab87c3e367584648020903187edb70b52697646f4b2a93022" + url: "https://pub.dev" + source: hosted + version: "5.0.2" + package_config: + dependency: transitive + description: + name: package_config + sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + path: + dependency: transitive + description: + name: path + sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + url: "https://pub.dev" + source: hosted + version: "1.9.0" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 + url: "https://pub.dev" + source: hosted + version: "6.0.2" + platform: + dependency: transitive + description: + name: platform + sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" + url: "https://pub.dev" + source: hosted + version: "3.1.4" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" + polylabel: + dependency: transitive + description: + name: polylabel + sha256: "41b9099afb2aa6c1730bdd8a0fab1400d287694ec7615dd8516935fa3144214b" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + pool: + dependency: transitive + description: + name: pool + sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a" + url: "https://pub.dev" + source: hosted + version: "1.5.1" + process: + dependency: transitive + description: + name: process + sha256: "53fd8db9cec1d37b0574e12f07520d582019cb6c44abf5479a01505099a34a09" + url: "https://pub.dev" + source: hosted + version: "4.2.4" + proj4dart: + dependency: transitive + description: + name: proj4dart + sha256: c8a659ac9b6864aa47c171e78d41bbe6f5e1d7bd790a5814249e6b68bc44324e + url: "https://pub.dev" + source: hosted + version: "2.1.0" + pub_semver: + dependency: transitive + description: + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + pubspec_parse: + dependency: transitive + description: + name: pubspec_parse + sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + url: "https://pub.dev" + source: hosted + version: "1.2.3" + quiver: + dependency: transitive + description: + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 + url: "https://pub.dev" + source: hosted + version: "3.2.1" + real_time_chart: + dependency: "direct main" + description: + path: "packages/real_time_chart" + relative: true + source: path + version: "0.0.1" + riverpod: + dependency: transitive + description: + name: riverpod + sha256: "548e2192eb7aeb826eb89387f814edb76594f3363e2c0bb99dd733d795ba3589" + url: "https://pub.dev" + source: hosted + version: "2.5.0" + riverpod_analyzer_utils: + dependency: transitive + description: + name: riverpod_analyzer_utils + sha256: d4dabc35358413bf4611fcb6abb46308a67c4ef4cd5e69fd3367b11925c59f57 + url: "https://pub.dev" + source: hosted + version: "0.5.0" + riverpod_annotation: + dependency: "direct main" + description: + name: riverpod_annotation + sha256: "77e5d51afa4fa3e67903fb8746f33d368728d7051a0b6c292bcee60aeba46d95" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + riverpod_generator: + dependency: "direct dev" + description: + name: riverpod_generator + sha256: "7340e2ac0fb05b2d893b049c45188a81443c2bd825d5f266cba06197867b0ade" + url: "https://pub.dev" + source: hosted + version: "2.3.10" + riverpod_lint: + dependency: "direct dev" + description: + name: riverpod_lint + sha256: b9288432e7f3e9a2c160f5ed4ad086b5ebb5c98198ce134c6f2cb0a81a2bda83 + url: "https://pub.dev" + source: hosted + version: "2.3.8" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" + url: "https://pub.dev" + source: hosted + version: "0.27.7" + scaled_app: + dependency: "direct main" + description: + name: scaled_app + sha256: "3415fad16d1cf283112988985ccd14c4cd28bf48cbe6432d59e158f3b632d58d" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + url: "https://pub.dev" + source: hosted + version: "2.2.2" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7708d83064f38060c7b39db12aefe449cb8cdc031d6062280087bc4cdb988f5c" + url: "https://pub.dev" + source: hosted + version: "2.3.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "9f2cbcf46d4270ea8be39fa156d86379077c8a5228d9dfdb1164ae0bb93f1faa" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "22e2ecac9419b4246d7c22bfbbda589e3acf5c0351137d87dd2939d984d37c3b" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: "9aee1089b36bd2aafe06582b7d7817fd317ef05fc30e6ba14bff247d0933042a" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "841ad54f3c8381c480d0c9b508b89a34036f512482c407e6df7a9c4aa2ef8f59" + url: "https://pub.dev" + source: hosted + version: "2.3.2" + shelf: + dependency: transitive + description: + name: shelf + sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 + url: "https://pub.dev" + source: hosted + version: "1.4.1" + shelf_web_socket: + dependency: transitive + description: + name: shelf_web_socket + sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" + url: "https://pub.dev" + source: hosted + version: "1.0.4" + simple_tiles_map: + dependency: "direct main" + description: + name: simple_tiles_map + sha256: e0b74e5c3209a46872711f01d62cede59cf6aeb2e9845d2cb16ed3203832334c + url: "https://pub.dev" + source: hosted + version: "1.3.4" + sky_engine: + dependency: transitive + description: flutter + source: sdk + version: "0.0.99" + source_gen: + dependency: transitive + description: + name: source_gen + sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + url: "https://pub.dev" + source: hosted + version: "1.5.0" + source_helper: + dependency: transitive + description: + name: source_helper + sha256: "6adebc0006c37dd63fe05bca0a929b99f06402fc95aa35bf36d67f5c06de01fd" + url: "https://pub.dev" + source: hosted + version: "1.3.4" + source_span: + dependency: transitive + description: + name: source_span + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + url: "https://pub.dev" + source: hosted + version: "1.10.0" + sprintf: + dependency: transitive + description: + name: sprintf + sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" + url: "https://pub.dev" + source: hosted + version: "7.0.0" + stack_trace: + dependency: transitive + description: + name: stack_trace + sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + url: "https://pub.dev" + source: hosted + version: "1.11.1" + state_notifier: + dependency: transitive + description: + name: state_notifier + sha256: b8677376aa54f2d7c58280d5a007f9e8774f1968d1fb1c096adcb4792fba29bb + url: "https://pub.dev" + source: hosted + version: "1.0.0" + stream_channel: + dependency: transitive + description: + name: stream_channel + sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + url: "https://pub.dev" + source: hosted + version: "2.1.2" + stream_transform: + dependency: transitive + description: + name: stream_transform + sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" + url: "https://pub.dev" + source: hosted + version: "2.1.0" + string_scanner: + dependency: transitive + description: + name: string_scanner + sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + url: "https://pub.dev" + source: hosted + version: "1.2.0" + term_glyph: + dependency: transitive + description: + name: term_glyph + sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + url: "https://pub.dev" + source: hosted + version: "1.2.1" + test_api: + dependency: transitive + description: + name: test_api + sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + url: "https://pub.dev" + source: hosted + version: "0.6.1" + timing: + dependency: transitive + description: + name: timing + sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + url: "https://pub.dev" + source: hosted + version: "1.0.1" + tuple: + dependency: transitive + description: + name: tuple + sha256: a97ce2013f240b2f3807bcbaf218765b6f301c3eff91092bcfa23a039e7dd151 + url: "https://pub.dev" + source: hosted + version: "2.0.2" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c + url: "https://pub.dev" + source: hosted + version: "1.3.2" + unicode: + dependency: transitive + description: + name: unicode + sha256: "0f69e46593d65245774d4f17125c6084d2c20b4e473a983f6e21b7d7762218f1" + url: "https://pub.dev" + source: hosted + version: "0.3.1" + uuid: + dependency: transitive + description: + name: uuid + sha256: cd210a09f7c18cbe5a02511718e0334de6559871052c90a90c0cca46a4aa81c8 + url: "https://pub.dev" + source: hosted + version: "4.3.3" + vector_math: + dependency: transitive + description: + name: vector_math + sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" + url: "https://pub.dev" + source: hosted + version: "2.1.4" + video_player: + dependency: "direct main" + description: + name: video_player + sha256: afc65f4b8bcb2c188f64a591f84fb471f4f2e19fc607c65fd8d2f8fedb3dec23 + url: "https://pub.dev" + source: hosted + version: "2.8.3" + video_player_android: + dependency: transitive + description: + name: video_player_android + sha256: "4dd9b8b86d70d65eecf3dcabfcdfbb9c9115d244d022654aba49a00336d540c2" + url: "https://pub.dev" + source: hosted + version: "2.4.12" + video_player_avfoundation: + dependency: transitive + description: + name: video_player_avfoundation + sha256: "309e3962795e761be010869bae65c0b0e45b5230c5cee1bec72197ca7db040ed" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + video_player_platform_interface: + dependency: transitive + description: + name: video_player_platform_interface + sha256: "236454725fafcacf98f0f39af0d7c7ab2ce84762e3b63f2cbb3ef9a7e0550bc6" + url: "https://pub.dev" + source: hosted + version: "6.2.2" + video_player_web: + dependency: transitive + description: + name: video_player_web + sha256: "41245cef5ef29c4585dbabcbcbe9b209e34376642c7576cabf11b4ad9289d6e4" + url: "https://pub.dev" + source: hosted + version: "2.3.0" + virtual_keyboard_multi_language: + dependency: "direct main" + description: + path: "packages/virtual_keyboard_multi_language" + relative: true + source: path + version: "1.0.3" + vm_service: + dependency: transitive + description: + name: vm_service + sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + url: "https://pub.dev" + source: hosted + version: "13.0.0" + watcher: + dependency: transitive + description: + name: watcher + sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + url: "https://pub.dev" + source: hosted + version: "0.5.1" + web_socket_channel: + dependency: transitive + description: + name: web_socket_channel + sha256: "1d8e795e2a8b3730c41b8a98a2dff2e0fb57ae6f0764a1c46ec5915387d257b2" + url: "https://pub.dev" + source: hosted + version: "2.4.4" + win32: + dependency: transitive + description: + name: win32 + sha256: "8cb58b45c47dcb42ab3651533626161d6b67a2921917d8d429791f76972b3480" + url: "https://pub.dev" + source: hosted + version: "5.3.0" + wkt_parser: + dependency: transitive + description: + name: wkt_parser + sha256: "8a555fc60de3116c00aad67891bcab20f81a958e4219cc106e3c037aa3937f13" + url: "https://pub.dev" + source: hosted + version: "2.0.0" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + url: "https://pub.dev" + source: hosted + version: "1.0.4" + xml: + dependency: transitive + description: + name: xml + sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 + url: "https://pub.dev" + source: hosted + version: "6.5.0" + yaml: + dependency: transitive + description: + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + url: "https://pub.dev" + source: hosted + version: "3.1.2" +sdks: + dart: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml new file mode 100644 index 0000000..fcca57f --- /dev/null +++ b/pubspec.yaml @@ -0,0 +1,127 @@ +name: flutter_elinux +description: A demo Flutter project for embedded Linux systems. +# The following line prevents the package from being accidentally published to +# pub.dev using `flutter pub publish`. This is preferred for private packages. +publish_to: "none" # Remove this line if you wish to publish to pub.dev + +# The following defines the version and build number for your application. +# A version number is three numbers separated by dots, like 1.2.43 +# followed by an optional build number separated by a +. +# Both the version and the builder number may be overridden in flutter +# build by specifying --build-name and --build-number, respectively. +# In Android, build-name is used as versionName while build-number used as versionCode. +# Read more about Android versioning at https://developer.android.com/studio/publish/versioning +# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. +# Read more about iOS versioning at +# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html +# In Windows, build-name is used as the major, minor, and patch parts +# of the product and file versions while build-number is used as the build suffix. +version: 1.0.0+1 + +environment: + sdk: ">=3.3.0 <4.0.0" + +# Dependencies specify other packages that your package needs in order to work. +# To automatically upgrade your package dependencies to the latest versions +# consider running `flutter pub upgrade --major-versions`. Alternatively, +# dependencies can be manually updated by changing the version numbers below to +# the latest version available on pub.dev. To see which dependencies have newer +# versions available, run `flutter pub outdated`. +dependencies: + flutter: + sdk: flutter + virtual_keyboard_multi_language: + path: ./packages/virtual_keyboard_multi_language + real_time_chart: + path: ./packages/real_time_chart + scaled_app: ^2.1.0 + flutter_riverpod: ^2.4.5 + riverpod_annotation: ^2.3.0 + freezed: ^2.4.5 + go_router: ^12.0.1 + nm: ^0.5.0 + dio: ^5.4.0 + freezed_annotation: ^2.4.1 + flutter_gpiod: ^0.5.1+5 + flutter_adaptive_scaffold: ^0.1.7+2 + dart_periphery: ^0.9.5 + flex_color_picker: ^3.3.1 + flame: ^1.15.0 + flutterpi_gstreamer_video_player: ^0.1.1+1 + shared_preferences: ^2.2.2 + intl: ^0.19.0 + simple_tiles_map: ^1.3.4 + flutter_map: ^5.0.0 + video_player: ^2.8.2 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^2.0.0 + riverpod_generator: ^2.3.5 + build_runner: ^2.4.6 + custom_lint: ^0.5.5 + riverpod_lint: ^2.3.3 + freezed_lint: ^0.0.1-dev+1 + json_serializable: ^6.7.1 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true + + # To add assets to your application, add an assets section, like this: + assets: + - assets/ + - assets/images/flame_benchmark/ + - assets/images/flame_benchmark/rogue_shooter/ + - assets/images/flame_benchmark/sprite_benchmark/ + - assets/img/ + - assets/video/ + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + + shaders: + - shaders/aobench.frag + - shaders/mario.frag + - shaders/water.frag + - shaders/demo.frag + - shaders/cube.frag + - shaders/vsync.frag + - shaders/triangleRotation.frag + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/assets-and-images/#resolution-aware + + # For details regarding adding assets from package dependencies, see + # https://flutter.dev/assets-and-images/#from-packages + + # To add custom fonts to your application, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts from package dependencies, + # see https://flutter.dev/custom-fonts/#from-packages diff --git a/shaders/aobench.frag b/shaders/aobench.frag new file mode 100644 index 0000000..e93a5c4 --- /dev/null +++ b/shaders/aobench.frag @@ -0,0 +1,5 @@ +out vec4 fragColor; + +void main() { + fragColor = vec4(0.318,0.373,1.000,1.000); +} \ No newline at end of file diff --git a/shaders/cube.frag b/shaders/cube.frag new file mode 100644 index 0000000..0176fa7 --- /dev/null +++ b/shaders/cube.frag @@ -0,0 +1,90 @@ +#version 460 core +#include + +uniform vec2 uSize; +uniform float iTime; +vec2 iResolution; +out vec4 fragColor; + +#define PI 3.1415926535897932384626433832795 +#define TWO_PI 6.28318530718 + +#define SCALE_FACTOR 8 +#define TIME_SCALE 0.005 + +mat2 rotate2d(float _angle){ + return mat2(cos(_angle),-sin(_angle), + sin(_angle),cos(_angle)); +} + +#define pi 3.14159265 + +float drawLine (vec2 p1, vec2 p2, vec2 uv, float a) +{ + float r = 0.; + float one_px = 1. / iResolution.x; //not really one px + + // get dist between points + float d = distance(p1, p2); + + // get dist between current pixel and p1 + float duv = distance(p1, uv); + + //if point is on line, according to dist, it should match current uv + r = 1.-floor(1.-(a*one_px)+distance (mix(p1, p2, clamp(duv/d, 0., 1.)), uv)); + + return r; +} + +float drawCircle(vec2 p, float d, vec2 uv) +{ + return (distance(p, uv) <= d) ? 1. : 0.; +} + +void main(void) { + + iResolution = uSize; + vec2 fragCoord = gl_FragCoord.xy; // Get the fragment coordinates + vec2 center = 2.0 * vec2(fragCoord.xy - 0.5 * iResolution.xy) / iResolution.y; + + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = fragCoord/iResolution.xy; + + float t = iTime/2; //Pro Tip™: multipluy time to go faster! + + //in uv space + vec2 p1 = vec2(0.5 + sin(t)*.2, 0.25 + cos(t+pi)*.1); + vec2 p2 = vec2(0.5 + sin(t+pi)*.2, 0.25 + cos(t)*.1); + vec2 p3 = vec2(0.5 + sin(t+pi/2.)*.2, 0.25 + cos(t-.5*pi)*.1); + vec2 p4 = vec2(0.5 + sin(t-pi/2.)*.2, 0.25 + cos(t+.5*pi)*.1); + vec2 p5 = vec2(0.5 + sin(t)*.2, 0.75 + cos(t+pi)*.1); + vec2 p6 = vec2(0.5 + sin(t+pi)*.2, 0.75 + cos(t)*.1); + vec2 p7 = vec2(0.5 + sin(t+pi/2.)*.2, 0.75 + cos(t-.5*pi)*.1); + vec2 p8 = vec2(0.5 + sin(t-pi/2.)*.2, 0.75 + cos(t+.5*pi)*.1); + + + float lines = drawLine(p1, p5, uv, 1.) + + drawLine(p2, p6, uv, 1.) + + drawLine(p1, p3, uv, 1.) + + drawLine(p3, p2, uv, 1.) + + drawLine(p1, p4, uv, 1.) + + drawLine(p4, p2, uv, 1.) + + drawLine(p5, p7, uv, 1.) + + drawLine(p7, p6, uv, 1.) + + drawLine(p6, p8, uv, 1.) + + drawLine(p8, p5, uv, 1.) + + drawLine(p3, p7, uv, 1.) + + drawLine(p4, p8, uv, 1.); + + float d = 0.003; + float dots = drawCircle(p1, d, uv) + + drawCircle(p2, d, uv) + + drawCircle(p3, d, uv) + + drawCircle(p4, d, uv) + + drawCircle(p5, d, uv) + + drawCircle(p6, d, uv) + + drawCircle(p7, d, uv) + + drawCircle(p8, d, uv); + + fragColor = vec4(lines*.6-dots, dots, dots, 1.); +} diff --git a/shaders/demo.frag b/shaders/demo.frag new file mode 100644 index 0000000..a51014d --- /dev/null +++ b/shaders/demo.frag @@ -0,0 +1,94 @@ +#version 460 core +#include + +uniform vec2 uSize; +uniform float iTime; +vec2 iResolution; +out vec4 fragColor; + +const float PI = 3.141592653589793; +const float TWO_PI = 6.283185307179586; +const float INV_PI = 0.318309886183791; + +#define SCALE_FACTOR 8 +#define TIME_SCALE 0.005 + +vec3 triangleIntersect(vec3 ro, vec3 rd, vec3 v0, vec3 v1, vec3 v2) { + vec3 v1v0 = v1 - v0; // ~3 FLOP (3x SUB) + vec3 v2v0 = v2 - v0; // ~3 FLOP (3x SUB) + vec3 rov0 = ro - v0; // ~3 FLOP (3x SUB) + + vec3 n = cross(v1v0, v2v0); // ~9 FLOP (6x MUL, 3x SUB) + vec3 q = cross(rov0, rd ); // ~9 FLOP (6x MUL, 3x SUB) + + float d = 1.0 / dot(rd, n); // ~6 FLOP (3x MUL, 2x ADD, 1x DIV) + + float u = d * dot(-q, v2v0); // ~7 FLOP (1x NEG, 3x MUL, 2x ADD, 1x MUL) + float v = d * dot( q, v1v0); // ~6 FLOP (3x MUL, 2x ADD, 1x MUL) + float t = d * dot(-n, rov0); // ~7 FLOP (1x NEG, 3x MUL, 2x ADD, 1x MUL) + + t = (u < 0.0 || v < 0.0 || (u + v) > 1.0) ? -1.0 : t; // no intersection, ~9 FLOP (2x LESS, 1x ADD, 1x GREAT, 2x MAX, 1x SUB, 2x MUL) + + return vec3(t, u, v); // Total: ~62? FLOP (16x SUB, 29x MUL, 1x DIV, 7x ADD, 2x NEG, 2x LESS, 1x GREAT, 2x MAX) +} + +void main(void) { + + iResolution = uSize; + vec2 fragCoord = gl_FragCoord.xy; // Get the fragment coordinates + vec2 center = 2.0 * vec2(fragCoord.xy - 0.5 * iResolution.xy) / iResolution.y; + + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = fragCoord/iResolution.xy; + + const vec2 s0 = vec2(-0.375, -0.125); + const vec2 s1 = vec2( 0.125, -0.375); + const vec2 s2 = vec2(-0.125, 0.375); + const vec2 s3 = vec2( 0.375, 0.125); + + // Triangle Vertex Positions + const vec3 v0 = vec3( 1.000, -1.000, -1.000); + const vec3 v1 = vec3(-1.000, 1.000, -1.000); + const vec3 v2 = vec3(-1.000, -1.000, 1.000); + + vec2 hres = 0.5 * iResolution.xy; // ~2 FLOP (2x ADD) + + float ires = 1.0 / max(hres.x, hres.y); // ~2 FLOP (1x MAX, 1x DIV) + + vec2 uv0 = fragCoord.xy + s0; // ~2 FLOP (2x ADD) + vec2 uv1 = fragCoord.xy + s1; // ~2 FLOP (2x ADD) + vec2 uv2 = fragCoord.xy + s2; // ~2 FLOP (2x ADD) + vec2 uv3 = fragCoord.xy + s3; // ~2 FLOP (2x ADD) + + uv0 = ires * (uv0 - hres); // ~4 FLOP (2x SUB, 2x MUL) + uv1 = ires * (uv1 - hres); // ~4 FLOP (2x SUB, 2x MUL) + uv2 = ires * (uv2 - hres); // ~4 FLOP (2x SUB, 2x MUL) + uv3 = ires * (uv3 - hres); // ~4 FLOP (2x SUB, 2x MUL) + + float theta = TWO_PI * fract(0.5 * iTime); + + float cos_theta = cos(theta), sin_theta = sin(theta); // ~2 FLOP (1x COS, 1x SIN) + + vec3 ro = vec3(sin_theta, cos_theta, 5.0); + + vec3 rd0 = normalize(vec3(uv0, -1.0)); // ~8 FLOP (3x MUL, 2x ADD, 3x DIV) + vec3 rd1 = normalize(vec3(uv1, -1.0)); // ~8 FLOP (3x MUL, 2x ADD, 3x DIV) + vec3 rd2 = normalize(vec3(uv2, -1.0)); // ~8 FLOP (3x MUL, 2x ADD, 3x DIV) + vec3 rd3 = normalize(vec3(uv3, -1.0)); // ~8 FLOP (3x MUL, 2x ADD, 3x DIV) + + vec3 t0 = triangleIntersect(ro, rd0, v0, v1, v2); // ~62 FLOP + vec3 t1 = triangleIntersect(ro, rd1, v0, v1, v2); // ~62 FLOP + vec3 t2 = triangleIntersect(ro, rd2, v0, v1, v2); // ~62 FLOP + vec3 t3 = triangleIntersect(ro, rd3, v0, v1, v2); // ~62 FLOP + + fragColor.xy = vec2(0); + + fragColor.xy += t0.x >= 0.0 ? t0.yz : vec2(0); // ~8 FLOP (1x GREATEQUAL, 1x SUB, 4x MUL, 2x ADD) + fragColor.xy += t1.x >= 0.0 ? t1.yz : vec2(0); // ~8 FLOP (1x GREATEQUAL, 1x SUB, 4x MUL, 2x ADD) + fragColor.xy += t2.x >= 0.0 ? t2.yz : vec2(0); // ~8 FLOP (1x GREATEQUAL, 1x SUB, 4x MUL, 2x ADD) + fragColor.xy += t3.x >= 0.0 ? t3.yz : vec2(0); // ~8 FLOP (1x GREATEQUAL, 1x SUB, 4x MUL, 2x ADD) + + fragColor.xy *= 0.25; // ~2 FLOP (2x MUL) + + fragColor = vec4(fragColor.xy, 0.0, 1.0); // Total: 347 FLOP per pixel +} diff --git a/shaders/example.frag b/shaders/example.frag new file mode 100644 index 0000000..aaff2e0 --- /dev/null +++ b/shaders/example.frag @@ -0,0 +1,23 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// source: https://github.com/zanderso/fragment_shader_example/blob/main/shaders/example.frag + +layout(location = 0) uniform float iTime; +layout(location = 1) uniform vec2 iResolution; + +layout(location = 0) out vec4 fragColor; + +void main() +{ + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = gl_FragCoord.xy/iResolution; + float t = 4 * iTime; + + // Time varying pixel color + vec3 col = 0.5 + 0.5*cos(t + uv.xyx + vec3(0,1,4)); + + // Output to screen + fragColor = vec4(col,1.0); +} \ No newline at end of file diff --git a/shaders/mario.frag b/shaders/mario.frag new file mode 100644 index 0000000..49e6c8d --- /dev/null +++ b/shaders/mario.frag @@ -0,0 +1,1433 @@ + +uniform vec2 iResolution; +uniform float iTime; +out vec4 fragColor; + +// Ported from https://www.shadertoy.com/view/XtlSD7 to Flutter + +// Interesting findings from original NES Super Mario Bros.: +// -Clouds and brushes of all sizes are drawn using the same small sprite (32x24) +// -Hills, clouds and bushes weren't placed manually. Every background object type is repeated after 768 pixels. +// -Overworld (main theme) drum sound uses only the APU noise generator +#define SPRITE_DEC( x, i ) mod( floor( i / pow( 4.0, mod( x, 8.0 ) ) ), 4.0 ) +#define SPRITE_DEC2( x, i ) mod( floor( i / pow( 4.0, mod( x, 11.0 ) ) ), 4.0 ) +#define RGB( r, g, b ) vec3( float( r ) / 255.0, float( g ) / 255.0, float( b ) / 255.0 ) + +const float MARIO_SPEED = 89.0; +const float GOOMBA_SPEED = 32.0; +const float INTRO_LENGTH = 2.0; + + +void SpriteBlock( inout vec3 color, float x, float y ) +{ + // black + float idx = 1.0; + + // light orange + idx = x < y ? 3.0 : idx; + + // dark orange + idx = x > 3.0 && x < 12.0 && y > 3.0 && y < 12.0 ? 2.0 : idx; + idx = x == 15.0 - y ? 2.0 : idx; + + color = RGB( 0, 0, 0 ); + color = idx == 2.0 ? RGB( 231, 90, 16 ) : color; + color = idx == 3.0 ? RGB( 247, 214, 181 ) : color; +} + +void SpriteHill( inout vec3 color, float x, float y ) +{ + float idx = 0.0; + + // dark green + idx = ( x > y && 79.0 - x > y ) && y < 33.0 ? 2.0 : idx; + idx = ( x >= 37.0 && x <= 42.0 ) && y == 33.0 ? 2.0 : idx; + + // black + idx = ( x == y || 79.0 - x == y ) && y < 33.0 ? 1.0 : idx; + idx = ( x == 33.0 || x == 46.0 ) && y == 32.0 ? 1.0 : idx; + idx = ( x >= 34.0 && x <= 36.0 ) && y == 33.0 ? 1.0 : idx; + idx = ( x >= 43.0 && x <= 45.0 ) && y == 33.0 ? 1.0 : idx; + idx = ( x >= 37.0 && x <= 42.0 ) && y == 34.0 ? 1.0 : idx; + idx = ( x >= 25.0 && x <= 26.0 ) && ( y >= 8.0 && y <= 11.0 ) ? 1.0 : idx; + idx = ( x >= 41.0 && x <= 42.0 ) && ( y >= 24.0 && y <= 27.0 ) ? 1.0 : idx; + idx = ( x >= 49.0 && x <= 50.0 ) && ( y >= 8.0 && y <= 11.0 ) ? 1.0 : idx; + idx = ( x >= 28.0 && x <= 30.0 ) && ( y >= 11.0 && y <= 14.0 ) ? 1.0 : idx; + idx = ( x >= 28.0 && x <= 30.0 ) && ( y >= 11.0 && y <= 14.0 ) ? 1.0 : idx; + idx = ( x >= 44.0 && x <= 46.0 ) && ( y >= 27.0 && y <= 30.0 ) ? 1.0 : idx; + idx = ( x >= 44.0 && x <= 46.0 ) && ( y >= 27.0 && y <= 30.0 ) ? 1.0 : idx; + idx = ( x >= 52.0 && x <= 54.0 ) && ( y >= 11.0 && y <= 14.0 ) ? 1.0 : idx; + idx = ( x == 29.0 || x == 53.0 ) && ( y >= 10.0 && y <= 15.0 ) ? 1.0 : idx; + idx = x == 45.0 && ( y >= 26.0 && y <= 31.0 ) ? 1.0 : idx; + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 0, 173, 0 ) : color; +} + +void SpritePipe( inout vec3 color, float x, float y, float h ) +{ + float offset = h * 16.0; + + // light green + float idx = 3.0; + + // dark green + idx = ( ( x > 5.0 && x < 8.0 ) || ( x == 13.0 ) || ( x > 15.0 && x < 23.0 ) ) && y < 17.0 + offset ? 2.0 : idx; + idx = ( ( x > 4.0 && x < 7.0 ) || ( x == 12.0 ) || ( x > 14.0 && x < 24.0 ) ) && ( y > 17.0 + offset && y < 30.0 + offset ) ? 2.0 : idx; + idx = ( x < 5.0 || x > 11.0 ) && y == 29.0 + offset ? 2.0 : idx; + idx = fract( x * 0.5 + y * 0.5 ) == 0.5 && x > 22.0 && ( ( x < 26.0 && y < 17.0 + offset ) || ( x < 28.0 && y > 17.0 + offset && y < 30.0 + offset ) ) ? 2.0 : idx; + + // black + idx = y == 31.0 + offset || x == 0.0 || x == 31.0 || y == 17.0 + offset ? 1.0 : idx; + idx = ( x == 2.0 || x == 29.0 ) && y < 18.0 + offset ? 1.0 : idx; + idx = ( x > 1.0 && x < 31.0 ) && y == 16.0 + offset ? 1.0 : idx; + + // transparent + idx = ( x < 2.0 || x > 29.0 ) && y < 17.0 + offset ? 0.0 : idx; + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 0, 173, 0 ) : color; + color = idx == 3.0 ? RGB( 189, 255, 24 ) : color; +} + +void SpriteCloud( inout vec3 color, float x, float y, float isBush ) +{ + float idx = 0.0; + + idx = y == 23.0 ? ( x <= 10.0 ? 0.0 : ( x <= 21.0 ? 5440.0 : 0.0 ) ) : idx; + idx = y == 22.0 ? ( x <= 10.0 ? 0.0 : ( x <= 21.0 ? 32720.0 : 0.0 ) ) : idx; + idx = y == 21.0 ? ( x <= 10.0 ? 0.0 : ( x <= 21.0 ? 131061.0 : 0.0 ) ) : idx; + idx = y == 20.0 ? ( x <= 10.0 ? 1048576.0 : ( x <= 21.0 ? 1179647.0 : 0.0 ) ) : idx; + idx = y == 19.0 ? ( x <= 10.0 ? 1048576.0 : ( x <= 21.0 ? 3670015.0 : 1.0 ) ) : idx; + idx = y == 18.0 ? ( x <= 10.0 ? 1048576.0 : ( x <= 21.0 ? 4190207.0 : 7.0 ) ) : idx; + idx = y == 17.0 ? ( x <= 10.0 ? 3407872.0 : ( x <= 21.0 ? 4177839.0 : 7.0 ) ) : idx; + idx = y == 16.0 ? ( x <= 10.0 ? 3997696.0 : ( x <= 21.0 ? 4194299.0 : 7.0 ) ) : idx; + idx = y == 15.0 ? ( x <= 10.0 ? 4150272.0 : ( x <= 21.0 ? 4194303.0 : 1055.0 ) ) : idx; + idx = y == 14.0 ? ( x <= 10.0 ? 4193536.0 : ( x <= 21.0 ? 4194303.0 : 7455.0 ) ) : idx; + idx = y == 13.0 ? ( x <= 10.0 ? 4194112.0 : ( x <= 21.0 ? 4194303.0 : 8063.0 ) ) : idx; + idx = y == 12.0 ? ( x <= 10.0 ? 4194240.0 : ( x <= 21.0 ? 4194303.0 : 73727.0 ) ) : idx; + idx = y == 11.0 ? ( x <= 10.0 ? 4194260.0 : ( x <= 21.0 ? 4194303.0 : 491519.0 ) ) : idx; + idx = y == 10.0 ? ( x <= 10.0 ? 4194301.0 : ( x <= 21.0 ? 4194303.0 : 524287.0 ) ) : idx; + idx = y == 9.0 ? ( x <= 10.0 ? 4194301.0 : ( x <= 21.0 ? 4194303.0 : 524287.0 ) ) : idx; + idx = y == 8.0 ? ( x <= 10.0 ? 4194292.0 : ( x <= 21.0 ? 4194303.0 : 131071.0 ) ) : idx; + idx = y == 7.0 ? ( x <= 10.0 ? 4193232.0 : ( x <= 21.0 ? 4194303.0 : 32767.0 ) ) : idx; + idx = y == 6.0 ? ( x <= 10.0 ? 3927872.0 : ( x <= 21.0 ? 4193279.0 : 131071.0 ) ) : idx; + idx = y == 5.0 ? ( x <= 10.0 ? 2800896.0 : ( x <= 21.0 ? 4193983.0 : 524287.0 ) ) : idx; + idx = y == 4.0 ? ( x <= 10.0 ? 3144960.0 : ( x <= 21.0 ? 3144362.0 : 262143.0 ) ) : idx; + idx = y == 3.0 ? ( x <= 10.0 ? 4150272.0 : ( x <= 21.0 ? 3845099.0 : 98303.0 ) ) : idx; + idx = y == 2.0 ? ( x <= 10.0 ? 3997696.0 : ( x <= 21.0 ? 4107775.0 : 6111.0 ) ) : idx; + idx = y == 1.0 ? ( x <= 10.0 ? 1310720.0 : ( x <= 21.0 ? 4183167.0 : 325.0 ) ) : idx; + idx = y == 0.0 ? ( x <= 10.0 ? 0.0 : ( x <= 21.0 ? 1392661.0 : 0.0 ) ) : idx; + + idx = SPRITE_DEC2( x, idx ); + + vec3 colorB = isBush == 1.0 ? RGB( 0, 173, 0 ) : RGB( 57, 189, 255 ); + vec3 colorC = isBush == 1.0 ? RGB( 189, 255, 24 ) : RGB( 254, 254, 254 ); + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? colorB : color; + color = idx == 3.0 ? colorC : color; +} + +void SpriteFlag( inout vec3 color, float x, float y ) +{ + float idx = 0.0; + idx = y == 15.0 ? 43690.0 : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 43688.0 : 42326.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 43680.0 : 38501.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43648.0 : 39529.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 43520.0 : 39257.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 43008.0 : 38293.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 40960.0 : 38229.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 32768.0 : 43354.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 0.0 : 43690.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 0.0 : 43688.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 0.0 : 43680.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 0.0 : 43648.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 0.0 : 43520.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 0.0 : 43008.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 0.0 : 40960.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 0.0 : 32768.0 ) : idx; + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 0, 173, 0 ) : color; + color = idx == 2.0 ? RGB( 255, 255, 255 ) : color; +} + +void SpriteCastleFlag( inout vec3 color, float x, float y ) +{ + float idx = 0.0; + idx = y == 13.0 ? ( x <= 10.0 ? 8.0 : 0.0 ) : idx; + idx = y == 12.0 ? ( x <= 10.0 ? 42.0 : 0.0 ) : idx; + idx = y == 11.0 ? ( x <= 10.0 ? 8.0 : 0.0 ) : idx; + idx = y == 10.0 ? ( x <= 10.0 ? 4194292.0 : 15.0 ) : idx; + idx = y == 9.0 ? ( x <= 10.0 ? 4161524.0 : 15.0 ) : idx; + idx = y == 8.0 ? ( x <= 10.0 ? 4161524.0 : 15.0 ) : idx; + idx = y == 7.0 ? ( x <= 10.0 ? 1398260.0 : 15.0 ) : idx; + idx = y == 6.0 ? ( x <= 10.0 ? 3495924.0 : 15.0 ) : idx; + idx = y == 5.0 ? ( x <= 10.0 ? 4022260.0 : 15.0 ) : idx; + idx = y == 4.0 ? ( x <= 10.0 ? 3528692.0 : 15.0 ) : idx; + idx = y == 3.0 ? ( x <= 10.0 ? 3667956.0 : 15.0 ) : idx; + idx = y == 2.0 ? ( x <= 10.0 ? 4194292.0 : 15.0 ) : idx; + idx = y == 1.0 ? ( x <= 10.0 ? 4.0 : 0.0 ) : idx; + idx = y == 0.0 ? ( x <= 10.0 ? 4.0 : 0.0 ) : idx; + + idx = SPRITE_DEC2( x, idx ); + + color = idx == 1.0 ? RGB( 181, 49, 33 ) : color; + color = idx == 2.0 ? RGB( 230, 156, 33 ) : color; + color = idx == 3.0 ? RGB( 255, 255, 255 ) : color; +} + +void SpriteGoomba( inout vec3 color, float x, float y, float frame ) +{ + float idx = 0.0; + + // second frame is flipped first frame + x = frame == 1.0 ? 15.0 - x : x; + + if ( frame <= 1.0 ) + { + idx = y == 15.0 ? ( x <= 7.0 ? 40960.0 : 10.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 43008.0 : 42.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 43520.0 : 170.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43648.0 : 682.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 43360.0 : 2410.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 42920.0 : 10970.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 22440.0 : 10965.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 47018.0 : 43742.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 49066.0 : 43774.0 ) : idx; + idx = y == 6.0 ? 43690.0 : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 65192.0 : 10943.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 65280.0 : 255.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 65280.0 : 1535.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 64832.0 : 5471.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 62784.0 : 5463.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 5376.0 : 1364.0 ) : idx; + } + else + { + idx = y == 7.0 ? ( x <= 7.0 ? 40960.0 : 10.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 43648.0 : 682.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 42344.0 : 10586.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 24570.0 : 45045.0 ) : idx; + idx = y == 3.0 ? 43690.0 : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 65472.0 : 1023.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 65280.0 : 255.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 1364.0 : 5456.0 ) : idx; + } + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 153, 75, 12 ) : color; + color = idx == 3.0 ? RGB( 255, 200, 184 ) : color; +} + +void SpriteKoopa( inout vec3 color, float x, float y, float frame ) +{ + float idx = 0.0; + + if ( frame == 0.0 ) + { + idx = y == 23.0 ? ( x <= 7.0 ? 768.0 : 0.0 ) : idx; + idx = y == 22.0 ? ( x <= 7.0 ? 4032.0 : 0.0 ) : idx; + idx = y == 21.0 ? ( x <= 7.0 ? 4064.0 : 0.0 ) : idx; + idx = y == 20.0 ? ( x <= 7.0 ? 12128.0 : 0.0 ) : idx; + idx = y == 19.0 ? ( x <= 7.0 ? 12136.0 : 0.0 ) : idx; + idx = y == 18.0 ? ( x <= 7.0 ? 12136.0 : 0.0 ) : idx; + idx = y == 17.0 ? ( x <= 7.0 ? 12264.0 : 0.0 ) : idx; + idx = y == 16.0 ? ( x <= 7.0 ? 11174.0 : 0.0 ) : idx; + idx = y == 15.0 ? ( x <= 7.0 ? 10922.0 : 0.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 10282.0 : 341.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 30730.0 : 1622.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 31232.0 : 1433.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 24192.0 : 8037.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 24232.0 : 7577.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 28320.0 : 9814.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 40832.0 : 6485.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 26496.0 : 9814.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 23424.0 : 5529.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 22272.0 : 5477.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 24320.0 : 64921.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 65024.0 : 12246.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 59904.0 : 11007.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 43008.0 : 10752.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 40960.0 : 2690.0 ) : idx; + } + else + { + idx = y == 22.0 ? ( x <= 7.0 ? 192.0 : 0.0 ) : idx; + idx = y == 21.0 ? ( x <= 7.0 ? 1008.0 : 0.0 ) : idx; + idx = y == 20.0 ? ( x <= 7.0 ? 3056.0 : 0.0 ) : idx; + idx = y == 19.0 ? ( x <= 7.0 ? 11224.0 : 0.0 ) : idx; + idx = y == 18.0 ? ( x <= 7.0 ? 11224.0 : 0.0 ) : idx; + idx = y == 17.0 ? ( x <= 7.0 ? 11224.0 : 0.0 ) : idx; + idx = y == 16.0 ? ( x <= 7.0 ? 11256.0 : 0.0 ) : idx; + idx = y == 15.0 ? ( x <= 7.0 ? 10986.0 : 0.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 10918.0 : 0.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 2730.0 : 341.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 18986.0 : 1622.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 18954.0 : 5529.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 24202.0 : 8037.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 24200.0 : 7577.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 28288.0 : 9814.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 40864.0 : 6485.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 26496.0 : 9814.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 23424.0 : 5529.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 22272.0 : 5477.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 24320.0 : 64921.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 65152.0 : 4054.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 60064.0 : 11007.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 2728.0 : 43520.0 ) : idx; + } + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 30, 132, 0 ) : color; + color = idx == 2.0 ? RGB( 215, 141, 34 ) : color; + color = idx == 3.0 ? RGB( 255, 255, 255 ) : color; +} + +void SpriteQuestion( inout vec3 color, float x, float y, float t ) +{ + float idx = 0.0; + idx = y == 15.0 ? ( x <= 7.0 ? 43688.0 : 10922.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 65534.0 : 32767.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 65502.0 : 30719.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 44030.0 : 32762.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 23294.0 : 32745.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 56062.0 : 32619.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 56062.0 : 32619.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 55294.0 : 32618.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 49150.0 : 32598.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 49150.0 : 32758.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 65534.0 : 32757.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 49150.0 : 32766.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 49150.0 : 32758.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 65502.0 : 30709.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 65534.0 : 32767.0 ) : idx; + idx = y == 0.0 ? 21845.0 : idx; + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 231, 90, 16 ) : color; + color = idx == 3.0 ? mix( RGB( 255, 165, 66 ), RGB( 231, 90, 16 ), t ) : color; +} + +void SpriteMushroom( inout vec3 color, float x, float y ) +{ + float idx = 0.0; + idx = y == 15.0 ? ( x <= 7.0 ? 40960.0 : 10.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 43008.0 : 22.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 43520.0 : 85.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43648.0 : 341.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 43680.0 : 2646.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 42344.0 : 10922.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 38232.0 : 10922.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 38234.0 : 42410.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 38234.0 : 38314.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 42346.0 : 38570.0 ) : idx; + idx = y == 5.0 ? 43690.0 : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 64856.0 : 9599.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 65280.0 : 255.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 65280.0 : 239.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 65280.0 : 239.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 64512.0 : 59.0 ) : idx; + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 181, 49, 33 ) : color; + color = idx == 2.0 ? RGB( 230, 156, 33 ) : color; + color = idx == 3.0 ? RGB( 255, 255, 255 ) : color; +} + +void SpriteGround( inout vec3 color, float x, float y ) +{ + float idx = 0.0; + idx = y == 15.0 ? ( x <= 7.0 ? 65534.0 : 49127.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 43691.0 : 27318.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 43691.0 : 27318.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43691.0 : 27318.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 43691.0 : 27254.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 43691.0 : 38246.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 43691.0 : 32758.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 43691.0 : 27318.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 43691.0 : 27318.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 43691.0 : 27318.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 43685.0 : 27309.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 43615.0 : 27309.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 22011.0 : 27307.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 32683.0 : 27307.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 27307.0 : 23211.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 38230.0 : 38231.0 ) : idx; + + idx = SPRITE_DEC( x, idx ); + + color = RGB( 0, 0, 0 ); + color = idx == 2.0 ? RGB( 231, 90, 16 ) : color; + color = idx == 3.0 ? RGB( 247, 214, 181 ) : color; +} + +void SpriteFlagpoleEnd( inout vec3 color, float x, float y ) +{ + float idx = 0.0; + + idx = y == 7.0 ? 1360.0 : idx; + idx = y == 6.0 ? 6836.0 : idx; + idx = y == 5.0 ? 27309.0 : idx; + idx = y == 4.0 ? 27309.0 : idx; + idx = y == 3.0 ? 27305.0 : idx; + idx = y == 2.0 ? 27305.0 : idx; + idx = y == 1.0 ? 6820.0 : idx; + idx = y == 0.0 ? 1360.0 : idx; + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 0, 173, 0 ) : color; + color = idx == 3.0 ? RGB( 189, 255, 24 ) : color; +} + +void SpriteMario( inout vec3 color, float x, float y, float frame ) +{ + float idx = 0.0; + + if ( frame == 0.0 ) + { + idx = y == 14.0 ? ( x <= 7.0 ? 40960.0 : 42.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 43008.0 : 2730.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 21504.0 : 223.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 56576.0 : 4063.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 23808.0 : 16255.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 62720.0 : 1375.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 61440.0 : 1023.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 21504.0 : 793.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 22272.0 : 4053.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 23488.0 : 981.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 43328.0 : 170.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 43584.0 : 170.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 10832.0 : 42.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 16400.0 : 5.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 16384.0 : 21.0 ) : idx; + } + else if ( frame == 1.0 ) + { + idx = y == 15.0 ? ( x <= 7.0 ? 43008.0 : 10.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 43520.0 : 682.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 54528.0 : 55.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 63296.0 : 1015.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 55104.0 : 4063.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 64832.0 : 343.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 64512.0 : 255.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 25856.0 : 5.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 38208.0 : 22.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 42304.0 : 235.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 38208.0 : 170.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 62848.0 : 171.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 62976.0 : 42.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 43008.0 : 21.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 21504.0 : 85.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 21504.0 : 1.0 ) : idx; + } + else if ( frame == 2.0 ) + { + idx = y == 15.0 ? ( x <= 7.0 ? 43008.0 : 10.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 43520.0 : 682.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 54528.0 : 55.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 63296.0 : 1015.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 55104.0 : 4063.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 64832.0 : 343.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 64512.0 : 255.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 42320.0 : 5.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 42335.0 : 16214.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 58687.0 : 15722.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 43535.0 : 1066.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 43648.0 : 1450.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 43680.0 : 1450.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 2708.0 : 1448.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 84.0 : 0.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 336.0 : 0.0 ) : idx; + } + else if ( frame == 3.0 ) + { + idx = y == 15.0 ? ( x <= 7.0 ? 0.0 : 64512.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 40960.0 : 64554.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 43008.0 : 64170.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 21504.0 : 21727.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 56576.0 : 22495.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 23808.0 : 32639.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 62720.0 : 5471.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 61440.0 : 2047.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 38224.0 : 405.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 21844.0 : 16982.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 21855.0 : 17066.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 39487.0 : 23470.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 43596.0 : 23210.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 43344.0 : 23210.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 43604.0 : 42.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 43524.0 : 0.0 ) : idx; + } + else if ( frame == 4.0 ) + { + idx = y == 29.0 ? ( x <= 7.0 ? 32768.0 : 170.0 ) : idx; + idx = y == 28.0 ? ( x <= 7.0 ? 43008.0 : 234.0 ) : idx; + idx = y == 27.0 ? ( x <= 7.0 ? 43520.0 : 250.0 ) : idx; + idx = y == 26.0 ? ( x <= 7.0 ? 43520.0 : 10922.0 ) : idx; + idx = y == 25.0 ? ( x <= 7.0 ? 54528.0 : 1015.0 ) : idx; + idx = y == 24.0 ? ( x <= 7.0 ? 57152.0 : 16343.0 ) : idx; + idx = y == 23.0 ? ( x <= 7.0 ? 24384.0 : 65535.0 ) : idx; + idx = y == 22.0 ? ( x <= 7.0 ? 24400.0 : 65407.0 ) : idx; + idx = y == 21.0 ? ( x <= 7.0 ? 65360.0 : 5463.0 ) : idx; + idx = y == 20.0 ? ( x <= 7.0 ? 64832.0 : 5471.0 ) : idx; + idx = y == 19.0 ? ( x <= 7.0 ? 62464.0 : 4095.0 ) : idx; + idx = y == 18.0 ? ( x <= 7.0 ? 43264.0 : 63.0 ) : idx; + idx = y == 17.0 ? ( x <= 7.0 ? 22080.0 : 6.0 ) : idx; + idx = y == 16.0 ? ( x <= 7.0 ? 22080.0 : 25.0 ) : idx; + idx = y == 15.0 ? ( x <= 7.0 ? 22096.0 : 4005.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 22160.0 : 65365.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 23184.0 : 65365.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 23168.0 : 64853.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 27264.0 : 64853.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 43648.0 : 598.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 43648.0 : 682.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 43648.0 : 426.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 43605.0 : 2666.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 43605.0 : 2710.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 43605.0 : 681.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 10837.0 : 680.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 85.0 : 340.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 5.0 : 340.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 1.0 : 5460.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 0.0 : 5460.0 ) : idx; + } + else if ( frame == 5.0 ) + { + idx = y == 30.0 ? ( x <= 7.0 ? 40960.0 : 42.0 ) : idx; + idx = y == 29.0 ? ( x <= 7.0 ? 43520.0 : 58.0 ) : idx; + idx = y == 28.0 ? ( x <= 7.0 ? 43648.0 : 62.0 ) : idx; + idx = y == 27.0 ? ( x <= 7.0 ? 43648.0 : 2730.0 ) : idx; + idx = y == 26.0 ? ( x <= 7.0 ? 62784.0 : 253.0 ) : idx; + idx = y == 25.0 ? ( x <= 7.0 ? 63440.0 : 4085.0 ) : idx; + idx = y == 24.0 ? ( x <= 7.0 ? 55248.0 : 16383.0 ) : idx; + idx = y == 23.0 ? ( x <= 7.0 ? 55252.0 : 16351.0 ) : idx; + idx = y == 22.0 ? ( x <= 7.0 ? 65492.0 : 1365.0 ) : idx; + idx = y == 21.0 ? ( x <= 7.0 ? 65360.0 : 1367.0 ) : idx; + idx = y == 20.0 ? ( x <= 7.0 ? 64832.0 : 1023.0 ) : idx; + idx = y == 19.0 ? ( x <= 7.0 ? 43520.0 : 15.0 ) : idx; + idx = y == 18.0 ? ( x <= 7.0 ? 38464.0 : 22.0 ) : idx; + idx = y == 17.0 ? ( x <= 7.0 ? 21904.0 : 26.0 ) : idx; + idx = y == 16.0 ? ( x <= 7.0 ? 21904.0 : 90.0 ) : idx; + idx = y == 15.0 ? ( x <= 7.0 ? 21904.0 : 106.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 21904.0 : 125.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 21904.0 : 255.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 21920.0 : 767.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 22176.0 : 2815.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 23200.0 : 2751.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 43680.0 : 2725.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 43648.0 : 661.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 27136.0 : 341.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 23040.0 : 85.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 26624.0 : 21.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 41984.0 : 86.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 21504.0 : 81.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 21760.0 : 1.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 21760.0 : 21.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 20480.0 : 21.0 ) : idx; + } + else if ( frame == 6.0 ) + { + idx = y == 31.0 ? ( x <= 7.0 ? 40960.0 : 42.0 ) : idx; + idx = y == 30.0 ? ( x <= 7.0 ? 43520.0 : 58.0 ) : idx; + idx = y == 29.0 ? ( x <= 7.0 ? 43648.0 : 62.0 ) : idx; + idx = y == 28.0 ? ( x <= 7.0 ? 43648.0 : 2730.0 ) : idx; + idx = y == 27.0 ? ( x <= 7.0 ? 62784.0 : 253.0 ) : idx; + idx = y == 26.0 ? ( x <= 7.0 ? 63440.0 : 4085.0 ) : idx; + idx = y == 25.0 ? ( x <= 7.0 ? 55248.0 : 16383.0 ) : idx; + idx = y == 24.0 ? ( x <= 7.0 ? 55252.0 : 16351.0 ) : idx; + idx = y == 23.0 ? ( x <= 7.0 ? 65492.0 : 1365.0 ) : idx; + idx = y == 22.0 ? ( x <= 7.0 ? 65364.0 : 1367.0 ) : idx; + idx = y == 21.0 ? ( x <= 7.0 ? 64832.0 : 1023.0 ) : idx; + idx = y == 20.0 ? ( x <= 7.0 ? 21504.0 : 15.0 ) : idx; + idx = y == 19.0 ? ( x <= 7.0 ? 43520.0 : 12325.0 ) : idx; + idx = y == 18.0 ? ( x <= 7.0 ? 38208.0 : 64662.0 ) : idx; + idx = y == 17.0 ? ( x <= 7.0 ? 21840.0 : 64922.0 ) : idx; + idx = y == 16.0 ? ( x <= 7.0 ? 21844.0 : 65114.0 ) : idx; + idx = y == 15.0 ? ( x <= 7.0 ? 21844.0 : 30298.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 38228.0 : 5722.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 42325.0 : 1902.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43605.0 : 682.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 44031.0 : 682.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 44031.0 : 17066.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 43775.0 : 21162.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 43772.0 : 21866.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 43392.0 : 21866.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 42640.0 : 21866.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 23189.0 : 21866.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 43605.0 : 21824.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 2389.0 : 0.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 84.0 : 0.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 84.0 : 0.0 ) : idx; + idx = y == 0.0 ? ( x <= 7.0 ? 336.0 : 0.0 ) : idx; + } + else + { + idx = y == 31.0 ? ( x <= 7.0 ? 0.0 : 16128.0 ) : idx; + idx = y == 30.0 ? ( x <= 7.0 ? 0.0 : 63424.0 ) : idx; + idx = y == 29.0 ? ( x <= 7.0 ? 40960.0 : 55274.0 ) : idx; + idx = y == 28.0 ? ( x <= 7.0 ? 43520.0 : 65514.0 ) : idx; + idx = y == 27.0 ? ( x <= 7.0 ? 43648.0 : 21866.0 ) : idx; + idx = y == 26.0 ? ( x <= 7.0 ? 43648.0 : 23210.0 ) : idx; + idx = y == 25.0 ? ( x <= 7.0 ? 62784.0 : 22013.0 ) : idx; + idx = y == 24.0 ? ( x <= 7.0 ? 63440.0 : 24573.0 ) : idx; + idx = y == 23.0 ? ( x <= 7.0 ? 55248.0 : 32767.0 ) : idx; + idx = y == 22.0 ? ( x <= 7.0 ? 55248.0 : 32735.0 ) : idx; + idx = y == 21.0 ? ( x <= 7.0 ? 65492.0 : 5461.0 ) : idx; + idx = y == 20.0 ? ( x <= 7.0 ? 64852.0 : 7511.0 ) : idx; + idx = y == 19.0 ? ( x <= 7.0 ? 64832.0 : 6143.0 ) : idx; + idx = y == 18.0 ? ( x <= 7.0 ? 43520.0 : 5477.0 ) : idx; + idx = y == 17.0 ? ( x <= 7.0 ? 38228.0 : 1382.0 ) : idx; + idx = y == 16.0 ? ( x <= 7.0 ? 21845.0 : 1430.0 ) : idx; + idx = y == 15.0 ? ( x <= 7.0 ? 21845.0 : 410.0 ) : idx; + idx = y == 14.0 ? ( x <= 7.0 ? 22005.0 : 602.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 38909.0 : 874.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43007.0 : 686.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 44031.0 : 682.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 43763.0 : 17066.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 43708.0 : 21162.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 43648.0 : 21930.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 43584.0 : 21930.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 42389.0 : 21930.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 23189.0 : 21930.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 43669.0 : 21920.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 43669.0 : 0.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 10901.0 : 0.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 5.0 : 0.0 ) : idx; + } + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 106, 107, 4 ) : color; + color = idx == 2.0 ? RGB( 177, 52, 37 ) : color; + color = idx == 3.0 ? RGB( 227, 157, 37 ) : color; +} + +void SpriteCoin( inout vec3 color, float x, float y, float frame ) +{ + float idx = 0.0; + if ( frame == 0.0 ) + { + idx = y == 14.0 ? ( x <= 7.0 ? 32768.0 : 1.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 32768.0 : 1.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 28672.0 : 5.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 28672.0 : 5.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 24576.0 : 5.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 32768.0 : 1.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 32768.0 : 1.0 ) : idx; + } + else if ( frame == 1.0 ) + { + idx = y == 14.0 ? ( x <= 7.0 ? 32768.0 : 2.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 40960.0 : 10.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 43008.0 : 42.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 59392.0 : 41.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 47616.0 : 166.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 47616.0 : 166.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 47616.0 : 166.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 47616.0 : 166.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 47616.0 : 166.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 47616.0 : 166.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 59392.0 : 41.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 43008.0 : 42.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 40960.0 : 10.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 32768.0 : 2.0 ) : idx;; + } + else if ( frame == 2.0 ) + { + idx = y == 14.0 ? ( x <= 7.0 ? 49152.0 : 1.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 49152.0 : 1.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 61440.0 : 7.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 49152.0 : 1.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 49152.0 : 1.0 ) : idx; + } + else + { + idx = y == 14.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 13.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 12.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 11.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 10.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 9.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 8.0 ? ( x <= 7.0 ? 0.0 : 3.0 ) : idx; + idx = y == 7.0 ? ( x <= 7.0 ? 0.0 : 3.0 ) : idx; + idx = y == 6.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 5.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 4.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 3.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 2.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + idx = y == 1.0 ? ( x <= 7.0 ? 0.0 : 2.0 ) : idx; + } + + idx = SPRITE_DEC( x, idx ); + + color = idx == 1.0 ? RGB( 181, 49, 33 ) : color; + color = idx == 2.0 ? RGB( 230, 156, 33 ) : color; + color = idx == 3.0 ? RGB( 255, 255, 255 ) : color; +} + +void SpriteBrick( inout vec3 color, float x, float y ) +{ + float ymod4 = floor( mod( y, 4.0 ) ); + float xmod8 = floor( mod( x, 8.0 ) ); + float ymod8 = floor( mod( y, 8.0 ) ); + + // dark orange + float idx = 2.0; + + // black + idx = ymod4 == 0.0 ? 1.0 : idx; + idx = xmod8 == ( ymod8 < 4.0 ? 3.0 : 7.0 ) ? 1.0 : idx; + + // light orange + idx = y == 15.0 ? 3.0 : idx; + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 231, 90, 16 ) : color; + color = idx == 3.0 ? RGB( 247, 214, 181 ) : color; +} + +void DrawCastle( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 80.0 && y >= 0.0 && y < 80.0 ) + { + float ymod4 = mod( y, 4.0 ); + float xmod8 = mod( x, 8.0 ); + float xmod16_4 = mod( x + 4.0, 16.0 ); + float xmod16_3 = mod( x + 5.0, 16.0 ); + float ymod8 = mod( y, 8.0 ); + + // dark orange + float idx = 2.0; + + // black + idx = ymod4 == 0.0 && y <= 72.0 && ( y != 44.0 || xmod16_3 > 8.0 ) ? 1.0 : idx; + idx = x >= 24.0 && x <= 32.0 && y >= 48.0 && y <= 64.0 ? 1.0 : idx; + idx = x >= 48.0 && x <= 56.0 && y >= 48.0 && y <= 64.0 ? 1.0 : idx; + idx = x >= 32.0 && x <= 47.0 && y <= 25.0 ? 1.0 : idx; + idx = xmod8 == ( ymod8 < 4.0 ? 3.0 : 7.0 ) && y <= 72.0 && ( xmod16_3 > 8.0 || y <= 40.0 || y >= 48.0 ) ? 1.0 : idx; + + // white + idx = y == ( xmod16_4 < 8.0 ? 47.0 : 40.0 ) ? 3.0 : idx; + idx = y == ( xmod16_4 < 8.0 ? 79.0 : 72.0 ) ? 3.0 : idx; + idx = xmod8 == 3.0 && y >= 40.0 && y <= 47.0 ? 3.0 : idx; + idx = xmod8 == 3.0 && y >= 72.0 ? 3.0 : idx; + + // transparent + idx = ( x < 16.0 || x >= 64.0 ) && y >= 48.0 ? 0.0 : idx; + idx = x >= 4.0 && x <= 10.0 && y >= 41.0 && y <= 47.0 ? 0.0 : idx; + idx = x >= 68.0 && x <= 74.0 && y >= 41.0 && y <= 47.0 ? 0.0 : idx; + idx = y >= 73.0 && xmod16_3 > 8.0 ? 0.0 : idx; + + color = idx == 1.0 ? RGB( 0, 0, 0 ) : color; + color = idx == 2.0 ? RGB( 231, 90, 16 ) : color; + color = idx == 3.0 ? RGB( 247, 214, 181 ) : color; + } +} + +void DrawKoopa( inout vec3 color, float x, float y, float frame ) +{ + if ( x >= 0.0 && x <= 15.0 ) + { + SpriteKoopa( color, x, y, frame ); + } +} + +void KoopaWalk( inout vec3 color, float worldX, float worldY, float time, float frame, float startX ) +{ + float x = worldX - startX + floor( time * GOOMBA_SPEED ); + DrawKoopa( color, x, worldY - 16.0, frame ); +} + +void DrawHitQuestion( inout vec3 color, float questionX, float questionY, float time, float questionT, float questionHitTime ) +{ + float t = clamp( ( time - questionHitTime ) / 0.25, 0.0, 1.0 ); + t = 1.0 - abs( 2.0 * t - 1.0 ); + + questionY -= floor( t * 8.0 ); + if ( questionX >= 0.0 && questionX <= 15.0 ) + { + if ( time >= questionHitTime ) + { + SpriteQuestion( color, questionX, questionY, 1.0 ); + if ( questionX >= 3.0 && questionX <= 12.0 && questionY >= 1.0 && questionY <= 15.0 ) + { + color = RGB( 231, 90, 16 ); + } + } + else + { + SpriteQuestion( color, questionX, questionY, questionT ); + } + } +} + +void DrawW( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + if ( ( x <= 3.0 || x >= 10.0 ) + || ( x >= 4.0 && x <= 5.0 && y >= 2.0 && y <= 7.0 ) + || ( x >= 8.0 && x <= 9.0 && y >= 2.0 && y <= 7.0 ) + || ( x >= 6.0 && x <= 7.0 && y >= 4.0 && y <= 9.0 ) + ) + { + color = RGB( 255, 255, 255 ); + } + } +} + +void DrawO( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + if ( ( x <= 1.0 || x >= 12.0 ) && ( y >= 2.0 && y <= 11.0 ) + || ( x >= 2.0 && x <= 4.0 ) + || ( x >= 9.0 && x <= 11.0 ) + || ( y <= 1.0 || y >= 11.0 ) && ( x >= 2.0 && x <= 11.0 ) + ) + { + color = RGB( 255, 255, 255 ); + } + } +} + +void DrawR( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + if ( ( x <= 3.0 ) + || ( y >= 12.0 && x <= 11.0 ) + || ( x >= 10.0 && y >= 6.0 && y <= 11.0 ) + || ( x >= 8.0 && x <= 9.0 && y <= 7.0 ) + || ( x <= 9.0 && y >= 4.0 && y <= 5.0 ) + || ( x >= 8.0 && y <= 1.0 ) + || ( x >= 6.0 && x <= 11.0 && y >= 2.0 && y <= 3.0 ) + ) + { + color = RGB( 255, 255, 255 ); + } + } +} + +void DrawL( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + if ( x <= 3.0 || y <= 1.0 ) + { + color = RGB( 255, 255, 255 ); + } + } +} + +void DrawD( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + color = RGB( 255, 255, 255 ); + + if ( ( x >= 4.0 && x <= 7.0 && y >= 2.0 && y <= 11.0 ) + || ( x >= 8.0 && x <= 9.0 && y >= 4.0 && y <= 9.0 ) + || ( x >= 12.0 && ( y <= 3.0 || y >= 10.0 ) ) + || ( x >= 10.0 && ( y <= 1.0 || y >= 12.0 ) ) + ) + { + color = RGB( 0, 0, 0 ); + } + } +} + +void Draw1( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + if ( ( y <= 1.0 ) + || ( x >= 5.0 && x <= 8.0 ) + || ( x >= 3.0 && x <= 4.0 && y >= 10.0 && y <= 11.0 ) + ) + { + color = RGB( 255, 255, 255 ); + } + } +} + +void DrawM( inout vec3 color, float x, float y ) +{ + if ( x >= 0.0 && x < 14.0 && y >= 0.0 && y < 14.0 ) + { + if ( y >= 4.0 && y <= 7.0 ) + { + color = RGB( 255, 255, 255 ); + } + } +} + +void DrawIntro( inout vec3 color, float x, float y, float screenWidth, float screenHeight ) +{ + color = RGB( 0, 0, 0 ); + + float offset = 18.0; + float textX = floor( x - ( screenWidth - offset * 8.0 - 7.0 ) / 2.0 ); + float textY = floor( y - ( screenHeight - 7.0 ) / 2.0 - 16.0 * 2.0 ); + float marioX = textX - offset * 4.0; + float marioY = textY + 16.0 * 3.0; + + DrawW( color, textX - offset * 0.0, textY ); + DrawO( color, textX - offset * 1.0, textY ); + DrawR( color, textX - offset * 2.0, textY ); + DrawL( color, textX - offset * 3.0, textY ); + DrawD( color, textX - offset * 4.0, textY ); + Draw1( color, textX - offset * 6.0, textY ); + DrawM( color, textX - offset * 7.0, textY ); + Draw1( color, textX - offset * 8.0, textY ); + + if ( marioX >= 0.0 && marioX <= 15.0 ) + { + SpriteMario( color, marioX, marioY, 4.0 ); + } +} + +float CoinAnimY( float worldY, float time, float coinTime ) +{ + return worldY - 4.0 * 16.0 - floor( 64.0 * ( 1.0 - abs( 2.0 * ( clamp( ( time - coinTime ) / 0.8, 0.0, 1.0 ) ) - 1.0 ) ) ); +} + +float QuestionAnimY( float worldY, float time, float questionHitTime ) +{ + return worldY - 4.0 * 16.0 - floor( 8.0 * ( 1.0 - abs( 2.0 * clamp( ( time - questionHitTime ) / 0.25, 0.0, 1.0 ) - 1.0 ) ) ); +} + +float GoombaSWalkX( float worldX, float startX, float time, float goombaLifeTime ) +{ + return worldX + floor( min( time, goombaLifeTime ) * GOOMBA_SPEED ) - startX; +} + +void DrawGame( inout vec3 color, float time, float pixelX, float pixelY, float screenWidth, float screenHeight ) +{ + float mushroomPauseStart = 16.25; + float mushroomPauseLength = 2.0; + float flagPauseStart = 38.95; + float flagPauseLength = 1.5; + + float cameraP1 = clamp( time - mushroomPauseStart, 0.0, mushroomPauseLength ); + float cameraP2 = clamp( time - flagPauseStart, 0.0, flagPauseLength ); + float cameraX = floor( min( ( time - cameraP1 - cameraP2 ) * MARIO_SPEED - 240.0, 3152.0 ) ); + float worldX = pixelX + cameraX; + float worldY = pixelY - 8.0; + float tileX = floor( worldX / 16.0 ); + float tileY = floor( worldY / 16.0 ); + float tile2X = floor( worldX / 32.0 ); + float tile2Y = floor( worldY / 32.0 ); + float worldXMod16 = mod( worldX, 16.0 ); + float worldYMod16 = mod( worldY, 16.0 ); + + + // default background color + color = RGB( 92, 148, 252 ); + + + // draw hills + float bigHillX = mod( worldX, 768.0 ); + float smallHillX = mod( worldX - 240.0, 768.0 ); + float hillX = min( bigHillX, smallHillX ); + float hillY = worldY - ( smallHillX < bigHillX ? 0.0 : 16.0 ); + SpriteHill( color, hillX, hillY ); + + + // draw clouds and bushes + float sc1CloudX = mod( worldX - 296.0, 768.0 ); + float sc2CloudX = mod( worldX - 904.0, 768.0 ); + float mcCloudX = mod( worldX - 584.0, 768.0 ); + float lcCloudX = mod( worldX - 440.0, 768.0 ); + float scCloudX = min( sc1CloudX, sc2CloudX ); + float sbCloudX = mod( worldX - 376.0, 768.0 ); + float mbCloudX = mod( worldX - 664.0, 768.0 ); + float lbCloudX = mod( worldX - 184.0, 768.0 ); + float cCloudX = min( min( scCloudX, mcCloudX ), lcCloudX ); + float bCloudX = min( min( sbCloudX, mbCloudX ), lbCloudX ); + float sCloudX = min( scCloudX, sbCloudX ); + float mCloudX = min( mcCloudX, mbCloudX ); + float lCloudX = min( lcCloudX, lbCloudX ); + float cloudX = min( cCloudX, bCloudX ); + float isBush = bCloudX < cCloudX ? 1.0 : 0.0; + float cloudSeg = cloudX == sCloudX ? 0.0 : ( cloudX == mCloudX ? 1.0 : 2.0 ); + float cloudY = worldY - ( isBush == 1.0 ? 8.0 : ( ( cloudSeg == 0.0 && sc1CloudX < sc2CloudX ) || cloudSeg == 1.0 ? 168.0 : 152.0 ) ); + if ( cloudX >= 0.0 && cloudX < 32.0 + 16.0 * cloudSeg ) + { + if ( cloudSeg == 1.0 ) + { + cloudX = cloudX < 24.0 ? cloudX : cloudX - 16.0; + } + if ( cloudSeg == 2.0 ) + { + cloudX = cloudX < 24.0 ? cloudX : ( cloudX < 40.0 ? cloudX - 16.0 : cloudX - 32.0 ); + } + + SpriteCloud( color, cloudX, cloudY, isBush ); + } + + + + // draw flag pole + if ( worldX >= 3175.0 && worldX <= 3176.0 && worldY <= 176.0 ) + { + color = RGB( 189, 255, 24 ); + } + + // draw flag + float flagX = worldX - 3160.0; + float flagY = worldY - 159.0 + floor( 122.0 * clamp( ( time - 39.0 ) / 1.0, 0.0, 1.0 ) ); + if ( flagX >= 0.0 && flagX <= 15.0 ) + { + SpriteFlag( color, flagX, flagY ); + } + + // draw flagpole end + float flagpoleEndX = worldX - 3172.0; + float flagpoleEndY = worldY - 176.0; + if ( flagpoleEndX >= 0.0 && flagpoleEndX <= 7.0 ) + { + SpriteFlagpoleEnd( color, flagpoleEndX, flagpoleEndY ); + } + + + + // draw blocks + if ( ( tileX >= 134.0 && tileX < 138.0 && tileX - 132.0 > tileY ) + || ( tileX >= 140.0 && tileX < 144.0 && 145.0 - tileX > tileY ) + || ( tileX >= 148.0 && tileX < 153.0 && tileX - 146.0 > tileY && tileY < 5.0 ) + || ( tileX >= 155.0 && tileX < 159.0 && 160.0 - tileX > tileY ) + || ( tileX >= 181.0 && tileX < 190.0 && tileX - 179.0 > tileY && tileY < 9.0 ) + || ( tileX == 198.0 && tileY == 1.0 ) + ) + { + SpriteBlock( color, worldXMod16, worldYMod16 ); + } + + + // draw pipes + float pipeY = worldY - 16.0; + float pipeH = 0.0; + float pipeX = worldX - 179.0 * 16.0; + if ( pipeX < 0.0 ) + { + pipeX = worldX - 163.0 * 16.0; + pipeH = 0.0; + } + if ( pipeX < 0.0 ) + { + pipeX = worldX - 57.0 * 16.0; + pipeH = 2.0; + } + if ( pipeX < 0.0 ) + { + pipeX = worldX - 46.0 * 16.0; + pipeH = 2.0; + } + if ( pipeX < 0.0 ) + { + pipeX = worldX - 38.0 * 16.0; + pipeH = 1.0; + } + if ( pipeX < 0.0 ) + { + pipeX = worldX - 28.0 * 16.0; + pipeH = 0.0; + } + if ( pipeX >= 0.0 && pipeX <= 31.0 && pipeY >= 0.0 && pipeY <= 31.0 + pipeH * 16.0 ) + { + SpritePipe( color, pipeX, pipeY, pipeH ); + } + + + // draw mushroom + float mushroomStart = 15.7; + if ( time >= mushroomStart && time <= 17.0 ) + { + float jumpTime = 0.5; + + float mushroomX = worldX - 1248.0; + float mushroomY = worldY - 4.0 * 16.0; + if ( time >= mushroomStart ) + { + mushroomY = worldY - 4.0 * 16.0 - floor( 16.0 * clamp( ( time - mushroomStart ) / 0.5, 0.0, 1.0 ) ); + } + if ( time >= mushroomStart + 0.5 ) + { + mushroomX -= floor( MARIO_SPEED * ( time - mushroomStart - 0.5 ) ); + } + if ( time >= mushroomStart + 0.5 + 0.4 ) + { + mushroomY = mushroomY + floor( sin( ( ( time - mushroomStart - 0.5 - 0.4 ) ) * 3.14 ) * 4.0 * 16.0 ); + } + + if ( mushroomX >= 0.0 && mushroomX <= 15.0 ) + { + SpriteMushroom( color, mushroomX, mushroomY ); + } + } + + + // draw coins + float coinFrame = floor( mod( time * 12.0, 4.0 ) ); + float coinX = worldX - 2720.0; + float coinTime = 33.9; + float coinY = CoinAnimY( worldY, time, coinTime ); + if ( coinX < 0.0 ) + { + coinX = worldX - 1696.0; + coinTime = 22.4; + coinY = CoinAnimY( worldY, time, coinTime ); + } + if ( coinX < 0.0 ) + { + coinX = worldX - 352.0; + coinTime = 5.4; + coinY = CoinAnimY( worldY, time, coinTime ); + } + + if ( coinX >= 0.0 && coinX <= 15.0 && time >= coinTime + 0.1 ) + { + SpriteCoin( color, coinX, coinY, coinFrame ); + } + + + // draw questions + float questionT = clamp( sin( time * 6.0 ), 0.0, 1.0 ); + if ( ( tileY == 4.0 && ( tileX == 16.0 || tileX == 20.0 || tileX == 109.0 || tileX == 112.0 ) ) + || ( tileY == 8.0 && ( tileX == 21.0 || tileX == 94.0 || tileX == 109.0 ) ) + || ( tileY == 8.0 && ( tileX >= 129.0 && tileX <= 130.0 ) ) + ) + { + SpriteQuestion( color, worldXMod16, worldYMod16, questionT ); + } + + + // draw hitted questions + float questionHitTime = 33.9; + float questionX = worldX - 2720.0; + if ( questionX < 0.0 ) + { + questionHitTime = 22.4; + questionX = worldX - 1696.0; + } + if ( questionX < 0.0 ) + { + questionHitTime = 15.4; + questionX = worldX - 1248.0; + } + if ( questionX < 0.0 ) + { + questionHitTime = 5.3; + questionX = worldX - 352.0; + } + questionT = time >= questionHitTime ? 1.0 : questionT; + float questionY = QuestionAnimY( worldY, time, questionHitTime ); + if ( questionX >= 0.0 && questionX <= 15.0 ) + { + SpriteQuestion( color, questionX, questionY, questionT ); + } + if ( time >= questionHitTime && questionX >= 3.0 && questionX <= 12.0 && questionY >= 1.0 && questionY <= 15.0 ) + { + color = RGB( 231, 90, 16 ); + } + + + // draw bricks + if ( ( tileY == 4.0 && ( tileX == 19.0 || tileX == 21.0 || tileX == 23.0 || tileX == 77.0 || tileX == 79.0 || tileX == 94.0 || tileX == 118.0 || tileX == 168.0 || tileX == 169.0 || tileX == 171.0 ) ) + || ( tileY == 8.0 && ( tileX == 128.0 || tileX == 131.0 ) ) + || ( tileY == 8.0 && ( tileX >= 80.0 && tileX <= 87.0 ) ) + || ( tileY == 8.0 && ( tileX >= 91.0 && tileX <= 93.0 ) ) + || ( tileY == 4.0 && ( tileX >= 100.0 && tileX <= 101.0 ) ) + || ( tileY == 8.0 && ( tileX >= 121.0 && tileX <= 123.0 ) ) + || ( tileY == 4.0 && ( tileX >= 129.0 && tileX <= 130.0 ) ) + ) + { + SpriteBrick( color, worldXMod16, worldYMod16 ); + } + + + // draw castle flag + float castleFlagX = worldX - 3264.0; + float castleFlagY = worldY - 64.0 - floor( 32.0 * clamp( ( time - 44.6 ) / 1.0, 0.0, 1.0 ) ); + if ( castleFlagX > 0.0 && castleFlagX < 14.0 ) + { + SpriteCastleFlag( color, castleFlagX, castleFlagY ); + } + + DrawCastle( color, worldX - 3232.0, worldY - 16.0 ); + + // draw ground + if ( tileY <= 0.0 + && !( tileX >= 69.0 && tileX < 71.0 ) + && !( tileX >= 86.0 && tileX < 89.0 ) + && !( tileX >= 153.0 && tileX < 155.0 ) + ) + { + SpriteGround( color, worldXMod16, worldYMod16 ); + } + + + // draw Koopa + float goombaFrame = floor( mod( time * 5.0, 2.0 ) ); + KoopaWalk( color, worldX, worldY, time, goombaFrame, 2370.0 ); + + + // draw stomped walking Goombas + float goombaY = worldY - 16.0; + float goombaLifeTime = 26.3; + float goombaX = GoombaSWalkX( worldX, 2850.0 + 24.0, time, goombaLifeTime ); + if ( goombaX < 0.0 ) + { + goombaLifeTime = 25.3; + goombaX = GoombaSWalkX( worldX, 2760.0, time, goombaLifeTime ); + } + if ( goombaX < 0.0 ) + { + goombaLifeTime = 23.5; + goombaX = GoombaSWalkX( worldX, 2540.0, time, goombaLifeTime ); + } + if ( goombaX < 0.0 ) + { + goombaLifeTime = 20.29; + goombaX = GoombaSWalkX( worldX, 2150.0, time, goombaLifeTime ); + } + if ( goombaX < 0.0 ) + { + goombaLifeTime = 10.3; + goombaX = worldX - 790.0 - floor( abs( mod( ( min( time, goombaLifeTime ) + 6.3 ) * GOOMBA_SPEED, 2.0 * 108.0 ) - 108.0 ) ); + } + goombaFrame = time > goombaLifeTime ? 2.0 : goombaFrame; + if ( goombaX >= 0.0 && goombaX <= 15.0 ) + { + SpriteGoomba( color, goombaX, goombaY, goombaFrame ); + } + + // draw walking Goombas + goombaFrame = floor( mod( time * 5.0, 2.0 ) ); + float goombaWalkX = worldX + floor( time * GOOMBA_SPEED ); + goombaX = goombaWalkX - 3850.0 - 24.0; + if ( goombaX < 0.0 ) goombaX = goombaWalkX - 3850.0; + if ( goombaX < 0.0 ) goombaX = goombaWalkX - 2850.0; + if ( goombaX < 0.0 ) goombaX = goombaWalkX - 2760.0 - 24.0; + if ( goombaX < 0.0 ) goombaX = goombaWalkX - 2540.0 - 24.0; + if ( goombaX < 0.0 ) goombaX = goombaWalkX - 2150.0 - 24.0; + if ( goombaX < 0.0 ) goombaX = worldX - 766.0 - floor( abs( mod( ( time + 6.3 ) * GOOMBA_SPEED, 2.0 * 108.0 ) - 108.0 ) ); + if ( goombaX < 0.0 ) goombaX = worldX - 638.0 - floor( abs( mod( ( time + 6.6 ) * GOOMBA_SPEED, 2.0 * 84.0 ) - 84.0 ) ); + if ( goombaX < 0.0 ) goombaX = goombaWalkX - 435.0; + if ( goombaX >= 0.0 && goombaX <= 15.0 ) + { + SpriteGoomba( color, goombaX, goombaY, goombaFrame ); + } + + + + // Mario jump + float marioBigJump1 = 27.1; + float marioBigJump2 = 29.75; + float marioBigJump3 = 35.05; + float marioJumpTime = 0.0; + float marioJumpScale = 0.0; + + if ( time >= 4.2 ) { marioJumpTime = 4.2; marioJumpScale = 0.45; } + if ( time >= 5.0 ) { marioJumpTime = 5.0; marioJumpScale = 0.5; } + if ( time >= 6.05 ) { marioJumpTime = 6.05; marioJumpScale = 0.7; } + if ( time >= 7.8 ) { marioJumpTime = 7.8; marioJumpScale = 0.8; } + if ( time >= 9.0 ) { marioJumpTime = 9.0; marioJumpScale = 1.0; } + if ( time >= 10.3 ) { marioJumpTime = 10.3; marioJumpScale = 0.3; } + if ( time >= 11.05 ) { marioJumpTime = 11.05; marioJumpScale = 1.0; } + if ( time >= 13.62 ) { marioJumpTime = 13.62; marioJumpScale = 0.45; } + if ( time >= 15.1 ) { marioJumpTime = 15.1; marioJumpScale = 0.5; } + if ( time >= 18.7 ) { marioJumpTime = 18.7; marioJumpScale = 0.6; } + if ( time >= 19.65 ) { marioJumpTime = 19.65; marioJumpScale = 0.45; } + if ( time >= 20.29 ) { marioJumpTime = 20.29; marioJumpScale = 0.3; } + if ( time >= 21.8 ) { marioJumpTime = 21.8; marioJumpScale = 0.35; } + if ( time >= 22.3 ) { marioJumpTime = 22.3; marioJumpScale = 0.35; } + if ( time >= 23.0 ) { marioJumpTime = 23.0; marioJumpScale = 0.40; } + if ( time >= 23.5 ) { marioJumpTime = 23.5; marioJumpScale = 0.3; } + if ( time >= 24.7 ) { marioJumpTime = 24.7; marioJumpScale = 0.45; } + if ( time >= 25.3 ) { marioJumpTime = 25.3; marioJumpScale = 0.3; } + if ( time >= 25.75 ) { marioJumpTime = 25.75; marioJumpScale = 0.4; } + if ( time >= 26.3 ) { marioJumpTime = 26.3; marioJumpScale = 0.25; } + if ( time >= marioBigJump1 ) { marioJumpTime = marioBigJump1; marioJumpScale = 1.0; } + if ( time >= marioBigJump1 + 1.0 ) { marioJumpTime = marioBigJump1 + 1.0; marioJumpScale = 0.6; } + if ( time >= marioBigJump2 ) { marioJumpTime = marioBigJump2; marioJumpScale = 1.0; } + if ( time >= marioBigJump2 + 1.0 ) { marioJumpTime = marioBigJump2 + 1.0; marioJumpScale = 0.6; } + if ( time >= 32.3 ) { marioJumpTime = 32.3; marioJumpScale = 0.7; } + if ( time >= 33.7 ) { marioJumpTime = 33.7; marioJumpScale = 0.3; } + if ( time >= 34.15 ) { marioJumpTime = 34.15; marioJumpScale = 0.45; } + if ( time >= marioBigJump3 ) { marioJumpTime = marioBigJump3; marioJumpScale = 1.0; } + if ( time >= marioBigJump3 + 1.2 ) { marioJumpTime = marioBigJump3 + 1.2; marioJumpScale = 0.89; } + if ( time >= marioBigJump3 + 1.2 + 0.75 ) { marioJumpTime = marioBigJump3 + 1.2 + 0.75; marioJumpScale = 0.5; } + + float marioJumpOffset = 0.0; + float marioJumpLength = 1.5 * marioJumpScale; + float marioJumpAmplitude = 76.0 * marioJumpScale; + if ( time >= marioJumpTime && time <= marioJumpTime + marioJumpLength ) + { + float t = ( time - marioJumpTime ) / marioJumpLength; + marioJumpOffset = floor( sin( t * 3.14 ) * marioJumpAmplitude ); + } + + + // Mario land + float marioLandTime = 0.0; + float marioLandAplitude = 0.0; + if ( time >= marioBigJump1 + 1.0 + 0.45 ) { marioLandTime = marioBigJump1 + 1.0 + 0.45; marioLandAplitude = 109.0; } + if ( time >= marioBigJump2 + 1.0 + 0.45 ) { marioLandTime = marioBigJump2 + 1.0 + 0.45; marioLandAplitude = 109.0; } + if ( time >= marioBigJump3 + 1.2 + 0.75 + 0.375 ) { marioLandTime = marioBigJump3 + 1.2 + 0.75 + 0.375; marioLandAplitude = 150.0; } + + float marioLandLength = marioLandAplitude / 120.0; + if ( time >= marioLandTime && time <= marioLandTime + marioLandLength ) + { + float t = 0.5 * ( time - marioLandTime ) / marioLandLength + 0.5; + marioJumpOffset = floor( sin( t * 3.14 ) * marioLandAplitude ); + } + + + // Mario flag jump + marioJumpTime = flagPauseStart - 0.3; + marioJumpLength = 1.5 * 0.45; + marioJumpAmplitude = 76.0 * 0.45; + if ( time >= marioJumpTime && time <= marioJumpTime + marioJumpLength + flagPauseLength ) + { + float time2 = time; + if ( time >= flagPauseStart && time <= flagPauseStart + flagPauseLength ) + { + time2 = flagPauseStart; + } + else if ( time >= flagPauseStart ) + { + time2 = time - flagPauseLength; + } + float t = ( time2 - marioJumpTime ) / marioJumpLength; + marioJumpOffset = floor( sin( t * 3.14 ) * marioJumpAmplitude ); + } + + + // Mario base (ground offset) + float marioBase = 0.0; + if ( time >= marioBigJump1 + 1.0 && time < marioBigJump1 + 1.0 + 0.45 ) + { + marioBase = 16.0 * 4.0; + } + if ( time >= marioBigJump2 + 1.0 && time < marioBigJump2 + 1.0 + 0.45 ) + { + marioBase = 16.0 * 4.0; + } + if ( time >= marioBigJump3 + 1.2 && time < marioBigJump3 + 1.2 + 0.75 ) + { + marioBase = 16.0 * 3.0; + } + if ( time >= marioBigJump3 + 1.2 + 0.75 && time < marioBigJump3 + 1.2 + 0.75 + 0.375 ) + { + marioBase = 16.0 * 7.0; + } + + float marioX = pixelX - 112.0; + float marioY = pixelY - 16.0 - 8.0 - marioBase - marioJumpOffset; + float marioFrame = marioJumpOffset == 0.0 ? floor( mod( time * 10.0, 3.0 ) ) : 3.0; + if ( time >= mushroomPauseStart && time <= mushroomPauseStart + mushroomPauseLength ) + { + marioFrame = 1.0; + } + if ( time > mushroomPauseStart + 0.7 ) + { + float t = time - mushroomPauseStart - 0.7; + if ( mod( t, 0.2 ) <= mix( 0.0, 0.2, clamp( t / 1.3, 0.0, 1.0 ) ) ) + { + // super mario offset + marioFrame += 4.0; + } + } + if ( marioX >= 0.0 && marioX <= 15.0 && cameraX < 3152.0 ) + { + SpriteMario( color, marioX, marioY, marioFrame ); + } +} + +vec2 CRTCurveUV( vec2 uv ) +{ + uv = uv * 2.0 - 1.0; + vec2 offset = abs( uv.yx ) / vec2( 6.0, 4.0 ); + uv = uv + uv * offset * offset; + uv = uv * 0.5 + 0.5; + return uv; +} + +void DrawVignette( inout vec3 color, vec2 uv ) +{ + float vignette = uv.x * uv.y * ( 1.0 - uv.x ) * ( 1.0 - uv.y ); + vignette = clamp( pow( 16.0 * vignette, 0.3 ), 0.0, 1.0 ); + color *= vignette; +} + +void DrawScanline( inout vec3 color, vec2 uv ) +{ + float scanline = clamp( 0.95 + 0.05 * cos( 3.14 * ( uv.y + 0.008 * iTime ) * 240.0 * 1.0 ), 0.0, 1.0 ); + float grille = 0.85 + 0.15 * clamp( 1.5 * cos( 3.14 * uv.x * 640.0 * 1.0 ), 0.0, 1.0 ); + color *= scanline * grille * 1.2; +} + +void main() +{ + // we want to see at least 224x192 (overscan) and we want multiples of pixel size + float resMultX = floor( iResolution.x / 224.0 ); + float resMultY = floor( iResolution.y / 192.0 ); + float resRcp = 1.0 / max( min( resMultX, resMultY ), 1.0 ); + + float time = iTime; + float screenWidth = floor( iResolution.x * resRcp ); + float screenHeight = floor( iResolution.y * resRcp ); + float pixelX = floor( gl_FragCoord.x * resRcp ); + float pixelY = floor( gl_FragCoord.y * resRcp ); + + vec3 color = RGB( 92, 148, 252 ); + DrawGame( color, time, pixelX, pixelY, screenWidth, screenHeight ); + if ( time < INTRO_LENGTH ) + { + DrawIntro( color, pixelX, pixelY, screenWidth, screenHeight ); + } + + + // CRT effects (curvature, vignette, scanlines and CRT grille) + vec2 uv = gl_FragCoord.xy / iResolution.xy; + vec2 crtUV = CRTCurveUV( uv ); + if ( crtUV.x < 0.0 || crtUV.x > 1.0 || crtUV.y < 0.0 || crtUV.y > 1.0 ) + { + color = vec3( 0.0, 0.0, 0.0 ); + } + DrawVignette( color, crtUV ); + DrawScanline( color, uv ); + + fragColor.xyz = color; + fragColor.w = 1.0; +} \ No newline at end of file diff --git a/shaders/triangleRotation.frag b/shaders/triangleRotation.frag new file mode 100644 index 0000000..ed566ac --- /dev/null +++ b/shaders/triangleRotation.frag @@ -0,0 +1,57 @@ +#version 460 core +#include + +uniform vec2 uSize; +uniform float iTime; +vec2 iResolution; +out vec4 fragColor; + +#define PI 3.1415926535897932384626433832795 +#define TWO_PI 6.28318530718 + +#define SCALE_FACTOR 8 +#define TIME_SCALE 0.005 + +mat2 rotate2d(float _angle){ + return mat2(cos(_angle),-sin(_angle), + sin(_angle),cos(_angle)); +} + +void main(void) { + + iResolution = uSize; + vec2 fragCoord = gl_FragCoord.xy; // Get the fragment coordinates + vec2 center = 2.0 * vec2(fragCoord.xy - 0.5 * iResolution.xy) / iResolution.y; + + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = fragCoord/iResolution.xy; + + uv = uv * 2.0 - vec2(1.0); + + uv.y *= iResolution.y/iResolution.x; + vec2 pos = vec2(0.) - uv; + //pos += vec2(cos(iTime) - 0.04,sin(iTime)-0.04); + mat2 rotation = rotate2d(PI/2. * float(iTime/2)); + pos = rotation * pos; + + float len = length(pos); + float angle = atan(pos.x,pos.y); + + float r = TWO_PI/float(3); + + float d = cos(floor(.5+angle/r)*r-angle)*length(pos); + + + // Time varying pixel color + vec3 col = vec3(0.0); + col.g += (1.0 - step(0.2,d)); + // Output to screen + fragColor = vec4(col,1.0); + + // Triangle logic (adjust for size and position) + // if (center.y > -0.1 && // Smaller bound on the bottom + // center.x > -sqrt(1.0) * center.y - 0.001 && // Smaller bound on the left + // center.x < sqrt(1.0) * center.y + 0.1) { // Smaller bound on the right + // fragColor = vec4(1.0, 0.5, 0.2, 1.0); + // } +} diff --git a/shaders/vsync.frag b/shaders/vsync.frag new file mode 100644 index 0000000..65314f7 --- /dev/null +++ b/shaders/vsync.frag @@ -0,0 +1,20 @@ +// adapted from https://www.shadertoy.com/view/Xd3BWr + +uniform vec2 uSize; +uniform float iTime; +vec2 iResolution; +out vec4 fragColor; + +void main(void) { + + iResolution = uSize; + vec2 fragCoord = gl_FragCoord.xy; // Get the fragment coordinates + + // Normalized pixel coordinates (from 0 to 1) + vec2 uv = fragCoord/iResolution.xy; + + float c = uv.x * 2.0 + iTime*2.0; + c = mod(c, 1.0); + c = step(c, 0.5); + fragColor = vec4(0.0, 0.0, 0.0, c); +} diff --git a/shaders/water.frag b/shaders/water.frag new file mode 100644 index 0000000..d27c605 --- /dev/null +++ b/shaders/water.frag @@ -0,0 +1,193 @@ +uniform vec2 iResolution; +uniform float iTime; +float SEA_HEIGHT = 0.3; +vec2 iMouse = vec2(0); +out vec4 fragColor; + +// Ported from https://www.shadertoy.com/view/Ms2SD1 to Flutter + +const int NUM_STEPS = 8; +const float PI = 3.141592; +const float EPSILON = 1e-3; +#define EPSILON_NRM (0.1 / iResolution.x) +#define AA + +// sea +const int ITER_GEOMETRY = 3; +const int ITER_FRAGMENT = 5; +const float SEA_CHOPPY = 4.0; +const float SEA_SPEED = 0.8; +const float SEA_FREQ = 0.16; +const vec3 SEA_BASE = vec3(0.0,0.09,0.18); +const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6)*0.6; +#define SEA_TIME (1.0 + iTime * SEA_SPEED) +const mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); + +// math +mat3 fromEuler(vec3 ang) { + vec2 a1 = vec2(sin(ang.x),cos(ang.x)); + vec2 a2 = vec2(sin(ang.y),cos(ang.y)); + vec2 a3 = vec2(sin(ang.z),cos(ang.z)); + mat3 m; + m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x); + m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x); + m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y); + return m; +} +float hash( vec2 p ) { + float h = dot(p,vec2(127.1,311.7)); + return fract(sin(h)*43758.5453123); +} +float noise( in vec2 p ) { + vec2 i = floor( p ); + vec2 f = fract( p ); + vec2 u = f*f*(3.0-2.0*f); + return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), + hash( i + vec2(1.0,0.0) ), u.x), + mix( hash( i + vec2(0.0,1.0) ), + hash( i + vec2(1.0,1.0) ), u.x), u.y); +} + +// lighting +float diffuse(vec3 n,vec3 l,float p) { + return pow(dot(n,l) * 0.4 + 0.6,p); +} +float specular(vec3 n,vec3 l,vec3 e,float s) { + float nrm = (s + 8.0) / (PI * 8.0); + return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; +} + +// sky +vec3 getSkyColor(vec3 e) { + e.y = (max(e.y,0.0)*0.8+0.2)*0.8; + return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4) * 1.1; +} + +// sea +float sea_octave(vec2 uv, float choppy) { + uv += noise(uv); + vec2 wv = 1.0-abs(sin(uv)); + vec2 swv = abs(cos(uv)); + wv = mix(wv,swv,wv); + return pow(1.0-pow(wv.x * wv.y,0.65),choppy); +} + +float map(vec3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + vec2 uv = p.xz; uv.x *= 0.75; + + float d, h = 0.0; + for(int i = 0; i < ITER_GEOMETRY; i++) { + d = sea_octave((uv+SEA_TIME)*freq,choppy); + d += sea_octave((uv-SEA_TIME)*freq,choppy); + h += d * amp; + uv *= octave_m; freq *= 1.9; amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +float map_detailed(vec3 p) { + float freq = SEA_FREQ; + float amp = SEA_HEIGHT; + float choppy = SEA_CHOPPY; + vec2 uv = p.xz; uv.x *= 0.75; + + float d, h = 0.0; + for(int i = 0; i < ITER_FRAGMENT; i++) { + d = sea_octave((uv+SEA_TIME)*freq,choppy); + d += sea_octave((uv-SEA_TIME)*freq,choppy); + h += d * amp; + uv *= octave_m; freq *= 1.9; amp *= 0.22; + choppy = mix(choppy,1.0,0.2); + } + return p.y - h; +} + +vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) { + float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0); + fresnel = pow(fresnel,3.0) * 0.5; + + vec3 reflected = getSkyColor(reflect(eye,n)); + vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; + + vec3 color = mix(refracted,reflected,fresnel); + + float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); + color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten; + + color += vec3(specular(n,l,eye,60.0)); + + return color; +} + +// tracing +vec3 getNormal(vec3 p, float eps) { + vec3 n; + n.y = map_detailed(p); + n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y; + n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y; + n.y = eps; + return normalize(n); +} + +float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) { + float tm = 0.0; + float tx = 1000.0; + float hx = map(ori + dir * tx); + if(hx > 0.0) { + p = ori + dir * tx; + return tx; + } + float hm = map(ori + dir * tm); + float tmid = 0.0; + for(int i = 0; i < NUM_STEPS; i++) { + tmid = mix(tm,tx, hm/(hm-hx)); + p = ori + dir * tmid; + float hmid = map(p); + if(hmid < 0.0) { + tx = tmid; + hx = hmid; + } else { + tm = tmid; + hm = hmid; + } + } + return tmid; +} + +vec3 getPixel(in vec2 coord, float time) { + vec2 uv = coord / iResolution.xy; + uv = uv * 2.0 - 1.0; + uv.x *= iResolution.x / iResolution.y; + + // ray + vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time); + vec3 ori = vec3(0.0,3.5,time*5.0); + vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.14; + dir = normalize(dir) * fromEuler(ang); + + // tracing + vec3 p; + heightMapTracing(ori,dir,p); + vec3 dist = p - ori; + vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM); + vec3 light = normalize(vec3(0.0,1.0,0.8)); + + // color + return mix( + getSkyColor(dir), + getSeaColor(p,n,light,dir,dist), + pow(smoothstep(0.0,-0.02,dir.y),0.2)); +} + +void main() { + float time = iTime * 0.3; + + vec3 color = getPixel(gl_FragCoord.xy, time); + + // post + fragColor = vec4(pow(color,vec3(0.65)), 1.0); +} \ No newline at end of file diff --git a/test/widget_test.dart b/test/widget_test.dart new file mode 100644 index 0000000..5a9ef44 --- /dev/null +++ b/test/widget_test.dart @@ -0,0 +1,46 @@ +// This is a basic Flutter widget test. +// +// To perform an interaction with a widget in your test, use the WidgetTester +// utility in the flutter_test package. For example, you can send tap and scroll +// gestures. You can also use WidgetTester to find child widgets in the widget +// tree, read text, and verify that the values of widget properties are correct. + + + +// @GenerateMocks([Process, ProcessResult]) +void main() { + // testWidgets('Counter increments smoke test', (WidgetTester tester) async { + // // Build our app and trigger a frame. + // await tester.pumpWidget(const App()); + + // // Verify that our counter starts at 0. + // expect(find.text('0'), findsOneWidget); + // expect(find.text('1'), findsNothing); + + // // Tap the '+' icon and trigger a frame. + // await tester.tap(find.byIcon(Icons.add)); + // await tester.pump(); + + // // Verify that our counter has incremented. + // expect(find.text('0'), findsNothing); + // expect(find.text('1'), findsOneWidget); + // }); + + // group('SystemCpu', () { + // test('totalPercentageUsed', () async { + // final cpu = SystemCpu(); + // final stream = cpu.totalPercentageUsed(); + // final result = await stream.first; + // expect(result, isA()); + // }); + // }); + + // group('SystemMemory', () { + // test('totalPercentageUsed', () async { + // final memory = SystemMemory(); + // final stream = memory.freeMemory(); + // final result = await stream.first; + // expect(result, isA()); + // }); + // }); +}