diff --git a/.github/labeler.yml b/.github/labeler.yml index d4b5dba42..4eba0868c 100644 --- a/.github/labeler.yml +++ b/.github/labeler.yml @@ -63,6 +63,8 @@ - packages/tizen_package_manager/**/* "p: tizen_rpc_port": - packages/tizen_rpc_port/**/* +"p: tizen_window_manager": + - packages/tizen_window_manager/**/* "p: url_launcher": - packages/url_launcher/**/* "p: video_player": diff --git a/.github/recipe.yaml b/.github/recipe.yaml index 2cd04c327..236687241 100644 --- a/.github/recipe.yaml +++ b/.github/recipe.yaml @@ -15,6 +15,7 @@ plugins: tizen_bundle: ["tv-9.0"] tizen_package_manager: ["tv-9.0"] tizen_rpc_port: ["tv-9.0"] + tizen_window_manager: ["tv-9.0"] url_launcher: ["tv-9.0"] wakelock_plus: ["tv-9.0"] diff --git a/README.md b/README.md index d3af10f02..21b96012d 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina | [**tizen_notification**](packages/tizen_notification) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_notification.svg)](https://pub.dev/packages/tizen_notification) | N/A | | [**tizen_package_manager**](packages/tizen_package_manager) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_package_manager.svg)](https://pub.dev/packages/tizen_package_manager) | N/A | | [**tizen_rpc_port**](packages/tizen_rpc_port) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_rpc_port.svg)](https://pub.dev/packages/tizen_rpc_port) | N/A | +| [**tizen_window_manager**](packages/tizen_window_manager) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/tizen_window_manager.svg)](https://pub.dev/packages/tizen_window_manager) | N/A | | [**url_launcher_tizen**](packages/url_launcher) | [url_launcher](https://pub.dev/packages/url_launcher) (1st-party) | [![pub package](https://img.shields.io/pub/v/url_launcher_tizen.svg)](https://pub.dev/packages/url_launcher_tizen) | No | | [**video_player_avplay**](packages/video_player_avplay) | (Tizen-only) | [![pub package](https://img.shields.io/pub/v/video_player_avplay.svg)](https://pub.dev/packages/video_player_avplay) | N/A | | [**video_player_tizen**](packages/video_player) | [video_player](https://pub.dev/packages/video_player) (1st-party) | [![pub package](https://img.shields.io/pub/v/video_player_tizen.svg)](https://pub.dev/packages/video_player_tizen) | No | @@ -91,6 +92,7 @@ The _"non-endorsed"_ status means that the plugin is not endorsed by the origina | [**tizen_notification**](packages/tizen_notification) | ✔️ | ✔️ | ❌ | API not supported | | [**tizen_package_manager**](packages/tizen_package_manager) | ✔️ | ✔️ | ✔️ | | [**tizen_rpc_port**](packages/tizen_rpc_port) | ✔️ | ✔️ | ✔️ | +| [**tizen_window_manager**](packages/tizen_window_manager) | ✔️ | ✔️ | ✔️ | | [**url_launcher_tizen**](packages/url_launcher) | ✔️ | ❌ | ❌ | No browser app | | [**video_player_avplay**](packages/video_player_avplay) | ✔️ | ❌ | ❌ | See README for details | | [**video_player_tizen**](packages/video_player) | ✔️ | ❌ | ✔️ | TV emulator issue | diff --git a/packages/tizen_window_manager/.gitignore b/packages/tizen_window_manager/.gitignore new file mode 100644 index 000000000..a247422ef --- /dev/null +++ b/packages/tizen_window_manager/.gitignore @@ -0,0 +1,75 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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/ +.dart_tool/ +.flutter-plugins +.flutter-plugins-dependencies +.packages +.pub-cache/ +.pub/ +build/ + +# Android related +**/android/**/gradle-wrapper.jar +**/android/.gradle +**/android/captures/ +**/android/gradlew +**/android/gradlew.bat +**/android/local.properties +**/android/**/GeneratedPluginRegistrant.java + +# iOS/XCode related +**/ios/**/*.mode1v3 +**/ios/**/*.mode2v3 +**/ios/**/*.moved-aside +**/ios/**/*.pbxuser +**/ios/**/*.perspectivev3 +**/ios/**/*sync/ +**/ios/**/.sconsign.dblite +**/ios/**/.tags* +**/ios/**/.vagrant/ +**/ios/**/DerivedData/ +**/ios/**/Icon? +**/ios/**/Pods/ +**/ios/**/.symlinks/ +**/ios/**/profile +**/ios/**/xcuserdata +**/ios/.generated/ +**/ios/Flutter/App.framework +**/ios/Flutter/Flutter.framework +**/ios/Flutter/Flutter.podspec +**/ios/Flutter/Generated.xcconfig +**/ios/Flutter/ephemeral +**/ios/Flutter/app.flx +**/ios/Flutter/app.zip +**/ios/Flutter/flutter_assets/ +**/ios/Flutter/flutter_export_environment.sh +**/ios/ServiceDefinitions.json +**/ios/Runner/GeneratedPluginRegistrant.* + +# Exceptions to above rules. +!**/ios/**/default.mode1v3 +!**/ios/**/default.mode2v3 +!**/ios/**/default.pbxuser +!**/ios/**/default.perspectivev3 diff --git a/packages/tizen_window_manager/CHANGELOG.md b/packages/tizen_window_manager/CHANGELOG.md new file mode 100644 index 000000000..607323422 --- /dev/null +++ b/packages/tizen_window_manager/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.1.0 + +* Initial release. diff --git a/packages/tizen_window_manager/LICENSE b/packages/tizen_window_manager/LICENSE new file mode 100644 index 000000000..9b10b19e9 --- /dev/null +++ b/packages/tizen_window_manager/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2025 Samsung Electronics Co., Ltd. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * 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. + * Neither the name of the copyright holder nor the names of the + 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 OWNER 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/packages/tizen_window_manager/README.md b/packages/tizen_window_manager/README.md new file mode 100644 index 000000000..cf6324b13 --- /dev/null +++ b/packages/tizen_window_manager/README.md @@ -0,0 +1,48 @@ +# tizen_window_manager + + [![pub package](https://img.shields.io/pub/v/tizen_window_manager.svg)](https://pub.dev/packages/tizen_window_manager) + +Tizen window manager APIs. Used to control windows and get window geometry information. + +## Usage + +To use this package, add `tizen_window_manager` as a dependency in your `pubspec.yaml` file. + +```yaml +dependencies: + tizen_window_manager: ^0.1.0 +``` + +## API Reference + +The `WindowManager` class provides methods to control window behavior on the Tizen platform. + +### Controls windows + +Activates the window and brings it to the foreground. + +```dart +await WindowManager.activate(); +``` + +Lowers the window and sends it to the background. + +```dart +await WindowManager.lower(); +``` + +### Retrieving window geometry + +Gets the geometry(position and size) of the window. + +Returns a `Map` containing: +- `x`: The x coordinate of the window +- `y`: The y coordinate of the window +- `width`: The width of the window +- `height`: The height of the window + +```dart +Map geometry = await WindowManager.getGeometry(); +print('Window position: (${geometry['x']}, ${geometry['y']})'); +print('Window size: ${geometry['width']}x${geometry['height']}'); +``` diff --git a/packages/tizen_window_manager/example/.gitignore b/packages/tizen_window_manager/example/.gitignore new file mode 100644 index 000000000..0fa6b675c --- /dev/null +++ b/packages/tizen_window_manager/example/.gitignore @@ -0,0 +1,46 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ + +# 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/ + +# Web related +lib/generated_plugin_registrant.dart + +# 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 diff --git a/packages/tizen_window_manager/example/README.md b/packages/tizen_window_manager/example/README.md new file mode 100644 index 000000000..9183c8ee3 --- /dev/null +++ b/packages/tizen_window_manager/example/README.md @@ -0,0 +1,7 @@ +# tizen_window_manager_example + +Demonstrates how to use the tizen_window_manager plugin. + +## Getting Started + +To run this app on your Tizen device, use [flutter-tizen](https://github.com/flutter-tizen/flutter-tizen). diff --git a/packages/tizen_window_manager/example/integration_test/tizen_window_manager_test.dart b/packages/tizen_window_manager/example/integration_test/tizen_window_manager_test.dart new file mode 100644 index 000000000..0b9fd1ddf --- /dev/null +++ b/packages/tizen_window_manager/example/integration_test/tizen_window_manager_test.dart @@ -0,0 +1,32 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:tizen_window_manager/tizen_window_manager.dart'; + +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + testWidgets('Can get window geometry info', (WidgetTester tester) async { + final Map geometry = await WindowManager.getGeometry(); + expect(geometry['x'], 100); + expect(geometry['y'], 100); + expect(geometry['width'], 1000); + expect(geometry['height'], 700); + }); + + testWidgets('Control lower/activate window', (WidgetTester tester) async { + await WindowManager.lower(); + await Future.delayed(const Duration(seconds: 2)); + + expect(WidgetsBinding.instance.lifecycleState, AppLifecycleState.paused); + + await WindowManager.activate(); + await Future.delayed(const Duration(seconds: 2)); + + expect(WidgetsBinding.instance.lifecycleState, AppLifecycleState.resumed); + }); +} diff --git a/packages/tizen_window_manager/example/lib/main.dart b/packages/tizen_window_manager/example/lib/main.dart new file mode 100644 index 000000000..4ffd3f0df --- /dev/null +++ b/packages/tizen_window_manager/example/lib/main.dart @@ -0,0 +1,195 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ignore_for_file: public_member_api_docs + +import 'dart:async'; + +import 'package:flutter/material.dart'; +import 'package:tizen_window_manager/tizen_window_manager.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + Map? _geometry; + bool _isLoadingGeometry = false; + + Timer? _windowTimer; + + void _activateWindow() { + final AppLifecycleState currentLifecycleState = + WidgetsBinding.instance.lifecycleState!; + if (currentLifecycleState != AppLifecycleState.resumed) { + _windowTimer?.cancel(); + _windowTimer = Timer.periodic(const Duration(milliseconds: 500), ( + Timer timer, + ) { + final AppLifecycleState? currentState = + WidgetsBinding.instance.lifecycleState; + if (currentState == AppLifecycleState.resumed) { + timer.cancel(); + _windowTimer = null; + } else { + debugPrint( + 'AppLifecycleState is still not resumed (current: $currentState), calling activate window again', + ); + WindowManager.activate(); + } + }); + } else { + debugPrint( + 'AppLifecycleState is resumed, no need to start periodic calls'); + } + } + + void _lowerWindow() { + WindowManager.lower(); + debugPrint('Window lowered successfully'); + } + + void _lowerWindowAndActivateWindow() { + WindowManager.lower(); + debugPrint('Window lowered successfully'); + Future.delayed(const Duration(seconds: 3), () { + debugPrint('3 seconds passed, showing window again'); + _activateWindow(); + }); + } + + Future _getGeometry() async { + setState(() { + _isLoadingGeometry = true; + }); + + try { + final Map geometry = await WindowManager.getGeometry(); + debugPrint('Window geometry: $geometry'); + debugPrint('Position: (${geometry['x']}, ${geometry['y']})'); + debugPrint('Size: ${geometry['width']} x ${geometry['height']}'); + + setState(() { + _geometry = geometry; + _isLoadingGeometry = false; + }); + } catch (e) { + debugPrint('Error getting geometry: $e'); + setState(() { + _isLoadingGeometry = false; + }); + } + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Tizen Window Manager Example'), + backgroundColor: Colors.blue, + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + const SizedBox(height: 20), + const Text( + 'Tizen Window Manager API Test', + style: TextStyle( + fontSize: 24, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), + const SizedBox(height: 20), + const Text( + 'Window Control', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: _lowerWindow, + child: const Text('Call lower window.'), + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: _lowerWindowAndActivateWindow, + child: const Text( + 'Call lower window and call activate window 3 seconds later.'), + ), + const SizedBox(height: 10), + const Text( + 'Window Information', + style: TextStyle( + fontSize: 18, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 10), + if (_geometry != null) ...[ + Container( + padding: const EdgeInsets.all(16.0), + decoration: BoxDecoration( + color: Colors.grey[100], + borderRadius: BorderRadius.circular(8.0), + border: Border.all(color: Colors.grey[300]!), + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const Text( + 'Window Geometry:', + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 8), + Text( + 'Position: (${_geometry!['x']}, ${_geometry!['y']})', + style: const TextStyle(fontSize: 14), + ), + const SizedBox(height: 4), + Text( + 'Size: ${_geometry!['width']} x ${_geometry!['height']}', + style: const TextStyle(fontSize: 14), + ), + ], + ), + ), + ], + const SizedBox(height: 10), + ElevatedButton( + onPressed: _getGeometry, + child: _isLoadingGeometry + ? const SizedBox( + width: 16, + height: 16, + child: CircularProgressIndicator( + strokeWidth: 2, + valueColor: + AlwaysStoppedAnimation(Colors.white), + ), + ) + : const Text('Get Window Geometry'), + ), + ], + ), + ), + ), + ); + } +} diff --git a/packages/tizen_window_manager/example/pubspec.yaml b/packages/tizen_window_manager/example/pubspec.yaml new file mode 100644 index 000000000..2198f7604 --- /dev/null +++ b/packages/tizen_window_manager/example/pubspec.yaml @@ -0,0 +1,26 @@ +name: tizen_window_manager_example +description: Demonstrates how to use the tizen_window_manager plugin. +publish_to: "none" + +environment: + sdk: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + tizen_window_manager: + path: ../ + +dev_dependencies: + flutter_driver: + sdk: flutter + flutter_test: + sdk: flutter + integration_test: + sdk: flutter + integration_test_tizen: + path: ../../integration_test/ + +flutter: + uses-material-design: true diff --git a/packages/tizen_window_manager/example/test_driver/integration_test.dart b/packages/tizen_window_manager/example/test_driver/integration_test.dart new file mode 100644 index 000000000..b38629cca --- /dev/null +++ b/packages/tizen_window_manager/example/test_driver/integration_test.dart @@ -0,0 +1,3 @@ +import 'package:integration_test/integration_test_driver.dart'; + +Future main() => integrationDriver(); diff --git a/packages/tizen_window_manager/example/tizen/.gitignore b/packages/tizen_window_manager/example/tizen/.gitignore new file mode 100644 index 000000000..ac0738be2 --- /dev/null +++ b/packages/tizen_window_manager/example/tizen/.gitignore @@ -0,0 +1,12 @@ +flutter/ +.vs/ +*.user +bin/ +obj/ + +# Tizen Core CLI (tz) related files +tizen_dotnet_project.yaml +*.csproj.backup + +# Flutter-tizen dependency information file +.app.deps.json diff --git a/packages/tizen_window_manager/example/tizen/App.cs b/packages/tizen_window_manager/example/tizen/App.cs new file mode 100644 index 000000000..e9ca12765 --- /dev/null +++ b/packages/tizen_window_manager/example/tizen/App.cs @@ -0,0 +1,24 @@ +using Tizen.Flutter.Embedding; + +namespace Runner +{ + public class App : FlutterApplication + { + protected override void OnCreate() + { + WindowOffsetX = 100; + WindowOffsetY = 100; + WindowHeight = 700; + WindowWidth = 1000; + base.OnCreate(); + + GeneratedPluginRegistrant.RegisterPlugins(this); + } + + static void Main(string[] args) + { + var app = new App(); + app.Run(args); + } + } +} diff --git a/packages/tizen_window_manager/example/tizen/Runner.csproj b/packages/tizen_window_manager/example/tizen/Runner.csproj new file mode 100644 index 000000000..da93051cc --- /dev/null +++ b/packages/tizen_window_manager/example/tizen/Runner.csproj @@ -0,0 +1,19 @@ + + + + Exe + tizen80 + + + + + + + + + + %(RecursiveDir) + + + + diff --git a/packages/tizen_window_manager/example/tizen/shared/res/ic_launcher.png b/packages/tizen_window_manager/example/tizen/shared/res/ic_launcher.png new file mode 100644 index 000000000..4d6372eeb Binary files /dev/null and b/packages/tizen_window_manager/example/tizen/shared/res/ic_launcher.png differ diff --git a/packages/tizen_window_manager/example/tizen/tizen-manifest.xml b/packages/tizen_window_manager/example/tizen/tizen-manifest.xml new file mode 100644 index 000000000..585404a6c --- /dev/null +++ b/packages/tizen_window_manager/example/tizen/tizen-manifest.xml @@ -0,0 +1,10 @@ + + + + + + ic_launcher.png + + + + diff --git a/packages/tizen_window_manager/lib/tizen_window_manager.dart b/packages/tizen_window_manager/lib/tizen_window_manager.dart new file mode 100644 index 000000000..c66343506 --- /dev/null +++ b/packages/tizen_window_manager/lib/tizen_window_manager.dart @@ -0,0 +1,37 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/services.dart'; + +/// A plugin for managing windows on Tizen platform. +/// +/// This plugin provides methods to control window behavior such as +/// activating, raising, lowering, showing, hiding, and getting window geometry. +class WindowManager { + WindowManager._(); + + static const MethodChannel _channel = MethodChannel('tizen/window_manager'); + + /// Activates the window. + static Future activate() => _channel.invokeMethod('activate'); + + /// Lowers the window. + static Future lower() => _channel.invokeMethod('lower'); + + /// Gets the geometry (position and size) of the window. + /// + /// Returns a map containing: + /// - 'x': The x coordinate of the window + /// - 'y': The y coordinate of the window + /// - 'width': The width of the window + /// - 'height': The height of the window + static Future> getGeometry() async { + final dynamic result = await _channel.invokeMethod('getGeometry'); + if (result is Map) { + return Map.from(result); + } + throw Exception( + 'Unexpected result type from getGeometry: ${result.runtimeType}'); + } +} diff --git a/packages/tizen_window_manager/pubspec.yaml b/packages/tizen_window_manager/pubspec.yaml new file mode 100644 index 000000000..e75043d9d --- /dev/null +++ b/packages/tizen_window_manager/pubspec.yaml @@ -0,0 +1,20 @@ +name: tizen_window_manager +description: Tizen window APIs. +homepage: https://github.com/flutter-tizen/plugins +repository: https://github.com/flutter-tizen/plugins/tree/master/packages/tizen_window_manager +version: 0.1.0 + +environment: + sdk: ">=3.1.0 <4.0.0" + flutter: ">=3.13.0" + +dependencies: + flutter: + sdk: flutter + +flutter: + plugin: + platforms: + tizen: + pluginClass: TizenWindowManagerPlugin + fileName: tizen_window_manager_plugin.h diff --git a/packages/tizen_window_manager/tizen/.gitignore b/packages/tizen_window_manager/tizen/.gitignore new file mode 100644 index 000000000..a2a7d62b1 --- /dev/null +++ b/packages/tizen_window_manager/tizen/.gitignore @@ -0,0 +1,5 @@ +.cproject +.sign +crash-info/ +Debug/ +Release/ diff --git a/packages/tizen_window_manager/tizen/inc/tizen_window_manager_plugin.h b/packages/tizen_window_manager/tizen/inc/tizen_window_manager_plugin.h new file mode 100644 index 000000000..1615c8ae3 --- /dev/null +++ b/packages/tizen_window_manager/tizen/inc/tizen_window_manager_plugin.h @@ -0,0 +1,27 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_PLUGIN_TIZEN_WINDOW_MANAGER_PLUGIN_H_ +#define FLUTTER_PLUGIN_TIZEN_WINDOW_MANAGER_PLUGIN_H_ + +#include + +#ifdef FLUTTER_PLUGIN_IMPL +#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) +#else +#define FLUTTER_PLUGIN_EXPORT +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +FLUTTER_PLUGIN_EXPORT void TizenWindowManagerPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // FLUTTER_PLUGIN_TIZEN_WINDOW_MANAGER_PLUGIN_H_ diff --git a/packages/tizen_window_manager/tizen/project_def.prop b/packages/tizen_window_manager/tizen/project_def.prop new file mode 100644 index 000000000..5e44bab53 --- /dev/null +++ b/packages/tizen_window_manager/tizen/project_def.prop @@ -0,0 +1,20 @@ +# See https://docs.tizen.org/application/tizen-studio/native-tools/project-conversion +# for details. + +APPNAME = tizen_window_manager_plugin +type = staticLib +profile = common-5.5 + +# Source files +USER_SRCS += src/*.cc + +# User defines +USER_DEFS = +USER_UNDEFS = +USER_CPP_DEFS = FLUTTER_PLUGIN_IMPL +USER_CPP_UNDEFS = + +# User includes +USER_INC_DIRS = inc src +USER_INC_FILES = +USER_CPP_INC_FILES = diff --git a/packages/tizen_window_manager/tizen/src/ecore_wl2_window_proxy.cc b/packages/tizen_window_manager/tizen/src/ecore_wl2_window_proxy.cc new file mode 100644 index 000000000..f35fe75c1 --- /dev/null +++ b/packages/tizen_window_manager/tizen/src/ecore_wl2_window_proxy.cc @@ -0,0 +1,79 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ecore_wl2_window_proxy.h" + +#include + +#include "log.h" + +typedef void (*FuncEcoreWl2WindowGeometryGet)(void *window, int *x, int *y, + int *width, int *height); +typedef void (*FuncEcoreWl2WindowActivate)(void *window); +typedef void (*FuncEcoreWl2WindowRaise)(void *window); +typedef void (*FuncEcoreWl2WindowLower)(void *window); + +EcoreWl2WindowProxy::EcoreWl2WindowProxy() { + ecore_wl2_window_handle_ = dlopen("libecore_wl2.so.1", RTLD_LAZY); + if (ecore_wl2_window_handle_ == nullptr) { + LOG_ERROR("Failed to open ecore wl2."); + } +} + +EcoreWl2WindowProxy::~EcoreWl2WindowProxy() { + if (ecore_wl2_window_handle_) { + dlclose(ecore_wl2_window_handle_); + ecore_wl2_window_handle_ = nullptr; + } +} + +void EcoreWl2WindowProxy::ecore_wl2_window_geometry_get(void *window, int *x, + int *y, int *width, + int *height) { + if (!ecore_wl2_window_handle_) { + LOG_ERROR("ecore_wl2_window_handle_ not valid"); + return; + } + + FuncEcoreWl2WindowGeometryGet ecore_wl2_window_geometry_get = + reinterpret_cast( + dlsym(ecore_wl2_window_handle_, "ecore_wl2_window_geometry_get")); + if (!ecore_wl2_window_geometry_get) { + LOG_ERROR("Fail to find ecore_wl2_window_geometry_get."); + return; + } + ecore_wl2_window_geometry_get(window, x, y, width, height); +} + +void EcoreWl2WindowProxy::ecore_wl2_window_activate(void *window) { + if (!ecore_wl2_window_handle_) { + LOG_ERROR("ecore_wl2_window_handle_ not valid"); + return; + } + + FuncEcoreWl2WindowActivate ecore_wl2_window_activate = + reinterpret_cast( + dlsym(ecore_wl2_window_handle_, "ecore_wl2_window_activate")); + if (!ecore_wl2_window_activate) { + LOG_ERROR("Fail to find ecore_wl2_window_activate."); + return; + } + ecore_wl2_window_activate(window); +} + +void EcoreWl2WindowProxy::ecore_wl2_window_lower(void *window) { + if (!ecore_wl2_window_handle_) { + LOG_ERROR("ecore_wl2_window_handle_ not valid"); + return; + } + + FuncEcoreWl2WindowLower ecore_wl2_window_lower = + reinterpret_cast( + dlsym(ecore_wl2_window_handle_, "ecore_wl2_window_lower")); + if (!ecore_wl2_window_lower) { + LOG_ERROR("Fail to find ecore_wl2_window_lower."); + return; + } + ecore_wl2_window_lower(window); +} diff --git a/packages/tizen_window_manager/tizen/src/ecore_wl2_window_proxy.h b/packages/tizen_window_manager/tizen/src/ecore_wl2_window_proxy.h new file mode 100644 index 000000000..0eed95ebf --- /dev/null +++ b/packages/tizen_window_manager/tizen/src/ecore_wl2_window_proxy.h @@ -0,0 +1,22 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_PLUGIN_ECORE_WL2_WINDOW_PROXY_H_ +#define FLUTTER_PLUGIN_ECORE_WL2_WINDOW_PROXY_H_ + +class EcoreWl2WindowProxy { + public: + EcoreWl2WindowProxy(); + ~EcoreWl2WindowProxy(); + void ecore_wl2_window_geometry_get(void *window, int *x, int *y, int *width, + int *height); + + void ecore_wl2_window_activate(void *window); + void ecore_wl2_window_lower(void *window); + + private: + void *ecore_wl2_window_handle_ = nullptr; +}; + +#endif // FLUTTER_PLUGIN_ECORE_WL2_WINDOW_PROXY_H_ diff --git a/packages/tizen_window_manager/tizen/src/log.h b/packages/tizen_window_manager/tizen/src/log.h new file mode 100644 index 000000000..6e7002473 --- /dev/null +++ b/packages/tizen_window_manager/tizen/src/log.h @@ -0,0 +1,28 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef __LOG_H__ +#define __LOG_H__ + +#include + +#ifdef LOG_TAG +#undef LOG_TAG +#endif +#define LOG_TAG "TizenWindowManagerPlugin" + +#ifndef __MODULE__ +#define __MODULE__ strrchr("/" __FILE__, '/') + 1 +#endif + +#define LOG(prio, fmt, arg...) \ + dlog_print(prio, LOG_TAG, "%s: %s(%d) > " fmt, __MODULE__, __func__, \ + __LINE__, ##arg) + +#define LOG_DEBUG(fmt, args...) LOG(DLOG_DEBUG, fmt, ##args) +#define LOG_INFO(fmt, args...) LOG(DLOG_INFO, fmt, ##args) +#define LOG_WARN(fmt, args...) LOG(DLOG_WARN, fmt, ##args) +#define LOG_ERROR(fmt, args...) LOG(DLOG_ERROR, fmt, ##args) + +#endif // __LOG_H__ diff --git a/packages/tizen_window_manager/tizen/src/tizen_window_manager.cc b/packages/tizen_window_manager/tizen/src/tizen_window_manager.cc new file mode 100644 index 000000000..d4ed5b57a --- /dev/null +++ b/packages/tizen_window_manager/tizen/src/tizen_window_manager.cc @@ -0,0 +1,58 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tizen_window_manager.h" + +#include +#include + +#include + +#include "ecore_wl2_window_proxy.h" +#include "log.h" + +TizenWindowManager::TizenWindowManager(void* handle) + : window_handle_(handle), proxy_(std::make_unique()) {} + +TizenWindowManager::~TizenWindowManager() {} + +void TizenWindowManager::Activate() { + if (!window_handle_) { + LOG_ERROR("Window handle is null"); + } + + proxy_->ecore_wl2_window_activate(window_handle_); +} + +void TizenWindowManager::Lower() { + if (!window_handle_) { + LOG_ERROR("Window handle is null"); + } + + proxy_->ecore_wl2_window_lower(window_handle_); +} + +flutter::EncodableMap TizenWindowManager::GetGeometry() { + flutter::EncodableMap geometry; + + if (!window_handle_) { + LOG_ERROR("Window handle is null"); + geometry[flutter::EncodableValue("x")] = flutter::EncodableValue(0); + geometry[flutter::EncodableValue("y")] = flutter::EncodableValue(0); + geometry[flutter::EncodableValue("width")] = flutter::EncodableValue(0); + geometry[flutter::EncodableValue("height")] = flutter::EncodableValue(0); + return geometry; + } + + int x, y, width, height; + proxy_->ecore_wl2_window_geometry_get(window_handle_, &x, &y, &width, + &height); + + geometry[flutter::EncodableValue("x")] = flutter::EncodableValue(x); + geometry[flutter::EncodableValue("y")] = flutter::EncodableValue(y); + geometry[flutter::EncodableValue("width")] = flutter::EncodableValue(width); + geometry[flutter::EncodableValue("height")] = flutter::EncodableValue(height); + + return geometry; +} diff --git a/packages/tizen_window_manager/tizen/src/tizen_window_manager.h b/packages/tizen_window_manager/tizen/src/tizen_window_manager.h new file mode 100644 index 000000000..0f3475eea --- /dev/null +++ b/packages/tizen_window_manager/tizen/src/tizen_window_manager.h @@ -0,0 +1,29 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_PLUGIN_TIZEN_WINDOW_MANAGER_H_ +#define FLUTTER_PLUGIN_TIZEN_WINDOW_MANAGER_H_ + +#include +#include + +#include + +class EcoreWl2WindowProxy; + +class TizenWindowManager { + public: + explicit TizenWindowManager(void* handle); + virtual ~TizenWindowManager(); + + void Activate(); + void Lower(); + flutter::EncodableMap GetGeometry(); + + private: + void* window_handle_; + std::unique_ptr proxy_; +}; + +#endif // FLUTTER_PLUGIN_TIZEN_WINDOW_MANAGER_H_ diff --git a/packages/tizen_window_manager/tizen/src/tizen_window_manager_plugin.cc b/packages/tizen_window_manager/tizen/src/tizen_window_manager_plugin.cc new file mode 100644 index 000000000..4ba8c98e1 --- /dev/null +++ b/packages/tizen_window_manager/tizen/src/tizen_window_manager_plugin.cc @@ -0,0 +1,86 @@ +// Copyright 2025 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tizen_window_manager_plugin.h" + +#include +#include +#include +#include + +#include + +#include "log.h" +#include "tizen_window_manager.h" + +namespace { + +class TizenWindowManagerPlugin : public flutter::Plugin { + public: + static void RegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar_ref, + flutter::PluginRegistrar *plugin_registrar) { + auto channel = + std::make_unique>( + plugin_registrar->messenger(), "tizen/window_manager", + &flutter::StandardMethodCodec::GetInstance()); + + auto plugin = std::make_unique(registrar_ref); + + channel->SetMethodCallHandler( + [plugin_pointer = plugin.get()](const auto &call, auto result) { + plugin_pointer->HandleMethodCall(call, std::move(result)); + }); + + plugin_registrar->AddPlugin(std::move(plugin)); + } + + TizenWindowManagerPlugin(FlutterDesktopPluginRegistrarRef registrar_ref) { + FlutterDesktopViewRef flutter_view = + FlutterDesktopPluginRegistrarGetView(registrar_ref); + void *handle = FlutterDesktopViewGetNativeHandle(flutter_view); + if (!handle) { + LOG_ERROR("Fail to get native window handle."); + return; + } + window_manager_ = std::make_unique(handle); + } + + virtual ~TizenWindowManagerPlugin() {} + + private: + void HandleMethodCall( + const flutter::MethodCall &method_call, + std::unique_ptr> result) { + const auto &method_name = method_call.method_name(); + + if (!window_manager_) { + result->Error("No window handle."); + return; + } + + if (method_name == "activate") { + window_manager_->Activate(); + result->Success(); + } else if (method_name == "lower") { + window_manager_->Lower(); + result->Success(); + } else if (method_name == "getGeometry") { + result->Success(flutter::EncodableValue(window_manager_->GetGeometry())); + } else { + result->NotImplemented(); + } + } + + std::unique_ptr window_manager_ = nullptr; +}; + +} // namespace + +void TizenWindowManagerPluginRegisterWithRegistrar( + FlutterDesktopPluginRegistrarRef registrar) { + TizenWindowManagerPlugin::RegisterWithRegistrar( + registrar, flutter::PluginRegistrarManager::GetInstance() + ->GetRegistrar(registrar)); +}